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
volatileunsignedintmillisekunden;
20
volatileunsignedintsekunde;
21
22
intconfig_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
return0;
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
voidoutu16(uint16_tval)
47
{
48
charouttext[32];
49
itoa(val,outtext,10);
50
outtext[31]='\0';
51
uart_puts(outtext);
52
}
53
54
intmain(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
return0;
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
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
Nö
> 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
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.
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
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.
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
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.
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..