|
|
(20 dazwischenliegende Versionen des gleichen Benutzers werden nicht angezeigt) |
Zeile 64: |
Zeile 64: |
| </pre> | | </pre> |
| Trigger können auch ODER (|) verknüpft werden (Pin.IRQ_FALLING | Pin.IRQ_RISING) . | | Trigger können auch ODER (|) verknüpft werden (Pin.IRQ_FALLING | Pin.IRQ_RISING) . |
| + | |
| + | == Interrupts Ein-/Ausschalten== |
| + | Interrupts können ein- und ausgeschaltet werden. Das ist z.B. wichtig, wenn eine Programmsequenz nicht unterbrochen werden darf. '''int_state''' speichert den Interrupt Status. |
| + | <br> |
| + | Funktioniert nicht in der REPL. |
| + | <pre> |
| + | import machine |
| + | |
| + | int_state = machine.disable_irq() |
| + | |
| + | machine.enable_irq(int_state) |
| + | </pre> |
| + | Der IRQ-Status des Programms muss '''.enable()''' übergeben werden, sonst gibt es eine Fehlermeldung! |
| | | |
| == Interrupt Handler== | | == Interrupt Handler== |
Zeile 77: |
Zeile 90: |
| * Es können nicht mehrere Werte zurückgegeben werden. | | * Es können nicht mehrere Werte zurückgegeben werden. |
| * Texte können nicht bearbeitet werden. | | * Texte können nicht bearbeitet werden. |
− | * Es können keine Fehlermeldungen erzeugt werden. | + | * Es können keine Fehlermeldungen erzeugt werden. |
− | | + | |
− | <pre>
| + | |
− | # Source: Electrocredible.com, Language: MicroPython
| + | |
− | from machine import Pin
| + | |
− | import time
| + | |
− | interrupt_flag=0
| + | |
− | debounce_time=0
| + | |
− | pin = Pin(5, Pin.IN, Pin.PULL_UP)
| + | |
− | led = Pin("LED", Pin.OUT)
| + | |
− | count=0
| + | |
− | | + | |
− | def callback(pin):
| + | |
− | global interrupt_flag, debounce_time
| + | |
− | if (time.ticks_ms()-debounce_time) > 500:
| + | |
− | interrupt_flag= 1
| + | |
− | debounce_time=time.ticks_ms()
| + | |
− | | + | |
− | pin.irq(trigger=Pin.IRQ_FALLING, handler=callback)
| + | |
− | | + | |
− | while True:
| + | |
− | if interrupt_flag is 1:
| + | |
− | interrupt_flag=0
| + | |
− | print("Interrupt Detected")
| + | |
− | led.toggle()
| + | |
− | </pre>
| + | |
| | | |
| === Interrupt Handler schreiben=== | | === Interrupt Handler schreiben=== |
Zeile 108: |
Zeile 96: |
| === Probleme bei Interrupts=== | | === Probleme bei Interrupts=== |
| Interrupts arbeiten auf Maschinencode Ebene. Deshalb kann es passieren, das ein Pythonbefehl mitten in seiner Ausführung unterbrochen wird und erst die ISR abgearbeitet wird bevor der Pythonbefehl zu Ende ausgeführt wird. Das kann zu Fehlern führen wenn die ISR etwas verändert, das vom Hauptprogramm bearbeitet wird. Dadurch können Fehler entstehen, die nur sporadisch auftreten und sehr schwer zu finden sind. | | Interrupts arbeiten auf Maschinencode Ebene. Deshalb kann es passieren, das ein Pythonbefehl mitten in seiner Ausführung unterbrochen wird und erst die ISR abgearbeitet wird bevor der Pythonbefehl zu Ende ausgeführt wird. Das kann zu Fehlern führen wenn die ISR etwas verändert, das vom Hauptprogramm bearbeitet wird. Dadurch können Fehler entstehen, die nur sporadisch auftreten und sehr schwer zu finden sind. |
| + | |
| + | === Parameter des Handlers=== |
| + | Dem Interrupt-Handler wird beim Aufruf ein Parameter übergeben. Es ist wichtig, das dieser bei der Definition berücksichtigt wird. Es gibt keinen Zwang diesen auch zu benutzen. |
| + | <br> |
| + | Bei dem an den Interrupthandler übergebenen Wert int_info handelt es sich um das Objekt des Pins an dem der Interrupt eingegangen ist. <br> |
| + | Wenn dieser Wert ausgewertet werden soll dann so: '''if int_info == b_up'''. |
| | | |
| == Beispiele== | | == Beispiele== |
| | | |
− | === Mit Handler=== | + | === Handler mit globaler Variablen und print()=== |
| + | Um im Interrupt-Handler Variablen verwenden zu können sind globale Variablen erforderlich. Diese werden im Hauptprogramm initialisiert und können dann vom Handler benutzt werden. |
| <pre> | | <pre> |
| # int_test_03.py | | # int_test_03.py |
Zeile 127: |
Zeile 122: |
| interrupt_zahl += 1 | | interrupt_zahl += 1 |
| print('Interrupt:', str(interrupt_zahl)) | | print('Interrupt:', str(interrupt_zahl)) |
− | return
| |
| | | |
| int_pin.irq(trigger=Pin.IRQ_RISING, handler=int_handler) | | int_pin.irq(trigger=Pin.IRQ_RISING, handler=int_handler) |
Zeile 137: |
Zeile 131: |
| </pre> | | </pre> |
| | | |
− | == Interrupts aus dem Kurs von 2022== | + | === Handler mit Aufruf einer Funktion=== |
− | | + | Hier wird vom Handler eine im Hauptprogramm definierte Funktion aufgerufen. |
− | 13.02.2023 Micropython Kurs 2022 1
| + | |
− | Interrupts
| + | |
− | Was ist ein Interrupt?
| + | |
− | Interrupt bedeutet unterbrechen. Genau das macht ein Interrupt.
| + | |
− | Er unterbricht das laufende Programm und führt stattdessen eine
| + | |
− | Interrupt-Service-Routine aus. Anschließend wird das zuvor
| + | |
− | unterbrochene Programm fortgesetzt.
| + | |
− | Interrupts werden in der Regel von einer Hardware ausgelöst. Z.B.
| + | |
− | Taster, Timer oder ADC
| + | |
− | <br>
| + | |
− | Um einen Interrupt zu implementieren sind 2 Dinge erforderlich:
| + | |
− | ● Eine Interrupt-Service-Routine (ISR) auch Callback-
| + | |
− | Funktion (Cb) oder Interrupt Handler genannt, die den | + | |
− | Interrupt bearbeitet.
| + | |
− | ● Eine Interruptquelle, die mit der ISR verbunden wird.
| + | |
− | Micropython kennt 2 Interruptquellen:
| + | |
− | ● Pin-Interrupts
| + | |
− | ● Timer-Interrupts
| + | |
− | <br>
| + | |
− | Micropython stellt für die Pin
| + | |
− | Interrupts bereit. Dazu dient die
| + | |
− | Methode Pin.irq().
| + | |
| <pre> | | <pre> |
− | >>> from m5import import *
| + | # int_test_04.py |
− | >>> from machine import Pin
| + | |
− | >>> dir(Pin)
| + | |
− | ['__class__', '__name__', 'value',
| + | |
− | '__bases__', '__dict__', 'IN',
| + | |
− | 'IRQ_FALLING', 'IRQ_RISING',
| + | |
− | 'OPEN_DRAIN', 'OUT', 'PULL_DOWN',
| + | |
− | 'PULL_HOLD', 'PULL_UP', 'WAKE_HIGH',
| + | |
− | 'WAKE_LOW', 'init', 'irq', 'off',
| + | |
− | 'on']
| + | |
− | </pre>
| + | |
| | | |
− | Wir müssen 3 Dinge tun um einen
| |
− | Interrupt zu implementieren:
| |
− | 1. Zuerst muss der Pin als
| |
− | Eingang konfiguriert werden.
| |
− | 2. Dann sollte der Handler
| |
− | definiert werden.
| |
− | 3. Und schließlich kann der
| |
− | Interrupt eingerichtet werden.
| |
− |
| |
− | <pre>
| |
− | # Pin von Button A konfigurieren
| |
− | from m5import import *
| |
| from machine import Pin | | from machine import Pin |
− | int_pin = Pin(37, Pin.IN, Pin.PULL_UP)
| + | from time import sleep_ms |
− | def int_handler():
| + | |
− | pass
| + | |
− | int_pin.irq(trigger=Pin.IRQ_FALLING,
| + | |
− | handler=int_handler)
| + | |
− | </pre>
| + | |
| | | |
− | <pre>
| |
− | from m5import import *
| |
− | from machine import Pin
| |
− | from time import sleep_ms
| |
− | lcd.clear()
| |
| main_loop_zahl = 0 | | main_loop_zahl = 0 |
| interrupt_zahl = 0 | | interrupt_zahl = 0 |
− | main_text_box =
| + | |
− | M5TextBox(20, 10, str(main_loop_zahl), lcd.FONT_DejaVu24, 0xFFFFFF, rotate=0)
| + | int_pin = Pin(11, Pin.IN, Pin.PULL_DOWN) |
− | interrupt_text_box =
| + | |
− | M5TextBox(120, 10, str(interrupt_zahl), lcd.FONT_DejaVu24,0xFFFFFF, rotate=0)
| + | def inc_zahl(zahl): |
− | int_pin = Pin(37, Pin.IN, Pin.PULL_UP) # Pin von Taster A mit Pullup-R | + | zahl += 1 |
| + | return zahl |
| + | |
| def int_handler(int_info): # muss einen Parameter haben | | def int_handler(int_info): # muss einen Parameter haben |
− | global interrupt_zahl | + | global interrupt_zahl |
− | global interrupt_text_box
| + | interrupt_zahl = inc_zahl(interrupt_zahl) |
− | interrupt_zahl += 1 | + | print('Interrupt:', str(interrupt_zahl)) |
− | interrupt_text_box.setText(str(interrupt_zahl))
| + | |
− | interrupt_text_box.show()
| + | |
− | print('Interrupt wurde ausgelöst!') | + | |
− | return
| + | |
− | int_pin.irq(trigger=Pin.IRQ_FALLING, handler=int_handler)
| + | |
− | while True:
| + | |
− | main_loop_zahl += 1
| + | |
− | main_text_box.setText(str(main_loop_zahl))
| + | |
− | main_text_box.show()
| + | |
− | sleep_ms(1000)
| + | |
− | </pre>
| + | |
− | Wir wollen jetzt den an den
| + | |
− | Interrupt-Handler übergebenen
| + | |
− | Wert näher untersuchen.
| + | |
− | Dazu schaffen wir eine zweite
| + | |
− | Interruptquelle mit GPIO26.
| + | |
− | <pre>
| + | |
− | # int_test_006.py
| + | |
− | from m5import import *
| + | |
− | from machine import Pin
| + | |
− | from time import sleep_ms
| + | |
− | lcd.clear()
| + | |
− | interrupt_data = 0
| + | |
− | interrupt_data_text_box = M5TextBox(60, 30, str(interrupt_data),
| + | |
− | lcd.FONT_DejaVu24,0xFFFFFF, rotate=0)
| + | |
− | int_pin37 = Pin(37, Pin.IN, Pin.PULL_UP) # Pin von Taster A
| + | |
− | int_pin26 = Pin(26, Pin.IN, Pin.PULL_UP) # Pin GPIO26
| + | |
| | | |
− | def int_handler(int_data): # muss einen Parameter haben
| + | int_pin.irq(trigger=Pin.IRQ_RISING, handler=int_handler) |
− | global interrupt_data
| + | |
− | print(int_data)
| + | |
− | print(type(int_data))
| + | |
− | interrupt_data = int_data
| + | |
− | interrupt_data_text_box.setText(str(interrupt_data))
| + | |
− | interrupt_data_text_box.show()
| + | |
− | if int_data == Pin(37):
| + | |
− | print('Interrupt 37 wurde ausgelöst!')
| + | |
− | elif int_data == Pin(26):
| + | |
− | print('Interrupt 26 wurde ausgelöst!')
| + | |
− | else:
| + | |
− | print('Fehler!')
| + | |
− | return
| + | |
| | | |
− | int_pin37.irq(trigger=Pin.IRQ_FALLING, handler=int_handler)
| |
− | int_pin26.irq(trigger=Pin.IRQ_FALLING, handler=int_handler)
| |
| while True: | | while True: |
− | pass
| + | main_loop_zahl += 1 |
| + | print('Mainloop:',str(main_loop_zahl)) |
| + | sleep_ms(1000) |
| </pre> | | </pre> |
− | Bei dem an den Interrupthandler übergebenen Wert int_info
| + | |
− | handelt es sich um das Objekt des Pins an dem der Interrupt
| + | === Parameter auswerten=== |
− | eingegangen ist.
| + | Hier wird die Auswertung des an den Handler übergebenen Parameters getestet. |
− | Ich hatte aufgrund der Darstellung im Display angenommen es
| + | |
− | wäre ein String.
| + | |
− | Wenn dieser Wert ausgewertet werden soll dann so:
| + | |
− | if int_info == Pin(37):
| + | |
− | Also keine Anführungszeichen! Es ist der Name einer Instanz der
| + | |
− | Klasse Pin.
| + | |
− | <br>
| + | |
− | nterrupts können ein- und
| + | |
− | ausgeschaltet werden.
| + | |
− | int_state speichert den Interrupt
| + | |
− | Status.
| + | |
− | Das ist z.B. wichtig, wenn eine
| + | |
− | Programmsequenz nicht unter-
| + | |
− | brochen werden darf.
| + | |
− | Funktioniert nicht in der REPL.
| + | |
| <pre> | | <pre> |
− | import machine
| + | # int_test_05.py |
− | int_state = machine.disable_irq()
| + | |
− | machine.enable_irq(int_state)
| + | |
− | count = 0
| + | |
− | def cb(): # An interrupt callback
| + | |
− | count +=1
| + | |
− | def main():
| + | |
− | # Code to set up the interrupt
| + | |
− | # callback omitted
| + | |
− | while True:
| + | |
− | count += 1
| + | |
− | </pre>
| + | |
| | | |
− | Einschränkungen für Interrupt-Handler.
| + | from machine import Pin |
− | Interrupt Handler können keinen Speicher benutzen. Deshalb ist
| + | from time import sleep_ms |
− | ihre Anwendung eingeschränkt. Alles was zusätzlichen Speicher
| + | |
− | benötigt kann in einer ISR nicht ausgeführt werden. Z.B:
| + | |
− | ● Es können keine Fließkommazahlen benutzt werden.
| + | |
− | ● Listen und Dictionaries können nicht erweitert werden.
| + | |
− | ● Es können nicht mehrere Werte zurückgegeben werden.
| + | |
− | ● Texte können nicht bearbeitet werden.
| + | |
− | ● Es können keine Fehlermeldungen erzeugt werden.
| + | |
− | <br>
| + | |
− | Interrupt Handler schreiben.
| + | |
− | ISR’s sollten immer so kurz wie möglich gehalten werden. Nur
| + | |
− | Aktivitäten sie sofort ausgeführt werden müssen (z.B. Notstop)
| + | |
− | sollten in der ISR ausgeführt werden. Alles, was etwas warten
| + | |
− | kann sollte erst in der Hauptschleife bearbeitet werden. Dort gelten
| + | |
− | auch die eben dargestellten Einschränkungen nicht. Dazu ist in der
| + | |
− | ISR ein Flag zu setzen und in der Hauptschleife abzufragen.
| + | |
− | <br>
| + | |
− | Probleme bei Interrupts.
| + | |
− | Interrupts arbeiten auf Maschinencode Ebene. Deshalb kann es
| + | |
− | passieren, das ein Pythonbefehl mitten in seiner Ausführung
| + | |
− | unterbrochen wird und erst die ISR abgearbeitet wird bevor der
| + | |
− | Pythonbefehl zu Ende ausgeführt wird. Das kann zu Fehlern
| + | |
− | führen wenn die ISR etwas verändert, das vom Hauptprogramm
| + | |
− | bearbeitet wird.
| + | |
− | Dadurch können Fehler entstehen, die nur sporadisch auftreten
| + | |
− | und sehr schwer zu finden sind.
| + | |
| | | |
| + | main_loop_zahl = 0 |
| + | interrupt_zahl = 0 |
| | | |
| + | int_pin = Pin(11, Pin.IN, Pin.PULL_DOWN) |
| | | |
| + | def int_handler(int_info): # muss einen Parameter haben |
| + | print(int_info) |
| + | if int_info == int_pin: |
| + | print('Es ist b_up') |
| | | |
− | Hinweise zum Schreiben von Interrupt Handlern: http://docs.micropython.org/en/latest/reference/isr_rules.html
| + | int_pin.irq(trigger=Pin.IRQ_RISING, handler=int_handler) |
| + | |
| + | while True: |
| + | main_loop_zahl += 1 |
| + | print('Mainloop:',str(main_loop_zahl)) |
| + | sleep_ms(1000) |
| + | </pre> |
| | | |
| == ToDo== | | == ToDo== |
− | * Handler Muster schreiben
| |
− | * Funktionieren Funktionen, aus dem Handler aufgerufen?
| |
| * Buffer Konzept erarbeiten | | * Buffer Konzept erarbeiten |
− |
| |
− |
| |
| | | |
| ==Navigation== | | ==Navigation== |
Interrupt bedeutet unterbrechen. Genau das macht ein Interrupt. Er unterbricht das laufende Programm und führt stattdessen eine Interrupt-Service-Routine aus. Anschließend wird das zuvor unterbrochene Programm fortgesetzt.
Interrupts werden in der Regel von einer Hardware ausgelöst. Z.B.
Micropython stellt für die Pins Interrupts bereit. Dazu dient die Methode Pin.irq().
Die Interruptfunktion ist eine Methode der Klasse Pin.
Wir müssen 3 Dinge tun um einen Interrupt zu implementieren:
Hier mit Lambda-Funktion als Interrupt-Handler. (s. Timer)
Trigger können auch ODER (|) verknüpft werden (Pin.IRQ_FALLING | Pin.IRQ_RISING) .
Interrupts können ein- und ausgeschaltet werden. Das ist z.B. wichtig, wenn eine Programmsequenz nicht unterbrochen werden darf. int_state speichert den Interrupt Status.
Funktioniert nicht in der REPL.
Während die Einrichtung eines Interrupts einfach ist, unterliegt das Schreiben des Handlers einigen Einschränkungen. Wenn man diese nicht berücksichtigt wird man auf mysteriöses Fehlverhalten stoßen.
Interrupt Handler können keinen Speicher benutzen. Deshalb ist ihre Anwendung eingeschränkt.
Alles was zusätzlichen Speicher benötigt kann in einer ISR nicht ausgeführt werden. Z.B:
ISR’s sollten immer so kurz wie möglich gehalten werden. Nur Aktivitäten sie sofort ausgeführt werden müssen (z.B. Notstop) sollten in der ISR ausgeführt werden. Alles, was etwas warten kann sollte erst in der Hauptschleife bearbeitet werden. Dort gelten auch die eben dargestellten Einschränkungen nicht. Dazu ist in der ISR ein Flag zu setzen und in der Hauptschleife abzufragen.
Interrupts arbeiten auf Maschinencode Ebene. Deshalb kann es passieren, das ein Pythonbefehl mitten in seiner Ausführung unterbrochen wird und erst die ISR abgearbeitet wird bevor der Pythonbefehl zu Ende ausgeführt wird. Das kann zu Fehlern führen wenn die ISR etwas verändert, das vom Hauptprogramm bearbeitet wird. Dadurch können Fehler entstehen, die nur sporadisch auftreten und sehr schwer zu finden sind.
Dem Interrupt-Handler wird beim Aufruf ein Parameter übergeben. Es ist wichtig, das dieser bei der Definition berücksichtigt wird. Es gibt keinen Zwang diesen auch zu benutzen.
Bei dem an den Interrupthandler übergebenen Wert int_info handelt es sich um das Objekt des Pins an dem der Interrupt eingegangen ist.
Wenn dieser Wert ausgewertet werden soll dann so: if int_info == b_up.
Um im Interrupt-Handler Variablen verwenden zu können sind globale Variablen erforderlich. Diese werden im Hauptprogramm initialisiert und können dann vom Handler benutzt werden.
Hier wird vom Handler eine im Hauptprogramm definierte Funktion aufgerufen.
Hier wird die Auswertung des an den Handler übergebenen Parameters getestet.