Pfad: Home =>
AVR-Übersicht =>
Tutorium => Teil 4
; 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
; Zähler Anzahl Nulldurchgänge, MSB Zähler, Software-Zähler
.DEF z1 = R0
; Arbeitsregister für die Interrupt-Service-Routine
.DEF ri = R1
; Register zum Zählen 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 Rückkehr 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 überge-
; laufen, dann wird das Programm unterbrochen, der Programmzähler auf
; dem Stapel abgelegt, der Befehl an der Adresse 0007 ausgeführt. Nach
; Beendigung des Interrupts wird der Programmzähler wieder hergestellt
; und mit dem unterbrochenen Programm fortgefahren.
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 für
den Zähler
tc0i: IN ri,SREG
; Rette den Inhalt des Flag-Registers
INC z1
; Erhöhe Software-Zähler mit Bit
8-15
OUT SREG,ri
; Stelle Flag-Register wieder her
RETI ; Kehre vom
Interrupt zurück
; 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-Zähler-Register auf Null setzen
LDI mp,0
; z1 kann nicht direkt gesetzt werden, da R0
MOV z1,mp
; Kopieren von 0 in den Software-Zähler
MOV sec,mp
; und Sekundenzähler auf Null
; Vorteiler des Zählers = 256, 4 MHz/256 = 15625 Hz = $3D09
LDI mp,0x04
;Initiate Timer/Counter 0 Vorteiler
OUT TCCR0,mp
; an Timer 0 Control Register
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
; Einfacher wäre dieser Befehl einfacher:
; SBI TIMSK,TOIE0
; Timer Interrupt Mask Flag setzen
; Dieser Befehl geht aber nicht, weil mit SBI
nur Ports bis 0x1F angesprochen werden
; können, und TIMSK liegt darüber.
; Alle Interrupts allgemein freigeben
SEI ; Gib Interrupts
im Status-Register frei
; Hauptprogramm-Loop fragt oberes Byte
des Zählers ab, bis dieser
; hex 3D erreicht hat. Dann den Timer,
bis dieser 09 erreicht hat
; (eine Sekunde = dez 15625 = hex 3D09
Zählerimpulse). Die Zähler
; werden auf Null gesetzt und eine Sekunde weitergezählt. 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 Zählerstand
BRLT loop1
; z1 < mp, weiter warten
loop2: IN mp,TCNT0
; Zähler LSB lesen
CPI mp,$09
; mit LSB vergleichen
BRLT loop2
; TCNT0 < 09, weiter warten
LDI mp,0
; Null
OUT TCNT0,mp
; in Hardware-Zähler LSB
MOV z1,mp
; und in Software-Zähler MSB
RCALL IncSec
; Unterprogramm Sekunden-Zähler
erhöhen
RCALL Display
; Unterprogramm Sekunden an LED ausgeben
RJMP loop
; Das Ganze von Vorne
; Unterprogramm eine Sekunde zum Sekundenzähler
; in BCD-Arithmetik!
Unteres Nibble = Bit 0..3, Oberes N. =
4..7
IncSec: SEC ; Setze
Carry-Flag für Addition
LDI mp,6
; Provoziere Überlauf unteres Nibble
ADC sec,mp
; beim Addieren von 6 + 1 (Carry)
BRHS Chk60
; Springe zum 60-Check, wenn Überlauf
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 zurück
; Unterprogramm zur Ausgabe des Sekundenzählers
an die LEDs
Display:
MOV mp,sec
; Zählerstand nach mp kopieren
COM mp
; Einer-Komplement = XOR(FF) wg. Lampen
OUT PORTB,mp
; Software-Zählerstand an LEDs
RET ; Zurück
zum Hauptprogramm
©2002 by http://www.avr-asm-tutorial.net