Forum: Compiler & IDEs Timer0 zählt nicht!


von Andreas V. (andreas_v)


Angehängte Dateien:

Lesenswert?

Hi Leutz bin noch ziemlich frisch auf dem Gebiet. Meine Frage: Wieso 
zählt der TCNT0 nicht hoch oder sieht Jemand einen Fehler im Code.

Ich benutze einen Atmega8 mit 4.000 MHz



#include <avr/io.h>

int takt;
int sekunde;

void setup (void)
{
  TCCR0 |= ( 1<<CS02 )|( 1<<CS00 ); //counter0,Prescaler CK/1024
  TIMSK |= ( 1<<TOIE0 );       // enable counter0 overflow interrupt
  TCNT0 = 0x00;           // Counter0 auf Null setzen

  takt = 0;              // 4.000.000  1024  256 = 15
  sekunde = 0;
}

void aus (void)
{
   if(TCNT0 == 0xFF)
  {
    TCNT0 = 0x00;
    takt++;

    if (takt == 15)
    {
      sekunde++;
      takt = 0;
    }

    if(sekunde == 1)
    {
      sekunde = 0;

      if(PORTB == 0x01)
      {
        PORTB = 0x00;
      }

      else
      {
        PORTB = 0x01;
      }

    }

  }
}

int main (void)
{

  setup();
  DDRB = 0xFF;;
  while(1)
  {
    aus();
  }
}

von Stefan B. (stefan) Benutzerseite


Lesenswert?

In dem Programm sind mehrere Probleme, die nicht da wären, wenn du dir 
das Tutorial zum Timer genau angesehen hättest.

Du hast kein echtes Timer-Programm. Du schaltest zwar den enable 
counter0 overflow interrupt ein, hast aber keine Interrupt Service 
Routine ISR dafür.

Und du schaltest die Interrupts nicht global mit sei() frei. Zum Glück, 
denn wegen der fehlenden ISR würde das Programm abstürzen.

In der aus() fragst du TCNT0 auf 0xFF ab.

Es kann passieren, dass deine Aufrufe von aus() langsamer sind als der 
Timer0 zählt. Dann ist es Zufall mal TCNT0 == 0xFF zu erwischen.

Oder die Aufrufe von aus() sind wesentlcih schneller als der Timer0 
hochzählt. Dann erwischst du TCNT0 mehrfach im Zählzustand 0xFF.

Abhilfe ist exakt dann einen Interrupt auszulösen, wenn der Timer0 
Zähler von 0xFF auf 0x0 wechselt - die ISR eben.

von Karl H. (kbuchegg)


Lesenswert?

Deine LED (oder was auch immer du an dem Port Pin angeschlossen hast) 
blinkt mit rund 1kHz. Ich denke nicht, dass du das sehen wirst.


Dein Programm .... es ist zwar unorthodox und du hast da ein paar Dinge, 
die man so nicht macht, aber richtiger Fehler ist da erst mal keiner 
drinn. Du hast die (schlummernden) Fehler alle mit eventuell ein wenig 
Glück erfolgreich umschifft.

Du hast zwar den Overflow Interrupt freigegeben, hast aber keine ISR 
dafür. Da du die Interrupts nicht mittels sei generell freigegeben hast, 
passiert da also nichts.

Einen TCNT0 direkt abfragen bedeutet, dass die Abfrage schneller sein 
muss, als der Timer zählt, damit du den Zählerstand genau erwischt. Da 
du einen Vorteiler von 1024 hast, wird das in deinem Fall sogar der Fall 
sein.

Und da du den Timer händisch auf 0 zurücksetzt, kriegst du den 
Zählerstand von 0xFF tatsächlich auch nur 1 mal mit.

All das sind eigentlich No-No's. Allerdings wirken sie sich momentan 
(noch) nicht aus.

von Andreas V. (andreas_v)


Lesenswert?

OK danke erstmal für die Tipps ich schau mir jetzt Erstmal das Tutorial 
an habe da etwas falsch verstanden denke ich.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:
> Deine LED (oder was auch immer du an dem Port Pin angeschlossen hast)
> blinkt mit rund 1kHz. Ich denke nicht, dass du das sehen wirst.

Hmm.
Da dürfte ich mich am Taschenrechner vertippt haben.
Erneutes nachrechnen bringt 1Hz zu Tage

4000000 / 1024  = 3906
3906 / 255 = 15
und dann noch durch 15  macht 1

Also das passt

von Andreas V. (andreas_v)


Lesenswert?

so hab das Programm bischen verändert aber das funktioniert irgend wie 
nicht.

Meine Rechnung:
    MHz    Vorteiler   8-Bit-Timer = Overflows pro Sekunde
4.000.000  1024        256         = 15

bei der Simulation habe ich Festgestellt das er die ganze Zeit in der 
ISR bleibt. Wieso???


#include <avr/io.h>
#include <avr/interrupt.h>


int sekunde;


void setup (void)
{
  TCCR0 |= ( 1<<CS02 )|( 1<<CS00 ); //counter0,Prescaler auf 1024
    TIMSK |= ( 1<<TOIE0 );      //counter0 overflow interrupt
    TCNT0 = 0x00;        //Counter0 auf Null setzen
  sei();          //Interrupts global aktivieren
}


ISR(TIMER0_OVF_vect)
{

sekunde ++;

}


int main (void)
{
  DDRB = 0xFF;
  setup();
  while(1)
  {
    if(sekunde >= 15)
    {
      sekunde = 0;
      if(PORTB == 0x01)
      {
        PORTB = 0x00;
      }
      else
      {
        PORTB = 0x01;
      }
    }
  }
}

von Mork (Gast)


Lesenswert?

Siehe den Abschnitt 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Datenaustausch_mit_Interrupt-Routinen

volatile ist hier nämlich das Problem.

MfG Mark

von Andreas V. (andreas_v)


Angehängte Dateien:

Lesenswert?

Vielen dank es funktioniert einwandfrei.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Besser so, aber noch nicht einwandfrei :-)

Und jetzt nicht aufgeben, sondern im Artikel Interrupt nachlesen, 
was es mit atomaren Zugriff auf Variablen zu tun hat und wieso 
volatile allein nicht reicht.

Dann hast du dir einen Riesenbatzen AVR-Wissen angeeignet!

von Andreas V. (andreas_v)


Lesenswert?

ok danke das ist echt super hier man bekommt sehr schnell Hilfe.

bin jetzt dabei das AVR-GCC-Tutorial zu lesen.

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.