Matrixanzeigen - LEDs in Reih und Glied
Materialbedarf
Grundlagen
Mit
7-Segment-Anzeigen kann man schon recht bequem Ziffern und auch einige
Symbole darstellen. Wer mit Mikrokontrollern arbeitet wird schnell etwas
mehr brauchen als nur die reine Ausgabe von Zahlen. Eine Möglichkeit sind so genannte Punktmatrix-Anzeigen. Diese Anzeigen haben ein ähnliches Gehäuse wie 7-Segment-Anzeigen. Diese besitzen aber mehr Anschlüsse wodurch es nötig ist, die Anzeige 'Quer' auf dem Steckboard unterzubringen. Ausgestattet sind Punktmatrix-Anzeigen mit einer ganzen Menge von LEDs welche in Spalten und Reihen angeordnet sind. Verdrahtet sind die einzelnen Leuchtdioden in einer Matrixstruktur. Hierdurch muss man nicht jede LED einzeln verdrahten. |
In
der Abbildung sieht man, wie so eine Matrixstruktur aussieht. Je nachdem
welche LED man ansteuern möchte, muss die Betriebsspannung an die
entsprechende Zeile, hier gekennzeichnet durch R1 bis R7, und der
Minuspol an die entsprechende Spalte, erkennbar durch C1 bis C5,
angelegt werden. Natürlich darf der Vorwiderstand nicht vergessen
werden. Soll z.B. die Leuchtdiode in Zeile 3 und Spalte 4 aufleuchten, muss die LED-Betriebsspannung an R3 und der Minuspol an C4 angeschlossen werden. Es ist aber ebenso möglich gleich eine komplette Spalte anzusteuern. Hierzu muss man nur die gewünschten Zeilen mit der LED-Betriebsspannung versorgen. Wird jetzt an der gewünschten Spalte das Nullpotential angeschlossen, leuchtet die gewünschte Spalte mit den angesteuerten LEDs auf. Will man nun ein Bild auf dem Display darstellen, muss dafür gesorgt werden, dass die entsprechenden Zeilen mit den Bilddaten zu der dazugehörigen Spalte angelegt werden. Jetzt muss die Spalte kurze Zeit eingeschaltet werden. Anschließend werden die Daten für die nächste Spalte angelegt. Dann wieder die entsprechende Spalte aktiviert usw. Wird dies schnell genug gemacht, sieht der Anwender ein entsprechendes Bild. |
Der AVR steuert die Matrix
Soll
ein Matrixdisplay am AVR angeschlossen werden, so sollten die Zeilen an
einem Port und die Spalten an einen anderen Port. Die Spalten können
wir, aufgrund des größeren Stromes, nicht direkt mit dem AVR verbinden.
Hier setzen wir noch Transistoren als Leistungsverstärker ein. Als
Nebeneffekt reicht es nun aus, den entsprechenden Port auf '1' zu legen
und schon können die entsprechenden LEDs in der Spalte aufleuchten. Will man nun in einer Spalte eine oder mehrere LEDs zum leuchten bringen, muss man das entsprechende LED-Bild an Port D anlegen. In welcher Spalte die Leuchtdioden sich nun befinden, welche aufleuchten sollen, bestimmen die Pins in Port C. Port C Bit 0 steuert die erste Spalte, Port C Bit 1 die zweite Spalte usw. Soll also die erste Spalte angesteuert werden, so muss dem Port C den Wert 1 zugewiesen werden, um Bit 0 des Ports zu aktivieren. Wird Port C den Wert 2 zugewiesen, schaltet der AVR die zweite Spalte frei. Bei einer 4 am Port wird die 3. Spalte aktiv. Usw. Testen wir aber erst einmal die Ansteuerung einer einzigen Spalte mit folgendem kleinen Programm: |
$regfile "m8def.dat" Stop |
Nach dem Übertragen und dem Neustart des AVRs erscheinen auf dem Display mehrere Punkte in der 2. Spalte. Wer den Befehl 'Portd=&B00101100' mit dem Bild des Displays vergleicht, wird eine Übereinstimmung feststellen. Wenn man die Bitfolge im Befehl ändert und dann das Programm zum ATMega überträgt, merkt man schnell wie die einzelnen Bitbilder der aktuell ausgewählten Spalte aufgebaut ist. |
In
der Grafik wird die Zuordnung des Ports zu den einzelnen Zeilen des
Displays dargestellt. Um einige Sicherheit mit dem Ansteuern der
einzelnen Bitmuster zu bekommen, sollte man verschiedene Muster
ausprobieren. Das Ansteuern einzelner Spalten des Displays mag ja schon recht interessant sein aber wirklich sinnvoll ist dies noch nicht. Um das Display komplett zu verwenden, sollten schon alle Spalten angesteuert werden. Wir können aber immer nur mit einer Spalte zur Zeit arbeiten. Wie steuert man da das ganze Display an? Hierzu schreiben wir unser Programm ein wenig um: |
$regfile "m8def.dat" $crystal = 1000000 Config Portd = Output Config Portc = Output Do Portc = 1 : Portd = &B00100000 : Waitms 1 Portc = 2 : Portd = &B00010000 : Waitms 1 Portc = 4 : Portd = &B00001000 : Waitms 1 Portc = 8 : Portd = &B00000100 : Waitms 1 Portc = 16 : Portd = &B00000010 : Waitms 1 Loop |
Do Portc = 1 : Portd = &B00100000 Portc = 2 : Portd = &B00010000 Portc = 4 : Portd = &B00001000 Portc = 8 : Portd = &B00000100 Portc = 16 : Portd = &B00000010 Loop |
$regfile "m8def.dat" $crystal = 1000000 '### Variablendefination für Zeichengenerator Dim Dispcol As Byte Dim Dispcnt As Byte Dim Bitbld As Byte Dim Chradr As Word Dim Charnr As Byte '### Konfiguration für Display Config Portd = Output Config Portc = Output Dispcnt = 0 : Dispcol = 1 : Charnr = 65 '### Hauptprogramm: Zeichenanzeige Do Chradr = Charnr - 32 : Chradr = Chradr * 5 : Chradr = Chradr + Dispcnt Bitbld = Lookup(chradr , Chartab) Portc = Dispcol : Portd = Bitbld : Waitms 1 Incr Dispcnt : Dispcnt = Dispcnt Mod 5 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 Loop '### Zeichensatztabelle Chartab: Data &B00000000 , &B00000000 , &B00000000 , &B00000000 , &B00000000 ' 32=spc Data &B00000000 , &B00000000 , &B01011111 , &B00000000 , &B00000000 ' 33=! Data &B00000000 , &B00000011 , &B00000000 , &B00000011 , &B00000000 ' 34=Bracket Data &B00010100 , &B01111111 , &B00010100 , &B01111111 , &B00010100 ' 35=# Data &B00100110 , &B01001001 , &B01111111 , &B01001001 , &B00110010 ' 36=$ Data &B00100011 , &B00010011 , &B00001000 , &B01100100 , &B01100010 ' 37=% Data &B00110010 , &B01001101 , &B01010010 , &B00100000 , &B00010000 ' 38=& Data &B00000000 , &B00000000 , &B00000011 , &B00000000 , &B00000000 ' 39=' Data &B00000000 , &B00011100 , &B00100010 , &B01000001 , &B00000000 ' 40=( Data &B00000000 , &B01000001 , &B00100010 , &B00011100 , &B00000000 ' 41=) Data &B00101010 , &B00011100 , &B00111110 , &B00011100 , &B00101010 ' 42=* Data &B00001000 , &B00001000 , &B00111110 , &B00001000 , &B00001000 ' 43=+ Data &B00000000 , &B01011000 , &B00111000 , &B00000000 , &B00000000 ' 44=, Data &B00001000 , &B00001000 , &B00001000 , &B00001000 , &B00001000 ' 45=- Data &B00000000 , &B01100000 , &B01100000 , &B00000000 , &B00000000 ' 46=. Data &B00100000 , &B00010000 , &B00001000 , &B00000100 , &B00000010 ' 47=/ Data &B00111110 , &B01010001 , &B01001001 , &B01000101 , &B00111110 ' 48=0 Data &B00000000 , &B01000010 , &B01111111 , &B01000000 , &B00000000 ' 49=1 Data &B01000010 , &B01100001 , &B01010001 , &B01001001 , &B01000110 ' 50=2 Data &B00100010 , &B01000001 , &B01000001 , &B01001001 , &B00110110 ' 51=3 Data &B00011000 , &B00010100 , &B00010010 , &B01111111 , &B00010000 ' 52=4 Data &B01001111 , &B01001001 , &B01001001 , &B01001001 , &B00110001 ' 53=5 Data &B00111110 , &B01001001 , &B01001001 , &B01001001 , &B00110000 ' 54=6 Data &B00000001 , &B00000001 , &B01110001 , &B00001001 , &B00000111 ' 55=7 Data &B00110110 , &B01001001 , &B01001001 , &B01001001 , &B00110110 ' 56=8 Data &B00000110 , &B01001001 , &B01001001 , &B01001001 , &B00111110 ' 57=9 Data &B00000000 , &B00110110 , &B00110110 , &B00000000 , &B00000000 ' 58=: Data &B00000000 , &B01011011 , &B00111011 , &B00000000 , &B00000000 ' 59=; Data &B00001000 , &B00010100 , &B00100010 , &B01000001 , &B00000000 ' 60=< Data &B00010100 , &B00010100 , &B00010100 , &B00010100 , &B00010100 ' 61== Data &B00000000 , &B01000001 , &B00100010 , &B00010100 , &B00001000 ' 62=> Data &B00000010 , &B00000001 , &B01010001 , &B00001001 , &B00000110 ' 63=? Data &B00111110 , &B01000001 , &B01011001 , &B01010001 , &B00011110 ' 64=@ Data &B01111100 , &B00010010 , &B00010001 , &B00010010 , &B01111100 ' 65=A Data &B01111111 , &B01001001 , &B01001001 , &B01001001 , &B00110110 ' 66=B Data &B00111110 , &B01000001 , &B01000001 , &B01000001 , &B00100010 ' 67=C Data &B01111111 , &B01000001 , &B01000001 , &B01000001 , &B00111110 ' 68=D Data &B01111111 , &B01001001 , &B01001001 , &B01001001 , &B01000001 ' 69=E Data &B01111111 , &B00001001 , &B00001001 , &B00001001 , &B00000001 ' 70=F Data &B00111110 , &B01000001 , &B01001001 , &B01001001 , &B01111001 ' 71=G Data &B01111111 , &B00001000 , &B00001000 , &B00001000 , &B01111111 ' 72=H Data &B00000000 , &B01000001 , &B01111111 , &B01000001 , &B00000000 ' 73=I Data &B00100001 , &B01000001 , &B01000001 , &B01000001 , &B00111111 ' 74=J Data &B01111111 , &B00001000 , &B00010100 , &B00100010 , &B01000001 ' 75=K Data &B01111111 , &B01000000 , &B01000000 , &B01000000 , &B01000000 ' 76=L Data &B01111111 , &B00000010 , &B00000100 , &B00000010 , &B01111111 ' 77=M Data &B01111111 , &B00000100 , &B00001000 , &B00010000 , &B01111111 ' 78=N Data &B00111110 , &B01000001 , &B01000001 , &B01000001 , &B00111110 ' 79=O Data &B01111111 , &B00001001 , &B00001001 , &B00001001 , &B00000110 ' 80=P Data &B00111110 , &B01000001 , &B01000001 , &B01100001 , &B01111110 ' 81=Q Data &B01111111 , &B00001001 , &B00011001 , &B00101001 , &B01000110 ' 82=R Data &B00100110 , &B01001001 , &B01001001 , &B01001001 , &B00110010 ' 83=S Data &B00000001 , &B00000001 , &B01111111 , &B00000001 , &B00000001 ' 84=T Data &B00111111 , &B01000000 , &B01000000 , &B01000000 , &B00111111 ' 85=U Data &B00011111 , &B00100000 , &B01000000 , &B00100000 , &B00011111 ' 86=V Data &B01111111 , &B00100000 , &B00010000 , &B00100000 , &B01111111 ' 87=W Data &B01100011 , &B00010100 , &B00001000 , &B00010100 , &B01100011 ' 88=X Data &B00000001 , &B00000010 , &B01111100 , &B00000010 , &B00000001 ' 89=Y Data &B01100001 , &B01010001 , &B01001001 , &B01000101 , &B01000011 ' 90=Z Data &B00000000 , &B00000000 , &B01111111 , &B01000001 , &B00000000 ' 91=[ Data &B00000010 , &B00000100 , &B00001000 , &B00010000 , &B00100000 ' 92=\ Data &B00000000 , &B01000001 , &B01111111 , &B00000000 , &B00000000 ' 93=] Data &B00000100 , &B00000010 , &B00000001 , &B00000010 , &B00000100 ' 94=^ Data &B01000000 , &B01000000 , &B01000000 , &B01000000 , &B01000000 ' 95=_ Data &B00000000 , &B00000000 , &B00000001 , &B00000010 , &B00000000 ' 96=` Data &B00100000 , &B01010100 , &B01010100 , &B01010100 , &B00111000 ' 97=a Data &B01111111 , &B01000100 , &B01000100 , &B01000100 , &B00111000 ' 98=b Data &B00111000 , &B01000100 , &B01000100 , &B01000100 , &B00101000 ' 99=c Data &B00111000 , &B01000100 , &B01000100 , &B01000100 , &B01111111 '100=d Data &B00111000 , &B01010100 , &B01010100 , &B01010100 , &B00001000 '101=e Data &B00000000 , &B00000100 , &B01111110 , &B00000101 , &B00000000 '102=f Data &B00001000 , &B01010100 , &B01010100 , &B01010100 , &B00111000 '103=g Data &B01111111 , &B00000100 , &B00000100 , &B00000100 , &B01111000 '104=h Data &B00000000 , &B00000000 , &B01111101 , &B00000000 , &B00000000 '105=i Data &B00000000 , &B00100000 , &B01000000 , &B00111101 , &B00000000 '106=j Data &B01111111 , &B00010000 , &B00010000 , &B00101000 , &B01000100 '107=k Data &B00000000 , &B00111111 , &B01000000 , &B00000000 , &B00000000 '108=l Data &B01111100 , &B00000100 , &B01111100 , &B00000100 , &B01111000 '109=m Data &B01111100 , &B00000100 , &B00000100 , &B00000100 , &B01111000 '110=n Data &B00111000 , &B01000100 , &B01000100 , &B01000100 , &B00111000 '111=o Data &B01111100 , &B00010100 , &B00010100 , &B00010100 , &B00001000 '112=p Data &B00001000 , &B00010100 , &B00010100 , &B00010100 , &B01111100 '113=q Data &B01111100 , &B00001000 , &B00000100 , &B00000100 , &B00001000 '114=r Data &B00001000 , &B01010100 , &B01010100 , &B01010100 , &B00100000 '115=s Data &B00000000 , &B00000100 , &B00111111 , &B01000100 , &B00000000 '116=t Data &B00111100 , &B01000000 , &B01000000 , &B01000000 , &B01111100 '117=u Data &B00011100 , &B00100000 , &B01000000 , &B00100000 , &B00011100 '118=v Data &B00111100 , &B01000000 , &B00111100 , &B01000000 , &B00111100 '119=w Data &B01000100 , &B00101000 , &B00010000 , &B00101000 , &B01000100 '120=x Data &B00000100 , &B00001000 , &B01110000 , &B00001000 , &B00000100 '121=y Data &B01000100 , &B01100100 , &B01010100 , &B01001100 , &B01000100 '122=z Data &B00000000 , &B00001000 , &B00110110 , &B01000001 , &B00000000 '123={ Data &B00000000 , &B00000000 , &B01110111 , &B00000000 , &B00000000 '124=| Data &B00000000 , &B01000001 , &B00110110 , &B00001000 , &B00000000 '125=} Data &B00001000 , &B00000100 , &B00001000 , &B00010000 , &B00001000 '126=~ Data &B00000000 , &B00000000 , &B00000000 , &B00000000 , &B00000000 '127=Del |
Bevor wir Symbole und Zeichen ausgeben können, werden diverse Variablen benötigt. Dies wird mit den Dim-Anweisungen erreicht:
Dim Dispcol As Byte Dim Dispcnt As Byte Dim Bitbld As Byte Dim Chradr As Word Dim Charnr As Byte |
Dispcol und Dispcnt werden für die Steuerung der der Zeilen und Spalten benötigt. Bitbld enthält das aktuell darzustellende Bitbild. Chradr wo sich das entsprechende Bitbild in der Tabelle befindet. Mit Hilfe von Charnr übergeben wir dem Programm die Nummer des gewünschten Zeichens.
Dispcnt = 0 : Dispcol = 1 : Charnr = 65 |
Damit die Ausgabe funktioniert müssen wir die Hilfsvariablen zur Displaysteuerung vorbelegen. Mit der Anweisung 'Charnr=65' legen wir fest, dass wir das 'A' auf dem Display sehen möchten. Da wir in der Tabelle die Ascii-Zeichen definiert haben, kann man entsprechend ein Ascii-Zeichen zuweisen. Welche Nummer zu welchem Zeichen gehört, kann man im Internet leicht heraus finden. In der Tabelle wurde es auch als Bemerkung hinter den entsprechenden Datenblock vermerkt.
Als nächstes tritt das Programm in eine Endlosschleife ein. Als erstes muss die Adresse der Daten der auszugebenden Spalte berechnet werden:
Chradr = Charnr - 32 : Chradr = Chradr * 5 : Chradr = Chradr + Dispcnt |
Als erstes wird von unserer gewünschten Zeichennummer 32 abgezogen. Dies ist notwendig da in der Ascii-Tabelle das erste sichtbare Zeichen mit der Zeichennummer 32 beginnt. Unsere Zeichentabelle aber mit Zeichennummer 0.
Da jedes Zeichen in der Zeichentabelle für jede Spalte 1 Datenbyte benötigt, müssen wir für jedes Zeichen mit 5 multiplizieren. Dies macht die Anweisung 'Chradr=Charadr*5'. Nun muss wir noch die endgültige Datenposition mit der aktuellen Spalte addieren um das richtige Datenbyte auslesen zu können.
Nun können wir das aktuelle Bitbild der aktuell aktiven Spalte auslesen. Dies tun wir mit:
Bitbld = Lookup(chradr , Chartab) |
Jetzt haben wir schon alle notwendigen Daten zusammen um die Anzeige mit der aktuellen Spalte und dem dazu gehörigen Bitbild anzusteuern:
Portc = Dispcol : Portd = Bitbld : Waitms 1 |
Diese Befehle dürften schon bekannt sein. In Dispcol ist der Wert für die aktuell geschaltete Spalte vorhanden. Bitbld enthält das Bitbild für die aktuelle Spalte.
Nun müssen wir dafür sorgen, dass die nächste Spalte angezeigt werden kann. Hierzu dienen die letzten beiden Zeilen in der Schleife:
Incr Dispcnt : Dispcnt = Dispcnt Mod 5 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 |
Durch 'Incr Dispcnt' erhöhen wir den Zeiger auf die Bilddaten des aktiven Zeichens. Da dieser Zeiger nur einen Wert von 0 bis 4 haben darf, behelfen wir uns eines mathematischen Tricks. Indem wir immer den Rest einer Division einer Teilung durch 5 der Variablen Dispcnt zuweisen, wird dieser nie größer als 5. Sobald Dispcnt 5 erreicht hat, ergebt die Teilung einen Rest von 0 und dieser wird dann wieder gespeichert.
Die Änderung der Variablen für die Spaltensteuerung ist da schon etwas komplizierter. Hier müssen wir dafür sorgen, dass das Bit, welches dafür sorgt, das die entsprechende Spalte durchgesteuert wird, um 1 nach links verschoben wird. Dies wird durch den Shift-Befehl erreicht. Wenn wir das 6. Bit erreicht haben, also Dispcol den Wert 32 enthält, muss das Bit wieder an die erste Stelle verschoben werden. Das macht die folgende If-Anweisung.
Durch Loop sorgen wir nun, dass das Ganze unendlich wiederholt wird. Aber die meisten möchten doch sicher nicht nur 1 Zeichen sehen, sondern alle die in der Tabelle definiert sind. Dafür ersetzen wir einfach die Do-Loop-Schleife durch folgenden Programmtext:
'### Variablendefination
für Hauptprogramm Dim Cnt As Word '### Hauptprogramm: Zeichenanzeige Do For Charnr = 32 To 127 For Cnt = 0 To 500 Chradr = Charnr - 32 : Chradr = Chradr * 5 : Chradr = Chradr + Dispcnt Bitbld = Lookup(chradr , Chartab) Portc = Dispcol : Portd = Bitbld : Waitms 1 Incr Dispcnt : Dispcnt = Dispcnt Mod 5 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 Next Cnt Next Charnr Loop |
Hier werden nun alle definierten Zeichen dargestellt. Die Darstellungsroutine für die Zeichen wird pro Symbol 500 mal durchlaufen. Jedes Symbol ist also etwas mehr als eine halbe Sekunde sichtbar.
Wer noch Zeichen selbst definieren möchte, kann diese einfach am Ende der Tabelle hängen. Um sie dann mit obigem Programm zu sehen muss nur der Befehl 'For Charnr=32 To 127' geändert werden. Hat man 1 Zeichen definiert, muss die 127 durch 128 ersetzt werden. Bei mehreren Zeichen entsprechend höher.
Eine Routine die mit Hilfe einer Zeichennummer ein Zeichen/Symbol auf ein Display darstellt, nennt man übrigens Zeichengenerator. Wir werden in anderen Lehrgängen noch Bauteile kennen lernen, die ausschließlich mit Zeichengeneratoren arbeiten.
Die Steuerung wird zur Nebensache
Das letzte Programm zeigt aber ein Problem ganz deutlich. Das Hauptprogramm muss sich ständig auch um die Darstellung auf dem Display kümmern. Hat man kompliziertere Programme, ist dies ein ziemliches Problem. Dauernd muss immer wieder die Darstellungsroutine aufgerufen werden. In dem Lehrgang Timer - Der AVR und Vater Zeit wurde bereits gezeigt, dass es möglich ist, immer wiederkehrende Aufgaben im Grunde 'nebenbei' zu erledigen. Hierzu muss man nur einen Timer verwenden der per Interrupt in regelmäßigen Abständen eine Unterroutine aufruft. Genau dies können wir für unsere Anzeige gebrauchen. Ersetzen wir einmal den Programmteil bis zur Zeichentabelle durch folgenden Programmteil:
$regfile "m8def.dat" $crystal = 1000000 '### Variablendefination für Zeichengenerator Dim Dispcol As Byte Dim Dispcnt As Byte Dim Bitbld As Byte Dim Chradr As Word Dim Charnr As Byte '### Variablendefination für Hauptprogramm Dim Cnt As Byte Dispcnt = 0 : Dispcol = 1 '### Konfiguration für Display Config Portd = Output Config Portc = Output '### Timersteuerung für Multiplexansteuerung konfigurieren Config Timer0 = Timer , Prescale = 8 Enable Timer0 On Timer0 Refresh_display Enable Interrupts '### Hauptprogramm: Zeichenanzeige Do For Charnr = 32 To 127 Waitms 750 Next Charnr Loop '### Interrupt-Routine: Multiplexsteuerung des Displays Refresh_display: Chradr = Charnr - 32 : Chradr = Chradr * 5 : Chradr = Chradr + Dispcnt Bitbld = Lookup(chradr , Chartab) Portc = Dispcol : Portd = Bitbld Incr Dispcnt : Dispcnt = Dispcnt Mod 5 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 Return * Hier Zeichensatztabelle einfügen * |
Nach dem Start zeigt das Display wieder den gesamten Zeichenvorat an. Aber dieses mal kann man im Hauptprogramm erkennen, dass wir nur eine Zeichennummer zuweisen. Die Ansteuerung wird im Hintergrund erledigt.
Damit wir die Anzeige im Hintergrund steuern können, müssen wir erst einmal einen Timer initialisieren. Dies wird durch folgende Programmzeilen erreicht:
Config Timer0 = Timer ,
Prescale = 8 Enable Timer0 On Timer0 Refresh_display Enable Interrupts |
Mit der Config-Anweisung wird dafür gesorgt, dass der Timer 48,8 mal in der Sekunde einen Interrupt auslöst. Anschließend wird der Timer mit der Enable-Anweisung aktiviert. Damit unser Programm weiß, welche Routine er aufrufen soll, wenn der Timer einen Interrupt auslöst, wurde die On Timer0-Anweisung eingefügt. Jetzt müssen die Interrupts nur noch mit 'Enable Interrupts' aktiviert werden. Nun wird alle 48,8x in der Sekunde die Routine
Refresh_display: Chradr = Charnr - 32 : Chradr = Chradr * 5 : Chradr = Chradr + Dispcnt Bitbld = Lookup(chradr , Chartab) Portc = Dispcol : Portd = Bitbld Incr Dispcnt : Dispcnt = Dispcnt Mod 5 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 Return |
ausgeführt. Diese Routine ist die gleiche, welche bereits weiter oben verwendet wurde. Nur mit dem Unterschied, dass wir hier auf den Wartebefehl verzichten können. Die Wartezeit ergibt sich automatisch aus der Laufzeit des Timers.
Nun reicht es einfach aus, das wir der Variablen Charnr den gewünschten Zeichenwert zuweisen und schon zeigt unser Display das entsprechende Symbol. Um die Ansteuerung müssen wir uns im Hauptprogramm nicht weiter kümmern. Dies kann man gut an unserem jetzigen Hauptprogramm sehen:
Do For Charnr = 32 To 127 Waitms 750 Next Charnr Loop |
Hier brauchen wir nur noch die entsprechende Zeichennummer zuweisen. Damit das Symbol einige Zeit sichtbar bleibt ist in der For-Schleife nur ein Wait-Befehl eingebetter.
Nun wollen wir das Ganze noch ein wenig ergänzen und fügen 2 Taster hinzu.
Die
beiden Taster werden wieder gegen Null geschaltet, damit wir die
AVR-eigenen Pullup-Widerstände verwenden können. Ersetzen wir nun noch die Do-Loop-Schleife durch diesen Programmteil. Neben den zusätzlich Befehlen zur Tasterinitialisierung wird jetzt im Hauptprogramm die Zeichentabelle per Tastersteuerung angezeigt. Mit S1 erhöht man das aktuelle Zeichen um 1. Mit S2 wird die Zeichennummer um 1 verringert. |
'### Konfiguration der Taster
Config Pinb.0 = Input |
Grafische Ansteuerung des Displays
Das Ansteuern eines Punktmatrixdisplays mit Hilfe eines Zeichengenerators ist ja ganz nett. Aber die volle Leistungsfähigkeit so einer Anzeige kann man erst herausholen, wenn es möglich ist das Display mit Grafikbefehlen anzusteuern. Mit dem nächsten Programm erzeugen wir einen blinkenden Punkt in der 3. Spalte (X=3) und in der 3. Zeile (Y=3). Hier haben wir jetzt ein komplett neues Programm. Den Zeichengenerator und die Zeichentabelle können wir hier nicht mehr gebrauchen.
$regfile "m8def.dat"
Gosub Mtx_Preset :
Waitms 500 |
Um das Display grafisch anzusteuern muss natürlich die Interruptroutine zum Großteil abändern. Als erstes wird die Zeichentabelle ersetzt. Anstelle der Zeichentabelle benötigen wir nun einen Speicher, indem das aktuelle Bild auf dem Display festgehalten wird. Dies wird am Anfang des Programms definiert:
Dim Dispram(5) As Byte |
Wir benötigen hier fünf Feldelemente. Für jede Spalte eine Feldvariable. So ein Speicher wird Bildwiederholspeicher genannt und findet man immer dort wo man mit grafischen Displays arbeitet. Auch die Grafikkarte des heimischen PCs besitzt Bildwiederholspeicher. Dieser hat aber ganz andere Dimensionen, wie unsere 'mickrigen' 5 Bytes hier. Moderne Grafikkarten haben 64 MB und mehr.
Um jetzt das aktuelle Bild darzustellen wird die folgende Interruptroutine regelmäßig aufgerufen:
Refresh_display: Portc = Dispcol : Portd = Dispram(dispcnt) Incr Dispcnt : If Dispcnt = 6 Then Dispcnt = 1 Shift Dispcol , Left : If Dispcol = 32 Then Dispcol = 1 Return |
Hier wird jetzt nur bei jedem Aufruf ein Byte aus dem Grafikspeicher ans Display geschickt. Die ganze Berechnung für die Zeichenposition etc. wird hier nicht benötigt. Der Anwender muss nun nur im Hauptprogramm dafür sorgen, dass die Daten im Bildwiederholspeicher entsprechend geändert werden.
Zu diesem Zweck wurden einige Unterroutinen ins Programm eingefügt. Die einfachste ist der CLS-Befehl (CLear Screen, Bildschirmlöschbefehl).
Mtx_cls: For Yy = 1 To 5 Dispram(yy) = 0 Next Yy Return |
Hier wird einfach der Bildschirmspeicher mit 0 gefühlt.
Schwieriger wird es da schon mit dem Setzbefehl für einzelne Punkte:
Mtx_pset: Yy = Y -1 : Ys = 2 ^ Yy Dispram(x) = Dispram(x) Or Ys Return |
Um mit den Unterroutinen kommunizieren zu können, wurden 2 Variablen, X und Y, definiert. Damit übergeben wir der entsprechende Routine die gewünschte Position der anschließend aufgerufen Funktion.
Um nun einen Punkt zu setzen müssen wir erst einmal wissen, welches Bit in unserem Datenbyte die gewünschte Y-Position ist. Da wir einen Wertebereich für Y von 1 bis 7 haben, müssen wir erst einmal 1 abziehen um einen Bereich von 0 bis 6 zu bekommen. Um jetzt das entsprechende Bit zu berechnen muss nur 2 hoch Y gerechnet werden. Nun haben wir den genauen Bitwert des Punktes. Aber wie setzt man damit jetzt einen Punkt in einem Datenbyte?
Dabei hilft uns die Logik. Es reicht aus wenn wir das aktuelle Datenbyte mit der Oder-Funktion mit dem errechneten Wert verknüpfen. Diese kleine Tabelle verdeutlicht dies:
Datenbyte | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
Errechneter Punkt | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
Ergebnis nach Oder-Verknüpfung | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
Genau dies wird in der letzten Anweisung der Routine gemacht. Anschließend leuchtet an der gewünschten Position ein Punkt auf. Im jetzigen Hauptprogramm eben in der 3. Spalte und der 3. Zeile.
Ähnlich funktioniert dies auch, wenn man einen Punkt wieder löschen möchte:
Mtx_preset: Yy = Y -1 : Ys = 2 ^ Yy : Ys = Not Ys Dispram(x) = Dispram(x) And Ys Return |
Soll ein Punkt gelöscht werden müssen alle Punkte so bleiben wie sie sind. Nur der zu löschende Punkt muss immer 0 sein. Hierzu ist die And-Funktion ganz hilfreich. Es muss aber zusätzlich noch der errechnete Punkt invertiert werden. Hierzu auch eine Tabelle zur Verdeutlichung:
Datenbyte | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
Errechneter Punkt | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
Invertierung des er. Punktes | 1 | 1 | 1 | 0 | 1 | 1 | 1 |
Ergebnis nach Und-Verknüpfung | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
Durch diese Routinen haben wir die wichtigsten Routinen, die wir benötigen um eine umfangreiche Grafikbibliothek zu erstellen. Dies würde hier aber zu weit führen. Des Weiteren ist unser kleines Display nur beschränkt für leistungsfähige Grafikroutinen geeignet. Wer möchte, kann sich ja einmal daran versuchen eine Routine zum zeichnen von Linien zu entwerfen. Ein bekannter Algorithmus ist der Bresenham-Algorithmus. Im Internet gibt es reichlich Informationen darüber.
Zum Abschluss noch ein kleines Hauptprogramm, welches das Display langsam mit Punkten füllt und anschließend wieder löscht:
'### Hauptprogramm Do For X = 1 To 5 For Y = 1 To 7 Gosub Mtx_pset : Waitms 250 Next Y Next X For X = 1 To 5 For Y = 1 To 7 Gosub Mtx_preset : Waitms 250 Next Y Next X Loop |