RPi Pico – baremetal-PIO in der Arduino-IDE               

von Martin Müller                                

Elektronik-Labor  Projekte  Mikrocontroller  Raspberry            




Wie an anderer Stelle im Elektroniklabor beschrieben, kann die PIO-Funktionalität des RPi-Pico in der Arduino-IDE genutzt werden. Dazu schreibt man mit einem Texteditor den Quellcode für das Assemblerfile, das später von der PIO abgearbeitet werden soll. Mit der von Earle F. Philhower III. erstellten Boarderweiterung erhält man auch im der Arduino-IDE Zugriff auf alle Funktionen des RPi-Pico-SDK. So kann der Quellcode mit "pioasm" kompiliert werden und in den Arduino-Sketch eingebunden werden. Mit der Funktionalität des RPi-Pico-SDK werden die PIO-Einheiten konfiguriert und das Programm gestartet.


Tatsächlich kann man die PIO-Einheiten des RPi-Pico auch ohne das RPi-Pico-SDK und ohne "pioasm" nutzen. Dazu muss man die passenden Werte in die entsprechenden Register des RP2040 schreiben. Die Adressen der Register findet man im Datenblatt zum RP2040.




Wie im bereits erwähnten Artikel werden 2 LEDs (mit integrierten Vorwiderständen) an den RPi-Pico angeschlossen. Die LED an GPIO 0 wird klassisch durch den üblichen Programmcode zum Blinken gebracht, für die LED an GPIO 15 wird die PIO-Funktionalität (PIO0, Statemachine0) verwendet.


#define MEM32(address) (*(volatile uint32_t*)(address))
//Registeradressen
#define GPIO15_CTRL MEM32(0x4001407C)


#define PIO0_CTRL MEM32(0x50200000)
#define PIO0_MEM0 MEM32(0x50200048)
#define PIO0_MEM1 MEM32(0x5020004C)
#define PIO0_MEM2 MEM32(0x50200050)
#define PIO0_MEM3 MEM32(0x50200054)
#define PIO0_MEM4 MEM32(0x50200058)
#define PIO0_MEM5 MEM32(0x5020005C)
#define PIO0_MEM6 MEM32(0x50200060)
#define PIO0_MEM7 MEM32(0x50200064)
#define PIO0_MEM8 MEM32(0x50200068)
#define PIO0_MEM9 MEM32(0x5020006C)
#define PIO0_MEMA MEM32(0x50200070)
#define PIO0_MEMB MEM32(0x50200074)
#define PIO0_MEMC MEM32(0x50200078)
#define PIO0_MEMD MEM32(0x5020007C)
#define PIO0_MEME MEM32(0x50200080)
#define PIO0_MEMF MEM32(0x50200084)


#define PIO0_SM0_CLKDIV MEM32(0x502000C8)
#define PIO0_SM0_EXECCTRL MEM32(0x502000CC)
#define PIO0_SM0_PINCTRL MEM32(0x502000DC)


void setup() {
//PIO-Assemblerfile
PIO0_MEM0 = 0xFF01;
PIO0_MEM1 = 0xBF42;
PIO0_MEM2 = 0xBF42;
PIO0_MEM3 = 0xBF42;
PIO0_MEM4 = 0xBF42;
PIO0_MEM5 = 0xBF42;
PIO0_MEM6 = 0xBF42;
PIO0_MEM7 = 0xBF42;
PIO0_MEM8 = 0xFF00;
PIO0_MEM9 = 0xBF42;
PIO0_MEMA = 0xBF42;
PIO0_MEMB = 0xBF42;
PIO0_MEMC = 0xBF42;
PIO0_MEMD = 0xBF42;
PIO0_MEME = 0xBF42;
PIO0_MEMF = 0xBF42;
//PIO-Konfiguration
GPIO15_CTRL = 0x00003006;
PIO0_SM0_PINCTRL = 0x040001E0;
PIO0_SM0_CLKDIV = 0xC3500000;
PIO0_SM0_EXECCTRL = 0x0000F000;
PIO0_CTRL = 0x00000001;
gpio_init(0);
gpio_set_dir(0, GPIO_OUT);
}


void loop() {
sleep_ms(500);
gpio_put(0, 1);
sleep_ms(500);
gpio_put(0, 0);
}

Abgesehen von den 16 Registern in denen der auszuführende Programmcode gespeichert wird, werden nur 5 weitere Register benötigt um die Aufgabe zu bewältigen.


#define MEM32(address) (*(volatile uint32_t*)(address))

sorgt dafür, dass man beim Aufrufen der Register im Code deutlich weniger "*" und Klammern verwenden muss.


PIO0_MEM0 = 0xFF01;

der SET-Befehl des PIO-Assemblers wird in den ersten Speicherplatz geschrieben (Kap. 3.4.10 des RP2040 Datenblatts).



Da nur ein Pin (Bit 0) mit maximalem Delay (Bit 8 - 12) gesetzt wird, lautet der binäre Wert der hier einzutragen ist:

1111 1111 0000 0001, das entspricht FF01 in hexadezimal.


PIO0_MEM8 = 0xFF00;

schaltet den gesetzten PIN wieder auf zurück auf Null.



PIO0_MEM1...7 = 0xBF42;

PIO0_MEM9...F = 0xBF42;

hierbei handelt es sich um den "NOP-Befehl". Er wird dadurch realisiert, dass der Wert des Y-Registers (Bit 0 - 2) erneut ins Y-Register (Bit 5 - 7) geschrieben wird (Kap. 3.4.8). Auch hier wird wieder ein maximaler Delay (Bit 8 - 12) verwendet.




1011 1111 0100 0010 => BF42 hexadezimal


GPIO15_CTRL = 0x00003006;

verbindet GPIO15 mit PIO0 und konfiguriert den Pin (Bit 12 und 13) als Ausgang (Kap. 2. 19.6).




 


Der Wert der niederwertigsten 4 Bit ist dieser Tabelle zu entnehmen (Kap. 1.4.3):






PIO0_SM0_PINCTRL = 0x040001E0;

definiert die Pinkonfiguration der PIO-Einheit (Kap. 3.7).

SET_COUNT (Bit 26 - 28) wird auf 0x1 gesetzt, SET_BASE (Bit 5 - 9) erhält den Wert 15 (0xF).




0000 0100 0000 0000 0000 0001 1110 0000 => 040001E0 hexadezimal

PIO0_SM0_CLKDIV = 0xC3500000;

teilt den Systemtakt (Kap. 3.7) durch den ganzzahligen (Bit 16 - 31) Wert 50000 => C350 hexadezimal.





PIO0_SM0_EXECCTRL = 0x0000F000;

(Kap. 3.7) Bit 7 bis 11 legen die Adresse (PIO0_MEMX) fest von der aus der Code gestartet wird, nachdem der Befehl der in Bit 12 bis 16 gespeicherten Adresse (PIO0_MEMX) abgearbeitet wurde.




PIO0_CTRL = 0x00000001;

(Kap. 3.7) schaltet PIO0/Statemachine0 ein.








Elektronik-Labor  Projekte  Mikrocontroller  Raspberry