Mit dem 74HC595 Steuerleitungen einsparen
Wenn wir uns das letzte Projekt "eine 7-Segment Anzeige ansteuern" noch einmal anschauen, dann fällt uns auf, dass wir für die 8 LEDs der 7-Segment-Anzeige 8 Steuerleitungen gebraucht haben. Das ist eine ganze Menge. Wenn wir jetzt 4 Anzeigen ansteuern wollten, etwa für eine Digitaluhr, bräuchten wir auf diese Weise 32 Leitungen. Damit kann nicht einmal der Raspberry Pi aufwarten.In diesem Projekt wollen wir der Steuerleitungsverschwendung Einhalt gebieten, doch wie?
Wir werden dazu die Eigenschaften des IC (steht für integrated circuit, integrierte Schaltung) mit der kryptischen Bezeichnung 74HC595 benutzen, vorzugsweise in der 74HC595N-Version, die in einem Gehäuse daher kommt, die sich gut in unser Breadboard stecken lässt. Es gibt auch noch andere Version, zum SMD-Löten oder als Mini-Variante.
Wollen wir 74HC595N einmal auseinandernehmen:
- 74 steht für eine ganze Baureihe von Chips, von denen der Hersteller Texas Instruments mal in den 1970ern mit dem Vertrieb begonnen hat. Ursprünglich waren das Logic-ICs wie logische Gatter (z. B. der 7400 ist ein 4-fach-NAND-Gatter), später kamen noch weitere, auch komplexere Schaltungen hinzu.
- HC steht für High Speed CMOS. Für diese Bauteile ist eine Versorgungspannung von 2 bis 6V vorgeschrieben. Mit den 3.3 V des Raspi liegen wir da gut.
- 595 ist die eigentliche Bezeichnung und gibt an, welche Schaltung vorliegt
- N sagt etwas über die Bauform, also das Gehäuse. In diesem Fall ein DIL-Gehäuse (Dual in-line package), ein IC mit 2 x 8 Beinchen
- Data in (DS): Diese Leitung setzen wir auf High für eine 1 und auf Low für eine 0, mit BCM 10 verbunden
- Shift (SHCP): Diese Leitung pulsen wir nach jedem Bit (Shift = nächstes Bit "reinschieben"), mit BCM 11 verbunden
- Store (STCP): Diese Leitung pulsen wir, wenn wir mit den 8 Bits fertig sind. Wir speichern sozusagen (store). Mit BCM 9 verbunden
An Pin 14 werden die Bits 10101010 (höchstwertiges Bit zuerst) an DS angeliefert. Nach jedem Bit erfolgt ein Shift. Der 74HC595 speichert dadurch das Bit. Nachdem alle 8 Bits angeliefert sind, erfolgt ein Store. Und schon verteilt der HC595 die Bits auf die Ausgänge Q0 bis Q7, schaltet sie also auf High. Hier können wir jetzt über Vorwiderstände unsere 8 LEDs der Segmentanzeige anschließen.
Die Funktionalität, die der '595 abbildet heißt auf englisch "8-bit serial-in/serial or parallel-out shift register with output latches; 3-state", auf gut deutsch und etwas flapsig ausgedrückt würde ich sie als "auf einer Leitung rein, auf 8 Leitungen raus" bezeichnen. "8 Bit Shift Register" ist die kürzere Bezeichnung und meint damit, dass 8 bits nacheinander am Eingang "eingeschoben" ("shift") werden.
Damit die Schaltung funktioniert, müssen wir am '595 noch etwas mehr verkabeln:
- VCC bekommt +3.3V (selbstredend)
- GND geht auf Masse (auch klar)
- MR (Master Reset) verbinden wir mit +3.3V. Hier wird ein HIGH erwartet als Normalzustand und ein LOW für einen Reset. Vergessen wir die Leitung, befindet sich der 595er im Dauer-Reset und tut rein gar nichts
- OE (Output Enable) verbinden wir mit Masse, dadurch erreichen wir ein definiertes LOW. Denn nur, wenn hier LOW anliegt, ist die Ausgabe aktiviert.
- Q7' (Serial Data Output): Hier kann ein zweiter 595er angeschlossen werden (an Pin Data Input DS). Alle Bits die ab dem Achten durch einen Shift "hinaus" geschoben werden, werden hier wieder ausgegeben und so an den nächsten 595 weitergesendet.
So kann man im Prinzip beliebig viele 595er hintereinander anschließen, beispielsweise vier, in die man dann 32 Bit schieben kann. Die vier 595er teilen sich die beiden Clock-Leitungen. Die ersten 8 Bit werden in den 1. 595er hineingeschoben. Die anderen drei 595er laufen mit und schieben Nullen, da an deren DS-Eingängen ja nichts anliegt. Wird das 9. Bit "rechts" in den ersten 595er hineingeschoben, fällt das 1. "links" wieder heraus, triggert den Q7' Ausgang des 1. 595, der den DS-Eingang des 2. 595 triggert, der das 9. Bit als "sein" 1. Bit aufnimmt. Beim 10. Bit stehen dann wieder die 8 niederwertigsten Bit im 1. 595 und 2 Bits ganz rechts im 2. 595. Ab dem 17. Bit werden dann Bits aus dem 2. 595 herausgeschoben und in den 3. 595er hinein. ab dem 25. Bit wird auch vom 3. in den 4. 595er geschoben. Nach 32 in den 1. 595er hinengeschobenen Bits sind alle 595er mit je 8 bit gefüllt. Es folgt ein Store-Pulse und die vier 595er schalten jeweils ihre acht Ausgänge auf High oder Low.
Damit lassen sich etwa vier 7-Segment-Anzeigen ansteuern, und das mit nur 3 Steuerleitungen.
Gegenüber dem letzten Projekt ist der HC595 in der Mitte hinzugekommen. Von der GPIO-Steckerleister geht die Daten- und die zwei Clock-Leitungen an die entsprechenden Pins des 595. An den 8 Ausgängen hängt jeweils ein Vorwiderstand, der zu den LEDs der Segmente der Anzeige führt. Insgesamt sieht die Schaltung jetzt komplizierter aus, aber wenn man bedenkt, dass das wertvollste die Leitungen am Pi sind, dann ist es den Aufwand wert, wenn man mal mehr als nur eine Segmentanzeige ansteuern muss.
Der GPIO-Port sieht jetzt wesentlich aufgeräumter aus.
Es sind jetzt nur noch 3 Steuerleitungen, GND und +3.3V belegt.
Der HC595 stellt das neue Zentrum der Schaltung dar.
An ihm laufen alle Leitungen zusammen.
Da muss man schon geschickt stecken, damit sich die ganzen Widerstände nicht gegenseitig im Weg stehen.
Der 7-Segment-Anzeige ist es egal, ob sie direkt oder über einen HC595 angesteuert wird.
Der Verkabeleungsaufwand bleibt hier gleich.
Hauptsache, die Vorwiderstände passen, dann fühlt sie sich wohl.
Da wir jetzt nicht mehr die LEDs der Anzeige direkt ansteuern, sondern den HC595 muss der Code natürlich auch angepasst werden. Wie immer: zuerst der Code, dann zusätzliche Erklärungen...
# -*- encoding: utf-8 -*-
# (C) 2018 by Oliver Kuhlemann
# Bei Verwendung freue ich mich über Namensnennung,
# Quellenangabe und Verlinkung
# Quelle: http://cool-web.de/raspberry/
import RPi.GPIO as GPIO # Funktionen für die GPIO-Ansteuerung laden
from time import sleep # damit müssen wir nur noch sleep() statt time.sleep schreiben
from sys import exit # um das Programm ggf. vorzeitg zu beenden
GPIO.setmode(GPIO.BCM) # die GPIO-Pins im BCM-Modus ansprechen
pinData=10
pinClockStore=9
pinClockShift=11
GPIO.setup(pinData, GPIO.OUT)
GPIO.setup(pinClockStore, GPIO.OUT)
GPIO.setup(pinClockShift, GPIO.OUT)
clockPulseLen=.00001 # als HighSpeed-Baustein verträgt der HC595 auch kurzer Impulse
# Dictionary: welche Ziffer/Buchstabe -> welche Segmente sind an
# Dictionary: welche Ziffer/Buchstabe -> welche Segmente sind an
chars = {" ":"", "0":"ABCDEF", "1":"BC", "2":"ABDEG", "3":"ABCDG", "4":"BCFG", "5":"ACDFG", "6":"ACDEFG", "7":"ABC", "8":"ABCDEFG", "9":"ABCDFG", "A":"ABCEFG", "B":"CDEFG", "C":"ADEF", "D":"BCDEG", "E":"ADEFG", "F":"AEFG", "G":"ACDEF", "H":"BCEFG", "I":"AE", "J":"ACD", "K":"ACEFG", "L":"DEF", "M":"ACEG", "N":"ABCEF", "O":"CDEG", "P":"ABEFG", "Q":"ABCFG", "R":"EG", "S":"ACDF", "T":"DEFG", "U":"BCDEF", "V":"BEFG", "W":"BDFG", "X":"CE", "Y":"BCDFG", "Z":"ABDE"}
def storeTick(): # einen Puls auf die ClockStore-Leitung schicken
GPIO.output(pinClockStore, GPIO.HIGH)
sleep (clockPulseLen)
GPIO.output(pinClockStore, GPIO.LOW)
sleep (clockPulseLen)
def shiftTick(): # einen Puls auf die ClockShift-Leitung schicken
GPIO.output(pinClockShift, GPIO.HIGH)
sleep (clockPulseLen)
GPIO.output(pinClockShift, GPIO.LOW)
sleep (clockPulseLen)
def showSegs (onSegs, dotState): # schaltet die als Buchstaben angegebenen Segmente ein
# angeschaltete Segmente vom höchstwertigen Bit abwärts pulsen
for seg in "GFEDCBA":
if onSegs.find(seg) > -1:
GPIO.output(pinData, GPIO.HIGH)
else:
GPIO.output(pinData, GPIO.LOW)
shiftTick() # nächstes Bit
# Der Punkt liegt auf Q0 des HC595, darum den immer zuerst pulsen
if dotState:
GPIO.output(pinData, GPIO.HIGH)
else:
GPIO.output(pinData, GPIO.LOW)
shiftTick() # nächstes Bit
storeTick() # alle Bits fertig. Speichern
def show(char, dotState): # das übergebene Zeichen auf der 7-Segment-Anzeige anzeigen
# segmente für char suchen
try:
onSegs = chars[char]
except KeyError:
print "Das Zeichen " + char + " ist nicht definiert."
return
showSegs (onSegs, dotState)
def say(word): # mehrere Zeichen wiedergeben
for char in word.upper():
show (char,0)
sleep (.5)
show (" ",0) # alles wieder aus
sleep (.1)
def test1(): # Test, ob alle Segmente funktionieren
showSegs ("ABCDEFG",1)
sleep (1)
show (" ",0)
def test2(): # alle Segmente der Reihe nach testen - Reihenfolge richtig? A-G und DP
for segment in "ABCDEFG":
showSegs (segment,0)
sleep (.5)
show (" ",1)
sleep (1)
show (" ",0)
def waitAnim(wdhl): # Round and round it goes.. Wdhl = wie oft
for wd in range(0,wdhl):
for segment in "ABCDEF":
showSegs (segment,0)
sleep (.1)
show (" ",0)
def waitAnim2(secs): # lässt den Punkt im Sekundentakt blinken
for i in range(0,secs):
show (" ",1)
sleep (.5)
show (" ",0)
sleep (.5)
def testZiffern():
for ziffer in "0123456789 ":
show (ziffer,1)
sleep (.5)
show (" ",0)
def testBuchstaben():
for bst in "ABCDEFGHIJKLMNOPQRSTUVWXYZ ":
show (bst,0)
sleep (.5)
# --- Ende Funktionen --- Beginn Hauptprogramm -------------------------------------------
test1()
sleep (1)
test2()
sleep (1)
waitAnim(5)
sleep (1)
waitAnim2(5)
sleep (1)
testZiffern()
sleep (1)
testBuchstaben()
sleep (1)
say ("Hallo Welt ")
sleep (1)
GPIO.cleanup() # Programm sauber verlassen und Ressourcen wieder freigeben
Statt der 8 direkten Steuerleitungen gibt es jetzt 3: pinData, pinClockStore und pinClockShift. Die sind für das zuständig, was der Name schon sagt. Wie genau die anzusprechen werden später in der entsprechenden Funktion. clockPulseLen gibt die Länge eines Pulses für das Timing (Clocking an). Hier verträgt der HC595 auch sehr kleine Werte. Ich habe fast das Gefühl, kleiner als das der Raspi den Befehl sleep() ausführen kann.Die Funktionen storeTick() und shiftTick() sind neu. Sie machen nichts anderes, als einen High-Puls und dann einen Low-Pulse der Länge clockPulseLen auf die benannte Leitung zu schicken.
_
| |_
Auch die Funktion showSegs (onSegs, dotState) ist neu. Sie ist die Kernfunktion und schaltet die als Buchstaben angegebenen Segmente ein und ggf. auch den Dezimalpunkt (dotState=1). Dazu wird in onSegs ein String übergeben mit den einzuschaltenden Segmenten.Der HC595 erwartet das höchstwertige Bit zuerst, also das für Q7 bzw. das Segment G. Also wird in der Reihenfolge "GFEDCBA" abgefragt. Kommt der abgefragte Buchstabe im String vor, dann soll das Segment eingeschaltet werden und es wird auf der Datenleitung ein High gesetzt (aus entsprechen Low). Während sich die Datenleitung noch auf High befindet wird ein Puls auf der Shift-Leitung (shiftTick()) gesendet.
Das zuletzt zu sendende Bit ist das für den Dezimalpunkt, der Bit 0, also das niederwertigste Bit darstellt. Auch hier die Datenleitung auf High (Punkt an) oder Low (Punkt aus) und ein Shift gepulst (shiftTick()).
Dann sind alle Bits durch und es wird einmal auf der Store-Leitung (storeTick())gepulst als Zeichen, dass wir fertig sind und der HC595 nun das Zeichen darstellen soll.
Am Beispiel der Ziffer 7 wollen wir die Signale, die an den 595 geschickt werden, einmal durchgehen. Bei der 7 sind die drei Segment A, B und C an. Folgendes kommt beim HC595 an:
... G H F E D C B A ...
_ _ _ _ _ _ _ _
_______| |_| |_| |_| |_| |_| |_| |_| |____________ Shift-Clock
_ _ _
___________________________| |_| |_| |____________ Data
_
_______________________________________| |________ Store-Clock
Das weiß der HC595 zu interpretieren und schaltet die entsprechenden Ausgänge auf High, sobald er das Store-Signal empfangen hat.Der Rest der schon bekannten Funktionen wurde an das neue Verhalten angepasst. Es ist jetzt auch nicht mehr möglich, den Dezimalpunkt gesondert anzusprechen. Entweder man spendiert ihm doch noch eine extra Leitung, die dann direkt verbunden wird oder man behilft sich, indem man das bereits dargestellte Zeichen noch einmal per show(char, dotState) sendet, aber dabei den dotState entsprechend setzt.
Es gibt übrigens auch Module, die bereits vier 7-Segment-Anzeigen in sich vereinen. Diese haben nur 12 Anschluss-Pins. Wie man diese anspricht, im nächsten Projekt.