Forum: Compiler & IDEs Atmega8 / Timer / Problem


von Thomas L. (tom)


Lesenswert?

Hallo :) Ich bins schon wieder (ja, derzeit bin ich leider wirklich
lästig).

Ich habe einen Atmega8, welcher mit 1MHz läuft mit folgendem Code
versehen:

---snip---
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

volatile uint8_t timerStartValue = 155; // Reload Value for Timer0

volatile uint8_t primaryRun = 0;
volatile uint8_t secondaryRun = 0;

// Interrupt Service Routine every 100 Cycles
ISR(TIMER0_OVF_vect)
{
  /* Test for each second: */
  primaryRun++;
  if (primaryRun == 100)
  {
    primaryRun = 0;
    secondaryRun++;
  }

  if (secondaryRun == 100)
  {
    secondaryRun = 0;
    PORTB = !(PORTB);
  }
}

int main(void)
{
  // Set PortB as output (we only need pins 0...2)
  PWM_PORT_DIRECTION = 0x00;
  PWM_PORT = 0x00;

  // Set values for Timer 0: No Prescaler, Timer start, Preload
  TCCR0 |= (1 << CS00);
  TCNT0 = timerStartValue;
  TIMSK |= (1 << TOIE0);

  // Enable global interrupts and wait
  sei();
  while(1);
}

---snip---

Im Prinzip funktioniert das auch, aber nicht mit dem Timing das ich
gerne hätte. Wenn ichs richtig berechnet hätte, müsste er jede Sekunde
einmal toggeln. Tut er jedoch nicht. Er toggelt zwar, aber nur alle 2,3
Sekunden (habe 10 mal getoggelt und es hat etwa 23 Sekunden gedauert).

Ich habe 1 000 000 Takzyklen / Sekunde. Meine Methode stellt sicher,
dass also 10 000 Zyklen mal getoggelt wird (100 * 100). Das heisst
doch, ich muss einfach bei jedem 100sten Taktzyklus toggeln. Also setze
ich den TCNT0 auf 155 ... er rennt hoch bis 255, dann kommt der Overflow
- das wären doch 100 Zyklen - oder denke ich falsch ?

von Thomas L. (tom)


Lesenswert?

Sorry, natürlich wird meine Methode nicht alle 10000 Zyklen toggeln,
aber sie muss 10 000 mal aufgerufen werden, bis sie toggelt.

von Thomas L. (tom)


Lesenswert?

Ok, natürlich hatte ich die Zeile mit TCNT0 = timerStartValue;
ausgekommentiert (in meinem Testwahn). Leider stimmt es jedoch noch
immer nicht ganz genau (auch wenns schon recht nahe kommt).

von Thomas L. (tom)


Lesenswert?

ok, habs nochmal ausgemessen ...
20 Toggles dauern 24 Sekunden - also ein Faktor von 1,2 .. das kann
doch nicht die Ungenauigkeit des internen Takts sein, oder ?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> das kann doch nicht die Ungenauigkeit des internen Takts sein, oder?

Nein, aber der Overhead, bis die Overflow-Routine vom Timer 0 gerufen
wird, schließlich lässt du den ja mit Volldampf laufen.

Tu dir einen Gefallen und benutze einen Timerkanal, der CTC-Modus
(clear timer on compare match) beherrscht, dann führt die Hardware den
Reset des Timers beim Erreichen eines bestimmten Zählerstandes ohne
Verzögerung aus.  Falls es Timer 0 sein muss, dann erwäge einen
ATmega88 statt des ATmega8 zu benutzen, der kann das auch auf Kanal 0
(und ist ansonsten der designierte Nachfolger des ATmega8).

Wenn's unbedingt Kanal 0 mit ATmega8 sein muss, dann mach den
Vorteiler größer und zirkele aus, wieviel ``fuzz'' du auf den
Ladewert
draufschlagen musst, damit das Timing stimmt.  Dennoch wirst du einen
gewissen Jitter in Kauf nehmen müssen, insbesondere dann, wenn es im
System noch andere Interruptquellen gibt (sodass zumindest zeitweise
die Interrupts blockiert sind).

von Thomas L. (tom)


Lesenswert?

Also verstehe ich das so richtig:
Meine Lösung: Wenn bei mir der Overflow auftritt, dann wird die ISR
aufgerufen, ausgeführt und dann erst der Timer wieder neu gestartet?

Wenn ich nun also Timer1 nehme (ich habe leider nut Atmega8 da), könnte
ich den CTC Modus nutzen (WGM12->1), würde in OCR1A einen Wert schreiben
und das würde funktionieren? Oder habe ich das jetzt falsch verstanden?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Meine Lösung: Wenn bei mir der Overflow auftritt, dann wird die ISR
> aufgerufen, ausgeführt und dann erst der Timer wieder neu gestartet?

Es müssten erst einige Befehle der ISR abgearbeitet werden, bis der
TCNT0 zurückgesetzt werden kann.  Ohne Prescaler bedeutet ,einige
Befehle' aber auch, dass der Timer bereits um einiges weitergezählt
hat.

Warum Konkunktiv?  Du hast ja ganz und gar vergessen, am Anfang von
TIMER0_OVF_vect TCNT0 überhaupt wieder auf timerStartValue zu setzen.
Übrigens wäre timerStartValue eher ein #define wert, auf keinen Fall
aber eine aufwändige `volatile'-Variable.  Auch primaryRun und
secondaryRun haben keine Notwendigkeit, sie mit `volatile' zu
verumständlichen.

(Sorry, habe mir den Code vorhin nicht richtig angeguckt.)

> Wenn ich nun also Timer1 nehme (ich habe leider nut Atmega8 da),
> könnte ich den CTC Modus nutzen (WGM12->1), würde in OCR1A einen
> Wert schreiben und das würde funktionieren?

So ungefähr ja.  Der Unterschied ist, dass die CTC-Logik positiv ist,
der Zähler zählt also von 0 bis OCR1A und wird dann von der Hardware
auf 0 rückgesetzt.  Die Overflow-Logik ist negativ (wenn man sie zur
Verkürzung der Zählweite benutzen will), d. h. du lädst den Zähler auf
(256 - Zählweite) vor und wartest den Überlauf ab.

von Thomas L. (tom)


Lesenswert?

also die volatile Lösung war nur eine quick n dirty lösung. Das Setzen
des TCNT hatte ich nur vergessen, mitzukopieren (steht aber eh 2
Postings drunter).

Dank dir auf jeden Fall mal. Ich werde es jetzt mal mit dem CTC vom
8bit Timer2 probieren :)

Herzlichen Dank nochmals derweil :)

von Thomas L. (tom)


Lesenswert?

omg jetzt bin ich baff ...
Hat auf Anhieb funktioniert. Erstes Mal das Programm reingeschrieben,
geht ...

Dank dir vielmals :) So macht das Spaß :)

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
Noch kein Account? Hier anmelden.