Forum: Mikrocontroller und Digitale Elektronik ATMEGA644 Timer on Compare Match Problem


von Wolfram (Gast)


Lesenswert?

Hallo Zusammen,
ich habe jetzt den ganzen Abend versucht herauszufinden warum mein 
Compare Interrupt nicht so funktioniert wie gedacht. Und ich habe jetzt 
fast 1h nur alte Beiträge gewälzt, aber nichts passendes gefunden.
Ich möchte auf einem ATMEGA644 mit 8MHz Quarz einen Timer-Interrupt 
verwenden. Hierzu habe ich das folgende Programm verwendet...
1
// Test program to setup timer interrupts and compare with preset counter value.
2
// ATMEGA 644, 8MHz External Crystal
3
4
#include <avr/io.h>
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <string.h>
8
#include <util/delay.h>
9
#include <avr/interrupt.h>
10
#include <avr/pgmspace.h>
11
#include <avr/eeprom.h>
12
#include "uart.h"
13
14
/* define CPU frequency in MHz here if not defined in Makefile */
15
#ifndef F_CPU
16
#define F_CPU 8000000UL
17
#endif
18
19
volatile unsigned int millisekunden;
20
volatile unsigned int sekunde;
21
22
int config_timer(void)
23
{
24
    TCCR0A  = (1<<WGM01);             // CTC Modus
25
    TCCR0B |= (1<<CS01)| (1<<CS00);   // Prescaler 64
26
                                      // ((8000000/64)/1000) = 125
27
    OCR0A   = 125;                    // Load register
28
                                      // Compare Interrupt enable
29
    TIMSK0 |= (1<<OCIE0A);
30
    sei();
31
    return 0;
32
}
33
34
ISR (TIMER0_COMPA_vect)
35
{
36
  millisekunden++;
37
  if(millisekunden == 1000)
38
  {
39
    sekunde++;
40
    millisekunden = 0;
41
  }
42
  PORTB &= ~(1<<PB0); // LED on for test timing
43
  PORTB |= (1<<PB0);  // LED off
44
}
45
46
void outu16(uint16_t val)
47
{
48
  char   outtext[32];
49
  itoa(val,outtext,10);
50
  outtext[31] = '\0';
51
  uart_puts(outtext);
52
}
53
54
int main (void) 
55
{
56
    DDRB = 0xff;      // Data Direction to OUT on port b
57
    PORTB = 0x00;     // initiates to 0, all LEDs off
58
    config_timer();
59
60
    while(1)
61
    {
62
      uart_puts("s:");
63
      outu16(sekunde);
64
      uart_puts(" ms:");
65
      outu16(millisekunden);
66
      uart_puts("\n\r");
67
      delay_ms(1000);
68
    }
69
    return 0;
70
}

Das soll genau alle 1ms einen Interrupt auslösen. Zum Test hatte ich ein 
Bit getoggelt und mit dem Scope nachgemesssen. Soweit ich sehen kann 
stimmts eben fast. alle 1 ms wir die Routine aufgerufen.
Zum Test gebe ich jetzt die aktuellen Variablen im Hauptprogramm über 
UART jede 1000ms wieder aus.

So und jetzt kommt mein Problem, hier ist die Ausgabe:

s:1 ms:8<\n><\r>
s:2 ms:9<\n><\r>
s:3 ms:10<\n><\r>
s:4 ms:11<\n><\r>
s:5 ms:12<\n><\r>
s:6 ms:13<\n><\r>
s:7 ms:14<\n><\r>
s:8 ms:15<\n><\r>
s:9 ms:16<\n><\r>
s:10 ms:17<\n><\r>
s:11 ms:18<\n><\r>
s:12 ms:19<\n><\r>
s:13 ms:20<\n><\r>

Jetzt sollten doch eigentlich die Millisekunden immer nahezu 0 sein oder 
zuminest immer gleich. Da ja jede Sekunde quasi die Millisekundenwerte 
wieder auf 0 gesetzt werden (  if(millisekunden == 1000)...usw. )
Das ist leider nicht so. Man sieht, das sich jede Sekunde 1 ms 
aufaddiert.
Was hab ich übersehen ?

Auf dem Scope sieht man dass die IR-Routine stabil läuft...

Viele Grüße

Wolfram

von Karl H. (kbuchegg)


Lesenswert?

Wolfram schrieb:

> s:1 ms:8<\n><\r>
> s:2 ms:9<\n><\r>
> s:3 ms:10<\n><\r>
> s:4 ms:11<\n><\r>
> s:5 ms:12<\n><\r>
> s:6 ms:13<\n><\r>
> s:7 ms:14<\n><\r>
> s:8 ms:15<\n><\r>
> s:9 ms:16<\n><\r>
> s:10 ms:17<\n><\r>
> s:11 ms:18<\n><\r>
> s:12 ms:19<\n><\r>
> s:13 ms:20<\n><\r>
>
> Jetzt sollten doch eigentlich die Millisekunden immer nahezu 0 sein> oder
> zuminest immer gleich.

Auch das nicht.
Aber die Zeitdifferenzen sollten naehzu identisch sein. Und das sind sie 
ja. Von einer Ausgabe zur Nächsten vergehen ca. 1 Sekunde und 1 
Millisekunde. Die 1 Sekunde kommt vom _delay_ms( 1000 ) und der Rest von 
der Schleifensteuerung, den puts, der ASCII Wandlung etc.


> Was hab ich übersehen ?

Das der Rest deines Codes, also alles abgesehen vom _delay_ms( 1000 ), 
ja auch Rechenzeit verbraucht

von Stefan E. (sternst)


Lesenswert?

Abgesehen von dem, was Karl Heinz geschrieben hat, hast du auch einen 
Fehler von knapp 1% drin, weil der OCR-Wert falsch ist. Wenn du den Takt 
per CTC durch 125 teilen willst, muss 124 ins OC-Register.

von Wolfram (Gast)


Lesenswert?

Stefan Ernst schrieb:
> hast du auch einen
> Fehler von knapp 1% drin, weil der OCR-Wert falsch ist. Wenn du den Takt
> per CTC durch 125 teilen willst, muss 124 ins OC-Register.

Wenn ich 124 nehme, dann kommt folgende Ausgabe::110 ms:4<\n>

s:110 ms:4<\n>
s:111 ms:13<\n>
s:112 ms:22<\n>
s:113 ms:32<\n>
s:114 ms:41<\n>

Dann sind immer 9-10ms dazwischen

@Karl-Heinz
> die Zeitdifferenzen sollten naehzu identisch sein. Und das sind sie
> ja. Von einer Ausgabe zur Nächsten vergehen ca. 1 Sekunde und 1
> Millisekunde.
Den Timer will ich verwenden um eine Zeit zwischen zwei Ereignissen zu 
messen (z.B. Stoppuhrfunktion)die als Trigger über zwei Ports kommen.
Aber wenn ich jetzt genaue Zeiten über enen längeren Zeitraum (Minuten) 
messen will , hab ich dann nicht einen Fehler von 0.1% ?
Und wenn ja , wie kann ich das verhindern ?

Wolfram

von Stefan E. (sternst)


Lesenswert?

Wolfram schrieb:
> Dann sind immer 9-10ms dazwischen

Ja, wegen dem, was Karl Heinz schon gesagt hat. Der Schleifenoffset ist 
halt in Wirklichkeit noch viel größer, als die ursprüngliche eine 
Millisekunde (wegen der UART-Ausgaben).

Wolfram schrieb:
> @Karl-Heinz
>> die Zeitdifferenzen sollten naehzu identisch sein. Und das sind sie
>> ja. Von einer Ausgabe zur Nächsten vergehen ca. 1 Sekunde und 1
>> Millisekunde.
> Den Timer will ich verwenden um eine Zeit zwischen zwei Ereignissen zu
> messen (z.B. Stoppuhrfunktion)die als Trigger über zwei Ports kommen.
> Aber wenn ich jetzt genaue Zeiten über enen längeren Zeitraum (Minuten)
> messen will , hab ich dann nicht einen Fehler von 0.1% ?

Du hast Karl Heinz nicht verstanden. Der Fehler liegt nicht im Timer, 
sondern in der Methode, wie du ihn festzustellen versuchst.
Die Ausführung dieses Codes:
1
      uart_puts("s:");
2
      outu16(sekunde);
3
      uart_puts(" ms:");
4
      outu16(millisekunden);
5
      uart_puts("\n\r");
6
      delay_ms(1000);
dauert halt länger, als 1000 ms, nämlich eben ca 1009 ms.

von Wolfram (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Du hast Karl Heinz nicht verstanden. Der Fehler liegt nicht im Timer,
> sondern in der Methode, wie du ihn festzustellen versuchst.

Hallo Karl-Heinz, Stefan,
OK ich hab mal versucht es elektrisch festzustellen und nicht per UART, 
also hab ich jetzt nochmal alles aus dem Code rausgworfen, bis auf die 
Timer(interrupt)routine und habe dort ein Port-Bit getoggelt.
Diesen Ausgang hab ich mir auf dem Logicanalyser mal ausgemessen. Und 
siehe da, es sind nicht 1ms sondern die Pulse werden genau alle 
1,00288ms erzeugt.
d.h. 2.88 us zu lang. Das sind aber doch HW-Timer - wie kann es sein, 
dass die Pulse so ungenau kommen ? Ein 8MHz Quarz hat doch keinen Fehler 
von 0.2% ?
Oder sind meine Timerprogrammierungen falsch, und ich suche an der völlg 
falschen Stelle ?

vg Wolfram

von Karl H. (kbuchegg)


Lesenswert?

Hast du die 125 gegen 124 ausgetauscht?

von Stefan E. (sternst)


Lesenswert?

Wolfram schrieb:
> dort ein Port-Bit getoggelt.
> Diesen Ausgang hab ich mir auf dem Logicanalyser mal ausgemessen.

Hast du bedacht, dass eventuell (je nach Code) asymmetrische Pulse 
erzeugt werden, und hast entsprechend gemittelt?

Wolfram schrieb:
> Ein 8MHz Quarz hat doch keinen Fehler
> von 0.2% ?

Wenn der Fehler denn tatsächlich in dem Bereich liegt, würde ich mal 
doppelt checken, ob der Controller überhaupt mit dem externen Quarz 
läuft, und nicht vielleicht doch noch mit dem internen Oszillator.

von Wolfram (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Hast du die 125 gegen 124 ausgetauscht?

Hallo Karl-Heinz,
also die 124 für den Counter hab ich reingesetzt und nochmal die Fuses 
gesetzt auf external crystal, slowly rising power.
Jetzt hab ich statt einem Intervall von 1000us (1ms) genau: 999,98us. 
Jetzt sind die Abstände also noch 2us zu kurz/pro 1ms(!)
Vielleicht muss ich wirklich mal den Quarz nachmessen... so langsam 
wirds irgendwie blöd..

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.