Hallo und Frohes Neues Jahr,
ich versuche mich gerade am Timer1, dem 16bit Timer vom Atmega8. Ich
möchte, dass der Timer ab einem bestimmten Startwert anfängt zu zählen
und dann einen Overflow Interrupt auslöst, dann wieder vom Startwert
hochzählt usw. . Sinn des Ganzen soll - wie ihr bestimmt schon erwartet
habt ;) - das Hochzählen um je eine Sekunde sein.
Ich verwende einen Atmel Atmega8 bei einem Takt von 4MHz.
Das ist mein Code:
/* loesche das LCD Display und Cursor auf 1 Zeile, 1 Spalte */
39
lcd_clrscr();
40
41
42
//enable interrupts
43
sei();
44
45
while(1)//loop
46
{
47
}
48
return0;
49
50
}
51
52
/****************************/
53
/* InterruptServiceRoutine */
54
/****************************/
55
56
ISR(TIMER1_OVF_vect){
57
cli();//disable interrupts
58
59
lcd_clrscr();
60
61
itoa(i,Buffer,10);//int 2 string
62
i++;
63
64
lcd_puts(Buffer);
65
66
67
// Zaehler erneut auf Startwert setzen
68
TCNT1=0xF000;
69
70
sei();//enable interrupts
71
}
Ich habe bereits, die hier geschriebenen Tutorials sowie das Datenblatt
durchforstet. Allerdings wird auf dem LCD einfach nichts angezeigt.
Ich hoffe ihr könnt mir helfen.
Schönen Sonntag noch.
MfG
matEit
Das macht man anders.
Schau dir den CTC-Modus an. Dort geht es von einem Startwert 0 zu einem
von dir vorgegebenen Endwert. Ist der erreicht, wird der ...COMP...
Interrupt ausgelöst und automatisch der Zähler wieder auf den
Startwert 0 gesetzt.
cli()/sei() in der ISR ist seltsame Programmierung. Ich weiss nicht,
woher die Anfänger diese Sache immer finden. Gibt es da ein Tutorial in
dem das so drin steht?
Die Arbeitsanweisung in der ISR ist auch nicht toll.
> lcd_clrscr();> itoa( i, Buffer, 10 ); //int 2 string> i++;> lcd_puts(Buffer);
Wenn die zu lange dauert, gehen je nach Einstellung des Timers
Interrupts verloren und der Timer wird ungenau.
Üblich ist es in der ISR nur ein Flag (eine besondere Variable) zu
setzen und das Flag im Anwendungsprogramm auszuwerten. Das Flag muss, da
es in der ISR und dem Anwendungsprogramm benutzt wird volatile sein
und man muss auf atomaren Datenzugriff achten. Beide Begriffe sind im
Artikel Interrupt erklärt.
> Ich möchte, dass der Timer ab einem bestimmten Startwert anfängt zu> zählen und dann einen Overflow Interrupt auslöst, dann wieder vom> Startwert hochzählt usw. .
Eigentlich macht man das aber anders. Man nimmt den CTC-Modus. Da
startet der Timer immer bei 0, und man wählt über ein
Timer-Compare-Register aus, bis wo er zählen soll. Das ist genauer und
man kann sich ein manuelles Rücksetzen des Registers sparen, weil der
Timer das alles schon automatisch macht. Es wird dann einfach regelmäßig
die entsprechende Output-Compare-ISR aufgerufen.
> int i;
Wenn du die Variable in der ISR benutzt, sollte sie volatile sein.
> //interrupt mask, overflow interrupts enable timer 1> TIMSK |= TOIE1;
TIMSK |= (1 << TOIE1);
> ISR(TIMER1_OVF_vect){> cli(); //disable interrupts
cli() ist in einer ISR überflüssig. Die Interrupts werden automatisch
deaktiviert. Ein sei() ist in einer ISR meistens keine gute Idee.
> lcd_clrscr();> itoa( i, Buffer, 10 ); //int 2 string> lcd_puts(Buffer);
Solche Funktionen gehören nicht in eine ISR. Die ISR sollte man so kurz
wie möglich halten und stattdessen die eigentliche Arbeit in der Haupt
> // Zaehler erneut auf Startwert setzen> TCNT1 = 0xF000;
Das machst du ganz am Schluß. Damit verlierst du unter Umständen
Timer-Ticks.
Hallo
und vielen Dank an Alle, die mir hier geholfen haben.
Die Idee mit dem Setzen eines Startwertes habe ich hier:
Beitrag "Re: atmega8 uhr"
her.
Ich habe es jetzt mit eurem Vorschlag dem Compare Match Interrupt
gelöst. Schaut auch eleganter aus ;)
Hier mein Code:
Ja, ich habe was auszusetzen.
Deine Sekunde dauert 1,048576 Sekunden.
Dein Wert von 4096 past nicht zu 4 MHz sondern ist für 4,194306 MHz.
Wenn du den Quarz tauschen kannst -> gut
Wenn Quarz bei 4 MHz bleiben muß -> OCR1A = 0x0f61;
Wenn du den internen Oszillator nimmst -> sowieso egal!
Wenn der externe Quarz etwas daneben liegt den OCR1A-Wert
evtl. anpassen; dabei Temperaturen und Versorgungsspannung
bedenken.
avr
Willst du das wirklich?
PORTD = (1 << PD2);
...
PORTD = !(1 << PD2);
oder eher
PORTD |= (1 << PD2);
...
PORTD &= ~(1 << PD2);
s. Artikel Bitmanipulation
Auch in der ersten Fassung war schon ein Bug drin, der den Timer um ca.
50ms zu lahm gemacht hat.
//4MHz / prescaler (1024) -> 4.096 -> 0x1000
OCR1A = 0x1000;
Du bräuchtest
4000000 / 1024 = 3906,25
für ein exaktes OCR1A. Leider kannst du nur mit Ganzzahlen rechnen.
Diese Kombinationen passen genau:
Prescaler 256 und OCR1A = 4e6/256 - 1;
Prescaler 64 und OCR1A = 4e6/64 - 1;
Bei der Berechnung von OCR1A daran denken, dass der Timer ab 0 los
läuft. Für 10 (4e6/64) Schritte, muss er von 0 bis 9 (4e6/64-1) laufen,
d.h. der Vergleichswert ist 9 (4e6/64-1) nicht 10 (4e6/64).
IMHO lässt sich die Berechnung (die zur Compilezeit aufgelöst wird und
zur Laufzeit keine Rechenzeit verbraucht) angenehmer lesen und warten
als der absolute Wert 0xF423 ;)
Stefan B. schrieb:
> Für 10 (4e6/64) Schritte, muss er von 0 bis 9 (4e6/64-1) laufen,
Das ist so auch missverstaendlich: fuer 10 Schritte muss er von 0 bis 0
laufen. Der Uebergang von 9 zurueck auf 0 ist der 10. schritt.
Jo. Ich weiss nicht, wie man es besser/anschaulicher erklären kann. Im
Wiki ist das IMHO auch nicht so prall erklärt.
Jedenfalls in die Falle mit dem um 1 zu großen OCR1A bin ich ja letztens
selbst reingetappt. Deshalb habe ich das noch so frisch als
diesenbugnichtmehrmachen im Gedächnis.
Hallo,
>Willst du das wirklich?>> PORTD = (1 << PD2);> ...> PORTD = !(1 << PD2);>>oder eher>> PORTD |= (1 << PD2);> ...> PORTD &= ~(1 << PD2);
das spielt für mich keine Rolle, da ich das nur fix hingeklatscht habe,
um zu sehen, ob Tastendrücke erkannt werden. Aber du hast natürlich
Recht, man sollte es so wie du machen.
Vielen Dank für den Hinweis mit OCR1A! Ich habe jetzt einen Prescaler
von 256 und damit als Vergleichswert 0x3D08.
Den Code brauche ich sicher nicht nochmal posten.
Vielen Dank.
MfG
MatEit
M. Q. schrieb:
> if(flag == 1)> {> lcd_clrscr();>> itoa( i, Buffer, 10 ); //int 2 string> i++;
Das ist auch keine so gute Idee.
Erhöhe das i (welches ja die Sekunden enthalten soll, warum heisst die
Variable eigentlich i?) in der ISR.
Wenn die Ausgabe dann etwas zu lange dauert, oder der µC gerade mit
etwas anderem beschäftigt ist, engeht dir keine Erhöhung. Machst du die
Erhöhung aber hier, so kann es theoretisch passieren, dass eine Erhöhung
übersehen wird.
Auch wenn die Regel lautet, dass in einer ISR keine lang andauernden
Operationen ablaufen sollen, so geht das Hochzählen einer Uhr schnell
genug, dass man es in einer ISR durchführen kann. Und auch soll! Denn
auf die Art geht die Uhr nie falsch, solange die Interrupts enabled
sind.