mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timer des AVR (GCC-Tutorial)


Autor: Sebastian J. (jackophant)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
ich habe versucht mich, mit Hilfe des im AVR-GCC-Tutorial angegebenen 
Beispiels, mit den Timern des Atmega8 vertraut zu machen.
Um es für den Anfang einfach zu halten wollte ich lediglich eine an 
PORTC angeschlossene LED blinken lassen.
Dafür habe ich den Code aus dem Tutorial zu dem unten angehängten 
verändert.
Leider leuchtet die LED dauerhaft.

Wenn ich das ganze richtig verstehe (ich hab mir jetzt bereits die ein 
oder andere Stunde damit verbracht...), dann tut das Programm doch 
folgendes:
1)Ich setze die Bits des Timers 0CS00 und CS02 auf 1 und damit den 
Prescaler auf 1024
2)Ich setze OCR des Timers 0 als Vergleichswert auf 125
3)Ich sage dem Timer, dass er immer dann einen Interrupt auslösen soll, 
wenn sein Wert gleich OCR ist
4)Ich aktiviere alle Interrupts

Sofern jetzt der Timer einen Interrupt auslöst, wird ausgeführt was in 
ISR(TIMER0_COMP_vect) definiert ist.
Das heißt die Variable 'millisekunden' wird erhöht und bei erreichen der 
1000 wird der PORTC invertiert.

Wie gesagt, das Problem ist, dass sich einfach nichts tut.
Meine zwei Fragen daher, hab ich den Ablauf richtig verstanden, oder ist 
genau da der Wurm drin?
Falls das Prinzip soweit stimmt, wo kann sonst der Fehler liegen? (Ich 
bin auch nach langer Suche in diesem Forum leider nicht schlauer 
geworden.)

Vielen Dank schonmal im Voraus,
Sebastian

PS.: Ich verwende das Olimex-Board aus diesem Starterkit: 
http://shop.embedded-projects.net/product_info.php...
Der Prozessor ist ein Atmega8 mit Werkseinstellungen (betreibe ihn 
demnach auch mit internen 1MHz). Der Test mit der _delay_ms Funktion ist 
erfolgreich, die LED blinkt im Sekundentakt.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

 
//Variablen für die Zeit
volatile unsigned int  millisekunden=0;
volatile unsigned int  sekunde=0;

int main()
{
   DDRC = 0xff;                    //Alle Pins des Port C als Ausgang 

   TCCR0 =(1<<WGM01) |(1<<CS01);   //1)Timer 0 arbeitet mit 1/1024 * CPU-Takt
   OCR0=125;                       //2)Vergleichswert
    
   TIMSK|=(1<<OCIE0);              //3)Compare Interrupt aktivieren
   sei();                          //4)Interrupts aktivieren

   while(1)
   {
   //_delay_ms(1000);              //LED Funktionstest
   //PORTC = ~PORTC;
   }
 
   return 0;
}
 
// Der Compare Interrupt Handler
// Wird aufgerufen wenn TCNT0 = 125
ISR (TIMER0_COMP_vect)
{
   millisekunden++;
   if(millisekunden==1000)
   {
      sekunde++;
      millisekunden=0;

    PORTC = ~PORTC;              //Zustand des Port C invertieren

   }
}

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Meine zwei Fragen daher, hab ich den Ablauf richtig verstanden, oder ist
> genau da der Wurm drin?

Der Ablauf stimmt soweit, außer dass dort noch der Schritt fehlt:
- Konfiguriere Timer auf CTC-Modus

Und da sind wir dann schon beim Thema, denn der Timer 0 des ATmega8 
kennt keinen CTC-Modus. Darf ich fragen, wie du das Programm überhaupt 
compiliert bekommst? Der ATmega8 hat kein WGM01-Bit, kein OCR0-Register, 
kein OCIE0-Bit und damit natürlich auch keinen TIMER0_COMP-Vector. Das 
wird alles vom Compiler mit Fehlermeldungen quittiert. Wenn das bei dir 
compiliert, dann hast du einen falschen Controller eingestellt.

Autor: Sebastian J. (jackophant)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die sehr schnelle Antwort, die mich in die richtige 
Richtung geschubst hat!
Zuerst einmal habe ich im AVR-Studio den Controller auf einem Mega128 
stehen gehabt, daher konnte ich das ganze ohne weiteres kompilieren. =/
Nachdem ich das korrigiert habe habe ich auch prompt die von dir 
angesprochenen Fehler gemeldet bekommen.
Nachdem ich dann nochmal Datenblatt, Tutorial und die iom8.h 
durchforstet habe, habe ich meinen Code wie unten angehängt verändert.
Das Beste zuerst: Es funktioniert! =)

Nun hab ich aber nichts davon, wenn ich mir nicht sicher bin, dass ich 
den Spaß auch verstanden habe.
Daher nochmal die Gleiche Frage wie zum ersten Code, stimmt meine 
Vorstellung hier mit der Realität überein?
1)Ich aktiviere den Overflow Interrupt Modus des Timers 0
  Wenn ich das richtig sehe, ist das der einzige Modus, den der Timer 0 
des
  Mega8 kennt. Dieser sorgt dafür, dass bei jedem Überlauf der 8bit
  'Variable' TCNT0 ein Interrupt ausgelöst wird.
2)Ich stelle den Presclaer auf 1024
  Stand im oberen Code auf 8
3)Ich aktiviere alle Interrupts
4)Der zugehörige Vektor in der Interrupt-Routine legt fest durch welchen
  Interrupt diese ausgelöst wird.

Das heißt, das Ergebnis ist, dass die Routine mit der Frequenz
 ausgeführt wird.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

 
//Variablen für die Zeit
volatile unsigned int  millisekunden=0;
volatile unsigned int  sekunde=0;

int main()
{
   DDRC = 0xff;                    //Alle Pins des Port C als Ausgang 
   TIMSK |= (1 << TOIE0);          //1)Timer 0 mit "Overflow Itterrupt" aktivieren
   TCCR0 =(1<<CS00)|(1<<CS02);     //2)Prescaler auf 1024 stellen
    
   sei();                          //3)Interrupts aktivieren

   while(1)
   {
   //_delay_ms(1000);              //LED Funktionstest
   //PORTC = ~PORTC;
   }
 
   return 0;
}
 
ISR (TIMER0_OVF_vect)              //4)Führe diese ISR bei Overflow von TCNT0 aus
{
   millisekunden++;
   if(millisekunden==4)
   {
      sekunde++;
      millisekunden=0;

    PORTC = ~PORTC;              //Zustand des Port C invertieren

   }
}

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Daher nochmal die Gleiche Frage wie zum ersten Code, stimmt meine
> Vorstellung hier mit der Realität überein?

Ja.

Wenn man allerdings Interrupts mit einer bestimmten vorgegebenen 
Häufigkeit haben will, ist der CTC-Modus besser geeignet. Als nächste 
Übung würde ich dir daher empfehlen, nochmal deinen ersten Versuch 
aufzugreifen (1ms-Interrupts mittels CTC). Nur halt mit Timer 1 oder 2 
statt 0, denn die haben einen CTC-Modus.

Autor: Sebastian J. (jackophant)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, besten Dank.
Ich denke das erste Beispiel hab ich verstanden, daher dachte ich mir, 
kann das nächste nicht so schwer sein...
Mein Ziel war es diesmal, wie von dir vorgeschlagen, den Timer2 (die 
zwei Register des 16bit Timers 1 haben mich vorerst abgeschreckt, da 
unübersichtlich) im CTC Modus für die gleiche Aufgabe zu benutzen.
Bei einem Prescaler von 1, einer CPU Frequenz von einem Megahertz und 
einem Vergleichswert von 99 (weil 999 ja leider nicht mehr in die 8bit 
passen) sollte also ein Interrupt 1/10 Millisekunde entsprechen. Den 
Namen des Vektors habe ich wieder aus der iom8.h übernommen.
Leider bleibt die LED wieder mal dauerhaft eingeschaltet.
Stimmt die Initialisierung des Timers nicht?
Habe da im Datenblatt nur die Angabe gefunden, dass für den CTC Modus 
WGM21:0 = 2 gesetzt werden muss. Ich habe das so interpretiert, dass 
WGM21=1 und WGM20=0 gesetzt wird, damit ich die Binärzahl 10 also 2 
habe.
Ist das falsch? Ich habe leider keine genauere Angabe finden können.
#include <avr/io.h>
#include <avr/interrupt.h>

 
//Variablen für die Zeit
volatile unsigned int  zehntel_millisekunden=0;
volatile unsigned int  sekunde=0;

int main()
{
   DDRC = 0xff;               //Alle Pins des Port C als Ausgang 
    
   TCCR2 |=(1<<WGM21);        //1)Timer 2 mit CTC Modus aktivieren
   TCCR2 |=(1<<CS20);         //2)Prescaler auf 1 stellen
   OCR2=99; 
   sei();                     //3)Interrupts aktivieren

   while(1)
   {
   }
 
   return 0;
}
 
ISR (TIMER2_COMP_vect)       //4)Führe diese ISR aus wenn TCNT2==OCR2
{
   zehntel_millisekunden++;
   if(zehntel_millisekunden==10000)
   {
      sekunde++;
      zehntel_millisekunden=0;
      
      PORTC = ~PORTC;        //Zustand des Port C invertieren
   }
}

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast nur vergessen, den entsprechenden Interrupt freizuschalten.

Noch 'ne Zusatzbemerkung:
Ein Interrupt alle 99 Takte ist schon dicht an der Grenze des Möglichen. 
Geh lieber auf die ursprünglich angedachte 1ms. Das erreichst du, indem 
du zusätzlich den Vorteiler einsetzt.

Autor: Sebastian J. (jackophant)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Humpf, ok, das hätte ich anhand meines ersten Versuchs auch selbst sehen 
können... =/
Um so besser, dass es kein grundsätzliches Problem war.
Jetzt läuft alles so wie ich mir das vorgestellt habe. (Ich habe das 
korrigierte Programm der Vollständigkeit halber mal angehängt.)

Nochmal vielen Dank für die enorm schnelle und kompetente Hilfe!
Diese Seite inklusive dem Forum, mit seinen Usern, ist wirklich Gold 
wert!
Man trifft im Internet leider selten auf so viele hilfsbereite und 
fachkundige Menschen wie das hier der Fall ist!

Beste Grüße, und ein schönes Wochenende, wünsche ich dir!
  Sebastian

Edit:
Hab den Prescaler geändert. Es besteh ja kein Grund am Limit zu 
arbeiten!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.