Forum: Mikrocontroller und Digitale Elektronik AVR-Timer will nicht so wie ich


von Volker (Gast)


Lesenswert?

Hallo,

ich möchte den Timer0 im AVR missbrauchen, um eine fallende Flanke eines 
externen Signals zu detektieren. Ich simuliere das externe Signal, indem 
ich den T0-Pin als Ausgang festlege und per Software einen Pulse 
generiere.

Falls es wichtig sein sollte: uC ist der ATMega328P.

Mit folgendem Asm-Codeschnipsel will ich die Erkennung durchführen:
1
// PD4 (=T0) als Ausgang und auf low
2
            sbi         _SFR_IO_ADDR (DDRD), DDD4
3
            cbi         _SFR_IO_ADDR (PORTD), PD4
4
5
// Kein Int handling, Erkennung ueber Abfrage TIFR0
6
            cli
7
8
// Timer aus, falls er laeuft
9
            ldi         r18, (0b000 << CS00)
10
            out         _SFR_IO_ADDR (TCCR0B), r18
11
12
// Timer0 vorbereiten: CTC, OCR0A=0
13
            ldi         r18, 0
14
            out         _SFR_IO_ADDR (TCNT0), r18
15
            ldi         r18, 0
16
            out         _SFR_IO_ADDR (OCR0A), r18
17
            ldi         r18, (0b10 << WGM00)
18
            out         _SFR_IO_ADDR (TCCR0A), r18
19
            ldi         r18, (0 << WGM20) | (0b110 << CS00)
20
            out         _SFR_IO_ADDR (TCCR0B), r18
21
22
// Evtl. OCF0A loeschen
23
            sbi         _SFR_IO_ADDR (TIFR0), OCF0A
24
25
            nop
26
            nop
27
            nop
28
            nop
29
            nop
30
            nop
31
32
// Pulse auf T0 generieren
33
            sbi         _SFR_IO_ADDR (PORTD), PD4
34
            nop
35
            cbi         _SFR_IO_ADDR (PORTD), PD4
36
            nop
37
38
// Warten wg. Edge Detector
39
            nop
40
            nop
41
            nop
42
            nop
43
            nop
44
            nop
45
            nop
46
            nop
47
48
// Erkennung
49
            clr         r24
50
            sbic        _SFR_IO_ADDR (TIFR0), OCF0A
51
            inc         r24

Ergebnis ist jedoch immer 0, also Flanke nicht erkannt.

Leider ist das Datenblatt etwas missverständlich. Unter Output Compare 
Unit heisst es:
The 8-bit comparator continuously compares TCNT0 with the Output 
Compare Registers (OCR0A and OCR0B). Whenever TCNT0 equals OCR0A [...], 
the comparator signals a match. Somit würde ich erwarten, dass OCR0A 
eigentlich 1 sein müsste, damit er nach einem ClkTn den TCNT0 auf 1 
hätte und dann, gemäß obiger Aussage, einen Compare Match signalisieren 
würde.

Demgegenüber zeigt aber das Diagramm "Timer/Counter Timing Diagram, 
Clear Timer on Compare Match mode", dass der Compare Match nur 
durchgeführt wird, nachdem ein Puls auf clkTn vorliegt.

Beide Fälle habe ich im abgeprüft, hier codiert ist der zweite Fall (den 
ich auch für wahrscheinlich halte, sonst hätte Atmel nicht extra ein 
Diagramm dazu gemalt). Aber beide Prüfungen gaben auch dasselbe 
Ergebnis.

Habt ihr noch ne Idee?

Ach so: es muss der Weg über den Timer sein, da ich von insgesamt 4 
externen Signalen die Flanken beobachten muss und ich für die ersten 3 
schon die Wege INT0, INT1 und ICP1 beschreite.

Volker

von kalaus (Gast)


Lesenswert?

Volker schrieb:
> Habt ihr noch ne Idee?

Zu knapp beschrieben, wie das ganze funktionieren soll.

Nimm einen PCINT und frage in der ISR ab, ob der auslösende PIN high 
oder low ist. Ein high zeigt eine vorausgehende steigende Flanke an.

von Volker (Gast)


Lesenswert?

>ich möchte den Timer0 im AVR missbrauchen,
>um eine fallende Flanke eines externen
>Signals zu detektieren.

Was ist denn daran zu knapp beschrieben?

Wie würdest Du denn eine fallende Flanke detektieren? Dein Weg über den 
PCINT führt leider nicht zum Ziel, denn damit kann ich nur eine 
beliebige Flanke detektieren (siehe DB). Und Dein Vorschlag, in der ISR 
den Pegel zu prüfen, ist ja nicht wirklich das Wahre; während der Zeit, 
bis die ISR zur Ausführung kommt, kann sich der Pegel ja wieder ändern. 
...sonst bräuchte man ja nie eine Flankenerkennung.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Volker schrieb:
> Habt ihr noch ne Idee?

 Ja.
 Wie kommst du auf die Idee, OCR0A auf 0 zu setzen und zu erwarten,
 dass es funktioniert ?

 Es wird funktionieren, aber erst nach 256 Mal.

 Setze OCR0A auf 1 und es wird funktionieren.

von Volker (Gast)


Lesenswert?

Tut es nicht, ich schrieb ja, dass ich auch diesen Fall geprüft habe und 
es nicht funktionierte. Und dann wäre das referenzierte Diagramm im DB 
auch falsch, wonach der OCF0A dann gesetzt wird, wenn TCNT0=MAX (=OCR0A) 
zum Ende eines clkTn ist.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Volker schrieb:
> Tut es nicht, ich schrieb ja, dass ich auch diesen Fall geprüft habe und

 Tut es doch.
1
;*****************************************
2
 ;...
3
            ldi         r18, 1
4
            out         OCR0A, r18
5
;...
6
;...
7
            ldi      r18, 8
8
            clr         r24
9
lp1:
10
; Pulse auf T0 generieren
11
            sbi         PORTD, PD4
12
            nop
13
            cbi         PORTD, PD4
14
            nop
15
; Warten wg. Edge Detector
16
            nop
17
            nop
18
            nop
19
            nop
20
            nop
21
            nop
22
            nop
23
            nop
24
; Erkennung
25
            sbic        TIFR0, OCF0A
26
            inc         r24
27
      dec      r18
28
      brne    lp1
29
;*******************************************

 Und dann r24 prüfen.
 Sollte 7 sein...

von Stefan F. (Gast)


Lesenswert?

> Somit würde ich erwarten, dass OCR0A eigentlich 1 sein müsste

Ja, und warum hast du das Register dann mit 0 initialisiert?

Nachtrag: Während ich meine spontane Antwort mit dem DB vergleichen 
habe, um peinliche Fehler zu vermeinden, haben andere schon schneller 
geantwortet.

von Volker (Gast)


Lesenswert?

>  Sollte 7 sein...

Die 7 musst Du mir erklären, vllt. liegt hier mein Missverständnis. Du 
gibst 8 Pulse auf den T0, ich erwarte entsprechend ein r24=8.

Hab Deinen Vorschlag ausprobiert (es fehlt natürlich noch ein
1
sbi TIFR0, OCF0A
am Ende der Erkennung, um das gesetzte OCF0A wieder zu löschen) und das 
Ergebnis ist nicht wie von Dir vorausgesagt.

>Ja, und warum hast du das Register dann mit 0 initialisiert?
Weil ich dem Diagramm aus dem DB gefolgt bin (nur, dass wir vom Gleichen 
reden: Figure 15-11 im DB zum ATmega48A-328P) und ich ja auch schrieb:
>den ich auch für wahrscheinlich halte

Wie dem auch sei, auch die Initialisierung OCR0A=1 bringt nicht das zu 
erwartende Ergebnis.

Und noch ein Punkt: das DB redet beim CTC-Mode (vgl. 15.7.2) von 
Folgendem:
The waveform generated will have a maximum frequency of f OC0 = f 
clk_I/O /2 when OCR0A is set to zero (0x00).
Maximum wohlgemerkt, nicht mimimum

von S. Landolt (Gast)


Lesenswert?

Wie wäre es, ohne compare zu arbeiten? Also im normal mode den TCNT0 
mit 255 vorbelegen und auf TIFR0, TOV0 abfragen.

von Volker (Gast)


Lesenswert?

S. Landolt schrieb:
> Wie wäre es, ohne compare zu arbeiten? Also im normal mode den
> TCNT0
> mit 255 vorbelegen und auf TIFR0, TOV0 abfragen.

So einen ähnlichen Weg werde ich wohl einschlagen, ich werde 
wahrscheinlich TCNT0 mit einem seinem zwischengespeicherten letzten Wert 
vergleichen und bei Abweichung hab ich meine Flanke erkannt. Spart mir 
auch das Neusetzen von TCNT0 zur Erkennung weiteren Flanken.

Aber es wurmt mich schon, dass es da keinen eleganteren Weg gibt, wie 
beim INT0/1.

...und es erschüttert ein wenig mein Grundverständnis der AVR Timer.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Volker schrieb:
> Tut es nicht, ich schrieb ja, dass ich auch diesen Fall geprüft habe und
> es nicht funktionierte. Und dann wäre das referenzierte Diagramm im DB

 Nochmal.

 OCR0B bleibt auf 0.
 OCR0A setzst du auf 1.
 Bei 1 setzt OCR0A den Flag OCF0A.
 Bei 0 setzt OCR0B den Flag OCF0B.

 Einmal Dummy Puls am Anfang und dann läuft es.

 Zum Probieren:
1
; PD4 (=T0) als Ausgang und auf low
2
            sbi         DDRD, DDD4
3
            cbi         PORTD, PD4
4
5
; Kein Int handling, Erkennung ueber Abfrage TIFR0
6
            cli
7
8
; Timer aus, falls er laeuft
9
            ldi         r18, (0b000 << CS00)
10
            out         TCCR0B, r18
11
12
; Timer0 vorbereiten: CTC, OCR0B=0, OCR0A=´1
13
            ldi         r18, 0
14
            out         TCNT0, r18
15
            out         OCR0B, r18
16
            inc         r18
17
            out         OCR0A, r18
18
            ldi         r18, (0b10 << WGM00)
19
            out         TCCR0A, r18
20
            ldi         r18, (0 << WGM20) | (0b110 << CS00)
21
            out         TCCR0B, r18
22
23
;  OCF0A und OCF0B loeschen
24
            sbi         TIFR0, OCF0A
25
            sbi         TIFR0, OCF0B
26
27
            nop
28
            nop
29
            nop
30
            nop
31
            nop
32
            nop
33
34
            rcall  Pulse      ;* Dummy Pulse
35
;**************  TESTEN  ************************
36
            ldi    r18, 8
37
            clr         r24
38
            clr    r25
39
lp1:
40
            rcall  Pulse
41
; Erkennung
42
Chk0A:
43
            sbis        TIFR0, OCF0A
44
            rjmp  Chk0B
45
            inc         r24
46
            sbi         TIFR0, OCF0A
47
            rjmp  Loop
48
Chk0B:
49
            sbis        TIFR0, OCF0B
50
            rjmp  Loop
51
            inc         r25
52
            sbi         TIFR0, OCF0B
53
Loop:
54
            dec    r18
55
            brne  lp1
56
57
Halt:       rjmp  Halt
58
59
Pulse:
60
; Pulse auf T0 generieren
61
            sbi         PORTD, PD4
62
            nop
63
            cbi         PORTD, PD4
64
            nop
65
; Warten wg. Edge Detector
66
            nop
67
            nop
68
            nop
69
            nop
70
            nop
71
            nop
72
            nop
73
            nop
74
            ret
75
;*******************************************

 Jetzt sollte sowohl r24 als auch r25 4 sein.

 War mein Fehler vorher, hat nur jede 2te Flanke gezählt...

von Volker (Gast)


Lesenswert?

Marc V. schrieb:
>  Nochmal.
>
>  Einmal Dummy Puls am Anfang und dann läuft es.

Warum ein Dummy Pulse?

Und r24=4 bei 8 Pulsen hat ja weiterhin nur jede zweite Flanke gezählt, 
was ich nach meinen obigen Vermutungen auch angesichts von OCR0A=1 
erwarten würde.

Verstehe ich Deinen Post richtig, dass es ein Vorschlag sein soll, wie 
man mit dem Timer die fallenden Flanken erkennen könnte und weniger als 
Argumentation, ob jetzt OCR0A 0 oder 1 sein muss?

Ich bin nun maximal verwirrt, wie und vor allem wann der Compare Match 
funktioniert.

Was würdest Du denken, welchen Wert TCNT0 während der 8 Pulse hat?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Volker schrieb:
> Und r24=4 bei 8 Pulsen hat ja weiterhin nur jede zweite Flanke gezählt,
> was ich nach meinen obigen Vermutungen auch angesichts von OCR0A=1
> erwarten würde.

 Ja.
 Nur ist r25 auch 4, ergibt zusammen 8, OK ?
 Hätte ich bei OCF0B auch r24 anstatt r25 erhöht...


> Ich bin nun maximal verwirrt, wie und vor allem wann der Compare Match
> funktioniert.

 Dummy Pulse => Vorbereiten auf die nächste fallende Flanke.

 Pulse Nr.1 => OC0A setzt OCF0A, TCNT0 geht auf 0.
 Pulse Nr.2 => OC0B setzt OCF0B, TCNT0 geht auf 1.
 Pulse Nr.3 => OC0A setzt OCF0A, TCNT0 geht auf 0.
...
...

 So wie ich das in Erinnerung habe, zählen Timer Clocks, nicht die
 CPU Clocks und da du auf fallende Flanke reagierst ist es OK so.

: Bearbeitet durch User
von kalaus (Gast)


Lesenswert?

Volker schrieb:

> // PD4 (=T0) als Ausgang und auf low
>             sbi         _SFR_IO_ADDR (DDRD), DDD4
>             cbi         _SFR_IO_ADDR (PORTD), PD4

>             ldi         r18, (0 << WGM20) | (0b110 << CS00)
>             out         _SFR_IO_ADDR (TCCR0B), r18
>
> // Pulse auf T0 generieren
>             sbi         _SFR_IO_ADDR (PORTD), PD4
>             nop
>             cbi         _SFR_IO_ADDR (PORTD), PD4
>             nop

Ob da der PD4 auf Ausgang bleibt und nicht rumfloatet? Er wird aber 
außen beschaltet sein wie man der knappen Beschreibung nicht entnehmen 
kann.

von Volker (Gast)


Lesenswert?

kalaus schrieb:
> Volker schrieb:
>
>> // PD4 (=T0) als Ausgang und auf low
>>             sbi         _SFR_IO_ADDR (DDRD), DDD4
>>             cbi         _SFR_IO_ADDR (PORTD), PD4
>
>>             ldi         r18, (0 << WGM20) | (0b110 << CS00)
>>             out         _SFR_IO_ADDR (TCCR0B), r18
>>
>> // Pulse auf T0 generieren
>>             sbi         _SFR_IO_ADDR (PORTD), PD4
>>             nop
>>             cbi         _SFR_IO_ADDR (PORTD), PD4
>>             nop
>
> Ob da der PD4 auf Ausgang bleibt und nicht rumfloatet? Er wird aber
> außen beschaltet sein wie man der knappen Beschreibung nicht entnehmen
> kann.

???
Es wird nur zu Beginn der DDRD gesetzzt, danach bleibt es ein Ausgang.

von kalaus (Gast)


Lesenswert?

Volker schrieb:
> kalaus schrieb:
>> Volker schrieb:
>>>             ldi         r18, (0 << WGM20) | (0b110 << CS00)
>>>             out         _SFR_IO_ADDR (TCCR0B), r18
>> Ob da der PD4 auf Ausgang bleibt und nicht rumfloatet? Er wird aber

> ???
> Es wird nur zu Beginn der DDRD gesetzzt, danach bleibt es ein Ausgang.

Ich weiß nicht, was passiert, wenn er zu dem TO INPUT gemacht wird. Wenn 
es ein Input ist, schaltet ein CBI im PORTB den Pullup aus und der 
Eingang floatet. Kann sein, daß di Interrupt Simulation dann nicht geht. 
Nur eine Theorie.

von kalaus (Gast)


Lesenswert?

kalaus schrieb:
> Volker schrieb:
>> kalaus schrieb:
>>> Volker schrieb:
>>>>             ldi         r18, (0 << WGM20) | (0b110 << CS00)
>>>>             out         _SFR_IO_ADDR (TCCR0B), r18
>>> Ob da der PD4 auf Ausgang bleibt und nicht rumfloatet? Er wird aber
>
>> ???
>> Es wird nur zu Beginn der DDRD gesetzzt, danach bleibt es ein Ausgang.
>
> Ich weiß nicht, was passiert, wenn er zu dem TO INPUT gemacht wird. Wenn
> es ein Input ist, schaltet ein CBI im PORTB den Pullup aus und der
> Eingang floatet. Kann sein, daß di Interrupt Simulation dann nicht geht.
> Nur eine Theorie.

... schaltet ein CBI im PORTD den Pullup aus ...

von S. Landolt (Gast)


Lesenswert?

"If external pin modes are used for the Timer/Counter0, transitions on 
the T0 pin will clock the counter even if the pin is configured as an 
output. This feature allows software control of the counting."

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.