Matrixanzeigen - LEDs in Reih und Glied

 

Materialbedarf

 

Anz. Bezeichnung Datenblatt
1 Batterie/Spannungsquelle 9V  
1 Spannungsregler 7805
1 ATMega8 AVR-Prozessor
5 Transistor BC548C (BC546C-BC550C)
7 Widerstand 470 Ohm  
6 Widerstand 10 kOhm  
1 Elektrolytkondensator 100 µF/16V  
1 Kondensator 100nF  
2 Mikrotaster
1 TC07-11 Punktmatrix-Anzeige

 

 

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"
$crystal = 1000000

Config Portd = Output
Config Portc = Output

Portd = &B00101100 : Portc = 2

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
 
Nach dem Übertragen zeigt nun das Display eine schräge Linie an. Wie man an dem Programm gut erkennen kann, schalten wir mit dem Befehl 'Portc = 1' die erste Spalte ein. Anschließend wird ein Bitmuster an Port D gelegt. Mit Hilfe des Waitms-Befehls warten wir einen Augenblick. Dann schalten wir auf die 2. Spalte um und legen das nächste Bitbild an Port D. Wieder etwas warten und so weiter bis wir alle 5 Spalten mit Daten versorgt haben. Durch die Do-Loop-Struktur wird das Ganze wiederholt womit wir auf dem Display eine Linie sehen. Das Umschalten der einzelnen Spalten nehmen wir nicht mehr wahr.
 
Es ist bei der Ansteuerung von Matrixdisplays aber immer notwendig, dass man nach der Aktivierung des Bitbildes einen kurzen Augenblick wartet. Sollte man dies nicht machen bekommt man einen seltsamen Effekt. Nehmen wir doch einmal die Wartebefehle aus der Do-Loop-Schleife raus:
 
Do
  Portc = 1 : Portd = &B00100000
  Portc = 2 : Portd = &B00010000
  Portc = 4 : Portd = &B00001000
  Portc = 8 : Portd = &B00000100
  Portc = 16 : Portd = &B00000010
Loop
 
Wird nun das Programm gestartet sieht man auf dem Display so genannte Geisterbilder. Die eigentliche Linie ist zwar jetzt auch gut zu erkennen, aber neben dieser leuchten auch noch schwach einige weitere Elemente der Anzeige. Wie kommt das zustande?
 
Wird z.B. an der ersten Spalte ein Bitbild angelegt bleibt dieses Bitbild auch am Port D stehen, wenn auf die 2. Spalte umgeschaltet wird. Erst wenn das neue Bitbild angelegt wird, verschwindet die alte Spaltenstruktur. Es leuchten also kurzfristig auch die 'falschen' LEDs auf. Da diese kürzere Zeit leuchten als das 'echte' Bitbild' sind diese Elemente etwas dunkler.
 
Hat man nun aber einen Wartebefehl eingebaut ist die Leuchtzeit dieser 'Geisterbilder' erheblich geringer als die des richtigen Bildes. Für den Menschen ist es nicht mehr zu sehen.
 
Nun möchte man aber nicht einfach eine feste Grafik auf den Display darstellen sondern es soll evtl. Zahlen, Buchstaben, Symbole etc. anzeigen. Je nach Erfordernis. Da benötigt man ein Programm, welches aus einer vordefinierten Sammlung von Zeichen ein gewünschtes darstellt. Dies wird mit folgendem Programm erreicht. Das Programm ist nun sehr umfangreich. Wer dies nicht abtippen möchte kann auch einfach den Quelltext markieren und mit Hilfe der Copy/Paste-Methode das Programm im Bascom-Editor einfügen.
 
$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
Config Pinb.1 = Input

Portb.0 = 1 : Portb.1 = 1
Charnr = 32

'### Hauptprogramm: Tastergesteuerte Zeichenanzeige
Do
  If Pinb.0 = 0 Then
    If Charnr < 127 Then Incr Charnr
    Waitms 50
    While Pinb.0 = 0 : Wend
  End If
  If Pinb.1 = 0 Then
    If Charnr > 32 Then Decr Charnr
    Waitms 50
    While Pinb.1 = 0 : Wend
  End If
Loop

 

 

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"
$crystal = 1000000

'### Variablendefination für Displaysteuerung
Dim Dispram(5) As Byte
Dim Dispcol As Byte
Dim Dispcnt As Byte
Dim X As Byte
Dim Y As Byte
Dim Ys As Byte
Dim Yy As Byte


'### Konfiguration für Display
Config Portd = Output
Config Portc = Output

Dispcol = 1 : Dispcnt = 1


'### Timersteuerung für Multiplexansteuerung konfigurieren
Config Timer0 = Timer , Prescale = 8
Enable Timer0

On Timer0 Refresh_display
Enable Interrupts


Gosub Mtx_cls

'### Hauptprogramm
Do
  X = 3 : Y = 3 : Gosub Mtx_Pset : Waitms 500

  Gosub Mtx_Preset : Waitms 500
Loop


'### Routine: Display löschen
Mtx_cls:
  For Yy = 1 To 5
    Dispram(yy) = 0
  Next Yy
Return


'### Routine: Pixel setzen
Mtx_pset:
  Yy = Y -1 : Ys = 2 ^ Yy
  Dispram(x) = Dispram(x) Or Ys
Return


'### Routine: Pixel löschen
Mtx_preset:
  Yy = Y -1 : Ys = 2 ^ Yy : Ys = Not Ys
  Dispram(x) = Dispram(x) And Ys
Return


'### Interrupt-Routine: Multiplexsteuerung des Displays
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

 

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

 

 

Zurück zur Auswahlseite            Zur Hauptseite