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
intMYUBRR(longbaudRate)
7
{
8
return(((((F_CPU*10)/(16L*baudRate))+5)/10)-1);
9
}
10
11
voidcomInit(longbaudRate)
12
{
13
UBRR0H=(unsignedchar)(MYUBRR(baudRate)>>8);
14
UBRR0L=(unsignedchar)MYUBRR(baudRate);
15
UCSR0C=_BV(UCSZ01)|_BV(UCSZ00);
16
UCSR0B=_BV(RXEN0)|_BV(TXEN0);
17
}
18
19
voidsendeByte(charzeichen)
20
{
21
loop_until_bit_is_set(UCSR0A,UDRE0);
22
UDR0=zeichen;
23
}
24
25
voidzeitInit()
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
intmain(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
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
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.
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...)
> 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.
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 %.
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
> 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.
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.
> 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.
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.
> 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.
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!
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.