// eine 7-Segment-LED-Anzeige ansteuern mithilfe eines 74HC595 (kurz: '595)
#include "debounce.h"
#define SER 1
#define SCK 2
#define RCK 3
#define RAUF 5
#define RUNTER 14
// SER -> Serieller Datenpin (Bit für Bit Wert anlegen, beginnend mit MSB)
// SCK -> Takteingang zum Schieben um 1 Bit (danach neues Bit auf SER geben)
// RCK -> Takteingang zum Speichern (vorher reingeschobene Bits zum Ausgang geben)
// Der !Reset-Eingang (Chip-Pin 10) liegt dauerhaft auf (+), also auf 5V oder auf 3V3.
// Der !OE-Eingang (Chip-Pin 13) liegt dauerhaft auf (-), also auf GND.
/*
Anschlussübersicht für dieses Programm:
a Vcc Q0 SER !OE RCK SCK !CL Q7⇨
——————— Segment- a - Q6 ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯
│ │ Pins: b - Q5 16 15 14 13 12 11 10 9
f │ │ b c - Q4 │ │
│ │ d - Q3 ) 74HC595 │
——————— e - Q2 │ │
│ g │ f - Q1 └1 2 3 4 5 6 7 8
e │ │ c g - Q0 ♯ ♯ ♯ ♯ ♯ ♯ ♯ ♯
│ │ gemeinsame Anode an (+) Q1 Q2 Q3 Q4 Q5 Q6 Q7 GND
———————
d
Datenblatt-Infos zum '595:
"Die Dateneingabe erfolgt seriell über den Eingang SER. Bei jedem LH-Übergang (positive
Flanke) des Taktes an SCk (Shift Register Clock) werden die Informationen von Pin 14
übernommen und die im Schieberegister bereits befindlichen Daten um genau eine Stufe
weitergeschoben.
Am Anschluss 9 (Q7⇨) können die Daten seriell entnommen werden (-> Kaskadierungs möglichkeit).
Der asynchrone Löschanschluss !CL (shift register CLear) liegt normalerweise auf High. Wird
er auf Low gebracht, gehen alle Stufen des Schieberegisters auf Null.
Wenn am Takteingang für den Ausgangs-Zwischenspeicher RCK (Register ClocK) ein
LH-Übergang des Taktes anliegt, werden die im Schieberegister befindlichen Daten
in den 8-Bit-Zwischenspeicher übernommen (Latch).
Die parallelen Daten liegen an den Ausgängen Q0──Q7, wenn der Anschluss für die
Ausgangsfreigabe !OE (Output Enable) auf Low liegt. Legt man diesen Anschluss auf
High, gehen alle Ausgänge in den hochohmigen Zustand.
Man kann beide Takteingängen (SCK und RCK) miteinander verbinden. Dann wird der Inhalt
des Schieberegisters immer um einen Taktimpuls später in den Ausgangs-Speicher übernommen.
(Man muss also nach Übertragung von acht Bits noch einen neunten Takt senden.)" */
/* Beispielbelegung einer Siebensegmentanzeige:
g f * a b
| | | | |
╭───────────╮
│ ━━━━━ │
│ ┃ ┃ │
│ ┃ ┃ │https://wokwi.com/projects/399882519446519809
│ ━━━━━ │
│ ┃ ┃ │
│ ┃ ┃ │
│ ━━━━━ │
╰───────────╯
| | | | |
e d * c dp * = gemeinsame Anode (+) oder gemeinsame Kathode (-)
Es reicht, einen dieser Pins anzuschließen.
*/
// 0. Displaytyp festlegen (CA für gemeinsame Anode, CC für gemeinsame Kathode):
#define CA
// 1. globale Variablen -> Ausgabewert und Segmentzuordnungen:
byte wert; // was ausgegeben werden soll
byte zuordnung[10]; // Zuordnung der Segmente zu den Ziffern ( ⇉ "Was leuchtet wann?" )
// wird in der setup() passend eingerichtet für die Ziffern 0 bis 9
//3. Hilfshack für evtl. nötige Negierungen später (nämlich bei gemeinsamer Anode):
#ifdef CA
#define INV ~
#else
#define INV
#endif
//4. Standardfunktionen setup() und loop():
void setup() {
// Hinweis: für gemeinsame Anode müssen die Segmentdefinitionen mit "~" negiert werden -- hier via "ß"!
// Hier für gemeinsame Kathode:
// -abcdefg (siehe oben) 0 ⁼ aus 1 ⁼ an
// |||||||
zuordnung[0] = INV 0b01111110; // 🯰 -> nur g leuchtet bei 0 nicht
zuordnung[1] = INV 0b00110000; // 🯱 -> nur b und c leuchten bei 1
zuordnung[2] = INV 0b01101101; // 🯲 -- und so fort für die anderen Ziffern
zuordnung[3] = INV 0b01111001; // 🯳
zuordnung[4] = INV 0b00110011; // 🯴
zuordnung[5] = INV 0b01011011; // 🯵
zuordnung[6] = INV 0b01011111; // 🯶
//zuordnung[7] = INV 0b01110010; // 🯷
zuordnung[7] = INV 0b01110000; // ̄⡇( 7 mit nur drei leuchtenden Segmenten )
zuordnung[8] = INV 0b01111111; // 🯸
zuordnung[9] = INV 0b01111011; // 🯹
// Initialisierung
pinMode(SER, OUTPUT); digitalWrite(SER, 0);
pinMode(SCK, OUTPUT); digitalWrite(SCK, 0);
pinMode(RCK, OUTPUT); digitalWrite(RCK, 0);
pinMode(RAUF,INPUT_PULLUP);
pinMode(RUNTER,INPUT_PULLUP);
wert = 0;
ausgabe();
}
// Beispielanwendung "Rauf- oder Runter-Klicken":
void loop() {
if (debounce(RAUF)) {
if (wert < 9) {
wert++;
ausgabe();
}
}
if (debounce(RUNTER)) {
if (wert > 0) {
wert--;
ausgabe();
}
}
delay(1); // macht die Simulation schneller
}
// 2. Ausgabefunktion -> Schieberegister mit Inhalt der Variablen wert laden:
void ausgabe() {
for ( int i = 7; i >= 0; i-- ) {// hier kann 6 oder 7 da der 7 bit von zuordnung ist dp
digitalWrite(SER,bitRead(zuordnung[wert],i)); // Datenbit der Position i an SER anlegen
digitalWrite(SCK,1);digitalWrite(SCK,0); // SCK - Impuls senden
}
//shiftOut(SER, SCK, MSBFIRST, zuordnung[wert]);
digitalWrite(RCK,1);digitalWrite(RCK,0); // abschließend RCK - Impuls senden
}