Problemstellung
Ein ATtiny2313 läuft mit 1 MHz Systemtakt und soll eine LED mit 0,5 Hz blinken lassen unter Verwendung eines Timer-Overflow-Interrupts.
Prinzip des Timer-Overflow-Interrupts
Präsentation
(Pfeiltasten verwenden)
Die richtigen Einstellungen ermitteln
Die Interrupt Service Routine (ISR) soll zu bestimmten Zeiten aufgerufen werden, hier alle 250ms. Ein Overflow-Interrupt wird beim Überlauf des Zählers vom höchsten Wert -hier $FF nach $00 ausgelöst. Um die gewünschte Zeit zu erreichen muss eine geeignete Kombination aus Systemtakt-Vorteilung und Vorspannung des Zählers, also des Wertes von dem er bis zum Maximum läuft, gefunden werden. Dazu ermittelt man für verschiedene Vorteilerwerte die mögliche Genauigkeit:
Vorteilerwert
Abstand Zählerimpuls
Zählertakte
Genauigkeit
1024
1024µs
250000/1024 = 244.1
244*1024µs = 249,86 ms
256
256µs
250000/256 = 976,56
zu groß für 8 Bit
Hier ist nur der Vorteilerwert 1024 möglich, kleinere Werte würden mehr Zählimpulse erfordern als mit 8Bit zählbar wären. Bei einer Vorspannung des Zählers mit dem Wert 12 wird nach 256-12=244 Takten der Overflow-Interrupt ausgelöst.
Tipp: Ist der Vorteilerwert schon maximal und die notwendigen Zähltakte passen nicht mehr in den Zähler, dann entweder grösseren Zähler verwenden oder in der ISR weiterzählen.
Quellcode für Version mit 8Bit Timer0
Quellcode [blinker-mit-timer.asm ] ; *** Blinklicht mit 8Bit Timer 1.0 (c) Oliver Mezger 01.01.2011
.include "tn2313def.inc"
;* Blinker (PB0) soll mit 0,5Hz blinken, Interrupt wird alle 250ms aufgerufen
;* Interner RC-Oszillator mit 8MHz geteilt durch 8 (tiny2313 Spezialitaet)
;* Systemtakt Phi ist somit 1MHz
;* Zaehlertakt von 8Bit-Timer0 sei Phi/1024 somit waere Interruptintervall = 256*1024us = 262ms
;* Zaehler startet deshalb nicht mit 0 sondern einem hoeheren Wert 12 damit Zeit stimmt.
.def tmp = R16 ;Hilfsregister
.def itmp = R17 ;Hilfsregister fuer Interrupt
rjmp reset ;Sprung zur Initialisierung
.org OVF0addr ;naechter Befehl an der Interrupteinsprungadresse
rjmp isrOVF0 ;Sprung zur Interruptbehandlungsroutine
.org INT_VECTORS_SIZE ;weiter nach Interrupttabelle
reset: ;Initialisierung nach Reset
ldi tmp,low( RAMEND) ;oberste RAM-Adresse laden
out SPL,tmp ;Stackpointer initialisieren
ldi tmp,$ff
out PORTB,tmp ;alle LED aus
out DDRB,tmp ;PB als Ausgang
ldi tmp,5 ;Vorteiler auf Phi/1024 setzen
out TCCR0B,tmp ;alle 1024us ein Zaehl-Impuls
ldi tmp,1 << TOIE0 ;Timer Overflow Interrupt Enable 0 setzen
out TIMSK,tmp ;Timer kann nun Interrupt ausloesen
ldi tmp, 12 ;Vorspannen des Timers
out TCNT0,tmp ;damit die Zeit bis Overflow stimmt
sei ;globale Interrupt Freigabe
loop:
rjmp loop ;Endlos-Schleife
isrOVF0:
ldi itmp,12 ;Wert zum Vorspannen des Timers laden
out TCNT0,itmp ;in den Timer schreiben
sbic PORTB,0 ;wenn PB0
rjmp clearPB0 ;loesche PB0
sbi PORTB,0 ;sonst setze PB0
reti ;Interruptbehandlung ist zu Ende
clearPB0:
cbi PORTB,0 ;loesche PB0
reti ;Interruptbehandlung ist zu Ende Der Blinker soll nun mit 1Hz blinken, modifizieren Sie den Code.
Verwenden Sie nun den 16-Bit Timer1 um das 1Hz Blinken zu erzeugen.
Lösung anzeigen..
Quellcode [blinker-mit-interrupt.asm ]; *** Blinklicht mit Interrupt 1.0 (c) Oliver Mezger 20.07.2010
.include "tn2313def.inc"
;* Blinker (PB0) soll mit 1Hz blinken, Interrupt wird alle 500ms aufgerufen
;* Interner RC-Oszillator mit 8MHz geteilt durch 8 (tiny2313 Spezialitaet)
;* Systemtakt Phi ist somit 1MHz
;* Zaehlertakt von 16Bit-Timer1 sei Phi/8 somit waere Interruptintervall = 65536*8us= 524,29ms
;* Zaehler startet deshalb nicht mit 0 sondern einem hoeheren Wert damit Zeit stimmt.
.equ zaehlerstartwert = 3036 ;Wert mit dem der Zaehler startet
.def tmp = R16 ;Hilfsregister
.def itmp = R17 ;Hilfsregister fuer Interrupt
rjmp reset ;Sprung zur Initialisierung
.org OVF1addr ;naechter Befehl an der Interrupteinsprungadresse
rjmp isrOVF1 ;Sprung zur Interruptbehandlungsroutine
.org INT_VECTORS_SIZE ;weiter nach Interrupttabelle
reset: ;Initialisierung nach Reset
ldi tmp,low( RAMEND) ;oberste RAM-Adresse laden
out SPL,tmp ;Stackpointer initialisieren
ldi tmp,$ff
out PORTB,tmp ;alle LED aus
out DDRB,tmp ;PB als Ausgang
ldi tmp,1 << CS11 ;Bit CS11 setzen Vorteiler ist Phi/8
out TCCR1B,tmp ;Zaehler bekommt 125kHz Takt, alle 8us einen Impuls
ldi tmp,1 << TOIE1 ;Timer Overflow Interrupt Enable 1 setzen
out TIMSK,tmp ;Timer kann nun Interrupt ausloesen
sei ;generelle Interrupt Freigabe
loop:
rjmp loop ;Endlos-Schleife
isrOVF1: ;kein Register wird veraendert!
ldi itmp,high( zaehlerstartwert) ;obere 8Bit
out TCNT1H,itmp ;zuerst High schreiben
ldi itmp,low( zaehlerstartwert) ;untere 8Bit
out TCNT1L,itmp ;Uebernahme bei Schreiben des LowByte
sbic PORTB,0 ;wenn PB0
rjmp clearPB0 ;loesche PB0
sbi PORTB,0 ;sonst setze PB0
reti ;Interruptbehandlung ist zu Ende
clearPB0:
cbi PORTB,0 ;loesche PB0
reti ;Interruptbehandlung ist zu Ende
Weitere Übungsaufgaben
Gegeben: 4 MHz Systemtakt, 8Bit-Timer 0, Overflow-Interrupt, Blinken mit 1 Hz (0,5s an; 0,5s aus) an PB0.
Gegeben: 12 MHz Systemtakt, 16Bit-Timer 1, Overflow-Interrupt, Blinken mit 1 Hz an PB0.
Dimmen einer LED mit Pulsweitenmodulation
Die Helligkeit von LEDs kann man durch schnelles Ein- und Ausschalten variieren.
Eine LED an PB0 leuchtet bei log. 0.
Die Helligkeit der LED soll einstellbar sein.
Die Zeit in der sie leuchtet ist tein und in der sie aus ist taus .
Die Zeiten tein und taus werden durch einen 8 Bit Timer realisiert.
Die Periodendauer T ist tein +taus .
Bei maximaler Einschaltdauer (tein = T) ist die Ausschaltzeit taus = 0
Der Timer wird mit Systemtakt 1Mhz ohne Vorteilung betrieben.
Verwenden Sie den Overflow Interrupt.
Die LED soll zunächst ca. 10% Hell sein.
LED leuchtet hell da länger 0 als 1
tein > taus
LED leuchtet dunkel da länger 1 als 0
tein < taus
Wie viele Helligkeitsabstufungen sind möglich -Begründung?
Erstellen Sie das Programm.
Die Helligkeit soll selbständig langsam variieren.
Lösung anzeigen..
Quellcode [LED-Dimmer.asm ]; *** LED-Dimmer mit 8Bit Timer0 1.0 (c) Oliver Mezger 12.01.2011
.include "tn2313def.inc"
;* LED an PB0 neg. Logik soll gedimmt werden. Timer0 mit OverFlow Interrupt
;* Interner RC-Oszillator mit 8MHz geteilt durch 8 (tiny2313 Spezialitaet)
;* Systemtakt Phi ist somit 1MHz
;* Zaehlertakt von 8Bit-Timer0 sei Phi
;* LED soll nach Initialisierung mit 10% leuchten 25us 0, 230us 1
.def tmp = R16 ;Hilfsregister
.def itmp = R17 ;Hilfsregister fuer Interrupt
.def led = R18 ;LED Helligkeit
.def innen = R20 ;Hilfsregister fuer Warteschleife
.def aussen = R21 ;Hilfsregister fuer Warteschleife
.equ clk = 1 ;CPU-Takt in MHz
rjmp reset ;Sprung zur Initialisierung
.org OVF0addr ;naechter Befehl an der Interrupteinsprungadresse
rjmp isrOVF0 ;Sprung zur Interruptbehandlungsroutine
.org INT_VECTORS_SIZE ;weiter nach Interrupttabelle
reset: ;Initialisierung nach Reset
ldi tmp,low( RAMEND) ;oberste RAM-Adresse laden
out SPL,tmp ;Stackpointer initialisieren
ldi led,25 ;LED 10% Helligkeit
ldi tmp,$ff
out PORTB,tmp ;alle LED aus
out DDRB,tmp ;PB als Ausgang
ldi tmp,1 ;Vorteiler auf Phi
out TCCR0B,tmp ;alle 1us ein Zaehl-Impuls
ldi tmp,1 << TOIE0 ;Timer Overflow Interrupt Enable 0 setzen
out TIMSK,tmp ;Timer kann nun Interrupt ausloesen
sei ;globale Interrupt Freigabe
loop:
rcall warte10ms ;warte 10ms
inc led; ;erhoehe Helligkeit um 1
rjmp loop ;Endlos-Schleife
isrOVF0:
in itmp,SREG ;laden von SREG
push itmp ;retten von SREG
sbic PORTB,0 ;wenn LED aus
rjmp ledan ;dann LED an
out TCNT0,led ;0 = lange aus, 255 = kurz aus
sbi PORTB,0 ;LED ausschalten
rjmp isrend ;zum Ende
ledan:
ldi itmp,255 ;lade 255
sub itmp,led ;255-led ist an
out TCNT0,itmp ;in den Timer schreiben
cbi PORTB,0 ;LED einschalten
isrend:
pop itmp ;SREG wieder herstellen
out SREG,itmp
reti ;Interruptbehandlung ist zu Ende
warte10ms: ;Unterprogramm 10ms warten
ldi aussen,clk* 10 ;anpassen an CPU-Takt
loop_aussen:
ldi innen,250
loop_innen:
nop
dec innen
brne loop_innen
dec aussen
brne loop_aussen
ret
Die Helligkeit soll über zwei Taster an PD eingestellt werden können.