Hallo, ich habe mit folgender selbstgemachter delay-Routine das Problem, dass sie nicht in gewünschter Weise verzögert: void waitms(int16_t time) { int16_t takte, zaehler1, zaehler2; for(zaehler2=0;zaehler2==time;zaehler2++) { for(zaehler1=0;zaehler1==16000;zaehler1++) { ++takte; } } } Die Überlegung dahinter ist folgende: 1 Takt = 1/16000000 = 0,000000062s. 16000 = 0,001s / Dauer eines Takts. Und dann wird die innere Schleife solange wiederholt, wie Millisekunden verzögert werden soll. Wisst ihr, warum es nicht funktioniert? ATMega8 @ 16MHz, WinAVR Gruß Martin
Dann schau dir mal das erzeugte Assembler-Listing an. Dir wird schnell klar, warum die Schleife länger dauert als 1ms. 1ms wären es, wenn du z. B. genau 16000 NOPs hintereinander ausführst. Aber so hast du ja in der Schleife einen Integer hängen ('takte', 2 Bytes), dessen Inkrementierung ja schon mehr als ein Takt braucht. Benutze besser einen Timer.
> Hallo, > ich habe mit folgender selbstgemachter delay-Routine das Problem, > dass sie nicht in gewünschter Weise verzögert: Was macht sie denn stattdessen? Ich würde vom Compiler erwarten, daß er den gesamten Funktionsinhalt wegoptimiert. Falls das der Fall sein sollte: takte volatile machen. Abgesehen davon gilt natürlich das, was Spiritus gesagt hat. Deine Schleife braucht nicht genau einen Takt pro Durchlauf, sondern wesentlich mehr. Außer dem Inkrementieren (welches schon länger braucht), muß bei jedem Durchlauf noch der Schleifenzähler ebenfalls inkrementiert und sein Wert überprüft werden, dann kommt noch der bedingte Sprung. Deine Abbruchbedingungen sind auch falsch. Die äußere Schleife läuft so lange, wie zaehler2 den gleichen Wert wie time hat. Außer bei time==0 hat er das am Anfang nie, also wird deine Schleife nie ausgeführt.
. for (zaehler2 = 0; zaehler2 == time; zaehler2++) Diese Zeile (und die andere mit dem gleichen Fehler) solltest Du Dir nochmal genauer ansehen. Der zweite Ausdruck der for-Anweisung ist hier das Problem. Die Schleife wird solange durchgeführt, wie der zweite Ausdruck in der for-Anweisung zutrifft. Da steht "zaehler2 == time". Der Ausdruck trifft also höchstens einmal zu, nämlich, wenn die Funktion mit time = 0 aufgerufen wird. Ist das nicht der Fall, wird die Schleife kein einziges Mal durchlaufen. Abhilfe: == durch < ersetzen. Literaturhinweis: Kernighan & Ritchie, Programmieren in C, Zweite Auflage, Hanser Verlag
Last not least: don't roll your own. Nimm die Routinen aus <avr/delay.h>, die hat dir jemand passend zurechtgeschneidert.
Hallo, aber selbergemacht muss das ja auch gehen! Ich habs jetzt mit dem Timer2 versucht, allerdings scheint er nichtmal die Interrupt-routine zu erreichen :( volatile int16_t waitms_; void waitms(int16_t time) { waitms_ = 0; while(waitms_ <= time) { TCCR0 = (0<<WGM21) | (0<<WGM20) |(0<<COM21) | (0<<COM20) | (1<<CS22) | (0<<CS21) | (0<<CS20); TIMSK = (1<<TOIE2) | (0<<OCIE2); TCNT2 = (256-6); } } SIGNAL (SIG_OVERFLOW2) { waitms_ ++; } Gruß Martin
In der Zeile TCNT2 = (256-6); setzt Du den Wert des Timercounters ja auch andauernd neu, so wird der nie weit zählen.
> aber selbergemacht muss das ja auch gehen!
Klar, aber dafür sollte man seinen Compiler schon recht gut kennen.
Nicht umsonst nutzen die vorgefertigten Routinen lieber inline asm,
damit ihre Zyklenzahl vorherbestimmt bleibt.
Also dss ich TCNT2 jedesmal neusetze, sit Absicht, da ein Schleifendurchlauf 1ms dauern soll, und dies soll erreicht werden, indem der Timer 250-mal erhöht wird.
Das funktioniert so trotzdem nicht, wenn du dir das mal auf der Zunge zergehen lässt. Der Timer wird bei deinem Hase- und Igel-Spiel einfach niemals sein Ziel erreichen und waitms_ jemals erhöhen können.
Würdest du mir dann vielleicht verraten wo mein Fehler ist?
Du setzt doch den Timerwert laufen auf 254 zurück, in einer knallharten CPU-Schleife, die praktisch immer abgearbeitet wird. Damit kann der arme Timer gegen dich ankämpfen wie er will, das wird in jedem Falle so ausgehen wie der Kampf des Ritters Don Quixotte (sp?) gegen die Windmühlenflügel. Kaum hat der Timer um eins erhöht, boing, knallt ihm deine Schleife wieder eine 254 drauf. Der kommt nie dazu, jemals von 255 nach 0 überzulaufen (erst da triggert ja der Interrupt). Ich würde den Zähler in der ISR einen Wert herunterzählen lassen und dann beim Erreichen von 0 ein globales Flag setzen. Die Funktion waitms() wartet dann nur noch auf das Erreichen dieses Flags (das natürlich als volatile deklariert sein muss).
Hallo, ich hoffe ich bringe dich nicht zur Verzwiflung, aber es klappt immer noch nicht :/ volatile int16_t waitms_, time; void waitms(int16_t time_waitms) { time = time_waitms; time wird das routinen-argument übergeben waitms_ = 0; TCCR0 = (0<<WGM21) | (0<<WGM20) |(0<<COM21) | (0<<COM20) | (1<<CS22) | (0<<CS21) | (0<<CS20); TIMSK = (1<<TOIE2) | (0<<OCIE2); TCNT2 = (255-250); der timer wird einmalig vorgeladen, um in die ISR zu gelangen while (waitms_ < time) endlosschleife solange das argument nicht erfüllt ist { } } SIGNAL (SIG_OVERFLOW2) { waitms_ ++; variable wird inkrementiert if(waitms_ < time) { TCNT2 = (255-250); timer wird vorgeladen char *test; test = "hallo!!!"; send_string(test); (überprüfung) } }
Uah, nee, was soll denn ein send_string() in einer timer-ISR? Das bringt dir doch das Timing völlig aus dem Ruder. Ansonsten, falls du damit ausdrücken willst (,,geht immer noch nicht'' -- was passiert denn genau?), dass die ISR nie aufgerufen wird, hast du denn global die Interrupts freigegeben? Und hier noch: TIMSK = (1<<TOIE2) | (0<<OCIE2); Du gibst den output compare Interrupt frei, hast du denn auch einen Handler dafür? Andernfalls generiert dir der erste output compare (auf irgendeinem Wert wird OCR2 ja wohl stehen, also irgendwann schiesst der) einen Software-Reset.
Hallo, damit TIMSK = (1<<TOIE2) | (0<<OCIE2); enable ich doch den Overflow Interrupt und nicht den output compare. Und mit "sei" sind Interrupts global freigegeben. Und ja, ich habe das Gefühl dass die ISR nicht aufgerufen wird.
> damit TIMSK = (1<<TOIE2) | (0<<OCIE2); enable ich doch den Overflow > Interrupt und nicht den output compare. Ach, sorry, den etwas aufwändigen NOP mit (0<<OCIE2) habe ich verkannt. > Und mit "sei" sind Interrupts global freigegeben. OK, das war im Zitat nirgends zu sehen. Ich wollte dir gerade schreiben, dass ich den Fehler jetzt auch nicht sehe, aber umm, es fällt mir wie Schuppen aus den Haaren: TCCR0 = (0<<WGM21) | (0<<WGM20) |(0<<COM21) | (0<<COM20) | (1<<CS22) | (0<<CS21) | (0<<CS20); Du startest den Timer 0, willst aber einen Interrupt für Timer 2 haben?! Übrigens, ich persönlich hielte: TCCR2 = (1<<CS22); ggf. noch von einem Kommentar begleitet (damit man nicht erst ins Datenblatt gucken muss) für deutlich besser lesbar.
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.