Selbstgemachte delay-Routine geht nicht

Gast #237552
Lesenswert?
• ▲
▼
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
Gast #237553
Lesenswert?
• ▲
▼
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.
Gast #237554
Lesenswert?
• ▲
▼
> 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.
Persönliche Seite #237555
Lesenswert?
• ▲
▼
.

   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
Gast #237557
Lesenswert?
• ▲
▼
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
Moderator Persönliche Seite #237563
Lesenswert?
• ▲
▼
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).
Gast #237564
Lesenswert?
• ▲
▼
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)
  }
}
Moderator Persönliche Seite #237565
Lesenswert?
• ▲
▼
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.
Gast #237566
Lesenswert?
• ▲
▼
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.
Moderator Persönliche Seite #237567
Lesenswert?
• ▲
▼
> 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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren