PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer Interrupt - Pausen - Dauer



M1.R
28.11.2009, 18:41
Hallo,

auf folgende Weise versuche ich Pausen zu programmieren und eine Dauer zu ermitteln (Atmega 32, 8Mhz):
Zu Beginn wird ein Zähler gestartet der jede Millisekunde um 1 weiterzählt.


#define preloader 131
volatile uint32_t zaehler;

ISR(TIMER0_OVF_vect) // wird jede ms ausgeführt
{
TCNT0 = preloader;
zaehler++;
}


die Funktion für die Pause sieht so aus:

void pause(uint16_t wert) //millisek eingeben max 65535
{
uint16_t ende=zaehler+wert;
while (zaehler<ende);
}

die Funktion für die Dauer so:

uint32_t dauer(void)
{
uint16_t wert=zaehler-start_zeit;
return wert;
}

das Testprogramm:

//test45

#include "loeffler.h"
#include "uart.h" //fleury

/************************************************** ***************************/
void SerPrint (char *data)
{
unsigned char i = 0;
while (data [i] != 0x00) uart_putc (data [i++]);
}
/************************************************** **************************/
void PrintInt (int wert)
{
char text [7];
itoa (wert, text, 10);
SerPrint (text);
}
/************************************************** **************************/

int main(void)
{
loeffler_init();

uint8_t i;
uint16_t pausen[21];
led_rechts(rot);
led_links(rot);
for(i=0; i<20; i++)
{
start_zeit=zaehler;
pause(1000);
pausen[i]=(dauer());
}
led_rechts(gruen);
led_links(gruen);
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
SerPrint("\n\r");
for(i=0; i<20; i++)
{
SerPrint("\n\r");
PrintInt (i);
SerPrint("\t");
PrintInt (pausen[i]);
}
while(1);
return(0);
}

die ermittelten Pausenwerte sollten nun theoretisch jew. 1000 ms sein.

Das Ergebnis sieht aber so aus:


0 1000
1 1000
2 1000
3 840
4 1000
5 1000
6 1000
7 1000
8 864
9 1000
10 1000
11 1000
12 1000
13 864
14 1000
15 1000
16 1000
17 1000
18 864
19 1000


warum geht das so nicht?
Wäre schön, wenn mir jemand weiterhelfen könnte.

Gruss
M.

Besserwessi
28.11.2009, 23:20
Die Zugriffe auf die Variable Zähler sind nicht atomar. Es kann also passieren, das high und low Bytes nicht zusammengehören.
Zugriffe auf Zaehler müßte man also immer in CLI / SEI einschließen.

Damit die Zeiten besser stimmen sollte man beim Timer wenn irgendmöglich den CTC modus wählen, dann summieren sich Verzögerungen beim ISR-aufruf nicht auf.

M1.R
29.11.2009, 12:33
Vielen Dank für deine Antwort.

Leider sind meine Programmierkenntnisse (noch) ziemlich rudimentär, daher kann ich deiner Erklärung nicht so ganz folgen.

Die Zugriffe auf die Variable Zähler sind nicht atomar. Es kann also passieren, das high und low Bytes nicht zusammengehören.
Zugriffe auf Zaehler müßte man also immer in CLI / SEI einschließen.
Der Begriff "atomar" sagt mir nichts. Liegt das Problem darin, dass ein Interrupt auftritt, wenn der Zugriff auf die Variable noch nicht fertig ist?

Damit die Zeiten besser stimmen sollte man beim Timer wenn irgendmöglich den CTC modus wählen, dann summieren sich Verzögerungen beim ISR-aufruf nicht auf.Warum stimmen die Zeiten im CTC Modus besser?

Durch Einfügen von CLI und SEI sind die Ergebnisse schon mal besser geworden.
Nun werde ich noch versuchen in den CTC Modus umzustellen.

Gruss
M.

sternst
29.11.2009, 13:10
Dein Code hat noch ein paar andere Probleme.

Wieso ist ende in der Funktion pause 16 Bit groß, wo doch zaehler 32 Bits hat? Wie soll die Funktion irgendetwas Sinnvolles machen, wenn zaehler gerade größer als 65536 ist? Und wenn du ende auf 32 Bit änderst, hast du immer noch das Problem, dass die Funktion einen Überlauf nicht berücksichtigt.

Wieso soll dauer einen 32 Bit Wert zurückgeben, wo doch dem Code nach anscheinend die maximale Dauer auf 16 Bit beschränkt sein soll (und effektiv auch nur ein 16 Bit Wert zurückgegeben wird)?

Und wenn beides (Pause und Dauer) auf maximal 16 Bit beschränkt sein soll, warum ist dann zaehler überhaupt 32 Bit groß? 16 Bit würden dann auch reichen.

M1.R
29.11.2009, 13:35
Dein Code hat noch ein paar andere Probleme.

Wieso ist ende in der Funktion pause 16 Bit groß, wo doch zaehler 32 Bits hat? Wie soll die Funktion irgendetwas Sinnvolles machen, wenn zaehler gerade größer als 65536 ist? Und wenn du ende auf 32 Bit änderst, hast du immer noch das Problem, dass die Funktion einen Überlauf nicht berücksichtigt.

Wieso soll dauer einen 32 Bit Wert zurückgeben, wo doch dem Code nach anscheinend die maximale Dauer auf 16 Bit beschränkt sein soll (und effektiv auch nur ein 16 Bit Wert zurückgegeben wird)?

Und wenn beides (Pause und Dauer) auf maximal 16 Bit beschränkt sein soll, warum ist dann zaehler überhaupt 32 Bit groß? 16 Bit würden dann auch reichen.
Danke!
Ist natürlich falsch!
Ich werde es ändern in:
uint32_t ende und
uint32_t wert
Wenn ich mich nicht verrechnet habe, läuft mein Zähler bei 32 Bit erst nach ca. 50 Tagen über, und solange hält der Akku von meinem Roboter eh nicht. ;)

Gruss
M.