Hi!
Ich möchte vermeiden, daß der Timer0 überläuft. Er soll aber nicht
zurückgesetzt oder angehalten, sondern nur begrenzt werden. Damit würde
ich zwei Fliegen mit einer Klappe schlagen: Timer läuft nicht über und
der Timer-Wert wird in seiner Höhe begrenzt.
Dazu schreib ich in der Hauptschleife einfach:
1
if(TCNT1>25000)
2
TCNT1=25000;
Problem: Das wird irgendwie vom Compiler (WinAVR) wegoptimiert, sobald
ich die Optimierung einschalte.
Wie kann man das verhindern bzw. intelligenter lösen?
F. P. schrieb:> Problem: Das wird irgendwie vom Compiler (WinAVR) wegoptimiert, sobald> ich die Optimierung einschalte.
woher weist du das?
hast du in ASM Listing geschaut?
was soll denn deiner Meinung nach mit dem Timer sonst passieren wenn er
nicht zurückgesetzt oder angehalten wird?
Wenn du den einmal initialisiert hast, dann läuft der hald einfach...
Peter II schrieb:> woher weist du das?>> hast du in ASM Listing geschaut?
Im Disassembler hab ich das gesehen.
flo schrieb:> Du solltest dich entscheiden, Timer 0 oder 1.
Ups, ich meine natürlich den Timer1 mit seinen 16 Bit.
Das ganze Ansinnen klingt nicht logisch.
Wozu soll das gut sein?
Normalerweise würde man das wohl so machen, das man mit einer Compare
Unit den Timer darauf 'scharf macht', bei einem bestimmten Zählerstand
einen Interrupt auszulösen. In der zugehörigen ISR würde man dann dem
Timer einfach den Vorteiler abschalten wodurch der Timere steht.
Allerdings ist aich das Ständige Starten und Stoppen eines Timers
meistens ein Zeichen dafür, dass man etwas ganz grundsätzliches nicht
wirklich verstanden hat.
Also: Aus einer höheren Warte gesehen. Warum denkst du, du müsstest den
Timerwert 'einfrieren'? Was ist die Aufgabenstellung, die so etwas
erfordert?
Aufgabenstellung:
Ich messe die Periodendauer eines Rechtecksignals (Interrupt auf
fallende Flanke) und gebe ein Rechtecksignal nach einer beliebigen
Zeitverzögerung mit gleichbleibender High-Zeit wieder aus.
Forderung:
Die Ausgabe soll nur bis zu einer festgelegten maximalen Periodendauer
arbeiten, z.B. TCNT1 < 25000. Ist die Periodendauer länger, soll der
Ausgang inaktiv bleiben.
Lösungsansatz:
Timer1 setze ich in der CAPT-ISR auf null. Die Zeitverzögerung übernimmt
OCR1A: Beim COMPA-Interrupt wird der Ausgang auf eins und anschließend
in der Main-Schleife nach einer festen Verzögerungszeit wieder auf null
gesetzt.
Problem:
Durch den automatischen Timer-Überlauf bei zu großer Periodendauer des
Meßsignals wird zwangsweise ein Ausgangssignal bei jedem Überlauf
erzeugt.
Daher dachte ich, es ist am einfachsten, wenn man den Zähler gar nicht
erst die Chance gibt, überzulaufen.
F. P. schrieb:> Daher dachte ich, es ist am einfachsten, wenn man den Zähler gar nicht> erst die Chance gibt, überzulaufen.
Am einfachsten ist es, die Dinge voneinander zu trennen. Deine Aufgabe
besteht aus 2 Teilaufgaben
* ausmessen der Periodendauer der Rechteckschwingung
* Erzeugung eines Pules nach einer beliebigen Verzögerungszeit,
getriggert von der Flanke des Rechtecksignals.
Behandle die Ding auch so.
Nicht immer kann man den Timer als alleinigen Zeitgeber bzw. Zeitzähler
benutzen. Wenn 'beliebig' auch wirklich beliebig bedeutet, dann muss
eine Verzögerungszeit von einer halben Stunde auch möglich sein. Das
aber wirst du mit dem Timer alleine, ohne zusätzliche Zählstufen nicht
hinkriegen.
Und nein. Du brauchst dazu den Timer nicht zu limitieren. Machst du ja
mit deiner Armbanduhr auch nicht, wenn du Zeiten stoppen musst. Wenn der
Skiläufer am Start wegfährt, dann merkst du dir die NUmmer der Sekunde
in der das passiert und wenn er unten durchs Ziel fährt, dann merkst du
dir die Sekundennummer wann er im Ziel angekommen ist. Die Differenz ist
die Laufzeit. Ist er bei 23 oben weggefahren und bei 51 durchs Ziel
gegangen, dann war er 51-23 gleich 28 Sekunden unterwegs. Und diese
Differenz kann man darauf hin überprüfen ob sie kleiner oder größer als
zb 19 ist. Wenn sie kleiner ist, dann geht ein Licht an (bzw. dann
startest du die Verzögerung für die Pulsgenerierung), ist sie nicht
kleiner, dann passiert eben nichts.
Aber zu keinem Zeitpunkt musstest du an deiner Armbanduhr rumfummeln um
den Sekundenzeiger laufend zurückzusetzen.
F. P. schrieb:> Dazu schreib ich in der Hauptschleife einfach:if(TCNT1 > 25000)> TCNT1 = 25000;
Und wie soll das funktionieren?
Das Programm kommt an die Anweisung vorbei und setzt das Counterregister
auf 25000 zurueck. Danach laueft der Timer einfach weiter... bis
irgendwann mal wieder das Programm an die Anweisung vorbeikommt. Ist er
dann zufaellig uebergelaufen und dabei unter 25000, passiert ... nix.
Wenn du den Ueberlauf abfangen willst gibt es den Timeroverflow
Interrupt. Da kannst du dem Timer soviel Stellen geben wie du moechtest.
Mal wieder ein DAU der die Fehler seines vermurksten Codes dem Compiler
unterschieben will. Dabei ist das "wegoptimieren" immer wieder gerne
genommen. Gleich nach dem "offensichtlichen" Bug im Compiler.
Die Optimierung kann man übrigens auch abschalten, aber dann wäre der
Fehler vermutlich immer noch da. Müsste man dann halt auf die
Bug-Theorie ausweichen.
Habe mich jetzt nochmal genau mit den PWM-Modi befaßt und bin zu
folgender Strategie gelangt:
Es wäre doch ein Witz, wenn man die Timer nicht besser für sich arbeiten
lassen könnte. Die schnelle PWM scheint mir doch wunderbar geeignet:
# Als Ausgang wird /OC1A verwendet, dadurch setzt der Timer ihn
automatisch 1, wenn der Compare-Match auftritt.
# Überlauf ist kein Thema: OCR1A auf MAX setzen, dann bleibt der Ausgang
auf 0
# Timer wird automatisch zurückgesetzt beim ICR1-Interrupt
Das Problem mit der definierten High-Zeit des Ausgangssignals ließe sich
doch ganz einfach dadurch lösen, indem man nach dem OCR1A-Interrupt eine
Warteschleife startet, an deren Ende das DDR-Register auf 0 gesetzt
wird.
Wieder auf 1 setzt man es einfach in der ICR1-ISR, dann ist der Ausgang
ja wieder vom Timer auf null gesetzt und würde nicht wieder auf 1 gehen.
Ganz so genau muß die High-Zeit auch nicht eingehalten werden;
entscheidender ist die exakte Verzögerungszeit, was durch den PWM-Modus
gegeben ist.
Ich muß bloß nochmal genau schauen, ob es Probleme gibt, weil das
ICR1-Register nicht doppelt gepuffert ist.
Wenn ich einen laufenden Timer per Software ändere, riskiere ich dann
nicht Race conditions? Was passiert, wenn die Software einen konkreten
Wert in das Zählregister schreibt, während es "von der anderen Seite"
einen zähltakt erhält? Die Taktung findet immerhin nicht in allen Bits
zeitgleich statt.
Bei diskreter Elektronik würde ich sowas jedenfalls gar nicht erst
versuchen. Da würde ich einen laufenden Timer höchsten resetten aber
sicher nicht mit einem konkreten Wert laden.
stefan us schrieb:> Wenn ich einen laufenden Timer per Software ändere, riskiere ich dann> nicht Race conditions? Was passiert, wenn die Software einen konkreten> Wert in das Zählregister schreibt, während es "von der anderen Seite"> einen zähltakt erhält? Die Taktung findet immerhin nicht in allen Bits> zeitgleich statt.
Doch, auf dieser Ebene sind heutige uC synchron. Speziell dafür gibt es
ja auch ein Schattenregister, das ein Byte des 16-Bit-Zählers
zwischenspeichert...
Allerdings verhindert niemand, dass nicht (sofort) danach der Zähler
einfach weiterzählt. Sinnvollerweris müsste der Zähler bei 25000 einfach
gestoppt werden.
Aber auch ich fasse (wie Peter) sehr ungern einen Zähler an. Die Zähler
werden bei mir initialisiert, gestartet und dann die Differenz zum
letzten Zählerwert oder Startwert berechnet. Einzig beim (leicht
erkennbaren) Zählerüberlauf muss evtl. noch ein wenig von Hand
nachgearbeitet werden....
F. P. schrieb:> # Timer wird automatisch zurückgesetzt beim ICR1-Interrupt
Nö.
Das geht nur in SW, ergibt also einen Jitter (Interruptlatenz).
F. P. schrieb:> indem man nach dem OCR1A-Interrupt eine> Warteschleife startet, an deren Ende das DDR-Register auf 0 gesetzt> wird.
Ummpf, warten im Interrupt.
Und was willst Du mit einem schwachen Pullup bzw. Tristate?
Ist Dir mein Lösungsvorschlag zu einfach, zu genau und zu wenig Code?
Lothar Miller schrieb:> Einzig beim (leicht> erkennbaren) Zählerüberlauf muss evtl. noch ein wenig von Hand> nachgearbeitet werden....
Nö.
Solange alle Zeitintervalle <65535 sind, kann der Überlauf einem
herzlich egal sein.
Und das sind sie, da ja nach 25000 der Timeout zuschlagen soll.