Guten Morgen,
ich programmiere grad einen Countdown für eine Alarmanlage. Dafür
benutze ich den Timer1 des 2313. Der Timer soll die zahl auf dem display
nach jeder sekunde dekrementieren. (Countdown eben) Der Systemtakt ist 4
Mhz, der timer vorteiler beträgt 64. Das macht dann 62500 Zählerschritte
pro sekunde. Der Uhrsprüngliche Code des Interrupts sieht wie folgt aus:
1
Timer1CompareHandler:
2
3
ldi lcd_temp1,0x4A
4
rcall lcd_cursorAdress
5
lds lcd_temp1,RAM_CountdownTime
6
rcall lcd_valueOut
7
lds lcd_temp1,RAM_CountdownTime
8
cpi lcd_temp1,0
9
breq Alert
10
dec lcd_temp1
11
sts RAM_CountdownTime,lcd_temp1
12
ldi r16,0
13
out TCNT1H,r16
14
out TCNT1L,r16
15
16
reti
Unter RAM_CountdownTime befindet sich der entsprechende Wert der runter
gezählt werden soll.
Im AVR studio4 funktioniert alles im simulator. In der realität zählt
der controller aber viel zu schnell. Systemtakt und vorteiler stimmen
auch.
Nachdem ich die ganze nacht rumprobiert hab, konnte ich irgendwann diese
3 Zeilen für das problem lokalisieren:
1
ldi r16,0
2
out TCNT1H,r16
3
out TCNT1L,r16
wenn ich die auskommentiere und den timer einfach überlaufen lasse, was
65535 schritten, also auch fast eine sekunde entspricht, funktionierts.
Darauf hin hab ich nochmal im datenblatt nachgelesen und bin auf
folgende Zeile gestoßen:
"Writing to the TCNT1 Register blocks (removes) the compare match on the
following
timer clock for all compare units."
Soll das jetzt heißen der setzt die OCR1A Register auf 0 zurück wenn ich
in die TCNT1 register schreibe?
ich hab daraufhin mal folgendes ausprobiert:
1
ldi r16,0
2
out TCCR1B,r16 ;timer Stoppen
3
ldi r16,0
4
out TCNT1H,r16 ; auf 0 zurücksetzen
5
out TCNT1L,r16
6
Timer1CompareA r16,62500 ; CompareA neu laden
7
StartTimer1 r16,64 ; timer starten
Funktioniert aber auch nicht, er zählt immer noch viel zu schnell.
Timer1CompareA und StartTimer1 sind Macros von mir. Die funktionieren
auch auf jeden fall.
Ich weiß jetzt echt nicht mehr weiter, der Timer scheint aus irgendeinem
grund den vorteiler zu ändern wenn ich das Zähler register auf 0 setze.
Obwohl dieser ja durch mein StartTimer1 macro ja wieder auf 64 gesetzt
werden müsste. Oder Das Compare Register ist nicht auf den richtigen
wert gestellt.
Ich verstehs einfach nicht. Im Simulator funktioniert ja alles.
Weiß jemand rat?
mfg Multikulti
Steffen P. schrieb:> Countdown> Uhrsprünglich
Noem est omen... ;-)
> Dafür benutze ich den Timer1 des 2313.
In ungünstiger Art&Weise. Für eine Uhr brauchst du einen Zähler, der
einfach immer durchläuft, ohne dass du da manuell was rumtricksen
mußt. Probiers mal mit dem CTC (Clear Timer on Compare Match) Modus, wo
das Zählerregister automatisch beim Compare Match zurückgesetzt wird.
Dann sparst du dir auch die Ungenauigkeiten bei der ISR-Ausführung...
> In der realität zählt der controller aber viel zu schnell.
Wieviel?
Hast du mal eine LED mit einer Zählschleife (ohne Interrupts) blinken
lassen? Passt die Frequenz zu deinen Erwartungen?
Hi
Wenn ich mir deinen 'Timer1CompareHandler' ansehe, wirst du noch ganz
andere Probleme bekommen. Das funktioniert nur wenn du in deinem
Hauptprogramm nichts machst. Für dein Zwecke reicht es in der
Interruptroutine einen Contdownzähler zu dekrementieren. Der ganze Rest
gehört ins Hauptprogramm.
MfG Spess
spess53 schrieb:> Wenn ich mir deinen 'Timer1CompareHandler' ansehe, wirst du noch ganz> andere Probleme bekommen. Das funktioniert nur wenn du in deinem> Hauptprogramm nichts machst. Für dein Zwecke reicht es in der> Interruptroutine einen Contdownzähler zu dekrementieren. Der ganze Rest> gehört ins Hauptprogramm.
Die Interrupt routine braucht etwa 10ms und wird einmal die sekunde
aufgerufen. Das ist 1% der prozessorzeit. Wo soll es da probleme geben?
Im Hauptprogramm gibt es nicht viel zu tun, außer ein bischen tastenfeld
abfragen.
Lothar Miller schrieb:> Hast du mal eine LED mit einer Zählschleife (ohne Interrupts) blinken> lassen? Passt die Frequenz zu deinen Erwartungen?
ja dann passt es. Hab ja auch wie gesagt den zähler einfach mal
durchlaufen lassen und im InterruptHandler die Lampe blinken lassen.
Funktioniert auch. nur wenn ich das zählerregister manuel zurücksetze
spielt alles verrückt. Allerdings nur auf dem board, im simulator
funktioniert es =/
Lothar Miller schrieb:> Probiers mal mit dem CTC (Clear Timer on Compare Match) Modus, wo> das Zählerregister automatisch beim Compare Match zurückgesetzt wird.
Das ergibt Sinn :D Ich hab mal wieder ein paar Zeilen zu viel
hingeklatscht anstadt im Datenblatt zu lesen. Trozdem währe es
interessant zu wissen warum dieses Problem jetzt auftritt. Zumal es halt
im Simulator funktioniert.
Hi
>Die Interrupt routine braucht etwa 10ms und wird einmal die sekunde>aufgerufen. Das ist 1% der prozessorzeit. Wo soll es da probleme geben?>Im Hauptprogramm gibt es nicht viel zu tun, außer ein bischen tastenfeld>abfragen.
Du hast weder eine Sicherung des Statusregisters noch irgendwelcher
anderer Register in der Interruptroutine. Mag in Einzelfällen
funktionieren, ist aber in 99,9% aller Fälle tödlich. Und solcher
Mischmasch:
> dec lcd_temp1 <<<<<<<<<<<<<<<<<<<<<<> sts RAM_CountdownTime,lcd_temp1> ldi r16,0 <<<<<<<<<<<<<<<<<<<<<<
auch.
MfG Spess
Hi
Lies mal in de Tutorials die Geschichte mit den Timern. Und dann mach
dir mal Gedanken, wie eine ISR funktioniert... ja, sie wird irgendwo und
irgendwann im Programm aktiviert. Wenn da grad mal ein Befehl stand
1
CPI R16, 5
und dann deine ISR zuschlägt, kannst du getrost vergessen, ob der
Vergleich irgendein Ergebnis gebracht hat. Daher wird von Experten
empfohlen, die Push und POP Befehle zu nutzen und die Register, die in
der ISR benutzt werden vorher auf einen ! vorhandenen Stack zu sichern.
Wenn du "komische" Bearbeitung deines Programms hast, dann denk mal
drüber nach, welchen Inhalt ein Register vor und nach dem Interrupt hat.
Des weiteren solltest du dir gar nicht erst angewöhnen, ISR mit 10 msek.
Bearbeitungszeit zu schreiben. Der Grund: Es gibt ja noch andere
Interrupts, die du irgendwann mal verwenden willst. Und wenn du dir
nicht zwingend auferlegst, mit so wenig Zeit wie möglich, in der ISR zu
verweilen, dann bekommst du früher oder später ein Problem. In ASM mag
es noch gehen, da die Bearbeitungszeit vom Code nachzurechnen ist, aber
in einer Hochsprache erwischt es dich dann Eiskalt. Ob es der Compiler
abfängt, kann ich nicht sagen, da ich nicht in BASCOM oder C
programmiere, aber da gibt's hier bestimmt einen Fachleut für, der dir
das genau sagen kann.
In einer ISR werden nur Zeitkritische Vorgänge durchgeführt. Ob das der
Empfang über RS232 ist oder ein mSek.Takt. Die Bewertung und
verarbeitung erledigst du außerhalb in deiner Programmschleife...
Übrigends, es macht nicht wirklich viel Sinn, eine ISR so aufzubauen,
das sie nur jede Sekunde aufgerufen wird. Mach 1mSek. draus und zähl
eine Variable bis 1000. So kannst du wesentlich mehr mit deiner
Timer-ISR anfangen.
Angenommen, der Interrupt lommt alle mSek. dann zähl diese bis 100. Das
ergibt 1/10 Sek.
1
Timer_ISR: ; Aufruf erfolgt jede msek.
2
Push r16 ; r16 sichern
3
In r16, SReg ; Statusregister holen
4
Push r16 ; und sichern
5
Push .... ; weitere verwendete Register
6
lds r16, milli_Sek
7
INC r16
8
STS milli_Sek, r16
9
CPI r16, 100
10
BRLO End_ISR
11
CLR r16
12
STS milli_Sek
13
LDS R16, Time_Flag
14
ORI R16, 0b00000001 ; Bit 0 ist Flag für 1/10tel Sek.
15
STS Time_Flag, r16
16
End_ISR:
17
pop.... ; evtl. andere verwendete Register
18
POP R16 ; Statusregister
19
OUT SReg, R16 ; wieder herstellen
20
POP r16 ; und r16 wieder herstellen
21
RETI
In deiner Programmschleife fragst du das Bit 0 in Time_Flag ab und setzt
es nach einer evtl. Bearbeitung zurück. Wenn du angst hast, das 100
mSek. im Hauptprogramm zu ungenau werden könnten, weil große
Zykluszeiten hast, dann schieb halt eine weitere Stufe in die ISR und
mach dir ein Sekunden-Flag.
Gruß oldmax
Die register sollte man schon retten, da habt ihr recht^^ Im
Hauptprogramm steht noch gar nix, deshalb ist mir der fatale Fehler noch
gar nicht aufgefallen.
Meine ursprüngliche Frage wurde aber immer noch nicht beantwortet.
Die hier?
>> Soll das jetzt heißen der setzt die OCR1A Register auf 0 zurück wenn ich>> in die TCNT1 register schreibe?
Das heisst es nicht. OCR1A bleibt unverändert.
Wenn du TCNT1 beschreibst, tritt im unmittelbar darauf folgenden Takt
kein Compare Match Ereignis auf, selbst wenn das neue TCNT1 und der
OCR1A Wert gleich sind.
Steffen P. schrieb:> Die Interrupt routine braucht etwa 10ms und wird einmal die sekunde> aufgerufen. Das ist 1% der prozessorzeit. Wo soll es da probleme geben?
Solange es keine anderen Interrupts gibt, die schneller als nach 10ms
kommen können und solange Du sämtliche LCD-Ausgaben im Main unter
Interruptsperre machst, gibt es keine Probleme.
Aber mir wären das zu krasse Einschränkunen für den weiteren Ausbau
eines Programms.
Ich mag es, wenn Code erweiterbar und nachnutzbar ist und ich nicht
jedesmal von vorn anfangen muß.
Peter