Forum: Mikrocontroller und Digitale Elektronik 16bit Timer1 - Inerrupt beim Schreiben von TCNTx deaktivieren?


von M. G. (ixil96)


Lesenswert?

Hallo,

ich habe eine Frage zum 16bit Timer1. Im Datenblatt wird man darauf 
hingewiesen, dass man beim Zugriff auf das 16bit Register (2 x 8 Bit 
Register) Interrupts vor dem Zugriff deaktivieren und nachher wieder 
aktivieren soll, damit ein eventuell auftretender Interrupt beim 
Schreiben oder Lesen zwischen den beiden 8 Bit Operationen nicht rein 
pfuscht.

Muss ich das in C auch machen oder erledigt das der Compiler für mich?

von kaplic (Gast)


Lesenswert?

Da im Datenblatt steht, was du tun sollst, musst du das auch in C tun! 
Der Compiler ist keine Glaskugel, welche weiß, was du vor hast!!!

von M. G. (ixil96)


Lesenswert?

Ok, d.h. wenn ich zum Timer Interrupt einen weiteren Interrupt im 
Programm verwende, muss ich einen möglichen weiteren Interrupt in dem 
Moment verhindern, wenn auf die 16bit Register des Timers zugegriffen 
wird.

z.B. so?

TCNTx lesen:
1
unsigned int TIM16_ReadTCNTn( void )
2
{
3
  unsigned char sreg;
4
  unsigned int i;
5
6
  sreg = SREG;  /* Save global interrupt flag */
7
  cli();        /* Disable interrupts */
8
  i = TCNTn;    /* Read TCNTn into i */
9
  sei();        /* Enable interrupts */
10
  SREG = sreg;  /* Restore global interrupt flag */
11
  return i;
12
}

von Günni (Gast)


Lesenswert?

m. g. schrieb:
> ich habe eine Frage zum 16bit Timer1. Im Datenblatt wird man darauf
> hingewiesen, dass man beim Zugriff auf das 16bit Register (2 x 8 Bit
> Register) Interrupts vor dem Zugriff deaktivieren und nachher wieder
> aktivieren soll, damit ein eventuell auftretender Interrupt beim
> Schreiben oder Lesen zwischen den beiden 8 Bit Operationen nicht rein
> pfuscht.

Steht in meinem Datenblatt so aber nicht bzw noch mehr.

von M. G. (ixil96)


Lesenswert?

Günni schrieb:

> Steht in meinem Datenblatt so aber nicht bzw noch mehr.

Was meinst du konkret damit?

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

m. g. schrieb:
>   sreg = SREG;  /* Save global interrupt flag */
>   cli();        /* Disable interrupts */
>   i = TCNTn;    /* Read TCNTn into i */
>   sei();        /* Enable interrupts */
>   SREG = sreg;  /* Restore global interrupt flag */

Das "sei()" kannst - oder besser gesagt SOLLTEST du weglassen.

von Günni (Gast)


Lesenswert?

m. g. schrieb:
> Günni schrieb:
>
>> Steht in meinem Datenblatt so aber nicht bzw noch mehr.
>
> Was meinst du konkret damit?

Bei mir steht nicht, daß man bei Beschreiben von TCNT die Interrupts 
disablen soll. Auf manche 16Bit Register mag das zutreffen, bei mir 
nicht auf das TCNT.

von Günni (Gast)


Lesenswert?

Günni schrieb:
> Bei mir steht nicht, daß man bei Beschreiben von TCNT die Interrupts
> disablen soll. Auf manche 16Bit Register mag das zutreffen, bei mir
> nicht auf das TCNT.

... bei Lesen auch nicht.

von M. G. (ixil96)


Lesenswert?

Magnus M. schrieb:
> m. g. schrieb:
>>   sreg = SREG;  /* Save global interrupt flag */
>>   cli();        /* Disable interrupts */
>>   i = TCNTn;    /* Read TCNTn into i */
>>   sei();        /* Enable interrupts */
>>   SREG = sreg;  /* Restore global interrupt flag */
>
> Das "sei()" kannst - oder besser gesagt SOLLTEST du weglassen.

Original steht das auch so nicht im Datenblatt. Wie du sagst, ohne 
sei();
Aber ist es nicht logisch, wenn ich die Interrupts gleich nach dem Lesen 
des TCNTn wieder einschalte? Wann würden sie denn sonst wieder 
eingeschaltet?

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

> Original steht das auch so nicht im Datenblatt. Wie du sagst, ohne
> sei();

Eben.

> Aber ist es nicht logisch, wenn ich die Interrupts gleich nach dem Lesen
> des TCNTn wieder einschalte?

Nein.

> Wann würden sie denn sonst wieder eingeschaltet?

Ganz einfach durch die Zeile

SREG = sreg;  /* Restore global interrupt flag */

Das Global Interrupt Enable Flag ist Bestandteil des SREG. War dieses 
vorher gesetzt, so wird es nach dieser Zeile auch wieder gesetzt sein. 
War es vorher gelöscht, so wird es auch nach dieser Zeile gelöscht sein.

von M. G. (ixil96)


Lesenswert?

kaplic schrieb:
> Da im Datenblatt steht, was du tun sollst, musst du das auch in C tun!
> Der Compiler ist keine Glaskugel, welche weiß, was du vor hast!!!

Hier ein Auszug aus dem Datenblatt (AT90PWM316 Seite 105): (Habs erst 
jetzt gesehen)

The following code examples show how to access the 16-bit Timer 
Registers assuming that no interrupts updates the temporary register. 
The same principle can be used directly for accessing the OCRnx and ICRn 
Registers.

Note that when using “C”, the compiler handles the 16-bit access.

Wie es aussieht, erledigt der Compiler doch den 16-bit Zugriff und 
übernimmt das Deaktivieren und Aktivieren der Interrupts!

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

m. g. schrieb:
> Note that when using “C”, the compiler handles the 16-bit access.
>
> Wie es aussieht, erledigt der Compiler doch den 16-bit Zugriff und
> übernimmt das Deaktivieren und Aktivieren der Interrupts!

NEIN! Der Compiler kümmert sich nur um die korrekte Reihenfolge des 
Registerzugriffs.

> The following code examples show how to access the 16-bit Timer
> Registers assuming that no interrupts updates the temporary register.

Beachte die Aussage "assuming that no interrupts updates the temporary 
register"!

von Fabian O. (xfr)


Lesenswert?

m. g. schrieb:
> Wann würden sie denn sonst wieder eingeschaltet?

Wie es im Kommentar steht, durch das SREG = sreg. Ein bestimmtes Bit im 
Statusregister kodiert, ob die Interrupts an oder aus sind. Durch cli() 
kann man es löschen (Interrupts aus), und mit sei() setzen (Interrupts 
an).

Der Code stellt also den Zustand wieder her, den das Bit vorher hatte:
1
sreg = SREG;  /* Save global interrupt flag */
2
cli();        /* Disable interrupts */
3
i = TCNTn;    /* Read TCNTn into i */
4
SREG = sreg;  /* Restore global interrupt flag */
Wenn die Interrupts am Anfang aus waren, bleiben sie aus. Wenn sie an 
waren, werden sie wieder eingeschaltet.

Mit sei() würdest Du sie dagegen immer einschalten, egal ob sie vorher 
an oder aus waren. Wenn Du also sicher weißt, dass sie vorher an waren, 
kannst Du Dir das Sichern des Statusregisters sparen:
1
cli();        /* Disable interrupts */
2
i = TCNTn;    /* Read TCNTn into i */
3
sei();        /* Enable interrupts */

Beides Kombinieren macht aber keinen Sinn. In util/atomic.h gibt es 
übrigens Makros für diese beiden Sequenzen:
1
#include <util/atomic.h>
2
3
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
4
  i = TCNTn;
5
}

bzw.
1
ATOMIC_BLOCK(ATOMIC_FORCEON) {
2
  i = TCNTn;
3
}

von M. G. (ixil96)


Lesenswert?

Magnus M. schrieb:

> Beachte die Aussage "assuming that no interrupts updates the temporary
> register"!

Ok, jetzt wird mir die Sache schon klarer. Wenn ich das jetzt richtig 
verstanden habe, muss ich nur darauf achten, welches Ereignis mir das 
temporäre 8-bit Register im entscheidenten Moment überschreiben kann!

Und das kann denke ich jetzt nur das TCNTn, OCRnx oder das ICRn sein? 
Also ein 16-bit Register, dass ebenso das temporäre 8-bit Register 
verwendet.

Wenn ich also nur einen 16-bit Timer im CTC-Mode laufen habe und z.B. 
einen ext. Int0, dann brauche ich mir keine Gedanken darüber machen?

Sehe ich das richtig?

von Karl H. (kbuchegg)


Lesenswert?

m. g. schrieb:

> Wenn ich also nur einen 16-bit Timer im CTC-Mode laufen habe und z.B.
> einen ext. Int0, dann brauche ich mir keine Gedanken darüber machen?
>
> Sehe ich das richtig?

Nein, das siehst du falsch.

Wenn es möglich ist, egal wie, dass dir beim Auslesen eines 16 Bit 
Registers ein Interrupt reinkommt, wenn das eine Byte schon gelesen 
wurde und das andere noch nicht, dann kannst du in ein Problem laufen, 
weil die beiden Bytes nicht aus demselben Zählvorgang stammen können. 
Punkt.

Wozu willst du das überhaupt auslesen?

von Oliver (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Nein, das siehst du falsch.

Doch, das sieht er richtig. Schliesslich wird das zweite Byte ja nicht 
aus dem Zähler, sondern aus dem tmp-Register gelesen, und solange das 
niemand zwischenzeitlich verändert hat, verändert sich nicht.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Oliver schrieb:
> Karl Heinz Buchegger schrieb:
>> Nein, das siehst du falsch.
>
> Doch, das sieht er richtig. Schliesslich wird das zweite Byte ja nicht
> aus dem Zähler, sondern aus dem tmp-Register gelesen,


Hab ich einen Fehler gemacht?
Ist das TCNT Register gepuff.....

Tatsache. Ich hab da einen Fehler gemacht.
Danke für die Korrektur.

von Magnus M. (magnetus) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Hab ich einen Fehler gemacht?
> Ist das TCNT Register gepuff.....
>
> Tatsache. Ich hab da einen Fehler gemacht.

* aufdiefingerklatsch *

=)

von Peter D. (peda)


Lesenswert?

m. g. schrieb:
> Wenn ich also nur einen 16-bit Timer im CTC-Mode laufen habe und z.B.
> einen ext. Int0, dann brauche ich mir keine Gedanken darüber machen?

???

Der Knackpunt ist nicht der Interrupt, sondern was Du darin machst.

Wenn irgendein Interrupt auf TCNTn, OCRnx oder das ICRn zugreift, muß 
das Main solche Zugriffe atomar kapseln.
Z.B. der INT0 liest TCNT1 und das Main schreibt OCR1B.

Alternativ kann aber auch der Interrupt das temporäre Register vor dem 
Zugriff sichern.

von Oliver (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Alternativ kann aber auch der Interrupt das temporäre Register vor dem
> Zugriff sichern.

Das schon, da man das aber nicht zurückgeschrieben bekommt, ohne ohne 
eins der Timerregister zu verändern, bringt das auch keine großen 
Vorteil.

Oliver

von Oliver (Gast)


Lesenswert?

Nachtrag:

Oliver schrieb:
> Das schon, da man das aber nicht zurückgeschrieben bekommt, ohne ohne
> eins der Timerregister zu verändern, bringt das auch keine großen
> Vorteil.

ist falsch. das tmp-Register lässt sich natürlich doch zurückschreiben.

Oliver

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.