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.

; 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 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

; 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
; 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