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
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
> 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.
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.