Forum: Mikrocontroller und Digitale Elektronik ATmega: Timerproblem


von Jan P. (Gast)


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

von Hubert G. (hubertg)


Lesenswert?

Jan P. schrieb:
> (((((F_CPU * 10) / (16L * baudRate)) + 5) / 10) - 1);

Dein UBRR kommt mir sehr abenteuerlich vor. Lt. Datenblatt:
 MYUBRR FOSZ/16/BAUD-1

von Jan P. (Gast)


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...

von Stefan E. (sternst)


Lesenswert?

Hubert G. schrieb:
> Dein UBRR kommt mir sehr abenteuerlich vor.

Das passt schon. Das zusätzliche "Zeug" ist nur eine Integer-Rundung.

von Stefan E. (sternst)


Lesenswert?

Jan P. schrieb:
> Anschaulich gesagt: Es scheint, als hätte mein µC alle 3,4
> s einen Aussetzer von 4 ms.

Ich würde auf einen periodischen Reset tippen.

von Jan P. (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Ich würde auf einen periodischen Reset tippen.

Das klingt einleuchtend. Kannst du mir sagen, wie ich das nachweisen 
oder noch besser verhindern kann? (Ich habe noch nie so hardwarenah 
gearbeitet, komme aus der Java-Ecke...)

von g457 (Gast)


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.

von Jan P. (Gast)


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 %.

von Dietrich L. (dietrichl)


Lesenswert?

Jan P. schrieb:
> Jetzt, wo du es sagst: Auf dem Arduino ist ja eine LED, die beim Reset
> aufblinkt - was sie hier aber nicht tut.

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

Gruß Dietrich

von Jan P. (Gast)


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.

von Werner (Gast)


Lesenswert?

Jan P. schrieb:
> Im Mittel dauert es jedoch etwa 0,12 % länger.

Auch im Simulator? Da hast du direkt die "Stop Watch" und kannst dir den 
UART- und PC-Zirkus sparen.

von Jan P. (Gast)


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.

von STK500-Besitzer (Gast)


Lesenswert?

Jan P. schrieb:
> Ich habe in in der ISR() einen Breakpoint gesetzt, der Simulator kommt
> aber nie dort an, sondern läuft endlos weiter.

Setz den Breakpoint doch mal in der Sendbyte-Routine.
Mehr wird in der ISR ja auch nicht gemacht.

von Jan P. (Gast)


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.

von Anfänger (Gast)


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!

von Hubert G. (hubertg)


Lesenswert?

Kein Reset, hab das getestet. Ich lasse eine Led blinken und habe ein 
delay im main eingefügt.

von Jan P. (Gast)


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.

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.