Zweistellige Anzeige - Das Programm
und es geht weiter ...
Zuerst mal den Quellcode, denn ich danach dann erkläre ...
CodeBox ASM
; ----------------------------
; Projekt: 7-Segment an Port D
; ----------------------------
;
.include "m8def.inc" ; Definitionsdatei laden
.cseg ; Beginn eines Code-Segmentes
.org 0 ; Startadresse=0
;
start:
ldi r16,low(ramend)
ldi r17,high(ramend)
out spl,r16 ; Stackpointer auf
out sph,r17 ; RAM-Ende setzen
ldi ZH,0x04 ; hohes Byte fuer Flash-Zugriff speichern
ldi ZL,0x00 ; niedriges Byte fuer Flash-Zugriff speichern
ldi r16,0b11111111 ; PortD auf Ausgang ( alles auf 1 )
out ddrd,r16 ; setzen
ldi r16,0b11111111 ; PortC auf Ausgang ( alles auf 1 )
out ddrc,r16 ; setzen
clr r17 ; Zaehl-Register auf 0x00 setzen
ldi r18,200 ; Verzoegerungs-Zaehler auf 200 setzen (200 x 2ms => 400ms)
clr r24 ; Register fuer die Stellen-Auswahl (Einer/Zehner)
; Bit0 = 0 => Einer , Bit0 = 1 => Zehner
mainloop: ; Die Hauptschleife
rcall muxer ; ausgabe auf 7-Segment-Anzeige
rcall wait2m ; 2ms warten
dec r18 ; Verzoegerungszaehler um 1 verringern
brne mainloop ; Wenn der Verzoegerungs-Zaehler nicht auf 0 ist
; dann zum Start der Hauptschleife springen
inc r17 ; Zaehl-Register um 1 erhoehen
ldi r18,200 ; Verzoegerungs-Zaehler wieder auf 200 setzen
rjmp mainloop ; Schleife neu beginnen
; ==================================================================
; ===== Anzeigen-Multiplexer =======================================
; ==================================================================
muxer:
sbis r24,0 ; Ist Bit0 gesetzt? Dann die Zehner anzeigen
rjmp einer ; zu den Einern springen
zehner: ; ===== Zehner-Aanzeige ausgeben =====
mov ZL,r17 ; Zaehler in ZL kopieren
swap ZL ; Oberes Nibble nach unten bringen
cbr ZL,0b11110000 ; Die oberen 4 Bits loeschen (wir haben nur 16 Zeichen definiert)
lpm r16,Z ; Daten aus Flash-Zelle holen Flash(ZH,ZL) => r16
out portd,r16 ; Zehner-Segmente setzen
ldi r16,0b11111101 ; PC1 auf 0 schalten (Zehner an)
out portc,r16 ; und ausgeben
rjmp schluss ; Zehner sind bearbeitet -> Ende
einer: ; ===== Einer-Anzeige ausgeben =====
mov ZL,r17 ; Zaehler in zl kopieren
cbr ZL,0b11110000 ; Die oberen 4 Bits loeschen (wir haben nur 16 Zeichen definiert)
lpm r16,Z ; Daten aus Flash-Zelle holen Flash(ZH,ZL) => r16
out portd,r16 ; Einer-Segmente setzen
ldi r16,0b11111110 ; PC0 auf 0 schalten (Einer an)
out portc,r16 ; und ausgeben
schluss:
inc r24 ; Beim naechsten Durchlauf die andere Stelle anzeigen
; Beim hochzaehlen wechselt Bit 0 jedesmal den Zustand
ret ; Rueckkehr
; ==================================================================
; ===== 2ms warten =================================================
; ==================================================================
; rcall wait2m ; (3) Warteschleife aufrufen
; ================> ; 10318 Cyclen (incl Call+Ret) => ~2ms@1MHz
wait2m: push r20 ; (2) r20 auf Stack retten (1tes)
push r21 ; (2) r21 auf Stack retten (2tes)
clr r20 ; (1) r20+r21 ergeben zusammen
ldi r21,4 ; (1) einen 2-Byte-Zaehler (2053Cycl)
;
; 2_______2053___
; | 1__513__ |
; | | | |
; 12 Cycle = 4*((256*2+1)+2)+1+17
loop2m: dec r20 ; (1) || niedrigstes Byte -1
brne loop2m ; (1f,2t)_/| 0 erreicht? nein -> Schleife
dec r21 ; (1) | mittleres Byte -1
brne loop2m ; (1f,2t)__/ 0 erreicht? nein -> Schleife
pop r21 ; (2) r21 zurueckholen (2tes)
pop r20 ; (2) r20 zurueckholen (1tes)
ret ; (4) Schleifenende, Rueckkehr
; ================================
; ===== 7-Segment-Anzeige ========
; ================================
; --a--
; | |
; f b
; | |
; --g--
; | |
; e c
; | |
; --d-- O dp
;
; Port D - 7 6 5 4 3 2 1 0
; Segment - dp g f e d c b a
;
.cseg ;===== 7-Segment-Zeichensatz =====
.org 0x0200 ;Startadresse der Tabelle ( H=0x04 , L=0x00 )
; => im Speicher liegt der Start wegen 16Bit dann bei 0x0200
; PC 0x0200 => Speicherstelle 0x0400 (wegen 16Bit-Zellen)
; Speicherzugriff mit ZH/ZL dann aber auf die Speicherstelle
;
; Die beiden Bytes des 16-Bit-Registers Z
; ===========ZH========== ===========ZL==========
; 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 Bit
; 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Bit
; ========================Z======================
; 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 H/L Bit => Programmzaehler
; Der Programmzaehler fuer das Flash arbeitet mit 16-Bit DATENzugriff
; Er adressiert also in WORD-Breite gegenueber dem Z-Register, das die
; Flash-Zellen in BYTE-Breite adressiert.
;
; 76543210 - PortD Bits
; pgfedcba - Segmente *=an(0), -=aus(1)
segmente: .db 0b11111001 ;0 0400 (0200) --******
.db 0b10100100 ;1 0400 (0200) -----**-
.db 0b10100100 ;2 0400 (0200) -*-**-**
.db 0b10110000 ;3 0400 (0200) -*--****
.db 0b10011001 ;4 0400 (0200) -**--**-
.db 0b10010010 ;5 0400 (0200) -**-**-*
.db 0b10000010 ;6 0400 (0200) -*****-*
.db 0b11111000 ;7 0400 (0200) -----***
.db 0b10010000 ;8 0400 (0200) -**-****
.db 0b10000000 ;9 0400 (0200) -*******
.db 0b10001000 ;A 0400 (0200) -***-***
.db 0b10000011 ;b 0400 (0200) -*****--
.db 0b11000110 ;C 0400 (0200) --***--*
.db 0b10100001 ;d 0400 (0200) -*-****-
.db 0b10000110 ;E 0400 (0200) -****--*
.db 0b10001110 ;F 0400 (0200) -***---*
; ZHZL PC
Bis zu Zeile 30 hat sich nicht viel geändert. Ich habe lediglich ein weiteres
Register (r24) für die Umschaltung zwischen der Zehner und Einerstelle
eingeführt und noch einen Verzögerungszähler mit dem Register r18.
Nehmen wir mal zuerst den Verzögerungszähler ...
Im alten Programm haben wir in der Mainloop eine Verzögerung von 1 sec
gehabt. Die Schleife wurde also in einer Sekunde einmal durchlaufen und
hat in diesem Durchlauf unser Zählregister um 1 erhöht und auf der
Anzeige ausgegeben.
Damit haben wir aber bei der Multiplex-Ausgabe auf der 2stelligen Anzeige
ein Problem. Das Auge soll die Anzeige ja auch 2stellig sehen und nicht
eine sekündlich hin- und herblinkende Anzeige von Zehner- und Einerstelle.
Wir müssen da also was ändern wenn die Multiplex-Frequenz höher sein
soll, die Geschwindigkeit des Zählers aber weiter bei 1-2Hz liegen soll.
Also habe ich die Verzögerung schon mal auf 2ms runtergedreht (Zeile 2).
Die Schleife wird jetzt also 500x pro Sekunde durchlaufen. Das ist für eine
sichtbare Zählung von 1-2mal pro Sekunde viel zu schnell. Darum wurde das
Register r18 als Verzögerungszähler verwendet. Es wird auf den Wert 200
gesetzt (Z.26) und bei jedem Schleifendurchlauf um 1 verringert (Z.39).
Wenn 0 erreicht ist (Z.40), dann wird der Wert wieder auf 200 gesetzt
(Z.44) und unser Zählregister um eins erhöht (Z.43). Damit wird also bei
jedem 200ten Schleifendurchlauf um eins hochgezählt.
Da ein Schleifendurchlauf 2ms dauert wird also alle ...
2ms x 200 = 400ms um eins hochgezählt. Der Zähltakt ist jetzt also 2Hz.
Soweit verstanden ?
Jetzt nehmen wir uns die Multiplex-Routine (Zeile 54-85) vor ...
In Zeile 55 wird getestet ob das Bit 0 vom Multiplex-Register r24 gesetzt
ist. Wenn nein, wird über rjmp (Z.56) zur Anzeige der Einerstelle verzweigt.
Bei gesetztem Bit 0 wird rjmp übersprungen und bei der Anzeige der Zehner
weitergemacht. In Zeile 82 wird dieses Multiplexregister bei jedem Aufruf
der Routine um 1 hochgezählt. Das hat bei Binärzahlen einen Vorteil.
Das Bit 0 wechselt bei jedem Durchlauf den Zustand (gerade Zahl, ungerade
Zahl, gerade Zahl, ...). es toggled also. Damit wird immer abwechselnd die
Einer und die Zehnerstelle angezeigt.
Jetzt sehen wir uns die Einer-Anzeige an (Zeile 71-80). Da finden wir unsere
Zeile vom letzten Programm wieder (Zeilen 72-76 sind die Zeilen 25-29 aus
Posting #20). In Zeile 87+97 legen wir eine 0 auf PortC0 und schalten
damit über den pnp-Transistor die Einerstelle an.
Bei der Zehnerstelle (Zeile 58-70) sieht es ähnlich aus. Wir schalten
allerdings hier mit PortC1 die Zehnerstelle an (Z.66+67) und springen
danach zum Schluß der Routine damit wir nicht noch in die Eineranzeige
laufen und alles wieder durcheinanderbringen (Z.69). Damit wir aber
die Zehnerstelle anzeigen können brauchen wir auch die entsprechenden
Bits aus unserem Zählregister r17. Das machen wir auf ganz einfache
Weise
. Wir tauschen einfach die oberen und unteren 4 Bits gegeneinander.
Das geht mit dem swap Befehl (Z.60). Damit haben wir die oberen 4 Bit
an der Stelle, wo wir sie für unseren Pointer auf die 7-Segment-Tabelle
brauchen. Und damit haben wir die Zehneranzeige fertig.
Immer noch alles klar ?
Zum Schluß ist eigentlich nur noch die Warteroutine geändert. In diesem
Programm brauchen wir nicht mehr die hohe Wartezeit von n*130ms sondern
nur noch 2ms.
Das Ergebnis:
Es wird bei jedem Hauptschleifendurchlauf abwechelnd für 2ms die Zehner
und die Einerstelle angezeigt. Bei jedem 200ten Durchlauf wird hochgezählt.
Anmerkung ... Ich habe das kleine Progrämmchen so aus dem Kopf und ohne
Test auf wirklicher Hardware zusammengeschrieben
Der Assembler
(AVR-Studio) hat auf jeden Fall nicht gemotzt und meiner Meinung nach
sollte es auch funktionieren
probiert es einfach mal aus.
Damit haben wir jetzt eine funktionierende 2stellige Hexadezimale Anzeige
im Multiplexbetrieb. Das was ich hier als Zehner- und Einer-Stelle benannt
habe ist ja eigentlich eine Sechzehner- und Einer-Stelle. Also nicht motzen.
Habe ich der Einfachheit halber mal so gemacht.
.. Jetzt kommt noch als Ergänzung ein Schaltplan von einer mehrstelligen
Multiplexanzeige mit Treiber-ICs und eine Routine dafür. Aber ich gebe
jetzt einfach mal die Diskussion frei
Die grossen Erklärungen habe ich
jetzt soweit abgeschlossen.
Gruß
Dino