Forum: Mikrocontroller und Digitale Elektronik "Interrupt" durch Vergleich von Variablen möglich? Timingprobleme durch zu lange Programmlaufzeit


von rbcn (Gast)


Lesenswert?

Guten Abend,

ich habe bei einer Steuerung für eine Maschine das Problem, dass 
zeitkritisch
Ausgänge geschaltet werden müssen. Zeitkritisch heißt in diesem Fall 
maximal 4-5ms Ungenauigkeit. Ich schreibe nebenbei Werte auf ein I2C 
Display, und hier liegt das Problem: Die Steuerung empfängt Interrupts 
von einem Inkrementalgeber und fängt bei einem bestimmten Ereignis an 
die Impulse zu zählen. In der ISR inkrementiere ich entsprechend eine 
Variable. Wenn diese Variable >= einem definierten Wert ist, soll der 
Ausgang geschaltet werden. Das alles funktioniert genau genug, bis das 
Display ins Spiel komm. Durch das Schreiben wird die loop Schleife so 
langsam, dass ich meine zeitliche Genauigkeit nicht mehr einhalten kann, 
da die Bedingung zum schalten des Ausganges im worst case erst nach 
einem kompletten loop-Zyklus erkannt wird.
Wie kann man das besser lösen? Gibt es die Möglichkeit einen Interrupt 
auszulösen, wenn 2 Variablen gleich sind?

Viele Grüße
rbcn

von Thomas E. (thomase)


Lesenswert?

rbcn schrieb:
> Gibt es die Möglichkeit einen Interrupt
> auszulösen, wenn 2 Variablen gleich sind?

Nein, normalerweise nicht. Aber es weiß ja auch keiner, was für einen 
Controller du verwendest.

rbcn schrieb:
> Zeitkritisch heißt in diesem Fall
> maximal 4-5ms Ungenauigkeit.

Jetzt mach hier mal nicht so eine Hektik. Das ist ein Zeitbereich, in 
dem der lahmste Controller der Welt noch nebenbei Primzahlen und 
Mondphasen berechnen kann.

Zeig dein Programm, erklär genau, was du vorhast, dann sehen wir schon 
weiter.

von rbcn (Gast)


Lesenswert?

Ich erwähnte den Controller nicht, weil ich der Meinung war ich habe 
hauptsächlich ein strategisches Problem. Es ist ein Atmega 328P.
Den kompletten Code werde ich nicht posten, weil es total 
unübersichtlich wäre. Die relevanten Dinge sind wie oben geschrieben, 
dass das Schreiben aufs LCD mittels einer Library zu lange dauert und 
deshalb ein Vergleich nicht immer rechtzeitig ausgewertet wird. Und nein 
ich schreibe nicht in jedem Schleifendurchlauf aufs LCD, sondern nur 
wenn ein zweiter externer Interrupt dieses auslöst.

von Jacko (Gast)


Lesenswert?

> In der ISR inkrementiere ich entsprechend eine Variable.
> Wenn diese Variable >= einem definierten Wert ist, soll der
> Ausgang geschaltet werden.

Wo ist das Problem???
Warum kannst du den Ausgang nicht gleich in der ISR schalten???

Eine Display-Bearbeitung, die den Programmablauf für >= 4 ms
aufhält, kann aber auch nur suboptimal (vulgo: beschissen)
programmiert sein: Da kriegt das träge menschlische Auge ja schon
den Bildaufbau mit...

von rbcn (Gast)


Lesenswert?

Jacko schrieb:
> Warum kannst du den Ausgang nicht gleich in der ISR schalten???

Weil das gleiche auch noch mit drei weiteren Vergleichen und unter 
Einbezug von anderen Korrekturwerten passiert. Da hatte ich die Sorge, 
dass die Interrupts bei höheren Drehzahlen zu schnell kommen würden. 
Aber danke, guter Ansatz, das werd ich ausprobieren! Manchmal sieht man 
den Wald vor lauter Bäumen nicht

von Joachim B. (jar)


Lesenswert?

rbcn schrieb:
> Zeitkritisch heißt in diesem Fall
> maximal 4-5ms Ungenauigkeit. Ich schreibe nebenbei Werte auf ein I2C
> Display, und hier liegt das Problem: Die Steuerung empfängt Interrupts
> von einem Inkrementalgeber und fängt bei einem bestimmten Ereignis an
> die Impulse zu zählen.

da muss was grundlegend falsch sein in deinem Programm,

was für ein Display? mehr als 4x pro Sekunde schreiben lohnt kaum, kann 
kein Mensch erfassen oder ist es Video, dann ist I2C eh ungünstig.

4x20 Zeichen LCD sind ja auch in wenigen µs erledigt -> i2c 100kHz ca 
10k Zeichen/s (PI x Daumen) meist geht auch 400 kHz.

Ich frage im ms Raster den Inkrementalgeber ab, zähle bis 250 und 
aktualisiere das LCD

von Achim (Gast)


Lesenswert?

4 ms für ein Display sind OK. Selbst 100ms wären es, wenn man es richtig 
macht. Man muss die Display-ausgabe dann halt Niederprior machen, z.B:

- Rtos nehmen,
- Aufteilung in kleine Pakete
- verschiedene Interrupt Level und Loop im Interrupt
- SW-Interrupt triggern.

von Jacko (Gast)


Lesenswert?

Sieh es mal so:

Wenn die Reaktionszeit < 4...5 ms sein soll
und die ISR alles Wichtige (mit Nebenbedingungen, ...)
in < 1...2 ms erledigt hat, (schaffst du das?)

ist das doch besser,

als wenn die Hauptschleife im Programm nur alle 4...5 ms
die Bearbeitung drängender Probleme zulässt!

von Stefan F. (Gast)


Lesenswert?

Du sollst ja auch nicht den kompletten Code Posten, sondern nur den 
kritischen Teil. Reduziere das Programm auf eine minimale ausführbare 
Demo, die das Problem zeigt. So ist es uns allen am liebsten.

Also besteht deine erste Aufgabe darin, diesen Teil zu bestimmen. Wenn 
du das nicht kannst, helfen wir gerne, aber dann brauchen wir doch den 
kompletten Code.

von Stefan F. (Gast)


Lesenswert?

5 ms wären bei 8Mhz Taktfrequenz ca. 40.000 Maschinen-Befehle. Wenn wir 
pi mal Daumen 10 Maschinenbefehle pro C Codezeile annehmen, komme ich 
auf 4000 Zeilen Code, die in dieser Zeit ausgeführt werden.

Damit kann man eine ganze Menge anstellen.

von Wolfgang (Gast)


Lesenswert?

rbcn schrieb:
> Weil das gleiche auch noch mit drei weiteren Vergleichen und unter
> Einbezug von anderen Korrekturwerten passiert. Da hatte ich die Sorge, ...

Sorgen sind da ein schlechter Ratgeber. Gucke dir die Laufzeit an und 
sorge ggf. durch Interrupt-Freigabe dafür, dass der nächste Interrupt 
vom Inkrementalgeber heil durchkommt.

Noch eine ketzerische Frage: Was machst du eigentlich mit deinem Timing, 
wenn dein Inkrementalgeber prellt?

Kannst du deinen Inkrementalgeber nicht per Timerinterrupt abfragen?

von rbcn (Gast)


Lesenswert?

Wolfgang schrieb:
> Noch eine ketzerische Frage: Was machst du eigentlich mit deinem Timing,
> wenn dein Inkrementalgeber prellt?
>
> Kannst du deinen Inkrementalgeber nicht per Timerinterrupt abfragen?

Prellen sehe ich am Oszi keines, es gibt kein Problem damit. Ich stelle 
es mir auch schwierig vor, wie ein Hallgeber oder ein optischer Geber 
(eines von beiden ist es) prellen soll.

Aber Danke schonmal an alle, gemach gemach, es braucht etwas Zeit das 
auszuprobieren.

von Sebastian S. (amateur)


Lesenswert?

Ich muss mich der allgemeinen Meinung: 4-5 ms sind eine "Ewigkeit" 
anschließen.

Also auch wenn ich es für Unsinn halte, ist die Lösung recht einfach.

Da der Programmablauf innerhalb einer Interruptroutine etwas anders ist, 
wie im normalen Programmablauf, sind ein paar Sicherheitsmaßnahmen 
nötig.
1. Alles, was in der Interruptroutine passieren soll muss in eine
   Funktion ausgelagert werden. Also eine "leere" Interruptroutine,
   die nur den Funktionsaufruf enthält.
   Dadurch wird der Programmablauf unabhängig davon, ob Du die Routine
   aus der Unterbrechung heraus aufrufst oder aus dem normalen
   Programmablauf.
2. Du musst dafür Sorge tragen, dass die Routine nicht während sie
   gerade läuft, sich selber aufruft.
   Ein Flag als erstes setzen und als letzten Befehl zurück setzen und
   selbiges innerhalb der Unterbrechung abfragen. In der Routine und
   in der Unterbrechung.

Muss aber nicht sein.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ rbcn (Gast)

>Die Steuerung empfängt Interrupts
>von einem Inkrementalgeber und fäng

Was schon mal Unsinn ist. Ein Inkrementalgeber wird durch periodisches 
Abfragen in einem Timer-Interrupt ausgewertet, siehe Drehgeber. Und 
schwups hast du auch schon einen Timer-Interrupt, der all deine Probleme 
löst.

>Display ins Spiel komm. Durch das Schreiben wird die loop Schleife so

Arduino?

>langsam, dass ich meine zeitliche Genauigkeit nicht mehr einhalten kann,
>da die Bedingung zum schalten des Ausganges im worst case erst nach
>einem kompletten loop-Zyklus erkannt wird.

>Wie kann man das besser lösen?

Indem man die zeitkritischen Dinge in einen Interrupt packt, der hat 
Priorität und wird immer ausgeführt und die eher langsamen, 
zeitunkritischen Dinge in der Hauptschleife, aka Arduino-Loop audführt. 
Siehe Interrupt. Oder hier.

Beitrag "Re: Arduino Nano, SD Card, PCM"

> Gibt es die Möglichkeit einen Interrupt
>auszulösen, wenn 2 Variablen gleich sind?

Nö.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

rbcn schrieb:
> Ich schreibe nebenbei Werte auf ein I2C
> Display, und hier liegt das Problem

Du hast ein organisatorisches Problem.

Wenn du versuchst, langwierige Dinge in Interrupt-Routinen zu erledigen, 
dann wirst du in jedem System damit scheitern.

Da helfen auch keine Pseudo-Tips wie "Was schon mal Unsinn ist. Ein 
Inkrementalgeber wird durch periodisches Abfragen in einem 
Timer-Interrupt ausgewertet.."

Auch der Ratschlag, aus einer Interruptroutine irgendwelche Funktionen 
aufzurufen, die du ebenfalls von woanders her aufrufst, ist ein sehr 
schlechter Ratschlag.

Mein Rat:
1. Halte Interrupt-Funktionen stringent und kurz im Durchlauf. Also 
keine zeitaufwendigen Dinge darin tun, auch nicht warten.

2. Nur solche Reaktionen, die wirklich zeitkritisch sind, machst du in 
der Interruptroutine selbst.

3. Reaktionen, die sich mehr Zeit lassen können, machst du aus der 
Grundschleife in main heraus. Dazu richtest du dir einen Ereignis-Puffer 
ein (fifo), wo du in der Interrupt-Routine lediglich einen Kenner 
hineinschreibst, der dann später von der Grundschleife wieder 
herausgelesen und abgearbeitet wird. Wenn's einfacher gehen soll, dann 
kannst du dir auch einige globale Booleans einrichten, die im Interrupt 
gesetzt und in der Grundschleife abgearbeitet werden. Zum Beispiel

bool SetzeDisplayNeu;

oder so ähnlich.

W.S.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

rbcn schrieb:
> Ich schreibe nebenbei Werte auf ein I2C Display, und hier liegt das
> Problem: Die Steuerung empfängt Interrupts von einem Inkrementalgeber
> und fängt bei einem bestimmten Ereignis an die Impulse zu zählen. In der
> ISR inkrementiere ich entsprechend eine Variable. Wenn diese Variable >=
> einem definierten Wert ist, soll der Ausgang geschaltet werden. Das
> alles funktioniert genau genug, bis das Display ins Spiel komm.
Das Display ist an dieser Stelle unnötig und insgesamt sowieso das 
letzte Glied der Nahrungskette, das bedient werden muss. Denn ob ein 
Wert angezeigt wird oder nicht ist eigentlich völlig uninteressant. 
Zumindest für die Funktion einer Maschine.
Also gehört solch nebenrangiges Zeug wie das Display weg von der 
eigentlichen Maschninensteuerung.

Und das ist jetzt dein Problem: du hast alles miteinander verknotet und 
vermischt. Das Display wird immer sofort dann angesteuert, wenn 
eigentlich die Maschine was machen sollte.

Du solltest die Hauptarbeit in einer Hauptschleife machen und dafür 
sorgen, dass diese Hauptschleife durchschnittlich nicht länger als 
5..10ms braucht. Dann kannst du nämlich die allermeisten Sachen dort 
erledigen und die Interruptroutinen kurz und knackig halten.

Ich mache es z.B. so, dass ich einen lokalen Display-Puffer habe, in den 
ich auch aus einem Interrupt sofort reinschreiben kann. Und dann wird 
pro Hauptschleifendurchlauf 1 einziges Zeichen an das Display 
ausgegeben. Bei einem 20x2 Textdisplay und einer durchschnittlichen 
Hauptschleifen-Zykluszeit von 5ms wird das Display also 5x pro Sekunde 
komplett neu beschrieben.

W.S. schrieb:
> 2. Nur solche Reaktionen, die wirklich zeitkritisch sind, machst du in
> der Interruptroutine selbst.
Zeitkritisch bedeutet hier "zeitkritisch für die Maschinenfunktion".
Ein Display ist z.B. nicht zeitkritisch (wer kann schon mehr als 3 Werte 
pro Sekunde ablesen). Eine serielle Schnittstelle auch nicht (dafür gibt 
es Puffer), die Reaktion auf einen menschlichen Tastendruck auch nicht 
(die hätte auch 20ms später gedrückt werden können).

von Peter D. (peda)


Lesenswert?

Man kann das LCD auch per Interrupt im Hintergrund updaten:
Beitrag "Formatierte Zahlenausgabe in C"

Man kann auch zeitkritische Sachen in einen Interrupt verschieben, ohne 
den auslösenden Interrupt zu lang werden zu lassen. Der 
SPM-READY-Interrupt bietet sich dafür an.

von guest...Rainer (Gast)


Lesenswert?

...und ich frage mich überhaupt, warum der Vergleich von Variablen eine 
Interrupt-Routine auslösen soll. Je nach Ergebnis des Vergleichs wird 
einfach ein Unterprogramm ausgeführt...und wie schon mehrfach 
geschrieben, du mußt selbst Prioritäten für die Abarbeitungen setzen! 
Das macht weder eine "einfache"- noch eine Interrupt-Routine für dich.
Gruß Rainer

von Joachim B. (jar)


Lesenswert?

Joachim B. schrieb:
> Ich frage im ms Raster den Inkrementalgeber ab, zähle bis 250 und
> aktualisiere das LCD

genau das meinte ich, im IRQ läuft der ms Zähler, wenn bei mir 250 
erreicht ist setze ich das flag LCD_up und in der main loop wird es dann 
irgendwann passieren, schietegal ob +-10ms

Lothar M. schrieb:
> Zeitkritisch bedeutet hier "zeitkritisch für die Maschinenfunktion".
> Ein Display ist z.B. nicht zeitkritisch

jj janz jenau

Lothar M. schrieb:
> Eine serielle Schnittstelle auch nicht (dafür gibt
> es Puffer), die Reaktion auf einen menschlichen Tastendruck auch nicht
> (die hätte auch 20ms später gedrückt werden können).

desgleichen, Tasterabfrage mit Entprellung im IRQ nach Dannegger
in der main loop wird es dann irgendwann passieren, schietegal ob +-10ms

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.