Interrupts: Unterschied zwischen den Versionen
Zeile 439: | Zeile 439: | ||
Hier gibt es eine Übersetzung des Artikels von docs.micropython.org mit DeepL.com: | Hier gibt es eine Übersetzung des Artikels von docs.micropython.org mit DeepL.com: | ||
[[ | [[Media:ISR_schreiben.pdf | ISR schreiben]] |
Version vom 11. Februar 2023, 23:50 Uhr
In Arbeit
Hier habe ich die DeepL.com Übersetzungen von 2 Artikeln von docs.micropython.org zur Weiterverarbeitung zwischengespeichert.
Interrupts
Hier die Übersetzung von DeepL.com von https://docs.micropython.org/en/v1.12/pyboard/tutorial/switch.html?highlight=interrupts Sie bezieht sich speziell auf das Pyboard.
5.2. Technische Details von /interrupts)
Schauen wir uns die Details des Switch-Callbacks an. Wenn Sie eine Funktion mit sw.caiiback() registrieren, setzt der Schalter einen externen Interrupt-Trigger (fallende Flanke) an dem Pin, mit dem der Schalter verbunden ist. Das bedeutet, dass der Mikrocontroller auf dem Pin auf jede Änderungen ab, und es wird Folgendes geschehen:
1. Wenn der Schalter gedrückt wird, kommt es zu einer Änderung an dem Pin (der Pin geht von Low auf High), und der Mikrocontroller registriert diese Änderung.
2. Der Mikrocontroller beendet die Ausführung des aktuellen Maschinenbefehls, stoppt die Ausführung und speichert seinen aktuellen Zustand (schiebt die Register auf den Stack). Dies hat zur Folge, dass jeglicher Code angehalten wird Code, zum Beispiel Ihr laufendes Python-Skript.
3. Der Mikrocontroller beginnt mit der Ausführung des speziellen Interrupt-Handlers, der mit dem externen Trigger des Schalters externen Auslöser. Dieser Interrupt-Handler holt sich die Funktion, die Sie mit =w.ca11back() registriert haben, und führt sie aus.
4, Ihre Callback-Funktion wird ausgeführt, bis sie beendet ist, und gibt die Kontrolle an den Interrupt-Handler des Schalters Handler zurück.
5. Der Switch-Interrupt-Handler kehrt zurück, und der Mikrocontroller wird benachrichtigt, dass der Interrupt bearbeitet wurde.
6. Der Mikrocontroller stellt den Zustand wieder her, den er in Schritt 2 gespeichert hat.
7. Die Ausführung des Codes, der zu Beginn ausgeführt wurde, wird fortgesetzt. Abgesehen von der Pause merkt dieser merkt dieser Code nicht, dass er unterbrochen wurde.
Die obige Abfolge von Ereignissen wird etwas komplizierter, wenn mehrere iNESFFUBES) gleichzeitig auftreten zur gleichen Zeit auftreten. In diesem Fall wird der Interrupt mit der höchsten Priorität zuerst ausgelöst, dann die anderen in der Reihenfolge ihrer ihrer Priorität. Der Switch-Interrupt wird auf die niedrigste Priorität gesetzt.
Interrupt Handler schreiben
DeepL.com Übersetzung von: https://docs.micropython.org/en/v1.12/reference/isr_rules.html?highlight=interrupts
Schreiben von Interrupt-Handlern
(Auf geeigneter Hardware bietet MicroPython die Möglichkeit, Interrupt-Handler in Python zu schreiben. Unterbrechung Handler - auch bekannt als Interrupt-Service-Routinen (ISRs) - sind als Callback-Funktionen definiert. Diese werden als Reaktion auf ein Ereignis wie einen Timer-Trigger oder eine Spannungsänderung an einem Pin ausgeführt. Solche Ereignisse können an jedem beliebigen Punkt der Ausführung des Programmcodes auftreten. Dies hat erhebliche Konsequenzen, von denen einige spezifisch für die Sprache MicroPython sind. Andere sind für alle Systeme typisch die in der Lage sind, auf Echtzeit-Ereignisse zu reagieren. Dieses Dokument behandelt zunächst die sprachspezifischen Probleme, gefolgt von einer kurzen Einführung in die Echtzeitprogrammierung für diejenigen, die damit noch nicht vertraut sind.
In dieser Einführung werden vage Begriffe wie "langsam" oder "so schnell wie möglich" verwendet. Dies ist beabsichtigt, da Geschwindigkeiten von der Anwendung abhängig sind. Die akzeptable Dauer einer ISR hängt von der Geschwindigkeit ab, mit der [iRREFAEBB auftreten, von der Art des Hauptprogramms und dem Vorhandensein anderer gleichzeitiger Ereignisse ab.
Tipps und empfohlene Praktiken
Hier werden die unten aufgeführten Punkte zusammengefasst und die wichtigsten Empfehlungen für Interrupt Handler-Code.
+ Halten Sie den Code so kurz und einfach wie möglich. + Vermeiden Sie Speicherzuweisungen: kein Anhängen an Listen oder Einfügen in Wörterbücher, keine Fließkommazahlen.
- Ziehen Sie die Verwendung von sicropython.scneaute in Betracht, um die obige Einschränkung zu umgehen.
+ Wenn eine ISR mehrere Bytes zurückgibt, verwenden Sie ein vorab zugewiesenes "ytearray". Wenn mehrere ganze Zahlen zwischen einer ISR und dem Hauptprogramm geteilt werden, sollte ein Array ( aray.array ) verwendet werden.
+ Wenn Daten zwischen dem Hauptprogramm und einer ISR ausgetauscht werden, sollten Sie iNtSrFapES) deaktivieren zu deaktivieren, bevor auf die Daten im Hauptprogramm zugegriffen wird, und sie unmittelbar danach wieder zu aktivieren (siehe Kritische Abschnitte).
- Weisen Sie einen Notfall-Ausnahmepuffer zu (siehe unten).
MicroPython-Probleme Der Notfall-Ausnahmepuffer
Wenn in einer ISR ein Fehler auftritt, ist MicroPython nicht in der Lage, einen Fehlerbericht zu erstellen, es sei denn, ein spezieller Puffer für diesen Zweck erstellt wird. Die Fehlersuche wird vereinfacht, wenn der folgende Code in einem Programm enthalten ist
import micropython micropython.alloc_emergency_exception_buf(100)
Vereinfachung
Aus einer Reihe von Gründen ist es wichtig, den ISR-Code so kurz und einfach wie möglich zu halten. Er sollte nur das tun, was unmittelbar nach dem auslösenden Ereignis getan werden muss: Operationen, die aufgeschoben werden können aufgeschoben werden können, sollten an die Hauptprogrammschleife delegiert werden. Typischerweise befasst sich eine ISR mit dem Hardware Gerät, das die Unterbrechung verursacht hat, und macht es für die nächste Unterbrechung bereit. Sie wird mit der Hauptprogrammschleife kommunizieren, indem er die gemeinsamen Daten aktualisiert, um anzuzeigen, dass die Unterbrechung
aufgetreten ist, und kehrt zurück. Eine ISR sollte die Kontrolle so schnell wie möglich an die Hauptschleife zurückgeben. Dies ist kein spezifisches MicroPython-Problem und wird im Folgenden ausführlicher behandelt.
Kommunikation zwischen einer ISR und dem Hauptprogramm
Normalerweise muss eine ISR mit dem Hauptprogramm kommunizieren. Die einfachste Möglichkeit, dies zu tun, ist über ein oder mehrere gemeinsame Datenobjekte, die entweder als global deklariert oder über eine Klasse gemeinsam genutzt werden (siehe unten). Dabei gibt es verschiedene Einschränkungen und Gefahren, die im Folgenden näher erläutert werden weiter unten. Integer-, Byte- und Sytearray-Objekte werden üblicherweise für diesen Zweck verwendet, zusammen mit Arrays (aus dem Array-Modul), die verschiedene Datentypen speichern können.
Die Verwendung von Objektmethoden als Rückrufe
MicroPython unterstützt diese leistungsstarke Technik, die es einer ISR ermöglicht, Instanzvariablen mit dem darunter liegenden Code zu teilen. Außerdem ermöglicht es einer Klasse, die einen Gerätetreiber implementiert, mehrere Geräteinstanzen zu unterstützen. Das folgende Beispiel veranlasst zwei LEDs, mit unterschiedlicher Geschwindigkeit zu blinken.
import pyb, micropython micropython.alloc_emergency_exception_buf(100) class Foo(object):
def __init__(self, timer, led): self.led = led timer.callback(self.cb) def cb(self, tim): self.led.toggle()
red = Foo(pyb.Timer(4, freq=1), pyb.LED(1)) green = Foo(pyb.Timer(2, freq=0.8), pyb.LED(2))
In diesem Beispiel verbindet die rec-Instanz den Timer 4 mit der LED 1: Wenn ein Timer 4-Interrupt auftritt
wird red.cb() aufgerufen, wodurch die LED 1 ihren Zustand ändert. Die grüne Instanz funktioniert ähnlich: ein Timer 2 Unterbrechung führt zur Ausführung von green.cb() und schaltet die LED 2 um. Die Verwendung von Instanzmethoden bringt zwei Vorteile mit sich. Erstens ermöglicht eine einzige Klasse die gemeinsame Nutzung von Code durch mehrere Hardware Instanzen. Zweitens ist das erste Argument der Callback-Funktion als gebundene Methode. se1* . Dies ermöglicht Dies ermöglicht dem Callback, auf Instanzdaten zuzugreifen und den Zustand zwischen aufeinanderfolgenden Aufrufen zu speichern. Wenn zum Beispiel die Klasse eine Variable :e1.count im Konstruktor auf Null gesetzt, könnte cb() den Zähler erhöhen. Die Instanzen 'rea und green würden dann unabhängig voneinander zählen, wie oft wie oft jede LED ihren Zustand geändert hat.
Erstellung von Python-Objekten
ISRs können keine Instanzen von Python-Objekten erzeugen. Das liegt daran, dass MicroPython den Speicher für das Objekt Speicher für das Objekt aus einem freien Speicherblock, dem Heap, zuweisen muss. Dies ist nicht erlaubt in einem Interrupt-Handler nicht erlaubt, da die Heap-Zuweisung nicht re-entrant ist. Mit anderen Worten, der Interrupt könnte auftreten, wenn das Hauptprogramm gerade eine Zuweisung durchführt - um die Um die Integrität des Heaps zu wahren, verbietet der Interpreter Speicherzuweisungen im ISR-Code.
Eine Folge davon ist, dass ISRs keine Fließkomma-Arithmetik verwenden können, da Fließkomma Python-Objekte sind. In ähnlicher Weise kann eine ISR kein Element an eine Liste anhängen. In der Praxis kann es schwierig sein genau zu bestimmen, welche Codekonstrukte versuchen werden, eine Speicherzuweisung durchzuführen und eine eine Fehlermeldung auslöst: ein weiterer Grund dafür, ISR-Code kurz und einfach zu halten.
Eine Möglichkeit, dieses Problem zu vermeiden, besteht darin, dass die ISR vorab zugewiesene Puffer verwendet. Zum Beispiel eine Klasse Konstruktor eine Bytearray-Instanz und ein boolesches Flag erzeugt. Die ISR-Methode ordnet die Daten den. Stellen im Puffer zu und setzt das Flag. Die Speicherzuweisung erfolgt im Hauptprogrammcode wenn das Objekt instanziiert wird und nicht in der ISR.
Die E/A-Methoden der MicroPython-Bibliothek bieten in der Regel eine Option zur Verwendung eines vorab zugewiesenen Puffers. Für pyb.i2c.recv() kann beispielsweise einen veränderbaren Puffer als erstes Argument akzeptieren: Dies ermöglicht die Verwendung in einer ISR.
Die Erstellung eines Objekts ohne die Verwendung einer Klasse oder von Globals ist wie folgt möglich:
def set_volume(t, buf=bytearray(3)):
buf[0] = 0xa5 buf[1] = t >> 4 buf[2] = 0x5a return buf
Der Compiler instanziiert das Standard-Bus-Argument, wenn die Funktion zum ersten Mal geladen wird geladen wird (normalerweise, wenn das Modul, in dem sie enthalten ist, importiert wird).
Eine Instanz der Objekterzeugung tritt auf, wenn ein Verweis auf eine gebundene Methode erzeugt wird. Das bedeutet dass eine ISR eine gebundene Methode nicht an eine Funktion übergeben kann. Eine Lösung besteht darin, einen Verweis auf die gebundenen Methode im Klassenkonstruktor zu erstellen und diese Referenz in der ISR zu übergeben. Zum Beispiel:
class Foo():
def __init__(self): self.bar_ref = self.bar # Allocation occurs here self.x = 0.1 tim = pyb.Timer(4) tim.init(freq=2) tim.callback(self.cb)
def bar(self, _): self.x *= 1.2 print(self.x)
def cb(self, t): # Passing self.bar would cause allocation. micropython.schedule(self.bar_ref, 0)
Andere Techniken sind die Definition und Instanziierung der Methode im Konstruktor oder die Übergabe von Foo.bar() mit dem Argument self zu übergeben.
Verwendung von Python-Objekten
Eine weitere Einschränkung für Objekte ergibt sich aus der Arbeitsweise von Python. Wenn eine. inport Anweisung ausgeführt wird, wird der Python-Code zu Bytecode kompiliert, wobei eine Codezeile typischerweise auf mehrere Bytecodes abbildet. Wenn der Code ausgeführt wird, liest der Interpreter jeden Bytecode und führt ihn als eine Reihe von Maschinencode-Anweisungen aus. Da zwischen den Maschinencodeanweisungen jederzeit eine Unterbrechung auftreten kann zwischen Maschinencodeanweisungen auftreten kann, wird die ursprüngliche Python-Codezeile möglicherweise nur teilweise ausgeführt. Folglich kann ein Python-Objekt wie eine Menge, eine Liste oder ein Wörterbuch, das in der Hauptschleife geändert wird, zum Zeitpunkt der Unterbrechung nicht interne Konsistenz zum Zeitpunkt der Unterbrechung fehlen.
Ein untypisches Ergebnis ist wie folgt. In seltenen Fällen wird die ISR genau zu dem Zeitpunkt ausgeführt wenn das Objekt teilweise aktualisiert wird. Wenn die ISR versucht, das Objekt zu lesen, kommt es zu einem Absturz. Da solche Probleme typischerweise bei seltenen, zufälligen Gelegenheiten auftreten, können sie schwer zu diagnostizieren sein. Es gibt Möglichkeiten, dieses Problem zu umgehen, die in den folgenden kritischen Abschnitten beschrieben werden.
Es ist wichtig, sich darüber klar zu werden, was die Änderung eines Objekts ausmacht. Eine Änderung an einem eingebauten Typs wie z. B. einem Wörterbuch ist problematisch. Das Ändern des Inhalts eines Arrays oder Bytearrays ist nicht. Das liegt daran, dass Bytes oder Wörter als eine einzige Maschinencodeanweisung geschrieben werden, die nicht unterbrechbar ist: In der Sprache der Echtzeitprogrammierung ist der Schreibvorgang atomar. Ein benutzerdefiniertes Objekt kann eine Ganzzahl, ein Array oder ein Bytearray instanziieren. Es ist sowohl für die Hauptschleife als auch für die ISR zulässig, den deren Inhalt zu ändern.
- Übersetzt mit www.DeepL.com/Translator (kostenlose Version) ***
MicroPython unterstützt ganze Zahlen mit beliebiger Genauigkeit. Werte zwischen 2**30 -1 und -2**30 werden
in einem einzigen Maschinenwort gespeichert. Größere Werte werden als Python-Objekte gespeichert. Folglich können Änderungen
an langen Ganzzahlen nicht als atomar angesehen werden. Die Verwendung von langen Ganzzahlen in ISRs ist unsicher, weil
eine Speicherzuweisung versucht werden kann, wenn sich der Wert der Variablen ändert.
Überwindung der Float-Beschränkung
Im Allgemeinen ist es am besten, die Verwendung von Fließkommazahlen in ISR-Code zu vermeiden: Hardwaregeräte verarbeiten normalerweise Ganzzahlen und die Umwandlung in Fließkommazahlen wird normalerweise in der Hauptschleife vorgenommen. Es gibt jedoch ein paar DSP-Algorithmen die Fließkomma erfordern. Auf Plattformen mit Hardware-Fließkomma (wie z.B. dem Pyboard) kann der Inline-ARM-Thumb-Assembler verwendet werden, um diese Einschränkung zu umgehen. Das liegt daran, dass der Prozessor Fließkommawerte in einem Maschinenwort speichert; Werte können zwischen der ISR und dem Hauptprogramm Programmcode über ein Array von Fließkommazahlen ausgetauscht werden.
Verwendung von micropython.schedule
Diese Funktion ermöglicht es einer ISR, einen Rückruf für die Ausführung "sehr bald" zu planen. Der Rückruf wird für die Ausführung in eine Warteschlange gestellt, die zu einem Zeitpunkt ausgeführt wird, zu dem der Heap nicht gesperrt ist. Daher kann er Python-Objekte erstellen und Gleitkommazahlen verwenden. Der Callback wird auch garantiert zu einem Zeitpunkt ausgeführt, zu dem das Hauptprogramm alle Aktualisierungen von Python-Objekten abgeschlossen hat, so dass der Callback nicht auf
teilweise aktualisierte Objekte.
Eine typische Anwendung ist die Handhabung von Sensor-Hardware. Die ISR holt Daten von der Hardware ab und ermöglicht ihr einen weiteren Interrupt ausgeben kann. Anschließend wird ein Callback zur Verarbeitung der Daten eingeplant.
Typische Anwendung ist der Umgang mit Sensor-Hardware. Der ISR holt Daten von der Hardware ab und ermöglicht ihr einen weiteren Interrupt auszulösen. Anschließend plant er einen Rückruf zur Verarbeitung der Daten.
Geplante Rückrufe sollten den unten beschriebenen Grundsätzen für den Entwurf von Interrupt-Handlern entsprechen. Damit sollen Probleme vermieden werden, die sich aus E/A-Aktivitäten und der Änderung gemeinsam genutzter Daten ergeben und die in jedem Code auftreten können, der die in jedem Code auftreten können, der die Hauptprogrammschleife vorwegnimmt.
Die Ausführungszeit muss im Verhältnis zur Häufigkeit des Auftretens von {inteerupis) berücksichtigt werden. auftreten können. Wenn eine Unterbrechung auftritt, während der vorherige Rückruf ausgeführt wird, wird eine weitere Instanz des Rückrufs in die Warteschlange gestellt, die nach der Ausführung der aktuellen Instanz ausgeführt wird. A anhaltend hohe Unterbrechungsrate birgt daher das Risiko eines unkontrollierten Anwachsens der Warteschlange und eventuelles Scheitern mit einem. runtinetrror "
Wenn der an scheauie() zu übergebende Callback eine gebundene Methode ist, beachten Sie den Hinweis in "Erstellung von Python-Objekten".
Ausnahmen
Wenn eine ISR eine Ausnahme auslöst, wird diese nicht an die Hauptschleife weitergegeben. Der Interrupt wird deaktiviert es sei denn, die Ausnahme wird durch den ISR-Code behandelt.
Allgemeine Fragen
Dies ist nur eine kurze Einführung in das Thema der Echtzeitprogrammierung. Einsteiger sollten beachten dass Entwurfsfehler in Echtzeitprogrammen zu Fehlern führen können, die besonders schwer zu diagnostizieren sind. Dies liegt daran, dass sie selten und in eher zufälligen Abständen auftreten können. Es ist entscheidend Es ist wichtig, dass der ursprüngliche Entwurf richtig ist und dass man Probleme voraussieht, bevor sie auftreten. Sowohl Interrupt-Handler als auch das Hauptprogramm müssen unter Berücksichtigung der folgenden Punkte entworfen werden.
Entwurf des Interrupt-Handlers
Wie bereits erwähnt, sollten ISRs so einfach wie möglich gestaltet werden. Sie sollten immer in einer kurzen, vorhersehbaren Zeitspanne zurückkehren. Dies ist wichtig, denn wenn die ISR läuft, ist die Hauptschleife nicht: Die Hauptschleife macht zwangsläufig an beliebigen Stellen im Code Pausen in ihrer Ausführung dem Code. Solche Pausen können eine Quelle für schwer zu diagnostizierende Fehler sein, insbesondere wenn ihre Dauer lang oder variabel ist. Um die Auswirkungen der ISR-Laufzeit zu verstehen, ist ein grundlegendes Verständnis der Interrupt Prioritäten erforderlich.
(TREEFRUBES) sind nach einem Prioritätsschema organisiert. ISR-Code kann selbst durch eine Unterbrechung Interrupt höherer Priorität unterbrochen werden. Dies hat Auswirkungen, wenn die beiden Interrupts Daten gemeinsam nutzen (siehe Kritische Abschnitte unten). Wenn eine solche Unterbrechung auftritt, wird eine Verzögerung in den ISR-Code eingefügt. Wenn ein Interrupt niedrigerer Priorität Unterbrechung eintritt, während die ISR läuft, wird sie verzögert, bis die ISR abgeschlossen ist: Wenn die Verzögerung zu lang ist, kann die Unterbrechung niedrigerer Priorität fehlschlagen. Ein weiteres Problem bei langsamen ISRs ist der Fall, dass ein zweite Unterbrechung desselben Typs während ihrer Ausführung auftritt. Die zweite Unterbrechung wird behandelt nach Beendigung des ersten Interrupts bearbeitet. Übersteigt jedoch die Anzahl der eingehenden Unterbrechungen ständig die Kapazität des ISR übersteigt, um sie zu bedienen, ist das Ergebnis nicht erfreulich.
Folglich sollten Schleifenkonstrukte vermieden oder minimiert werden. E/A an andere Geräte als an das unterbrechenden Gerät sollte normalerweise vermieden werden: E/A wie Festplattenzugriff, Druckanweisungen und UART-Zugriffe sind relativ langsam, und ihre Dauer kann variieren. Ein weiteres Problem ist, dass Dateisystem Dateisystemfunktionen nicht reentrant sind: Die Verwendung von Dateisystem-E/A in einer ISR und dem Hauptprogramm wäre gefährlich. Entscheidend ist, dass ISR-Code nicht auf ein Ereignis warten sollte. E/A ist akzeptabel, wenn garantiert werden kann, dass der Code garantiert werden kann, dass er in einem vorhersehbaren Zeitraum zurückkehrt, z. B. das Umschalten eines Pins oder einer LED. Der Zugriff auf das Zugriff auf das unterbrechende Gerät über I2C oder SPI kann notwendig sein, aber die für solche Zugriffe benötigte Zeit sollte berechnet oder gemessen werden und die Auswirkungen auf die Anwendung bewertet werden.
In der Regel besteht die Notwendigkeit, Daten zwischen der ISR und der Hauptschleife auszutauschen. Dies kann entweder über globale Variablen oder über Klassen- oder Instanzvariablen. Variablen sind typischerweise ganzzahlige oder boolesche Typen oder Integer- oder Byte-Arrays (ein vorab zugewiesenes Integer-Array bietet schnelleren Zugriff als eine Liste). Wo mehrere Werte durch die ISR geändert werden, ist der Fall zu berücksichtigen, dass die Unterbrechung zu einem Zeitpunkt auftritt, zu dem das Hauptprogramm auf einige, aber nicht auf alle Werte zugegriffen hat. Dies kann zu zu Inkonsistenzen führen.
- Übersetzt mit www.DeepL.com/Translator (kostenlose Version) ***
Betrachten Sie den folgenden Entwurf. Ein ISR speichert eingehende Daten in einem Bytearray und addiert dann die Anzahl der
Bytes zu einer Ganzzahl, die die Gesamtzahl der Bytes angibt, die zur Verarbeitung bereit sind. Das Hauptprogramm liest
die Anzahl der Bytes, verarbeitet die Bytes und löscht dann die Anzahl der bereitstehenden Bytes. Dies wird
funktioniert, bis eine Unterbrechung auftritt, kurz nachdem das Hauptprogramm die Anzahl der Bytes gelesen hat. Der ISR
fügt die hinzugefügten Daten in den Puffer ein und aktualisiert die empfangene Anzahl, aber das Hauptprogramm hat
aber das Hauptprogramm hat die Zahl bereits gelesen und verarbeitet daher die ursprünglich empfangenen Daten. Die neu eingetroffenen Bytes gehen verloren.
Es gibt verschiedene Möglichkeiten, diese Gefahr zu vermeiden, wobei die einfachste darin besteht, einen Ringpuffer zu verwenden. Wenn es nicht möglich ist nicht möglich ist, eine Struktur mit inhärenter Thread-Sicherheit zu verwenden, werden im Folgenden andere Möglichkeiten beschrieben.
Wiederholbarkeit
Eine potenzielle Gefahr kann auftreten, wenn eine Funktion oder Methode vom Hauptprogramm und einer oder mehreren oder mehreren ISRs oder zwischen mehreren ISRs geteilt wird. Das Problem dabei ist, dass die Funktion selbst unterbrochen werden kann unterbrochen und eine weitere Instanz dieser Funktion ausgeführt wird. Wenn dies geschehen soll, muss die Funktion reentrant sein. Wie man das macht, ist ein fortgeschrittenes Thema, das den Rahmen dieses Tutorials sprengen würde.
Kritische Abschnitte
Ein Beispiel für einen kritischen Codeabschnitt ist ein Abschnitt, der auf mehr als eine Variable zugreift, die
die von einem ISR betroffen sein können. Tritt die Unterbrechung zufällig zwischen den Zugriffen auf die einzelnen Variablen auf,
sind ihre Werte inkonsistent. Dies ist ein Beispiel für eine Gefahr, die als Race Condition bekannt ist: die ISR
und die Hauptprogrammschleife wetteifern um die Änderung der Variablen. Um Inkonsistenz zu vermeiden, muss ein Mittel eingesetzt werden
um sicherzustellen, dass die ISR die Werte für die Dauer des kritischen Abschnitts nicht ändert.
Eine Möglichkeit, dies zu erreichen, besteht darin, pyb.disabie_ira() vor dem Beginn des Abschnitts auszuführen, und pyb.enable_irq() am Ende. Hier ein Beispiel für diesen Ansatz:
import pyb, micropython, array micropython.alloc_emergency_exception_buf(100)
class BoundsException(Exception):
pass
ARRAYSIZE = const(20) index = 0 data = array.array('i', 0 for x in range(ARRAYSIZE))
def callback1(t):
global data, index for x in range(5): data[index] = pyb.rng() # simulate input index += 1 if index >= ARRAYSIZE: raise BoundsException('Array bounds exceeded')
tim4 = pyb.Timer(4, freq=100, callback=callback1)
for loop in range(1000):
if index > 0: irq_state = pyb.disable_irq() # Start of critical section for x in range(index): print(data[x]) index = 0 pyb.enable_irq(irq_state) # End of critical section print('loop {}'.format(loop)) pyb.delay(1)
tim4.callback(None)
Ein kritischer Abschnitt kann aus einer einzigen Codezeile und einer einzigen Variablen bestehen. Betrachten Sie das folgende
Codefragment.
count = 0 def cb(): # An interrupt callback
count +=1
def main():
# Code to set up the interrupt callback omitted while True: count += 1
Dieses Beispiel veranschaulicht eine subtile Quelle von Fehlern. Die Zeile count += 1 in der Hauptschleife birgt eine spezielle Race Condition, die als Read-Modify-Write bekannt ist. Dies ist eine klassische Ursache von Fehlern in Echtzeit Echtzeitsystemen. In der Hauptschleife liest MicroPython den Wert von +.counter, addiert 1 dazu und schreibt ihn zurück. In seltenen Fällen tritt der Interrupt nach dem Lesen und vor dem Schreiben auf. Die Unterbrechung
ändert t.counter, aber seine Änderung wird von der Hauptschleife überschrieben, wenn der ISR zurückkehrt. In einem realen
System könnte dies zu seltenen, unvorhersehbaren Fehlern führen.
Wie bereits erwähnt, sollte man vorsichtig sein, wenn eine Instanz eines in Python eingebauten Typs im Hauptcode verändert wird und auf diese Instanz in einer ISR zugegriffen wird. Der Code, der die Änderung vornimmt, sollte als kritischer Abschnitt betrachtet werden, um sicherzustellen, dass die Instanz in einem gültigen Zustand ist, wenn die ISR läuft.
Besondere Vorsicht ist geboten, wenn ein Datensatz von verschiedenen ISRs gemeinsam genutzt wird. Die Gefahr dabei ist dass der Interrupt höherer Priorität auftreten kann, wenn der Interrupt niedrigerer Priorität die gemeinsamen Daten aktualisiert hat. Der Umgang mit dieser Situation ist ein fortgeschrittenes Thema, das den Rahmen dieser Einführung sprengen würde Es wird lediglich darauf hingewiesen, dass die unten beschriebenen Mutex-Objekte manchmal verwendet werden können.
Die Deaktivierung von l[int@MrUpES) für die Dauer eines kritischen Abschnitts ist die übliche und einfachste Vorgehensweise, allerdings werden dadurch alle Interrupts deaktiviert und nicht nur derjenige, der Probleme verursachen könnte. Es ist ist es im Allgemeinen nicht wünschenswert, einen Interrupt für längere Zeit zu deaktivieren. Im Falle von Timer-Interrupts führt dies zu Variabilität in Bezug auf den Zeitpunkt, zu dem ein Rückruf erfolgt. Im Falle von Geräte-Interrupts kann dies dazu führen, dass das Gerät zu spät bedient wird, was zu Datenverlusten oder Überlauffehlern in der Gerätehardware führen kann. Wie ISRs sollte auch ein kritischer Abschnitt im Hauptcode eine kurze, vorhersehbare Dauer haben.
- Übersetzt mit www.DeepL.com/Translator (kostenlose Version) ***
Ein Ansatz für den Umgang mit kritischen Abschnitten, der die Zeit, in der Finterrupes)
deaktiviert sind, ist die Verwendung eines Objekts, das als Mutex bezeichnet wird (der Name leitet sich von dem Begriff des gegenseitigen Ausschlusses ab).
Das Hauptprogramm sperrt den Mutex vor der Ausführung des kritischen Abschnitts und gibt ihn am Ende wieder frei. Die
ISR prüft, ob der Mutex gesperrt ist. Ist dies der Fall, umgeht er den kritischen Abschnitt und kehrt zurück. Der Entwurf
Herausforderung besteht darin, zu definieren, was die ISR tun soll, wenn der Zugriff auf die kritischen Variablen
verweigert wird. Ein einfaches Beispiel für einen Mutex finden Sie hier. Beachten Sie, dass der Mutex-Code zwar die
Interrupts deaktiviert, aber nur für die Dauer von acht Maschinenbefehlen: Der Vorteil dieses Ansatzes ist
dass andere Interrupts praktisch nicht betroffen sind.
(Unterbrechungen) und die REPL
Unterbrechungsbehandlungsprogramme, z. B. solche, die mit Zeitgebern verbunden sind, können weiterlaufen, nachdem ein Programm beendet wird. Dies kann zu unerwarteten Ergebnissen führen, wenn Sie erwartet hätten, dass das Objekt, das den den Rückruf auslöst, aus dem Anwendungsbereich verschwunden ist. Zum Beispiel auf dem Pyboard:
def bar():
foo = pyb.Timer(2, freq=4, callback=lambda t: print('.', end=))
bar()
Dieser läuft weiter, bis der Timer explizit deaktiviert oder die Karte mit ctrl D zurückgesetzt wird.
Hier gibt es eine Übersetzung des Artikels von docs.micropython.org mit DeepL.com: ISR schreiben