Statemachines und PIO - Programmierung beim Raspberry Pi Pico

  • Hallo Kai


    Ja, ich kenne den Heise - Artikel. Er hat mir auch sehr geholfen. Allerdings ist der Sprung von simplen Python - Beispiel zur in C geschriebenen Ampelsteuerung sehr gross. Ich habe inzwischen noch andere Quellen gefunden, allerdings wird man vor lauter Informationen beinahe erschlagen. Trotzdem hat man oft Mühe, genau das Detail zu finden, das man gerade benötigt.


    Hier eine Sammlung der Links, die ich gefunden habe:


    https://www.mathema.de/blog/ra…are-zwischen-cpu-und-fpga

    https://hackspace.raspberrypi.…d-programmable-i-o-part-2

    https://hackspace.raspberrypi.org/articles/pio1


    https://datasheets.raspberrypi…2040/rp2040-datasheet.pdf

    https://datasheets.raspberrypi…spberry-pi-pico-c-sdk.pdf

    https://datasheets.raspberrypi…ry-pi-pico-python-sdk.pdf


    Ich bin gespannt, ob sich die Programmierung der Statemachines in Makerkreisen durchsetzt. Es könnte auch sein, dass alle das einfach ignorieren. Das erinnert mich etwas an die Geschichte mit dem PSOCs von Cypress. Die wären eigentlich hochinteressant. Aber es war für viele Leute absolutes Neuland und die meisten haben sich davon ferngehalten.


    Gruss

    René

  • Interessantes Thema. Auf Heise Developer ist ein Artikel, der die Funktionalität recht ausführlich (in deutsch) beschreibt: I/O on Steroids.


    Es gibt auch ein kostenloses Buch (allerdings in englisch) "Get started with MicroPython on Raspberry Pi Pico" zum download. Wer möchte, kann aber auch spenden. Es ist eine Einführung zu dem Controller mit einigen Programmierbeispielen für Display(I2C) und Sensoren. Im Anhang wird auch etwas auf die PIO-Programmierung eingegangen (Ansteuerung von Neopixel WS2812B LEDs).

    Das Reh springt hoch. Das Reh springt weit. Warum auch nicht? Es hat ja Zeit. 8o

  • Hallo Hans,


    da hast Du natürlich recht. Allerdings ist das nur ein zusätzliches Feature, das man nicht zwingend benutzen muss. Wenn man mit den normalen Funktionen eines Mikrokontrollers zufrieden ist, erfolgt die Programmierung wie bei einem Arduino oder ESP32.


    Anfänger sollten sich nicht mit PIO - Programmierung beschäftigen. Es ist aber eine gute Idee, mit Micropython einzusteigen. Python ist sehr viel einfacher zu erlernen als C oder C++.


    Gruss

    René

  • Hallo René,


    ich finde Deinen Beitrag über den Raspberry PI sehr interessant, aber für mich sieht der Code ein wenig kryptisch aus.
    Ich denke, im Vergleich mit dem Arduino, ist dies für Anfänger einiges schwerer zu erlernen.
    Falls ich hier falsch liege, bitte berichtige mich.

    Gruß

    Hans

  • Teil 4


    Geblinkt wird mit dem set() - Befehl. Das ist einer der 9 Befehle, die die Statemachine kennt. Wie kann man jetzt gleichzeitig mehrere Pins ein- oder ausschalten?


    Code
    1. set(pins, 0b1101)[31] # Pins 2, 4, 5 ein, Pin 3 aus
    2. set(pins, 0b0010)[31] # Pin 3 ein, Pins 2, 4, 5 aus

    Im Kopf der Funktion haben wir mit set_init den Ausgangszustand für 4 Pins festgelegt. Dadurch können wir mit set() 4 Pins ansprechen. Der erste Pin liegt bei set_base (2) und geht bis set_base + 3 (5).

    Mit set() können wir diese Pins ansprechen. Jedes gesetzte Bit setzt den Pin auf HIGH, ein Bitwert von 0 setzt ihn auf LOW. Daher leuchten mit 0b1101 die Pins 2, 4, und 5, während Pin 3 dunkel bleibt.


    Mit set(pins, 0b0010) werden die Pins 2,4,5 auf LOW und Pin 3 auf HIGH geschaltet.


    Jeder set() - Befehl dauert 1 Zyklus. In eckigen Klammern geben wir eine weitere Verzögerung um 31 Zyklen an. Damit benötigt die Instruktionszeile 32 Zyklen.

    Wir arbeiten mit einer Frequenz von 2000 Hz. Somit dauert ein Zyklus 0.5 ms.


    Diese 32 Zyklen reichen nicht, um das Blinken sichtbar zu machen. Aus diesem Grund werden zusätzlich nop() - Operationen eingefügt. Ein nop() [31] braucht 1 + 31 = 32 Zyklen. Die zusätzliche Verzögerung kann maximal 31 Zyklen betragen, daher benötigen wir mehrere nops.


    Code
    1. ...
    2. nop() [31]
    3. nop() [31]
    4. ...

    Wir haben in unserer Funktion folgende Zyklen:

    1 + 31 = 32

    1 + 31 = 32

    1 + 31 = 32

    1 + 3 = 4

    1 + 31 = 32

    1 + 31 = 32

    1 + 31 = 32

    1 + 3 = 4


    Wir haben also ein Total von 200 Zyklen. Das dauert 200 x 0.5 ms = 100 ms.

    Das kann mit dem Oszilloskop verifiziert werden.



    Oszi.jpeg


    Solange die Statemachine aktiv ist, wird die Endlosschleife unabhängig von der CPU ausgeführt. Sie funktioniert auch während eines aktiven sleep() - Befehls oder wenn das Python - Programm beendet wurde.


    Die Statemachine kann aber auch durch einen Befehl deaktiviert werden:


    Code
    1. sm.active(1)
    2. time.sleep(3)
    3. sm.active(0)


    Das waren nur zwei Befehle von 9. Trotzdem lässt sich damit schon einiges machen.

    Wir haben hier nur an der Oberfläche gekratzt, PIO - Programmierung kann sehr anspruchsvoll sein. Ich blicke da auch noch nicht überall durch.

    Wenn es dann soweit ist, erstelle ich vielleicht ein Video dazu.

  • Teil 3


    Die Funktion blink() definiert das Assembler - Programm für die Statemachine.


    Bereits die erste Zeile ist sehr speziell. Man nennt dieses Konstruct einen Decorator. Das gehört zur fortgeschrittenen Python - Programmierung und soll hier nicht näher besprochen werden. In unserem Fall sorgt er dafür, dass der nachfolgende Code als Assembler - Programm an die Statemachine übermittelt wird. Dabei initialisieren wir auch die verwendeten Pins. Ich gebe hier mit set_init an, welche Pins ich für den set - Befehl verwenden möchte und wie ihr Anfangszustand ist. Die üblichen Beispiele übergeben hier nur einen Zustand, verwenden also auch nur einen Ausgangspin.

    Ich übergebe hier ein Tupel von 4 identischen Zuständen, daher kann ich 4 Pins ansprechen.


    Wer das noch nicht kennt:

    Code
    1. (PIO.OUT_LOW,)*4) entspricht (PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW)

    Ist also ein normales Tupel mit 4 Elementen.


    Innerhalb der Funktion blink() ist eine Endlosschleife definiert. Sobald der Pseudobefehl wrap() erreicht wird, startet die Funktion wieder bei wrap_target(). Das benötigt keinen Taktzyklus.


    Code
    1. def blink():
    2. wrap_target()
    3. ...
    4. wrap()

    Das eigentliche Blinken erfolgt mit dem set() - Befehl. Doch das ist ein Thema für den nächsten Beitrag.

  • Teil 2


    Wenn wir das Python - Programm betrachten sticht sofort die Funktion blink() ins Auge. So etwas hast du vielleicht noch nie gesehen. Aber schön der Reihe nach. Wir beginnen mit dem Start des Programms.


    Der unterste Pin ist Pin 2. Den erstellen wir ganz normal.

    Im Modul rp2 findet man die Klasse StateMachine. Davon muss eine Instanz erstellt werden.


    Code
    1. sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(2))

    Dabei ist 0 die Statemachine - Nummer (0 .. 7) und blink die Funktion.

    freq ist die Frequenz in Hz. Ohne Angabe wird der Systemtakt (125 MHz) verwendet. Das Minimum beträgt 1908 Hz. Das ist abhängig vom Systemtakt des RP2040. Dieser beträgt beim Pi Pico 125 MHz.

    set_base definiert den ersten Pin, der für set() verwendet wird.


    Wir erstellen also eine Statemachine 0 (möglich wären 0 .. 7). Diese arbeitet mit einer Taktfrequenz von 2000 Hz. Die Funktion blink definiert den Code, den die Statemachine ausführen soll. Die unterste Pinnummer, die wir ansprechen möchten, ist 2. Die Statemachine muss dann nur noch aktiviert werden.


    Code
    1. pin2 = Pin(2)
    2. sm = rp2.StateMachine(0, blink, freq=2000, set_base=pin2)
    3. sm.active(1)


    Das Python - Programm ist damit beendet. Die Statemachine läuft aber unbeirrt weiter.

    Die Funktion blink() betrachten wir im nächsten Beitrag.

  • Teil 1


    Momentan wird viel über den neuen Mikrokontroller der Raspberry Pi Foundation geschrieben und gesprochen. Er kann in Micropython, CircuitPython oder C++ programmiert werden. Auf den ersten Blick handelt es sich einfach um noch einen Mikrokontroller. Wenn man aber genauer hinschaut, findet man Begriffe wie 'State Machine' oder 'PIO Programmierung', die bei anderen Kontrollern nicht vorhanden sind.


    Genau damit habe ich mich etwas beschäftigt. Es handelt sich dabei um eigene Hardwarebereiche, die völlig unabhängig von der CPU agieren können. Zuerst sieht alles ganz einfach aus. Die Programmierung erfolgt in einer Art Assembler - Sprache (pioasm), die nur 9 Instruktionen kennt. Ein ganzes Programm kann maximal 32 Instruktionszeile enthalten. Das kann doch nicht so schwierig sein und kann man damit überhaupt etwas Vernünftiges machen?


    Ja, man kann! Aber es ist nicht gerade einfach. Ich möchte in diesem Artikel nur ein ganz einfaches Beispiel zeigen. Es deckt nur einen ganz kleinen Teil der Möglichkeiten ab, verlangt aber schon eine andere Denkweise, als man sich von der normalen Programmierung her gewohnt ist. Ich gehe hier auch nicht auf alle Details ein, es gibt bereits viele Informationen über den Hardwareaufbau der State Machines. Ich nehme das Einstiegsbeispiel aus dem Handbuch, baue es aber um einen entscheidenden Punkt aus.


    Programmiert wird in Micropython. Dieses bietet die Möglichkeit, pioasm - Programme direkt in das Script einzubinden.


    Das Beispiel ist ganz einfach. An den GPIOs 2, 3, 4, 5 ist je eine Led angeschlossen. Die Led an 2, 4, 5 blinken zusammen mit einer Frequenz von 10 Hz. Led 3 leuchtet immer dann, wenn die anderen 3 Leds dunkel sind.


    Das sieht dann so aus:



    Das Video ist um den Faktor 10 verlangsamt, da die tatsächliche Blinkfrequenz 10 Hz beträgt.

    Das ist das dazugehörende Python - Script:


    Da ist doch noch ein gewisser Erklärungsbedarf vorhanden. Also schauen wir uns das im nächsten Beitrag etwas näher an.