Forum: Mikrocontroller und Digitale Elektronik Attiny 2313 verzwicktes Problem mit Timer


von Multi K. (multikulti)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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?

von spess53 (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

Siehe Interrupt

von Multi K. (multikulti)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

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

von ein Gast (Gast)


Lesenswert?

Einen genauen Sekundentakt zu erzeugen, ist doch keine Neuerfindung.
Hiermit geht's:
Beitrag "Die genaue Sekunde / RTC"

von Oldmax (Gast)


Lesenswert?

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

von Multi K. (multikulti)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

Hi

>Meine ursprüngliche Frage wurde aber immer noch nicht beantwortet.

Auf Grund welcher Information von dir sollte man eine Antwort finden?

MfG spess

von Krapao (Gast)


Lesenswert?

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.

von Multi K. (multikulti)


Lesenswert?

Ah ok. Hab mein Bock auch gefunden.
Hab im OCR1A Register das Low Byte vorm High Byte beschrieben.

Danke an Alle.

von Peter D. (peda)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.