ATmega: Timerproblem

Gast #3100427
Lesenswert?

Guten Tag zusammen,

ich verwende ein Arduino-Mega-2560-Board (ATmega 2560 mit 16 MHz, 
Verbindung mit PC über ATmega8U2 als USB-COM-Konverter), das ich 
(erstmalig ohne Verwendung der Arduino-Bibliotheken) in AtmelStudio 
programmiere.

Bei einem Projekt, das recht genaues Timing erfordert, ist mir nun 
aufgefallen, dass mein Code geringfügig zu langsam läuft, deshalb habe 
ich ein minimalistisches Testprogramm geschrieben: Ein 16-bit-Timer wird 
mit einem Prescaler von 64 gestartet und bei jedem Overflow ein 
Interrupt erzeugt, durch das an den PC eine "1" gesendet wird. Dort 
läuft ein Java-Programm, welches mit jssc 
(https://code.google.com/p/java-simple-serial-connector/) die Bytes 
empfängt und den Zeitpunkt festhält. Hier der Code auf dem Mega:
1
#define F_CPU 16000000UL
2

3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5

6
int MYUBRR(long baudRate)
7
{
8
  return (((((F_CPU * 10) / (16L * baudRate)) + 5) / 10) - 1);
9
}
10

11
void comInit(long baudRate)
12
{
13
  UBRR0H = (unsigned char)(MYUBRR(baudRate) >> 8);
14
  UBRR0L = (unsigned char)MYUBRR(baudRate);
15
  UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
16
  UCSR0B = _BV(RXEN0) | _BV(TXEN0);
17
}
18

19
void sendeByte(char zeichen)
20
{
21
  loop_until_bit_is_set(UCSR0A, UDRE0);
22
  UDR0 = zeichen;
23
}
24

25
void zeitInit()
26
{
27
  TIMSK1 |= (1 << TOIE1);
28
  sei();
29
  TCCR1B |= (1 << CS11) | (1 << CS10);
30
}
31

32
ISR(TIMER1_OVF_vect)
33
{
34
  sendeByte(1);
35
}
36

37
int main(void)
38
{
39
  comInit(250000);
40
  zeitInit();
41
  while(1)
42
  {
43
  }
44
}

Das Resultat: Zu erwarten wäre ein Overflow (OF) alle 262144 µs (2^16 * 
64 / 16). Im Mittel dauert es jedoch etwa 0,12 % länger. Das 
Interessante daran ist, dass nicht etwa jeder OF länger dauert, sondern 
ziemlich genau jeder 13. etwa 4000 µs zu lang ist. Filtere ich diese 
Ausreißer aus und mittele wieder, stimmt alles.

Ein anderer Prescaler ändert nichts an den 4000 µs Überlapp, dafür aber 
die Periodizität: Bei /8 gibt es ca. alle 105 OFs einen Ausreißer, bei 
/1 ca. alle 840 OFs. Im Mittel ist der Timer also wieder um 0,12 % 
verlangsamt. Anschaulich gesagt: Es scheint, als hätte mein µC alle 3,4 
s einen Aussetzer von 4 ms.

Hat irgendjemand eine Erklärung für dieses Verhalten? Ich sitze jetzt 
schon zwei Nachmittage daran und bin ziemlich ratlos...

Viele Grüße
Jan
Gast #3100469
Lesenswert?

Hmm, den COM-Teil habe ich einfach irgendwoher kopiert, deshalb kann ich 
dazu nichts sagen. Ich habe die entsprechende Funktion geändert:
1
int MYUBRR(long baudRate)
2
{
3
  return F_CPU / 16 / baudRate - 1;
4
}

Macht aber keinen Unterschied in der Funktionsweise...
Gast #3100513
Lesenswert?

> Es scheint, als hätte mein µC alle 3,4 s einen Aussetzer von 4 ms.

Java entsorgen, die UART-Ausabe durch ein Pin-Toggeln ersetzen und 
sodann ein Ossi dranhängen. Adäquat getriggert lässt sich die Frage nach 
Aussetzern so sehr schnell beantworten. Ein Reset lässt sich so auch 
nachweisen, noch einfacher allerdings wenn man beim Starten eine LED 
(dauer-)leuchten oder in einem charakteristischen Muster blinken lässt.
Gast #3100546
Lesenswert?

Jetzt, wo du es sagst: Auf dem Arduino ist ja eine LED, die beim Reset 
aufblinkt - was sie hier aber nicht tut. Damit kann man einen 
periodischen Reset wohl ausschließen, hätte ich auch selber drauf kommen 
können...

Ein Oszilloskop steht mir gerade nicht zur Verfügung, da müsste ich mal 
bei Gelegenheit in der Uni vorbeischauen. Da ich den COM-Teil auch schon 
in Verdacht hatte, habe ich mal ein Programm geschrieben, das die OFs 
zählt und stündlich eine LED toggelt. Mit der Handy-Stoppuhr gemessen: 
Gut 4 s zu lang, sind wieder die 0,12 %.
Gast #3100621
Lesenswert?

> Das wird die LED aber nur bei einem externen (HW-) Reset machen, nicht
> bei einem internen Reset, den der Watchdog auslöst.

Aber der Watchdog sollte doch standardmäßig ausgeschaltet sein, oder? 
Ich habe sicherheitshalber mal ein
1
wdt_disable();
 am Anfang der main() eingefügt, das ändert aber nichts.
Gast #3100693
Lesenswert?

> Auch im Simulator?

Ich habe in in der ISR() einen Breakpoint gesetzt, der Simulator kommt 
aber nie dort an, sondern läuft endlos weiter. Bei einem Breakpoint an 
den Init-Methoden in der main() funktioniert es hingegen. Vielleicht 
mache ich aber auch etwas falsch, nutze den Simulator sonst nicht.
Gast #3100818
Lesenswert?

> Setz den Breakpoint doch mal in der Sendbyte-Routine.

Nee, das ändert leider auch nichts. Es scheint, als würde im Simulator 
der Interrupt nie erfolgen. Wenn ich in der while-Schleife einfach eine 
Variable inkrementiere und ihn dort anhalten lasse, funktioniert alles 
einwandfrei, ich kann auch im IO View sehen, wie sich TCNT1 erhöht.
Gast #3101006
Lesenswert?

Hallo,
auch ohne echte Ahnung, erscheint mir diese "Reset-Vermutung" nicht weit 
vom Schuss. Irgendwie (und das hat ja timerbezügliche Parameter) scheint 
der Timer an die Wand zu laufen und da ist es für mich zumindest denkbar 
dass das sowas auslösen könnte. Die dabei recht große verschwindende 
Zeit könnte zu so einem Vorgang passen. Ich will aber klar festhalten, 
dass das nur Vermutungen sind und kein explizit genaueres Wissen um die 
näheren Abläufe dahintersteht!
Gast #3101224
Lesenswert?

Sind wir uns denn prinzipiell einig, dass der Code so ok ist? Und woran 
kann es sonst liegen? Irgendwelche falsch eingestellte Parameter im 
Compiler oder gar ein Hardwaredefekt? Es wäre nett, wenn jemand mit dem 
gleichen Board mal versuchen würde, den Fehler zu reproduzieren.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren