31. Lektion: Interrupts: Unterschied zwischen den Versionen

Aus Attraktor Wiki

Wechseln zu: Navigation, Suche
(Interrupts Ein-/Ausschalten)
(Handler mit Aufruf einer Funktion)
 
(9 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 Ein-/Ausschalten==
Interrupts können ein- und ausgeschaltet werden. '''int_state''' speichert den Interrupt Status. Das ist z.B. wichtig, wenn eine Programmsequenz nicht unterbrochen werden darf.
+
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>
 
<br>
 
Funktioniert nicht in der REPL.  
 
Funktioniert nicht in der REPL.  
Zeile 77: Zeile 76:
 
machine.enable_irq(int_state)
 
machine.enable_irq(int_state)
 
</pre>
 
</pre>
Der IRQ-Status des Programms muss enable() übergeben werden, sonst gibt es eine Fehlermeldung!
+
Der IRQ-Status des Programms muss '''.enable()''' übergeben werden, sonst gibt es eine Fehlermeldung!
  
 
== Interrupt Handler==
 
== Interrupt Handler==
Zeile 107: Zeile 106:
  
 
=== Handler mit globaler Variablen und print()===
 
=== 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 132: Zeile 132:
  
 
=== Handler mit Aufruf einer Funktion===
 
=== Handler mit Aufruf einer Funktion===
 +
Hier wird vom Handler eine im Hauptprogramm definierte Funktion aufgerufen.
 
<pre>
 
<pre>
 +
# int_test_04.py
  
</pre>
+
from machine import Pin
 +
from time import sleep_ms
  
 +
main_loop_zahl = 0
 +
interrupt_zahl = 0
  
 +
int_pin = Pin(11, Pin.IN, Pin.PULL_DOWN)
  
Interrupts 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.
+
def inc_zahl(zahl):
 +
    zahl += 1
 +
    return zahl
  
== Interrupts aus dem Kurs von 2022==
+
def int_handler(int_info): # muss einen Parameter haben
 +
    global interrupt_zahl
 +
    interrupt_zahl  = inc_zahl(interrupt_zahl)
 +
    print('Interrupt:', str(interrupt_zahl))
  
13.02.2023 Micropython Kurs 2022 1
+
int_pin.irq(trigger=Pin.IRQ_RISING, handler=int_handler)
Interrupts
+
 
Was ist ein Interrupt?
+
while True:
Interrupt bedeutet unterbrechen. Genau das macht ein Interrupt.
+
    main_loop_zahl += 1
Er unterbricht das laufende Programm und führt stattdessen eine
+
    print('Mainloop:',str(main_loop_zahl))
Interrupt-Service-Routine aus. Anschließend wird das zuvor
+
    sleep_ms(1000)
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>
+
>>> from m5import import *
+
>>> 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>
 
</pre>
  
Wir müssen 3 Dinge tun um einen
+
=== Parameter auswerten===
Interrupt zu implementieren:
+
Hier wird die Auswertung des an den Handler übergebenen Parameters getestet.
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>
 
<pre>
# Pin von Button A konfigurieren
+
# int_test_05.py
from m5import import *
+
from machine import Pin
+
int_pin = Pin(37, Pin.IN, Pin.PULL_UP)
+
def int_handler():
+
pass
+
int_pin.irq(trigger=Pin.IRQ_FALLING,
+
handler=int_handler)
+
</pre>
+
  
<pre>
 
from m5import import *
 
 
from machine import Pin
 
from machine import Pin
 
from time import sleep_ms
 
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)
+
int_pin = Pin(37, Pin.IN, Pin.PULL_UP) # Pin von Taster A mit Pullup-R
+
 
def int_handler(int_info): # muss einen Parameter haben
 
def int_handler(int_info): # muss einen Parameter haben
global interrupt_zahl
+
    print(int_info)
global interrupt_text_box
+
    if int_info == int_pin:
interrupt_zahl += 1
+
        print('Es ist b_up')
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
 
eingegangen ist.
 
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>
 
import machine
 
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.
 
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:
 
● 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.
 
 
 
 
 
Hinweise zum Schreiben von Interrupt Handlern: http://docs.micropython.org/en/latest/reference/isr_rules.html
 
  
 
== ToDo==
 
== ToDo==
* Handler Muster schreiben
 
* Funktionieren Funktionen, aus dem Handler aufgerufen?
 
 
* Buffer Konzept erarbeiten
 
* Buffer Konzept erarbeiten
 
 
  
 
==Navigation==
 
==Navigation==

Aktuelle Version vom 30. Januar 2024, 12:54 Uhr

Interrupts

Die Interrupts funktionieren NICHT mit der REPL. Also immer aus dem Editor starten! 

Wenn ein Programm mit Ctrl-C oder dem STOP-Button abgebrochen wird, läuft der Interrupt weiter.
Also dann den Reset-Button auf dem Demoboard drücken oder den Stecker ziehen!

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
  • ADC

Micropython kennt 2 Interruptquellen:

  1. Pin-Interrupts
  2. Timer-Interrupts

Micropython stellt für die Pins Interrupts bereit. Dazu dient die Methode Pin.irq().

>>> from m5import import *
>>> 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']

Interrupts einrichten

Die Interruptfunktion ist eine Methode der Klasse Pin.
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.
Pin.irq(handler=None, trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, *, priority=1, wake=None, hard=False)
priority
legt die Prioritätsstufe des Interrupts fest. Die Werte, die er annehmen kann, sind portspezifisch, aber höhere Werte stehen immer für höhere Prioritäten.
wake
wählt den Energiemodus aus, in dem dieser Interrupt das System aufwecken kann. Es kann machine.IDLE, machine.SLEEP oder machine.DEEPSLEEP sein. Diese Werte können auch miteinander ODER-verknüpft werden, damit ein Pin in mehr als einem Stromversorgungsmodus Interrupts erzeugt.
hard
bei True wird ein Hardware-Interrupt verwendet. Dadurch wird die Verzögerung zwischen dem Pinwechsel und dem Aufruf des Handlers verringert. Hardware-Interrupt-Handler dürfen keinen Speicher zuweisen, siehe Schreiben von Interrupt-Handlern. Nicht alle Ports unterstützen dieses Argument.

Beispiel

Hier mit Lambda-Funktion als Interrupt-Handler. (s. Timer)

# int_test_01.py

from machine import Pin

int_pin = Pin(14, Pin.IN, Pin.PULL_DOWN)
led_pin = Pin(22, Pin.OUT)
led_pin.value(1)

int_pin.irq(handler=lambda t:led_pin.toggle(), trigger=int_pin.IRQ_RISING)

Interrupt Trigger

Für den Pico stehen nur diese beiden Trigger zur Verfügung:

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.
Funktioniert nicht in der REPL.

import machine

int_state = machine.disable_irq()

machine.enable_irq(int_state)

Der IRQ-Status des Programms muss .enable() übergeben werden, sonst gibt es eine Fehlermeldung!

Interrupt Handler

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.

Einschränkungen für Interrupt-Handler

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:

  • 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.

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.

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.

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.
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.

Beispiele

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.

# int_test_03.py

from machine import Pin
from time import sleep_ms

main_loop_zahl = 0
interrupt_zahl = 0

int_pin = Pin(11, Pin.IN, Pin.PULL_DOWN) # Pin von Taster A mit Pullup-R

def int_handler(int_info): # muss einen Parameter haben
    global interrupt_zahl
    interrupt_zahl += 1
    print('Interrupt:', str(interrupt_zahl))

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)

Handler mit Aufruf einer Funktion

Hier wird vom Handler eine im Hauptprogramm definierte Funktion aufgerufen.

# int_test_04.py

from machine import Pin
from time import sleep_ms

main_loop_zahl = 0
interrupt_zahl = 0

int_pin = Pin(11, Pin.IN, Pin.PULL_DOWN)

def inc_zahl(zahl):
    zahl += 1
    return zahl

def int_handler(int_info): # muss einen Parameter haben
    global interrupt_zahl
    interrupt_zahl  = inc_zahl(interrupt_zahl)
    print('Interrupt:', str(interrupt_zahl))

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)

Parameter auswerten

Hier wird die Auswertung des an den Handler übergebenen Parameters getestet.

# int_test_05.py

from machine import Pin
from time import sleep_ms

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')

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)

ToDo

  • Buffer Konzept erarbeiten

Navigation

Zurück zur "Micropython Kurs 2023 Teil 2" Startseite
Zurück zur "Micropython Kurs 2023" Startseite
Zurück zur Programmieren Startseite
Zurück zur Wiki Startseite

Diese Seite wurde zuletzt am 30. Januar 2024 um 12:54 Uhr geändert. Diese Seite wurde bisher 1.301 mal abgerufen.