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?
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!!!
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 | }
|
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.
Günni schrieb: > Steht in meinem Datenblatt so aber nicht bzw noch mehr. Was meinst du konkret damit?
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.
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.
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.
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?
> 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.
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!
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"!
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 | }
|
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?
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?
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
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.
Karl Heinz Buchegger schrieb: > Hab ich einen Fehler gemacht? > Ist das TCNT Register gepuff..... > > Tatsache. Ich hab da einen Fehler gemacht. * aufdiefingerklatsch * =)
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.
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.