PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit Timer0



luma
29.10.2005, 14:20
Hio. Ich versuch mich gerade an den Timern. Allerdings gibt's da ein kleines Problem. Ich verwende den Timer0 (8-Bit) eines ATMega32. Mit dem Tool rnAvr hab ich den Wert für OCR0 ausrechnen lassen. Jetzt schon mal die erste Frage: Wie ist die Formel zum berechnen von OCR0? Bei 7372800Hz und 1000 Interrupts pro Sekunde bekomme ich für OCR0 141 (mit einem Vorteiler von 64). Ich generiere dann im Programm “Output Compare0 Interrupts”. Dann hab ich noch ne extra Methode zum schlafen, welche mit dem Timer zusammenarbeitet. Nur schläft mein Programm bei einer Einstellung von 1 Sekunde über 40 Sekunden! Warum? Schaut euch doch bitte den Code an und sagt mir was falsch ist. (Hinweis: Das ist nur ein kleiner Ausschnitt. Wenn ihr den Code mit einem Atmega testen wollt müsst ihr ggf. ein paar Header-Dateien einbinden.) Kann es vielleicht sein, dass OCR0 nicht richtig berechnet ist? Das wichtige hab ich mit // eingerahmt.
Danke!




uint8_t cmd;

volatile uint32_t countTimer0;
volatile uint32_t tmpCountTimer0;

SIGNAL(SIG_OUTPUT_COMPARE0)
{
countTimer0++;
tmpCountTimer0++;
}

inline void sleep_millisec(uint16_t milliseconds)
{
// tmpCountTimer0 wird in der ISR oben inkrementiert
tmpCountTimer0 = 0;
while(tmpCountTimer0 < milliseconds)
asm volatile ("nop"); // Nichts tun
}


int main(void)
{
// Initializes the timer Timer0: ////////////////////////////////////////////////////
TCCR0 = (1<<WGM01) | (1<<CS01) | (1<<CS00); // CTC und Prescaler 64
OCR0 = 141; // Mit rnAVR berechnet
TIMSK |= (1<<OCIE0); // Interrupt aktivieren
////////////////////////////////////////////////////////////////////////////////

// Initialize USART and enable interrupts:
usart_init();
sei();

// Mainloop:
for(;;) {
cmd = usart_recieveChar();

switch(cmd) {
case 's': rncontrol_ledon(0); // Diese Funktion aktiviert eine LED
sleep_millisec(1000);
rncontrol_ledoff(0); // Diese Funktion deaktiviert eine LED
case 'd': rncontrol_ledon(0);
sleep_millisec(2000);
rncontrol_ledoff(0);
case 'l': rncontrol_ledon(0);
sleep_millisec(15000);
rncontrol_ledoff(0);
}
}


return 0;
}



Gruß
Lutz

PicNick
29.10.2005, 17:22
Mach doch einmal den Test sozusagen zu Fuß. D.h.:

Direkt in der SIGNAL-Routine zählst du UND vergleichst mit 1000, und direkt dort drehst du auch die LED's mal an und aus. Ob das Blinken etwa 1 sec. braucht, kannst du gut abschätzen. Mir scheinen die RnAVR-Werte plausibel.

Mehrbytige Felder in der ISR zählen und im non-ISR mode vergleichen ist so eine Sache.

luma
29.10.2005, 17:33
Mehrbytige Felder in der ISR zählen und im non-ISR mode vergleichen ist so eine Sache.

Beim Asuro geht das genau so!

Hat mir denn niemand einen eigenen Codeausschnitt? Und: Wie berechne ich den OCR0-Wert?

PicNick
29.10.2005, 18:22
Ich häng' da einen Code rein von einem Timer, der alle mS einmal tickert. Das ist ingesamt ein etwas komplexeres Projekt, aber das mit dem Timer kann man schon erkennen.
Die Werte sind für 8 MHZ, ist ja nicht soweit weg von deinen 7372800Hz
h-file


#ifndef __BACK_DEF
#define __BACK_DEF 1

#define F_CPU 8000000
#define USART_BAUD_RATE 9600
#define USART_BAUD_SELECT (F_CPU/(USART_BAUD_RATE*16l)-1)


#define TIME0_c_MS_PRE 3 // prescale
#define TIME0_c_MS_CNT 131 // preload

......



// ---------------------------------------------------
// TIME Interrupt
// ---------------------------------------------------
SIGNAL (SIG_OVERFLOW0)
{
TCNT0 = TIME0_c_MS_CNT;
sTime0.wCurr--;
if (!sTime0.wCurr)
{
sTime0.wCurr = sTime0.wCount;
sTime0.bFlag |= TIME0_M_TICK;
}
}
// ---------------------------------------------------
TIME_DEF* TimeInit(unsigned short Count)
{
sTime0.bFlag = 0;
sTime0.wCount = Count;
sTime0.wCurr = Count;
TCCR0 = TIME0_c_MS_PRE;
TCNT0 = TIME0_c_MS_CNT;
TIMSK |= (1 << TOIE0);
return((TIME_DEF*)&sTime0);
}


Ich attache auch noch ein XLS-Sheet zum ausrechnen der diversen Werte.
Is für internen Gebrauch, also ein bißchen mitdenken mußt du schon, wie es zu bedienen ist.
Es zeigt dir die möglichen alternativen an.


EDIT: seh auch schon, wo dein Fehler liegt: du mußt in der Signal routine den Preload-Wert nachladen

luma
29.10.2005, 18:32
[quote]
EDIT: seh auch schon, wo dein Fehler liegt: du musst in der Signal routine den Preload-Wert nachladen
[/quote}

Also so:



SIGNAL...
...
OCR0 = 141;
...
...

?

Gruß
Lutz

PicNick
29.10.2005, 18:45
Halt, stop, retour: Ich bin überhaupt am falschen Dampfer (--> Matschbirne).
Du machst ja keinen klassischen Timer, sondern Compare-Match .
Da ist erstens das mit dem Nachladen zu vergessen.
Aber auch das Rechnen stimmt dann so nicht. (das ist für counter-overflow)
Der Wert 141 heißt ja, von dort zählt er rauf bis 255 und dann schnackelt der TIMER
Beim Counter match zählt er von 0 -> OCR0 und löst dann aus.
d.h. statt 141 gelten bei dir 256 - 141 ---> also 115.
und nachladen brauchst du nix.

Das erkärt aber nicht einen Differenz 1 sek <--> 40 sekunden

da muß ich mal im DS nachlesen.

'Tschuldige nochmals.

luma
29.10.2005, 20:21
Hio.



Halt, stop, retour: Ich bin überhaupt am falschen Dampfer (--> Matschbirne).
Du machst ja keinen klassischen Timer, sondern Compare-Match .
Da ist erstens das mit dem Nachladen zu vergessen.
Aber auch das Rechnen stimmt dann so nicht. (das ist für counter-overflow)
Der Wert 141 heißt ja, von dort zählt er rauf bis 255 und dann schnackelt der TIMER
Beim Counter match zählt er von 0 -> OCR0 und löst dann aus.
d.h. statt 141 gelten bei dir 256 - 141 ---> also 115.
und nachladen brauchst du nix.

Das erkärt aber nicht einen Differenz 1 sek <--> 40 sekunden

da muß ich mal im DS nachlesen.

'Tschuldige nochmals.

:). Ich hab da auch net immer drangedacht. Hab dann am Schluss beides getestet und bin jetzt wieder beim klassischem Timer (Overflow) ;).

Hier mein neuer Code

ISR:


SIGNAL(SIG_OVERFLOW0)
{
TCNT0 = 141;
countTimer0++;
tmpCountTimer0++;
}


Und die Initialisierung:


// Initializes the timer Timer0
TCCR0 |= (1<<CS00) | (1<<CS01);
TCNT0 = 141;
TIMSK |= (1<<TOIE0);


Jetzt hab ich ein doch ziemlich komisches Testergebnis. Lasse ich den Mikrocontroller 15 Sekunden schalfen geht das sehr genau. D. h. der µC schläft exact 15 Sekunden! Lasse ich ihn dagegen nur eine oder zwei Sekunden schlafen dauert das teilweise über 20 Sekunden. Komisch: Wenn er zwei Sekunden schläft ist er schneller fertig (ca. 20 Sekunden) als wenn ich ihn nur eine Sekunde schlafen lass (ca. 25 Sekunden). Woran kann das liegen??

Gruß
Lutz

SprinterSB
29.10.2005, 21:58
Mehrbytige Felder in der ISR zählen und im non-ISR mode vergleichen ist so eine Sache.
Beim Asuro geht das genau so!

Eben nicht. Ein Vergleich auf 16-Bit-Wert ist mindestebns 2 Befehle lang. Auch beim Asuro. Wenn zwischen diesen beiden Befehlen ein Interrupt auftaucht, der den zu testenden Wert verändert, hast du ein Problem. Zwar nur seeeehr selten, aber du hast es. Hier sogar recht wahrscheinlich, weil du ausser nop und vergleichen nix machst. Das gilt ebenso für das Setzen solcher Werte.


inline void sleep_millisec(uint16_t milliseconds)
{
// tmpCountTimer0 wird in der ISR oben inkrementiert
tmpCountTimer0 = 0;
while(tmpCountTimer0 < milliseconds)
asm volatile ("nop"); // Nichts tun



while (1) {
uint16_t tmp;
cli();
tmp = tmpCountTimer0;
sei();
if (tmp < milliseconds)
return;
}


Oder du machst es über ein Flag wie von PicNick vorgeschlagen.

PicNick
30.10.2005, 10:15
Man könnt' aber (oder sollte) vor dem Non-Isr Vergleich ein "cli()" und danach ein "sei()" setzen. das sollte helfen. Das machen auch die meisten kompiler, wenn sie 16-Bit werte verfummeln (z.B. Stack u. Framepointer)

luma
09.12.2005, 17:29
Hio. Ich hab noch ein paar Fragen zu den Timern. Wieso les ich im AVR-GCC Tutorial, dass ein 8-Bit Timer eine Auflösung von 256 hat? Der Timer kann doch bis maximal 255 hoch zählen. Was ist mit den 16-Bit Timern?

Dann hab ich noch dieses kleine Programm. Das schläft zwar, aber immer zwei Sekunden, obwohl es nur eine Sekunde schlafen sollte…



// Variable which is used for Timer0
volatile uint8_t countTimer2;

// ISR for handling the Timer/Counter2 Compare Match interrupt
SIGNAL(SIG_OUTPUT_COMPARE2)
{
countTimer2++;
}

// Initializes the timer Timer2 (Prescaler=64 | CTC)
TCCR2 = (1<<CS22) | (1<<WGM21);
OCR2 = 115;
TIMSK |= (1<<OCIE2);

/**
* Sleeps the given time. For t=1 the function Sleeps 1 ms.
*/
inline void rncontrol_sleep(uint8_t t)
{
// countTimer2 wird in der ISR oben inkrementiert
countTimer2 = 0;
while (countTimer2 < t);
}


/**
* Sleeps the given milliseconds.
*/
inline void rncontrol_sleep_millisec(uint16_t msec)
{
uint16_t i;
for(i=0; i<msec; i++) {
rncontrol_sleep(1);
}
}
...
rncontrol_sleep_millisec(2000);


Wenn ich diesen Umweg hier mach, dann funktioniert’s aber:



// Initializes the timer Timer2 (Prescaler=1 | CTC)
TCCR2 = (1<<CS20) | (1<<WGM21);
OCR2 = 73;
TIMSK |= (1<<OCIE2);

/**
* Sleeps the given time. For t=100 the function Sleeps 1 ms.
*/
inline void rncontrol_sleep(uint8_t t)
{
// countTimer2 wird in der ISR oben inkrementiert
countTimer2 = 0;
while (countTimer2 < t)
asm volatile("nop");
}


/**
* Sleeps the given milliseconds.
*/
inline void rncontrol_sleep_millisec(uint16_t msec)
{
uint16_t i;
for(i=0; i<msec; i++) {
rncontrol_sleep(100);
}
}
...
rncontrol_sleep_millisec(2000);


Gruß
Lutz

PicNick
09.12.2005, 18:03
@luma
Wir haben auch ein Zehnersystem und haben nur die Ziffern bis 9

luma
09.12.2005, 18:06
@luma
Wir haben auch ein Zehnersystem und haben nur die Ziffern bis 9

Das sagt mir leider nichts.

SprinterSB
09.12.2005, 18:22
Das ist wie mit dem Informatiker am Bahnhof:

"Null, Eins, Zwei.... WO IST MEIN DRITTER KOFFER?!"

luma
09.12.2005, 18:30
Ahso, das ist mir gerade auch so in den Sinn gekommen.

Und was ist mit dem Progrämmchen? Kann es da auch sein, dass der Controller (aus welchen Grund auch immer) was überspringt oder so?