PIO State-Machine

Mit einer PIO State-Machine kannst du Peripheriegeräte am Raspberry Pi Pico direkt programmieren, also ohne die CPU einzubinden. Die Grundlagen dazu will ich dir in diesem Post zeigen.

PIO State-Machine

PIO steht für Programmable I/O. Mit PIO wird die I/O direkt in sog. Zustandsautomaten (State-Machines) programmiert.

Zustandsautomaten sind ein Programmierparadigma aus der Informatik und stammen ursprünglich aus der Automatentheorie. Sie begegnen uns überall im Leben. Beispielsweise bei der Steuerung eines Toasters.

Zustandsdiagramm für einen Toaster, das ihn als State-Machine visualisiert
Olli Graf - raspithek.de
toasterCreative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . loading=
Zustandsdiagramm für einen Toaster

Assembler der PIO State-Machine

Die PIO Programmierung ist sehr hardwarenah, deshalb setzt man dazu Assembler ein. Der Befehlssatz von Assembler ist immer vom verwendeten Prozessor abhängig. Du kannst ein Assemblerprogramm, dass für den 8088 geschrieben wurde nicht einfach so auf den ARM-Prozessor des Raspberry Pi Laufen lassen.

Befehlssatz

Jeder Prozessor hat seinen eigenen Assembler-Befehlssatz. Das gilt auch für unsere PIO State-Machine. Ich habe ihn hier mal aufgelistet.

BefehlFunktion
NOPNop Operation – Tut nichts, außer Zeit zu verbrauchen.
OUTÜberträgt Daten von einem internen Register (OSR) an einen PIO Pin
INLiest Daten von einem Eingangspin oder einem Statusregister und speichert sie im ISR
PUSHVerschiebt Daten vom ISR (in den FIFO der CPU.
PULLHolt Daten aus dem CPU-FIFO in das OSR
MOVVerschiebt Daten zwischen Registern (z.B. zwischen X, Y, ISR, OSR)
IRQSetzt oder löscht ein IRQ-Signal (Interrupt Request)
WAITWartet auf eine bestimmte Bedingung (z.B. auf einen bestimmten Pegel eines Eingabepins)
JMPSpringt zu einer bestimmten Adresse innerhalb des Programms, mit optionalen Bedingungen.
SETSetzt bestimmte Pins oder Statusregister auf einen gewünschten Wert.
EXECFührt eine spezifizierte Anweisung sofort aus, ohne das Programmzählerregister zu ändern.
JMP_RELSpringt relativ zur aktuellen Position im Programm.

Register

Register dienen dazu, Daten zwischenzuspeichern, sie entsprechen den Variablen in höheren Programmiersprachen. In der Regel werden dort nur Integerwerte abgelegt. Einige Register sind PIO intern und können nur abgefragt werden. Andere brauchst du,um Zwischenwerte zu speichern.

RegisterBedeutung
ISRInput Shift Register. Hier kommen Daten von den Eingabepins an. Mit PUSH können sie dann in den FIFO geschoben werden.
OSROutput Shift Register. Moit OUT kannst hierüber Daten an die PIO-Eingabepins ausgeben.
X und YArbeitsregister für Werte des Assemblerprogramms.
PCProgramm-Counter. Hier hällt der Prozessor fest, welcher Befehl als nächstes ausgeführt wird. Er wird automatisch hochgezählt und kann durch den JMP Befehl verändert werden.
FIFOFirst In -First Out Register um Daten zwischen Hauptprozessor und PIO auszutauschen.
StatusregisterDiese Register dokumentieren den aktuellen Zustand der PIO und können, z.B. mit SET manipuliert werden.
Dividerbestimmt die Taktfrequenz der PIO

Praxis

Nach so viel Theorie will ich dir noch den praktischen Einsatz zeigen. Dazu nehme ich einen Raspberry Pi Pico der mit Micropython geflasht ist. Das Pythonprogramm zum Blinken der onboard LED ist nichts besonderes mehr:

from machine import Pin
from utime import sleep

led = machine.Pin(25, machine.Pin.OUT)
state = False
running = True

#Endlosschleife

led.off()
while True:
  state =  not state # state hin- und herschalten
  sleep(1.0)
  if state:
    print('LED on')
    led.on()
#      running = False
  else:
    print('LED off')
    led.off()

In dieser Variante schaltet der Pythoncode selber die LED ein- und aus. Falls du noch etwas anderes berechnen möchtest, musst du diesen Mechanismus irgendwie an geeigneten Stellen in deinem Programmcode unterbringen oder einen zweiten Thread aufmachen.

Eleganter geht es aber, die PIO State-Machine die Arbeit machen zu lassen.

from machine import Pin
import rp2
from time import sleep

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink():
    wrap_target()
    set(pins, 1)   [31]
    nop()          [31]
    nop()          [31]
    nop()          [31]
    set(pins, 0)   [31]
    nop()          [31]
    nop()          [31]
    nop()          [31]
    wrap()
 
led = machine.Pin(25, machine.Pin.OUT)
sm = rp2.StateMachine(0, blink, freq=2000, set_base=led)

# StateMachine aktivieren

sm.active(1)

# Für 3 Sekunden laufen lassen.
sleep(3)

# deaktivieren
sm.active(0)

Dieses Pythonprogramm dient dazu, den Assemblercode der Methode blink() in die PIO State-Machine zu laden. Dazu wird mit dem Decorator @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW) bekannt gemacht, dass die nachfolgende Methode Assemblercode für die PIO enthält. Der Parameter set_init=rp2.PIO.OUT_LOW sorgt dafür, dass vor dem Start der State-Machine alle Pins auf LOW gezogen werden.

Die Methode blink() ist für ein Pythonprogramm ungewöhnlich, da sie keinen Pythoncode enthält. In der Methode wird mir wrap_target() eine Schleife definiert. In dieser wird mit set(pins, 1) [31] zunächst die LED eingeschaltet. Die [31] sorgt dafür, das nach dem Befehl für 31 Taktzyklen pausiert werden soll. Mittels nop() werden dann noch weitere Wartezyklen eingefügt. Dann schaltet set(pins, 0) [31] die LED wieder aus, gefolgt von weiteren nop() zum Warten. wrap() definiert dann das Ende der Schleife.


Nachdem die Methode für die State-Machine definiert ist, müssen wir diese initialisieren sm = rp2.StateMachine(0, blink, freq=2000, set_base=led) und starten sm.active(1)

Fazit

PIO ist eine interessante Möglichkeit, die I/O Hardware des Raspberry Pi Pico direkt anzusprechen. Gerade für der Treiber für Sensoren könnte damit ein höherer Datendurchsatz erreicht werden als mit einem Treiber, der in Python kodiert ist.

Schreibe einen Kommentar

Creative Commons License
Except where otherwise noted, the content on this site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Olli Graf - raspithek.de
WordPress Cookie Hinweis von Real Cookie Banner