Hallo, ich suche nach einer Möglichkeit, ein Ausgangspin möglichst genau im Bereich von 1ms bis 5s möglichst genau zu schalten. Mit dem Oszilloskop habe ich schon herausgefunden, dass ein _delay_ms(1) in einer for-Schleife (250 mal) zu einem genaueren Ergebnis führt als z.B. _delay_ms(250) Aber ganz genau ist es auch nocht nicht. Gibt es auch noch andere Varianten? Wenn es möglich ist, sollte man den Ausgang auch vorzeitig abschalten können. Der µC sollte also nicht vollständig ruhen.
:
Verschoben durch Admin
>Mit dem Oszilloskop habe ich schon herausgefunden, dass ein _delay_ms(1) >in einer for-Schleife (250 mal) zu einem genaueren Ergebnis führt als >z.B. _delay_ms(250) Läuft der Prozessor mit dem internem Oszillaotor, oder an einem Quarz? Oliver
Ich benutze den AT90CAN128 und er läuft mit einem 16 MHZ-Quarz. Ich habe schon im Datenblatt nachgeschaut, ob man die Frequenz erhöhen kann, aber bei 16 MHz ist leider Schluss....
lögel schrieb: > ... > Aber ganz genau ist es auch nocht nicht. Gibt es auch noch andere > Varianten? Wenn es möglich ist, sollte man den Ausgang auch vorzeitig > abschalten können. Der µC sollte also nicht vollständig ruhen. Jo, Timer halt, (ich find inzwischen die 16Bit-PWM ziemlich universell für alles mögliche), oder eben Taktzyklen zählen Peter Fleury hat in seiner bekannten LCD-Routine dazu ein Beispiel mit Inlineassembler.
Hallo, lögel schrieb: > Ich benutze den AT90CAN128 und er läuft mit einem 16 MHZ-Quarz. Ich habe > schon im Datenblatt nachgeschaut, ob man die Frequenz erhöhen kann, aber > bei 16 MHz ist leider Schluss.... Und warum nimmst Du keinen Timer? Selbst bei 1ms sind es 16000 Takte, die er irgendwas sinnvolles machen kann, bis der nächste IRQ zuschlägt. 1ms Timer initalisieren, gewünschte Zeit in passend große Variable schreiben, in der IRQ Variable um 1 runterzählen, wenn 0 Timer stoppen und Flag für main setzen. Vorzeitig stoppen: Timer anhalten. Gruß aus Berlin Michael
@Michael U.: hört sich schon ganz gut an! habe aber noch nie mit timern programmiert. hast du auch ein stückchen code-beispiel?
Ich habe jetzt in der main() folgendes:
1 | TCCR0A = (1<<CS01); |
2 | TIMSK0 |= (1<<TOIE0); |
3 | sei(); |
Leider tut das Proigramm dann nichts mehr. Auch nicht das, was vor dem Codestücken in main() steht. Wenn ich die zweite Zeile auskommentiere geht wieder alles... aber nicht der timer.
Dann muß man natürlich auch die Timerfunktion hinschreiben, also was beim Eintreffen getan werden soll. Sonst macht er dann gleich einen Reset. Siehe Tutorial?
ich habe natürlich auch
1 | ISR (TIMER0_OVF_vect) |
2 | { |
3 | .... |
4 | } |
als timerfunktion....
Dann würde ich vorschlagen du zeigst alles auf einmal und nicht nur in homöpathischen Dosen
Und warum sollte ein Timer genauer sein als _delay_ms(), so lange lezteres nicht durch Interrupts unterbrochen wird? Ob der Prozessor jetzt die Taktzyklen durch einen Hardwarezähler oder durch Abarbeiten genau abgezählter Befehle abzählt, die Genauigkeit sollte die selbe bleiben. Oder addieren sich bei _delay_ms() Rundunsgfehler auf, so daß das der Fehler am Ende größer als +/- eine Auflösungseinheit wird (wie groß auch immer diese ist)? Oliver
@ Oliver (Gast) >Und warum sollte ein Timer genauer sein als _delay_ms(), so lange >lezteres nicht durch Interrupts unterbrochen wird? Ob der Prozessor >jetzt die Taktzyklen durch einen Hardwarezähler oder durch Abarbeiten >genau abgezählter Befehle abzählt, die Genauigkeit sollte die selbe >bleiben. Nöö, der Timer läuft parallel zur CPU. Damit kann dies alles mögliche veranstalten, die Zeitbasis bleibt genau. Wenn man alles mit der CPU und _delay_ms() machen würde, müsste man für jede Aktion deren Ausführungszeit mit reinrechnen. Das heisst dann Takte im ASM Code zählen. Nicht sehr praktikabel. Vor allem wenn dann mal ne Änderung kommt. >Oder addieren sich bei _delay_ms() Rundunsgfehler auf, Das auch. MFG Falk
>Wenn man alles mit der CPU und >_delay_ms() machen würde, müsste man für jede Aktion deren >Ausführungszeit mit reinrechnen. Das heisst dann Takte im ASM Code >zählen. Ähem, genau das erwarte ich von den _delay-Funktionen. Die sollten ja nun so gebaut sein, daß da eine der gewünschten Delayzeit entsprechende Anzahl Taktzyklen verbraten wird. Nicht mehr und nicht weniger. Und ich hoffe doch, daß dafür der Jörg Wunsch (oder wer auch immer die geschrieben hat) ganz genau Takte im ASM-Code gezählt hat. Olver
@ Oliver (Gast) >>_delay_ms() machen würde, müsste man für jede Aktion deren >>Ausführungszeit mit reinrechnen. Das heisst dann Takte im ASM Code >>zählen. >Ähem, genau das erwarte ich von den _delay-Funktionen. Die sollten ja >nun so gebaut sein, daß da eine der gewünschten Delayzeit entsprechende >Anzahl Taktzyklen verbraten wird. Nicht mehr und nicht weniger. Das tut sie auch. Aber was machst du, wenn du 100ms warten willst, dann irgendwelche Aufgeban bearbeiten willst, und dann wieder EXAKT 100ms später das gleiche machen willst? > Und ich >hoffe doch, daß dafür der Jörg Wunsch (oder wer auch immer die >geschrieben hat) ganz genau Takte im ASM-Code gezählt hat. Das ist nicht das Problem, das Problem ist die undefineirte Zeit aus Warteschleife + Aktion danach. MFG Falk
Das ist die eine Sache. Die andere Sache ist, dass du deine Anwendung 'reagierend halten' willst. Wenn der µC in er delay Schleife steckt, steht (ausser ISR) alles andere. Der Schlüssel zu einer Anwendung, die scheinbar mehrere Dinge gleichzeitig abarbeitet liegt praktisch immer in der Nichtverwendung von _delay_xx und des Ersetzens durch einen Timer. Von daher ist das Erlernen dieser Technik einer der wichtigen Grundpfeiler in der µC Programmierung.
Jungs, ihr habt alle Recht, aber das war doch alles gar nicht die Frage. Das Ausgangsproblem lautet: Ein (völlig ungestörtes) _delay_ms() ist zu ungenau, wie bekomme ich genauere Zeiten erzeugt? Und da bringt ein Timer überhaupt nichts, da der genauso wie _delay_ms() die verstrichene Zeit aus der Anzahl der Prozessortakte bestimmt. Oliver
@Oliver (Gast) >Jungs, ihr habt alle Recht, aber das war doch alles gar nicht die Frage. Und ob. >Das Ausgangsproblem lautet: Ein (völlig ungestörtes) _delay_ms() ist zu >ungenau, Ohne das Makro im Detail zu kennen wage ich zu behaupten, dass es auf +/-3 Takte genau ist. > wie bekomme ich genauere Zeiten erzeugt? Mit einem Timer, wurde nun bis zum Erbrechen diskutiert. >Und da bringt ein Timer überhaupt nichts, da der genauso wie _delay_ms() >die verstrichene Zeit aus der Anzahl der Prozessortakte bestimmt. Du hast es nicht verstanden. Lies den Thread noch einmal. In Ruhe. Und nimm dann einen Timer. Denn dort ist die Abweichung dann nur noch durch den Quarz bestimmt, und der Fehler liegt irgendwo bei +/-100ppm und weniger. Oder hast du den internen RC-Oszillator verwendet (siehe [[AVR Fuses]]). Dann ist klar, warum deine Zeiten ungenau sind. MFG Falk
> Mit dem Oszilloskop habe ich schon herausgefunden, dass ein _delay_ms(1) > in einer for-Schleife (250 mal) zu einem genaueren Ergebnis führt als > z.B. _delay_ms(250) Ja, steht auch so ähnlich in dem Kommentar über der Funktion:
1 | The maximal possible delay is 262.14 ms / F_CPU in MHz. |
2 | |
3 | When the user request delay which exceed the maximum possible one, |
4 | _delay_ms() provides a decreased resolution functionality. In this |
5 | mode _delay_ms() will work with a resolution of 1/10 ms, providing |
6 | delays up to 6.5535 seconds (independent from CPU frequency). The |
7 | user will not be informed about decreased resolution. |
8 | */ |
9 | void |
10 | _delay_ms(double __ms) |
11 | { |
> Aber ganz genau ist es auch nocht nicht. Gibt es auch noch andere > Varianten? Naja, wenn es wirklich 100% genau sein muss gibt es eigentlich nur die Möglichkeit, die ASM-Befehle selbst zu schreiben und die Ausführungszeit zusammenzurechnen. Da lässt sich dann auch eine Prüfung einbauen, die bei bestimmten Bedingungen das ganze abbricht. Der IAR-Compiler hat dafür sogar eine Intrinsic-Funktion __delay_cycles(...), die exakt die angegebene Anzahl an Takten wartet - der Compiler erstellt dann ein Stückchen ASM-Code, das eben genau so viele Takte benötigt, aber so kurz wie möglich implementiert ist. Von dem Compiler gibts glaub ich ne kostenlose Version, die nur 4KB Code erzeugt, aber dir so ein ASM-Codestück compilieren kann. Eine Möglichkeit zum Abbrechen ist darin aber nicht vorgesehen. Die Alternative wären dann halt die schon genannten Timer. Entweder prüfst du in einer Schleife, ob der Timer abgelaufen ist, oder lässt einen Interrupt durchführen. Das ist denke ich auch hinreichend genau (aber ein paar Takte Abweichung gibt es trotzdem, da z.B. die Interruptfunktion ja erst aufgerufen werden muss), aber "sauberer" als die Takte-Warten-Methode. Mehr fällt mir nicht ein. Ich hoffe ich konnte helfen. Viele Grüße Randy
>Und nimm dann einen Timer. Denn dort ist die Abweichung dann nur noch durch >den Quarz bestimmt, und der Fehler liegt irgendwo bei +/-100ppm und >weniger. Und wieso wird bei der Abarbeitung einer der delay-Zeit entsprechenden Anzahl von Assemblerbefehlen der Fehler größer? Ob ich jetzt einen Timer oder ein Programm die selbe Anzahl Zyklen abzählen lasse, ändert an der dafür benötigten Zeit überhaupt nichts. Und ja, der Fehler bei Benutzung eines externen Quarzes sollte irgendwo bei +/-100ppm oder weniger liegen. UNd zwar sowohl bei Nutzeng eines Timers, als auch bei _delay_ms(), solange das nicht durch Interrupts unterbrochen wird. Da der TO aber eine Abweichung bei 250ms delay-Zeit mit einen Oszi messen kann, liegt diese wohl eher im Bereich einiger %. Also ist da was ganz anderes faul. Oliver
Oliver schrieb: > Und wieso wird bei der Abarbeitung einer der delay-Zeit entsprechenden > Anzahl von Assemblerbefehlen der Fehler größer? Ob ich jetzt einen Timer > oder ein Programm die selbe Anzahl Zyklen abzählen lasse, ändert an der > dafür benötigten Zeit überhaupt nichts. Wenn du Interrupts laufen hast, dann schon. Und wenn du die Optimierung durch den Compiler vergisst, dann erst recht. > Da der TO aber eine Abweichung bei 250ms delay-Zeit mit einen Oszi > messen kann, liegt diese wohl eher im Bereich einiger %. Also ist da was > ganz anderes faul. Ich hatte schon einmal mit einem Elektronik-Profi zu tun, der sich bei meinem selbstgebauten und ziemlich schräg aussehenden Netzteil über eine Netzfrequenz von 70Hz wunderte. Und stinksauer auf mich war, als ich (Schüler ~18) daraufhin auf seinem Oszi den Steller für die Zeitablenkung auf "kalibriert" drehte ;-).
Wie wäre es, wenn man den Timer einfach per output-compare den Pin hardwaremäßig toggeln läßt? Damit bekommt man es taktzyklengenau hin. Für die langen Delays, wo der Timer zwischendrin mehrfach überläuft, muß man eben etwas mehr nachenken, aber da geht es zur Not taktzyklengenau, ohne daß das den Prozessor merklich auslasten würde.
dann lasst den TE seine for() mit delay nutzen .. spätestens wenn der CAN128 mal eine tätigkeit oder eine echte funktion bekommt wird er darauf kommen das ein timer doch verdammt sinnvoll ist . denn einen pin toggeln is nunmal keine große aufgabe und einen µC damit zu beschäftigen 250ms oder bis 5sekunden in einer for() rumzurennen is nich sinnvoll was ist wenn vom UART was kommt oder vom CAN ? oder die zeit die man brauch um etwas zu berechnen oder um etwas aufs LCD zu schreiben willst du jedesmal die ASM befehle suchen + takte zählen ?
Jungs, ihr habt immer noch recht. Selbstvertändlich macht man in einem realen Programm so etwas mit einem Timer. Und trotzdem bleibe ich dabei: Wenn ein nacktes, durch nichts unterbrochenes _delay_ms(250) keine 250ms, sondern eine (mit einem Oszilloskop messbare !!!) Abweichung davon ergibt, wird es auch bei Nutzung eines Timers keinen Deut genauer. Da stimmt schlicht und einfach was mit dem Takt nicht. Oliver
Allerdings liegt lögels letztes Lebenszeichen bereits 3 Tage zurück und abgesehen von der allgemeinen aber nicht quantifizierten Aussage über Abweichungen und ein paar sinnlosen Programmfetzen liegt keine Information vor.
_delay_ms(250) übersetzt bis zu F_CPU von 1.048E6 Hz in eine direkte Schleife mit 4 Takten pro Schleife, also maximal Fehler von -3 Takten, plus der (nicht konstante) Compileraufwand, das Doppelregister dafür ggf. vorher freizuräumen. Bei F_CPU > 1.048E6 Hz übersetzt es in 2500 Schleifen _delay_ms(0.1), d. h. der Fehler von maximal -3 Takten wird ggf. maximal 7500 Takte hoch, was bei reichlich 1 MHz etwa 7 ms wären (allerdings nur für "pathologische" F_CPU-Fälle, reine MHz-Fälle gehen glatt in den 100 µs auf), außerdem kommen noch 6 * 2500 Takte (<= 15 ms, je nach F_CPU) für die äußere Schleife hinzu. Das macht also worst case (F_CPU = 1.1E6) 263 ms statt 250 ms, das kann man mit dem Oszilloskop schon sehen. (Bei F_CPU = 3.6864E6 sind es aber schon nur noch 253.6 ms statt 250.0 ms.) _delay_ms() war nie dafür konzipiert worden, überhaupt derartig lange Verzögerungen anzubieten, sondern delays benutzt man typisch dort, wo es einem entweder nicht auf genaues Timing ankommt, oder wo die Zeiten so kurz sind, dass sich der Overhead für einen Timer nicht lohnt. Mit einem Patch, den Eric Weddington gern auch in den AVR-GCC hinein bekommen will, kann der Compiler eine eingebaute Funktion __delay_cycles anbieten, bei der er in der Tat in der Lage ist, zyklengenaue Verzögerungen für praktisch beliebig kleine oder große Werte zu erzeugen. Damit könnte man auch _delay_us/_delay_ms anders implementieren. Im Moment enthält der Patch dafür allerdings kein Präprozessorsymbol, an Hand dessen man das Feature "on the fly" erkennen könnte; bliebe noch die Möglichkeit, das zur configure- Zeit der Bibliothek zu erkennen und den installierten Header util/delay.h entsprechend anzupassen.
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.