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?ref=2&info=p67
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.
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 | #include <util/delay.h>
| 4 |
| 5 |
| 6 | //Variablen für die Zeit
| 7 | volatile unsigned int millisekunden=0;
| 8 | volatile unsigned int sekunde=0;
| 9 |
| 10 | int main()
| 11 | {
| 12 | DDRC = 0xff; //Alle Pins des Port C als Ausgang
| 13 |
| 14 | TCCR0 =(1<<WGM01) |(1<<CS01); //1)Timer 0 arbeitet mit 1/1024 * CPU-Takt
| 15 | OCR0=125; //2)Vergleichswert
| 16 |
| 17 | TIMSK|=(1<<OCIE0); //3)Compare Interrupt aktivieren
| 18 | sei(); //4)Interrupts aktivieren
| 19 |
| 20 | while(1)
| 21 | {
| 22 | //_delay_ms(1000); //LED Funktionstest
| 23 | //PORTC = ~PORTC;
| 24 | }
| 25 |
| 26 | return 0;
| 27 | }
| 28 |
| 29 | // Der Compare Interrupt Handler
| 30 | // Wird aufgerufen wenn TCNT0 = 125
| 31 | ISR (TIMER0_COMP_vect)
| 32 | {
| 33 | millisekunden++;
| 34 | if(millisekunden==1000)
| 35 | {
| 36 | sekunde++;
| 37 | millisekunden=0;
| 38 |
| 39 | PORTC = ~PORTC; //Zustand des Port C invertieren
| 40 |
| 41 | }
| 42 | }
|
> 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.
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.
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 | #include <util/delay.h>
| 4 |
| 5 |
| 6 | //Variablen für die Zeit
| 7 | volatile unsigned int millisekunden=0;
| 8 | volatile unsigned int sekunde=0;
| 9 |
| 10 | int main()
| 11 | {
| 12 | DDRC = 0xff; //Alle Pins des Port C als Ausgang
| 13 | TIMSK |= (1 << TOIE0); //1)Timer 0 mit "Overflow Itterrupt" aktivieren
| 14 | TCCR0 =(1<<CS00)|(1<<CS02); //2)Prescaler auf 1024 stellen
| 15 |
| 16 | sei(); //3)Interrupts aktivieren
| 17 |
| 18 | while(1)
| 19 | {
| 20 | //_delay_ms(1000); //LED Funktionstest
| 21 | //PORTC = ~PORTC;
| 22 | }
| 23 |
| 24 | return 0;
| 25 | }
| 26 |
| 27 | ISR (TIMER0_OVF_vect) //4)Führe diese ISR bei Overflow von TCNT0 aus
| 28 | {
| 29 | millisekunden++;
| 30 | if(millisekunden==4)
| 31 | {
| 32 | sekunde++;
| 33 | millisekunden=0;
| 34 |
| 35 | PORTC = ~PORTC; //Zustand des Port C invertieren
| 36 |
| 37 | }
| 38 | }
|
> 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.
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.
1 | #include <avr/io.h>
| 2 | #include <avr/interrupt.h>
| 3 |
| 4 |
| 5 | //Variablen für die Zeit
| 6 | volatile unsigned int zehntel_millisekunden=0;
| 7 | volatile unsigned int sekunde=0;
| 8 |
| 9 | int main()
| 10 | {
| 11 | DDRC = 0xff; //Alle Pins des Port C als Ausgang
| 12 |
| 13 | TCCR2 |=(1<<WGM21); //1)Timer 2 mit CTC Modus aktivieren
| 14 | TCCR2 |=(1<<CS20); //2)Prescaler auf 1 stellen
| 15 | OCR2=99;
| 16 | sei(); //3)Interrupts aktivieren
| 17 |
| 18 | while(1)
| 19 | {
| 20 | }
| 21 |
| 22 | return 0;
| 23 | }
| 24 |
| 25 | ISR (TIMER2_COMP_vect) //4)Führe diese ISR aus wenn TCNT2==OCR2
| 26 | {
| 27 | zehntel_millisekunden++;
| 28 | if(zehntel_millisekunden==10000)
| 29 | {
| 30 | sekunde++;
| 31 | zehntel_millisekunden=0;
| 32 |
| 33 | PORTC = ~PORTC; //Zustand des Port C invertieren
| 34 | }
| 35 | }
|
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.
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!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|