Forum: Mikrocontroller und Digitale Elektronik ATmega8, Timer1 läuft viel zu schnell?


von Paul H. (powl)


Lesenswert?

Nachdem mein ursprüngliches Programm etwas zu schnell ablief hab ich es 
nun auf ein einfaches TimerTestprogramm reduziert. In der Timer-ISR wird 
meine Status-LED ein und ausgeschaltet. Leider geht das ganze vieeeeeel 
zu schnell. Eignetlich sollte zwischen den Ausführungen der Timer-ISR 
immer genau eine Sekunde liegen.

Der AVR läuft über Quarz mit 8 Mhz. 8000000 : 256 (Prescaler) = 31250. 
Und diesen Wert hab ich als Comparewert geladen. D.h. der Timer führt 
31250 Taktzyklen in der Sekunde aus und nach eben diesen 31250 Zyklen 
gibt es eine Compare Interrupt, also nach einer Sekunde.

Leider läuft das ganze viel zu Schnell.. das eigentliche Programm 
sendete in der Timer-ISR über UART ein Signal an den PC, das ganze 
geschah ungefär 900 mal in der Sekunde, kann auch sein dass es durchaus 
öfter passiert wäre wenn die Senderoutine schneller gewesen wär.

Wo steckt der Fehler im Code?

mfg PoWl
1
.include "m8def.inc"          ; Deklarationen für ATmega8
2
3
4
.cseg                  ; Programm-Flash
5
    rjmp  init          ; Reset-Einsprung
6
7
.org  OC1Aaddr
8
    rjmp  timer          ; Zu Timer ISR Springen
9
10
11
.org  INT_VECTORS_SIZE        ; Interrupteinsprünge übergehen
12
13
14
init:  ldi    R16, LOW(RAMEND)    ; Stapel anlegen
15
    out    SPL, R16
16
    ldi    R16, HIGH(RAMEND)
17
    out    SPH, R16
18
19
20
    sbi    DDRB, 0          ; Status-LED Ausgang
21
22
    
23
    ; Timer 1 Compare Wert
24
    ldi    R16, LOW(31250)
25
    out    OCR1AL, R16
26
27
    ldi    R16, HIGH(31250)
28
    out    OCR1AH, R16
29
30
    ; Timer 1 CTC-Modus & Prescaler
31
    ldi    R16, (1 << CTC1 | 0b100 << CS10)
32
    out    TCCR1B, R16    
33
34
    ; Timer 1 OCA Interrupt aktivieren
35
    ldi    R16, (1 << OCIE1A)
36
    out    TIMSK, R16
37
38
39
    sei                ; Interrupts aktivieren
40
41
42
43
loop:  rjmp  loop          ; Endlosschleife
44
45
46
47
48
49
; Timer-ISR
50
51
timer:
52
    in    R16, PORTB
53
    com    R16
54
    out    PORTB, R16
55
56
    reti

von Johannes M. (johnny-m)


Lesenswert?

>  ldi    R16, LOW(31250)
>  out    OCR1AL, R16

>  ldi    R16, HIGH(31250)
>  out    OCR1AH, R16
Ganz typischer Fehler: Bei solchen 16-Bit-Doppelregistern muss IMMER 
erst das High-Byte und dann das Low-Byte geschrieben werden. Das 
High-Byte wird in einem temporären Register gepuffert. Der Zugriff auf 
das Low-Byte "triggert" den eigentlichen Schreib- bzw. Lesevorgang. 
Also:
- Beim schreiben erst High-, dann Low-Byte
- Beim Lesen erst Low-, dann High-Byte

Du schreibst das Low-Byte, was dazu führt, dass das (noch leere) 
temporäre Register ins High-Byte übernommen wird. Das anschließende 
Schreiben des High-Bytes hat überhaupt keinen Effekt mehr, weil der Wert 
erst beim Schreibzugriff auf das Low-Byte ins Register übernommen werden 
würde.

von Jörg X. (Gast)


Lesenswert?

Achte bei den 16-bit Registern auf die Reihenfolge....

Kennt der Assembler eigentlich die Namen aus den Datasheet ?
1
;...
2
    ; Timer 1 Compare Wert
3
4
    ldi    R16, HIGH(31250) ; die Reihenfolge ist wichtig!
5
    out    OCR1AH, R16
6
7
    ldi    R16, LOW(31250)
8
    out    OCR1AL, R16
9
10
    ; Timer 1 CTC-Modus & Prescaler
11
;    ldi    R16, (1 << CTC1 | 0b100 << CS10)
12
; Geht das nicht als
13
    ldi    R16, (1<<WGM12 )|(1<<CS12) ; 0b101<<CS0 ergibt 1024 als Prescaler
14
    out    TCCR1B, R16    
15
16
    ; Timer 1 OCA Interrupt aktivieren
17
    ldi    R16, (1 << OCIE1A)
18
    out    TIMSK, R16
19
;...
SO, Ich glaube,  jetzt hab ich das Kapitel Timer im Datasheet 
gründlicher gelesen als du ;)

hth. Jörg

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.