Ersteinmal ein herzliches Hallo an dieses Forum.
Ich beschäftige mich seit nicht allzulanger Zeit mit µCs und habe sehr
viel Freude daran, mich in diese Welt einzuarbeiten - aber aller Anfang
ist schwer.
Aber nun zu meinem Problem: Ich möchte Impulszeiten messen von ca. 1,6ms
bis zu ca. 100ms mit einer Auflösung von 1µs und diese auf einem LCD
(4x20 mit einem üblichen Kontroller)ausgeben. Nun passiert folgendes.
Die Anzeige schwankt regelmäßig um 4,096ms und dies bei
unterschiedlichen Frequenzen. Dies entspricht genau einem Timeroverflow.
Ich habe einen Atmega32u4 auf einem Arduinoboard (16MHz), den ich an
einem I2C-LCD-Display angeschlossen habe. Die Anzeige funktioniert sehr
gut und dort kann ich u.a. die gemessene Impulszeit ablesen. An dem
ICP1-Eingang habe ich einen Frequenzgenerator angeschlossen, der mir
entsprechende 5V-Impulse generiert.
Ich habe schon sehr viel durchstöbert, mir die Tutorials angesehen und
bin aber noch nicht auf einen grünen Zweig gekommen und bin mir fast
sicher, etwas triviales übersehen zu haben. Mit der Zeit kommt die
Erfahrung und die Übersicht über bestimmte Problematiken.
Ich habe das Listing mal beigefügt. Vielen Dank für eure Hilfe.
Der Dativ ist dem Genitiv sein Tod.
Übersetzt heisst das
Die Floating Point Rechnung in der ISR wird dir die
Rechengeschwindigkeit
deines Prozessors zunichte machen.
Dazu kommt noch dass ein Interrupt von der Arduino IDE per Default
eingerichtet wird, der wird es auch nicht schneller machen.
Mache die Berechung so oft wie du die Anzeige aktualisierst, ausserhalb
der ISR, dann ist die Rechenlast deutlich geringer und die Interrupts
können alle abgearbeitet werden
Du hast eine Racecondition, ein weiteres Problem des nichtatomaren
Zugriffs, sowie eine Floatberechnung in einer ISR, die recht lang dauern
dürfte, obwohl die ISR so kurz wie möglich sein sollte.
Rechne ein Beispiel durch:
Angenommen der IC schlägt bei 65000 zu, dann trödelt Deine
Floatberechnung rum und in der Zwischenzeit läuft der Timer über.
cnthigh wird zwar auf 0 gesetzt, aber sofort nachdem die IC-ISR beendet
ist, wird die im Overflowflag gemerkte OVF-ISR ausgeführt, cnthigh wird
1. Nimm an, dass bei Zählerstand 1000 das nächste IC-Ereignis auftritt,
dann wird gerechnet unsigned 1000 - 65000 = 1536 + cnthigh * 65536
(warum 65535?). Und schon ist das Ergebnis gar nicht mehr das
Gewünschte.
Cntdiff würde man nur mit Wortbreite wählen, ein 0 - 65535 wird da
automatisch = 1 und so will man das, da man die Overflows separat
berücksichtigt.
Wg. atomar: während der Ausgabe auf dem LCD kann auch ein Teil des
Ausgabewerts von der ISR verändert werden.
Vielen Dank für die schnellen Antworten.
Also - mehr Code hab ich nicht und der läuft auch bis auf dieses
Problem.
Ich habe die Berechnungen mal ausgelagert,aber die Schwankungen wie
gehabt exakt die 4,096ms.
Hier der geänderte Programmteil.
void loop()
{
to = ((CNTH * 65535 + Diff) * 0.0625);
lcd.setCursor(0,0);lcd.print("to:");lcd.print(to);lcd.print(" ");
delay(1000);
}
ISR(TIMER1_CAPT_vect)
{
Capture1 = ICR1;
CNTDiff = Capture1 - Counter1;
Counter1 = Capture1;
Diff = CNTDiff; // word Diff;
CNTH = cnthigh; // word CNTH;
cnthigh = 0;
}
ISR(TIMER1_OVF_vect)
{
cnthigh++;
}
Jörg Müller schrieb:> aber die Schwankungen wie gehabt exakt die 4,096ms.
Versuche doch mal den Interrupt zu disablen den die
Arduino IDE generiert.
Dazu müsstest du aber dann die Rechnerei die in loop()
gemacht wird dann im main (...) in einer selbst geschriebenen
while (1) Schleife laufen lasssen da die loop() nicht mehr
angesprungen wird.
Normalerweise macht man die Berechnung der Differenz in einer Variable
mit Timer(Bit)breite. Dann ergibt ein 0 - 65535 eben 1, man hat dabei
einen natürlichen Überlauf des Timers. Man prüft also, ob Capture_alt
größer ist als Capture_neu, ist dies der Fall, so lag ein natürlicher
Überlauf dazwischen, den man abziehen muss, d.h. man zieht von cnthigh 1
ab.
Mitlesa schrieb:> Versuche doch mal den Interrupt zu disablen den die> Arduino IDE generiert.
Kannst du mir das noch mal näher erklären? Welchen meinst du? Mit "cli"
werden doch global die Interrupts gesperrt.
In einer Arduino-Umgebung verwende ich void loop(). Warum diese
Änderung? Ich stecke da leider noch nicht so tief in der Materie.
MWS schrieb:> Man prüft also, ob Capture_alt> größer ist als Capture_neu, ist dies der Fall, so lag ein natürlicher> Überlauf dazwischen, den man abziehen muss, d.h. man zieht von cnthigh 1> ab.
Dir noch mal vielen Dank, das war das Problem. Ich hatte mir die
einzelnen Variablen im Serial.print angesehen und siehe da, die 4ms
traten immer dann auf, wenn die Overflowvariable = 1 war und dies wenn
der neue Wert kleiner war als der alte. Ich hab das dann so gelöst.
ISR(TIMER1_CAPT_vect)
{
Capture1 = ICR1;
Cpt1 = ICR1;
Cnt1 = Counter1;
CNTDiff = Capture1 - Counter1;
Counter1 = Capture1;
Diff = CNTDiff;
CNTH = cnthigh;
if (Cpt1 < Cnt1)
{
CNTH = CNTH - 1;
}
else
{
CNTH = cnthigh;
}
cnthigh = 0;
}
Noch mal Dankeschön für alle Tipps.