Hallo in die Runde,
ich versuche seit Tagen einen UART IRQ mit Callback ans laufen zu bekommen.
Bei Datenempfang über den UART soll ein IRQ gefeuert werden um dann eine Callback oder ISR aufzurufen.
Hier https://docs.micropython.org/en/latest/ ... chine-uart habe ich nachgelesen und im Netz nach einem Beispiel gesucht
habe aber nicht brauchbares gefunden oder ans laufen bekommen.
Der µC ist ein original RP2040 board, UART an sich funktioniert tadellos.
Hat jemand vieleicht ein Beispiel oder ein Link wo diese Problematik behandelt wird ?
Frank
PI Pico UART IRQ mit Callback
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DL3AD: Was ist denn das konkrete Problem?
Wie es aussieht kann man sich beim Pico nur einen IRQ generieren lassen wenn die RX-Leitung „idle“ wird, also wenn die Gegenseite aufhört Zeichen zu schicken oder zumindest eine Pause einlegt. Nicht für jedes einzelne empfangene Zeichen.
Welches Problem soll denn mit IRQs hier gelöst werden?
Wie es aussieht kann man sich beim Pico nur einen IRQ generieren lassen wenn die RX-Leitung „idle“ wird, also wenn die Gegenseite aufhört Zeichen zu schicken oder zumindest eine Pause einlegt. Nicht für jedes einzelne empfangene Zeichen.
Welches Problem soll denn mit IRQs hier gelöst werden?
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
- DeaD_EyE
- User
- Beiträge: 1286
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Nicht jede Architektur unterstützt die IRQs. Hier ist eine Tabelle:
https://docs.micropython.org/en/latest/ ... T.html#id1
UART unterstützt asyncio.Stream beim rp2 leider noch nicht.
Beim esp32 ist es bereits implementiert.
Man kann sich aber einen eigenen Wrapper für asyncio bauen. Ist zwar nicht so effizient, aber funktioniert.
Die Frage ist, wie schnell willst du reagieren? Kommt es auf weniger als 10 ms an?
https://docs.micropython.org/en/latest/ ... T.html#id1
UART unterstützt asyncio.Stream beim rp2 leider noch nicht.
Beim esp32 ist es bereits implementiert.
Man kann sich aber einen eigenen Wrapper für asyncio bauen. Ist zwar nicht so effizient, aber funktioniert.
Die Frage ist, wie schnell willst du reagieren? Kommt es auf weniger als 10 ms an?
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
@__blckjack__,
genau den Fall wenn die RX-Leitung "idle" wird.
Ich möchte eine Funktion aufrufen und was mit den Daten machen.
Bisher habe ich es nicht hinbekommen und suche nach einem Beispiel andem ich mich orientieren kann.
genau den Fall wenn die RX-Leitung "idle" wird.
Ich möchte eine Funktion aufrufen und was mit den Daten machen.
Bisher habe ich es nicht hinbekommen und suche nach einem Beispiel andem ich mich orientieren kann.
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DL3AD: Was hast Du denn gemacht und was funktioniert daran nicht? Und was willst Du mit den Daten machen? Denn in einem ISR darf man ja so gut wie gar nichts machen. Alles was neuen Speicher dynamisch anfordern könnte ist Tabu und das ist in Python so gut wie alles.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
- DeaD_EyE
- User
- Beiträge: 1286
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Bei harten Interrupts kann man keinen Speicher zuweisen, d.h. man muss außerhalb der Funktion (z.B. auf Modulebene) einen Buffer (z.B. bytearray) anlegen und diesen dann beim Aufruf des IRQ befüllen.
Code nicht getestet.
Wenn der IRQ RXIDLE kommt, prüft der handler, ob daten verfügbar sind und falls ja, werden die Daten vom rxbuffer des UART in den uart_rx_buffer (bytearray) kopiert.
Der Speicher für den Buffer ist aber bereits vergeben und wird nicht beim Aufruf des Interrupts zugewiesen. D.h. der Code sollte auch mit harten Interrupts funktionieren, sofern der RP2 das unterstützt.
Wenn man die Methode uart.read(xxx) nutzt, wird, soweit ich weiß, neuer Speicher zugewiesen.
Code: Alles auswählen
from machine import UART, Pin
BUFFER_SIZE = 256
def uart_rx_idle(uart: UART):
if uart.any():
uart.readinto(uart_rx_buffer)
uart_rx_buffer = bytearray(BUFFER_SIZE)
uart = UART(1, baudrate=9600, rx=Pin(10), tx=Pin(11), rxbuf=BUFFER_SIZE)
uart.irq(handler=uart_rx_idle, trigger=UART.IRQ_RXIDLE, hard=False)
# handler is an optional function to be called when the interrupt event triggers.
# The handler must take exactly one argument which is the UART instance.
# kein harter Interrupt, aber es ist trotzdem besser, den Buffer zu verwenden
Wenn der IRQ RXIDLE kommt, prüft der handler, ob daten verfügbar sind und falls ja, werden die Daten vom rxbuffer des UART in den uart_rx_buffer (bytearray) kopiert.
Der Speicher für den Buffer ist aber bereits vergeben und wird nicht beim Aufruf des Interrupts zugewiesen. D.h. der Code sollte auch mit harten Interrupts funktionieren, sofern der RP2 das unterstützt.
Wenn man die Methode uart.read(xxx) nutzt, wird, soweit ich weiß, neuer Speicher zugewiesen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Die Frage an der Stelle ist warum man das so machen sollte, denn man kopiert hier ja nur aus dem Puffer den `UART` schon hat, in einen anderen Puffer um. Wirklich etwas interessantes kann man nur ausserhalb der ISR machen, da braucht man aber diesen zusätzlichen Puffer nicht wirklich.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
Hallo,
Danke für eure Hilfe - nun funktioniert es.
Danke für eure Hilfe - nun funktioniert es.
Code: Alles auswählen
from machine import UART, Pin
def myuart_rx(myuart: UART):
print(myuart.read().decode())
myuart = UART(0, baudrate=9600, rx=Pin(1), tx=Pin(0))
myuart.irq(handler=myuart_rx, trigger=UART.IRQ_RXIDLE, hard=False)
- DeaD_EyE
- User
- Beiträge: 1286
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Das Modul machine ist in C implementiert. Der UART-Buffer ist dementsprechend nicht direkt von Micropython aus erreichbar. Der zweite Buffer ist unumgänglich, wenn man mit IRQs arbeitet. Die Frage ist halt immer noch, wozu? Interrupts verwendet man, wenn man sehr schnell reagieren muss. Wenn 10 ms reichen, sollte man eher Asyncio nutzen. Leider muss man seinen eigenen Wrapper beim RP2 konstruieren, da asyncio.Stream nur UART-Objekte unterstützt, die die ioctl Methode unterstützen. Soweit ich weiß ist es beim ESP32 implementiert.__blackjack__ hat geschrieben: Dienstag 28. Oktober 2025, 16:09 Die Frage an der Stelle ist warum man das so machen sollte, denn man kopiert hier ja nur aus dem Puffer den `UART` schon hat, in einen anderen Puffer um. Wirklich etwas interessantes kann man nur ausserhalb der ISR machen, da braucht man aber diesen zusätzlichen Puffer nicht wirklich.
Wenn man das z.B. mit pyserial für CPython vergleicht, so ist die Nutzung von asyncio mit Micropython und UART einfacher. Micropythons asyncio fehlen noch einige Funktionen/Klassen. Das ist ein minimalistisches asyncio.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DeaD_EyE: Im ISR wird man den UART-internen Puffer ja nur in den eigenen Puffer umkopieren damit der normale Code etwas damit machen kann und da braucht man dass dann aber nicht umkopieren, denn im normalen Code kann man ja einfach die Daten aus dem internen Puffer anfordern, und das ohne sich Gedanken darüber machen zu müssen welche Einschränkungen für ISRs gelten.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
- DeaD_EyE
- User
- Beiträge: 1286
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Während im MainThread nach der Unterbrechung gelesen wird, können sich schon wieder neue Daten im Puffer befinden. Man muss kopieren und auch etwas zur Synchronisation nutzen. Besser wäre eine Queue. Dann ist die Synchronisation kostenlos mit dabei.__blackjack__ hat geschrieben: Dienstag 28. Oktober 2025, 20:06 @DeaD_EyE: Im ISR wird man den UART-internen Puffer ja nur in den eigenen Puffer umkopieren damit der normale Code etwas damit machen kann und da braucht man dass dann aber nicht umkopieren, denn im normalen Code kann man ja einfach die Daten aus dem internen Puffer anfordern, und das ohne sich Gedanken darüber machen zu müssen welche Einschränkungen für ISRs gelten.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DeaD_EyE: Aber genau das gleiche kann einem doch auch beim ISR passieren. Auch dort kann ein weiterer IRQ ausgelöst werden, bevor der normale Code dazu kommt den letzten kopierten Puffer zu verarbeiten. Diese angebliche Notwendigkeit läuft darauf hinaus, dass ein Protokoll verwendet wird, wo man aus den Nachrichten nicht erkennen kann wo sie anfangen/enden wenn man dazu den „idle“-Zustand des Senders missbraucht. Das wäre fehleranfällig und sollte beim Protokoll repariert werden.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
- DeaD_EyE
- User
- Beiträge: 1286
- Registriert: Sonntag 19. September 2010, 13:45
- Wohnort: Hagen
- Kontaktdaten:
Vielleicht kann das passieren. Typischerweise wird beim ISR der Code so minimal gehalten, dass es abgearbeitet werden kann, bevor die nächsten Daten kommen. Wie schnell der RX-Idle nacheinander ausgelöst werden kann, hängt dann von der eingestellten Baudrate ab. Der Code im MainThread wird solange unterbrochen, bis der ISR-Handler fertig ist. Was dann passieren kann, dass der nächste ISR ausgelöst wird, bevor der MainThread wieder dran ist.__blackjack__ hat geschrieben: Mittwoch 29. Oktober 2025, 09:45 @DeaD_EyE: Aber genau das gleiche kann einem doch auch beim ISR passieren. Auch dort kann ein weiterer IRQ ausgelöst werden, bevor der normale Code dazu kommt den letzten kopierten Puffer zu verarbeiten. Diese angebliche Notwendigkeit läuft darauf hinaus, dass ein Protokoll verwendet wird, wo man aus den Nachrichten nicht erkennen kann wo sie anfangen/enden wenn man dazu den „idle“-Zustand des Senders missbraucht. Das wäre fehleranfällig und sollte beim Protokoll repariert werden.
Wie auch immer. Ich glaube, die Diskussion führt nicht weiter. Jedenfalls ist der ISR nicht aus Langeweile implementiert worden. AFIK war es eine Anforderung von jemanden und ist gesponsort worden. D.h. einen echten Anwendungsfall gibt es definitiv.
Und nochmal zur Klarstellung, ich würde asyncio nutzen, wenn 10ms in Ordnung sind. IRQs sind immer problematisch, aber oftmals benötigt.
Für ein zukünftiges Projekt habe ich geprüft, ob es machbar ist, Encoder-Signale via GPIO einzulesen, diese via CAN-Bus @500 kbit/s zu übertragen und auf der Gegenseite mit einem RPi Zero + USBCan auf den GPIO-Pins auszugeben. Der RPi war jetzt die denkbar schlechteste Lösung, weil 1. der USB-Port Latenzen hat, das OS dazwischen hängt und der ISR Python-Code war. Trotz dessen hatte ich nur eine Latenz von 500 bis 700 µS gemessen. Der Sender war ein ESP32S3 + Can-Transceiver. TWAI ist beim ESP32 integriert. Die nennen das so, weil nicht der komplette Can-Bus Standard unterstützt wird. Wenn ich später beide Seiten mit Mikrocontroller realisiert habe, werde ich sicherlich nochmal 100-300 µs einsparen können. Ein Byte @500kbit/s => 16 µs
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@DeaD_EyE: Das es echte Anwendungsfälle gibt will ich gar nicht in Frage stellen. Die sind aber schon recht speziell und mich würde halt schon interessieren was *hier* der konkrete Anwendungsfall ist, damit man da keine Zeit mit verschwendet wenn das kein sinnvoller oder machbarer Fall ist. Mir würden auch eher sinnvolle Fälle für IRQ nach jedem Zeichen statt wenn IDLE einfallen, aber das gibt der Pico ja nicht her.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
... in meinem Fall kommen von einem Solarregler alle 1000ms Statusdaten die sollen eingelesen einige aufgedröselt und auf einem Display ausgegeben werden.
Da ist massig Zeit zum auswerten.
Hatte zuvor eine Lösung über die Mainloop mit uart.any() gebaut - funktioniert auch tadellos - fand die Sache mit dem ISR interessant und das funktioniert dank Eurer Hilfe nun auch.
Es ist ja nicht falsch verschiedene Lösungen zu probieren - so hat man Ansätze for zukünftige Bastelprojekte
Da ist massig Zeit zum auswerten.
Hatte zuvor eine Lösung über die Mainloop mit uart.any() gebaut - funktioniert auch tadellos - fand die Sache mit dem ISR interessant und das funktioniert dank Eurer Hilfe nun auch.
Es ist ja nicht falsch verschiedene Lösungen zu probieren - so hat man Ansätze for zukünftige Bastelprojekte
- __blackjack__
- User
- Beiträge: 14210
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
Naja, ist halt nicht wirklich ein ISR. Alleine das simple Beispiel ginge schon nicht als ISR weil `decode()` für das Ergebnis neuen Speicher anfordert.
“Ich bin für die Todesstrafe. Wer schreckliche Dinge getan hat, muss eine angemessene Strafe bekommen. So lernt er seine Lektion für das nächste Mal.” — Britney Spears, Interview in der französischen Zeitung Libération, 2. April 2002
