Interrupt-Probleme beim TTGO (ESP32)

Hier werden einzelne Projekte mit MicroPython vorgestellt
Antworten
Heinrichs
Beiträge: 185
Registriert: Do 21. Okt 2010, 18:31

Interrupt-Probleme beim TTGO (ESP32)

Beitrag von Heinrichs » Mi 20. Nov 2024, 13:39

Bei der Programmierung eines Empfangsprogramms für RC-5-Signale (vgl. auch meinen Beitrag RC-5 und mehr) hatte ich versucht, die Länge der einzelnen High- und Low-Phasen eines RC-5-Signals auch mit Hilfe einer ISR (Interrupt Service Routine) zu bestimmen.

Die Callback-Funktion war folgendermaßen:

Code: Alles auswählen

signal_in.irq(handler = signal, trigger = (Pin.IRQ_FALLING | Pin.IRQ_RISING))
Dabei wurde mit dem Objekt signal_in das Signal von dem IR-Receiver kontrolliert; es war folgendermaßen definiert worden:

Code: Alles auswählen

signal_in = Pin(25, Pin.IN)
Mit Hilfe der ISR signal sollte eine Liste deltaT_list mit den Zeitabständen zwischen zwei aufeinander folgenden Signal-Flanken angelegt werden:

Code: Alles auswählen

def signal(edge): # Interrupt Service Routine (IR): misst Zeitabstand zwischen zwei Flanken
    global edge_count
    global T0
    global T1
    if edge_count == 0: # pos. Flanke vom 1. Präambel-Bit; zu Beginn des Programms ist edge_count mit 0 initialisiert worden. 
    	test_signal.value(1) # s. u.
        T0 = ticks_us()
    else:  # die restlichen Flanken
    	test_signal.value(1) # s. u.
        T1 = ticks_us()
        deltaT_list.append(ticks_diff(T1, T0)) 
        T0 = T1        
    edge_count += 1
    test_signal.value(0) # s. u.

Es zeigte sich nun folgendes Problem: Das erste gemessene Zeitintervall war durchgängig um etwa 170 ms zu klein. Bei den folgenden Zeitintervallen waren die gemessenen Werte (noch) akzeptabel. Zunächst dachte ich, dass dies auf ein Problem mit ticks_us() zurückzuführen war. Um dies zu klären, fügte ich in die ISR die Zeilen test_signal.value(1) bzw. test_signal.value(0) ein, damit wurden über den Ausgang Pin 26 Testsignale ausgegeben. Diese wurde zusammen mit den Signalen des IR-Receivers mit einem Logic Analyzer aufgezeichnet (s. Abb. 1). Es zeigte sich: Beim ersten Interrupt erfolgte dieses Testsignal (Channel 0) relativ stark verzögert gegenüber dem Signal des IR-Receivers (Channel 1). Bei den folgenden Pulsen war die Verzögerung deutlich kleiner. Dies war auch der Fall, wenn das Testsignal direkt zu Beginn der ISR auf 1 gelegt wurde. Daraus lässt sich schließen: Die ISR startet ihre Arbeit (beim ersten Mal) mit einer deutlichen Verzögerung gegenüber dem auslösenden Ereignis (Rising). Das Problem lag also nicht bei der Funktion ticks_us selbst, sondern darin, dass sie zu spät aufgerufen wurde.

ISR_Problem.jpg
Abb. 1
ISR_Problem.jpg (67.29 KiB) 1837 mal betrachtet

Recherchen ergaben nun: Bei Micropython kommen Interrupts zunächst in eine Warteschlange (queue); die zugehörige ISR wird erst ausgeführt, wenn andere Prozesse zu einem sinnvollen Abschluss gekommen sind. Damit kann z. B. verhindert werden, dass die Ausführung einer ISR durch einen anderen Interrupt unterbrochen wird. (Beim Attiny wird dies z. B. durch ein entsprechendes Flag verhindert.) Diese Sicherheitsvorkehrung geht zu Lasten der Schnelligkeit: Die ISR startet i. A. erst mit einer gewissen Verspätung; wie groß diese im Einzelfall ist, kann nicht vorhergesagt werden.

Nun kann dieses Queueing bei Micropython i. A. unterbunden werden, indem man bei signal_in.irq den zusätzlichen Parameter hard = True einfügt. Diese Möglichkeit ist leider in der Micropython-Firmware für den ESP32 nicht implementiert (vgl. https://www.i-programmer.info/programmi ... ml?start=1), beim Rapberry Pi z. B. hingegen schon.

.

Antworten