; Test 4: Board kennenlernen, Timer im Interupt mode ; Was hier Neues zu lernen ist: ; - Timer im Interrupt modus ; - Interrupts, Interrupt-Vektoren ; - BCD-Arithmetik .NOLIST .INCLUDE "8515def.inc" .LIST ; Universalregister definieren .DEF mp = R16 ; Zaehler Anzahl Nulldurchgaenge, MSB Zaehler, Software-Zaehler .DEF z1 = R0 ; Arbeitsregister fuer die Interrupt-Service-Routine .DEF ri = R1 ; Register zum Zaehlen der Sekunden, gepackte BCD-Ziffern .DEF sec = R2 ; Reset-Vektor auf Adresse 0000 rjmp main ; Interrupt-Vektoren, fast alle blindgesetzt ausser dem Timer-Overflow ; RETI ist Rueckkehr vom Interrupt mit Wiederherstellung des Interrupt- ; Flags im Status-Register. Wichtig: Sprung zur Interrupt-Service- ; Routine tc0i muss an der Adresse 0007 stehen. ; Mechanismus des Interrupts: Ist der Timer von 255 auf Null ueberge- ; laufen, dann wird das Programm unterbrochen, der Programmzaehler auf ; dem Stapel abgelegt, der Befehl an der Adresse 0007 ausgefuehrt. Nach ; Beendigung des Interrupts wird der Programmzaehler wieder hergestellt ; und mit dem unterbrochenen Programm fortgefahren. ; Die Interrupt-Vektoren zu je 1 Byte: reti ; Int0-Interrupt reti ; Int1-Interrupt reti ; TC1-Capture reti ; TC1-Compare A reti ; TC1-Compare B reti ; TC1-Overflow rjmp tc0i ; Timer/Counter 0 Overflow, mein Sprung-Vektor reti ; Serial Transfer complete reti ; UART Rx complete reti ; UART Data register empty reti ; UART Tx complete reti ; Analog Comparator ; Interrupt-Service-Routine fuer den Zaehler tc0i: in ri,SREG ; Rette den Inhalt des Flag-Registers inc z1 ; Erhoehe Software-Zaehler mit Bit 8-15 out SREG,ri ; Stelle Flag-Register wieder her reti ; Kehre vom Interrupt zurueck ; Hauptprogramm beginnt hier main: ldi mp,LOW(RAMEND) ;Initiate Stackpointer out SPL,mp ; wegen Interrupts und Unterprogr. ldi mp,HIGH(RAMEND) out SPH,mp ; Software-Zaehler-Register auf Null setzen ldi mp,0 ; z1 kann nicht direkt gesetzt werden, da R0 mov z1,mp ; Kopieren von 0 in den Software-Zaehler mov sec,mp ; und Sekundenzaehler auf Null ; Vorteiler des Zaehlers = 256, 4 MHz/256 = 15625 Hz = $3D09 ldi mp,0x04 ;Initiate Timer/Counter 0 Vorteiler out TCCR0,mp ; an Timer 0 Control Register ; Port B ist LED-Port ldi mp,0xFF ; alle Bits als Ausgang out DDRB,mp ; in Datenrichtungsregister ; Interrupts bei Timer 0 einschalten ldi mp,$02 ; Bit 1 setzen out TIMSK,mp ; in Timer Interupt Mask Register ; Eigentlich koennte dieser Befehl folgendermassen aussehen: ; SBI TIMSK,TOIE0 ; Timer Interrupt Mask Flag setzen ; Das geht aber nicht, weil SBI nur bei Portnummern bis 0x1F ; geht, TIMSK liegt aber darueber. Deshalb der Umweg. ; Alle Interrupts allgemein freigeben sei ; Gib Interrupts im Status-Register frei ; Hauptprogramm-Loop fragt oberes Byte des Zaehlers ab, bis dieser ; hex 3D erreicht hat. Dann den Timer, bis dieser 09 erreicht hat ; (eine Sekunde = dez 15625 = hex 3D09 Zaehlerimpulse). Die Zaehler ; werden auf Null gesetzt und eine Sekunde weitergezaehlt. Die Se- ; kunden werden als gepackte BCD-Zahl behandelt (eine Ziffer zu ; vier Bit, 1 Byte entspricht zwei Ziffern), Die Sekunden werden ; bei Erreichen von 60 wieder auf Null gesetzt. Der Sekundenstand ; wird auf den LEDs ausgegeben. loop: ldi mp,$3D ; Vergleichswert MSB loop1: cp z1,mp ; Vergleiche mit MSB Zaehlerstand brlt loop1 ; z1 < mp, weiter warten loop2: in mp,TCNT0 ; Zaehler LSB lesen cpi mp,$09 ; mit LSB vergleichen brlt loop2 ; TCNT0 < 09, weiter warten ldi mp,0 ; Null out TCNT0,mp ; in Hardware-Zaehler LSB mov z1,mp ; und in Software-Zaehler MSB rcall IncSec ; Unterprogramm Sekunden-Zaehler erhoehen rcall Display ; Unterprogramm Sekunden an LED ausgeben rjmp loop ; Das Ganze von Vorne ; Unterprogramm eine Sekunde zum Sekundenzaehler ; in BCD-Arithmetik! Unteres Nibble = Bit 0..3, Oberes N. = 4..7 IncSec: sec ; Setze Carry-Flag fuer Addition ldi mp,6 ; Provoziere Ueberlauf unteres Nibble adc sec,mp ; beim Addieren von 6 + 1 (Carry) brhs Chk60 ; Springe zum 60-Check, wenn Ueberlauf sub sec,mp ; Ziehe die 6 wieder ab Chk60: ldi mp,$60 ; Vergleiche mit 60 cp sec,mp brlt SecRet ; Springe, wenn kleiner ldi mp,256-$60 ; Lade Rest auf Null add sec,mp ; Addiere auf Null SecRet: ret ; Kehre zum Hauptprogramm zurueck ; Unterprogramm zur Ausgabe des Sekundenzaehlers an die LEDs Display: mov mp,sec ; Zaehlerstand nach mp kopieren com mp ; Einer-Komplement = XOR(FF) wg. Lampen out PORTB,mp ; Software-Zaehlerstand an LEDs ret ; Zurueck zum Hauptprogramm