Code:
.include "m8def.inc"
;; Wir wollen mit dem Timer2 einen Interrupt pro Sekunde ausloesen.
;; Asuro hat einen 8MHz Quarz. Wir waehlen eine Prescaler von 1024 aus( 8, 32, 64, 128, 256, 1024)
;; 8MHz/1024 = 8000000Hz/1024 = 7812,5 Hz die Timerfrequenz.
;; -> ein Timerschritt dauert 1/7812,5 = 0,000128s = 128us.
;; -> Fuer eine Sekunde muessten also 1s/128us = 7812 Timerschritte ausgef?hrt werden.
;; Da das nur ein 8-bit Timer ist, macht er 256 Schritte zum Ueberlauf.
;; -> Des wegen barauchen wir eine Hilfsvariable,
;; um speichern zu koennen , wieviele Ueberlaeufe er schon hinter sich hat.
;; -> 7812 = 36 * 217
;; -> 36 mal soll der Timer bis 217 Zahlen -> dann ist eine Sekunde vergangen
.EQU TIMER2 = 256 - 217
.EQU counts2 = 36
.EQU FRONT_LED = PD6 ; Dran haengt auch ein Summer :)
.DEF erg = R0 ; Das Register wird vom LPM-Befehl benutzt
.DEF tmp = R16 ; Fuer algemeine Zwecke
.DEF counter2 = R17 ; Hier speichern wir, wieviele Ueberlaufe der Timer2 schon hatte
.DEF frequence = R18 ; Hier wird die aktuelle Frequenz des Summers gespeichert
.CSEG
.ORG 0x000
RJMP RESET ; Interruptvektor reset
.ORG OVF2addr ; Interruptvektor fuer Timer2 Ueberlauf.
RJMP TIM2_OVR ; Dahin wird gesprungen, wenn der Timer2 ueberlaeuft
.ORG OVF0addr ; Interruptvektor fuer Timer0 Ueberlauf.
RJMP TIM0_OVR ; Dahin wird gesprungen, wenn der Timer0 ueberlaeuft
RESET:
;; Initializierung des Stackpointers
LDI tmp, LOW(RAMEND) ; LOW-Byte der obersten RAM-Adresse
OUT SPL, tmp
LDI tmp, HIGH(RAMEND) ; HIGH-Byte der obersten RAM-Adresse
OUT SPH, tmp
SBI DDRD,FRONT_LED ; Der Pin an dem die FrontLed und jetzt der Summer haengen
; als Ausgang definieren
LDI frequence, 255 ; Die zahl mit dem der Counter vom Timer0 geladen wird
;; Timer2 Register werden belegt
;; Es wird Timer2 benutzt
LDI tmp,(1<<CS22)|(1<<CS21)|(1<<CS20) ; Prescaller ist 1024. Also Lade (0000 0100 V 0000 0010 V 0000 0001) = 0000 0111 in tmp
OUT TCCR2,tmp ; Register TCCR2 ist fuer den Prescaller zustaendig (siehe Datenblatt)
LDI tmp,TIMER2 ; Hier wird der Timer2 vorgeladen und zwar mit TIMER2. Siehe oben.
OUT TCNT2,tmp ; Er laeuft counts2 mal durch bevor ein Interrupt auftritt. Siehe CPI- Vergleich in main
;; Timer0 Register werden belegt
;; Es wird Timer0 benutzt
LDI tmp,(1<<CS01) | (1<<CS00) ; Prescaller ist 64. Also Lade (0000 0010 V 0000 0001) = 0000 00011 in tmp
OUT TCCR0,tmp ; Register TCCR0 ist fuer den Prescaller zustaendig (siehe Datenblatt)
OUT TCNT0,frequence ; Counter vom Timer0 laden.
LDI tmp,(1<<TOIE2) | (1<<TOIE0) ; Hier werden Interrupts fuer einen Ueberlauf von Timer2 bzw. Timer0 eingeschaltet
OUT TIMSK,tmp ; Register TIMSK ist dafuer zustaendig
LDI ZH,HIGH(2*toene) ; Lade die Adresse unserer Daten in das z-Register
LDI ZL,LOW(2*toene) ; "2*" - es ist so :)
SEI ; Interrupts allgemein zulassen
main:
CPI counter2, counts2 ; Vergleiche ob der Timer2 schon counts2 mal durchgelaufen hat
BREQ switch_frequence ; Wenn ja, gehe zu switch_frequnce
RJMP main ; sonts Looping
switch_frequence:
CLR counter2 ; Counter2 auf 0 setzen. Der Timer2 soll wieder counts2 mal durchlaufen
LPM ; Lese ein Byte aus dem Programmspeicher - Naechster Tonewert
TST erg ; R0 auf 0 testen
BREQ ende ; Ein ja bedeutet, dass wir am Ende sind -> Ende
LDI frequence, 255 ; Kein Kommentar
SUB frequence, erg ; Rechne den entsprechenden Wert fuer den Counter
OUT TCNT0, frequence ; Beschreibe den Counter von Time0 mit dem Wert
ADIW ZL,1 ; Pointer auf das naechste Byte
RJMP main ; Kehre zurueck zum main
ende:
LDI tmp,(0<<CS02)|(0<<CS01)|(0<<CS00) ; Den Timer0 ausschalten
OUT TCCR0,tmp ; Register TCCR0 ist dafuer zustaendig (siehe Datenblatt)
LDI tmp,(0<<CS22)|(0<<CS21)|(0<<CS20) ; Den Timer2 ausschalten
OUT TCCR2,tmp ; Register TCCR2 ist dafuer zustaendig (siehe Datenblatt)
looping:
RJMP looping ; Die unendliche Geschichte :)
TIM2_OVR:
PUSH tmp ; Benutztes Register auf den Stack
IN tmp, SREG ; Zustand des Statusregisters einlesen
PUSH tmp ; Ebenfalls auf den Stack ablegen
INC counter2 ; Der Timer2 hat wieder mal einen Ueberlauf
LDI tmp, TIMER2 ; Kein Kommentar
OUT TCNT2, tmp ; Conuter vom Timer2 erneut laden
POP tmp ; Alten SREG-Inhalt vom Stapel holen
OUT SREG, tmp ; und das Statusregister wiederherstellen
POP tmp ; tmp wiederherstellen
RETI ; Setzte das durch den Interrupt unterbrochenes Programm fort
TIM0_OVR:
PUSH tmp ; Benutztes Register auf den Stack
IN tmp, SREG ; Zustand des Statusregisters einlesen
PUSH tmp ; Ebenfalls auf den Stack ablegen
SBIC PIND,FRONT_LED ; Was ist auf dem PIND6?
RJMP off ; Wenn 1, dann schalte auf 0
RJMP on ; Wenn 0, dann schalte auf 1
off:
CBI PORTD,FRONT_LED ; schalte auf 0
RJMP exit ; gehe zu exit
on:
SBI PORTD,FRONT_LED ; schalte auf 1
exit:
OUT TCNT0,frequence ; Conuter vom Timer0 erneut laden
POP tmp ; Alten SREG-Inhalt vom Stapel holen
OUT SREG, tmp ; und das Statusregister wiederherstellen
POP tmp ; tmp wiederherstellen
RETI ; Setzte das durch den Interrupt unterbrochenes Programm fort
toene:
.DB 213, 234, 150, 34, 123, 45, 123, 100, 80, 200, 214, 0 ; ich hatte keine Lust die richtigen Tonwerte auszurechenn :)
So, wann kommt die naechste Aufgabe?
Lesezeichen