C-förlängning för MicroPython på Raspberry Pi Pico

Det här är en guide för hur man skriver och kör ett grundläggande C-tillägg för MicroPython på Raspberry Pi Pico. Jag kommer att behandla skälen till att utöka MicroPython, hur man skriver tilläggsmodulen, hur man kompilerar den och hur man installerar den på Pico. Om du vill läsa mer om det här ämnet kan du läsa följande https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Introduktion

Varför utöka MicroPython?

Det kan finnas flera anledningar till varför du vill utöka MicroPython. Eftersom MicroPython är en nedbantad version av Python fungerar det inte särskilt bra när det gäller beräkningstunga uppgifter (som Python). C å andra sidan är verkligen väl lämpad för sådana uppgifter. Ett C-förlängningstillägg för Micropython gör det möjligt att utföra dessa beräkningar med hjälp av C-språket och använda resultaten i MicroPython. Detta kan påskynda vissa processer med en storleksordning.

En annan anledning kan vara att du vill koppla ihop en maskinvara som använder ett C API från MicroPython. 

Typer av MicroPython C-tillägg

Det finns två olika sätt att utöka MicroPython: externa C-moduler och inhemsk maskinkod i .mpy-filer.

För att köra en extern C-modul måste du kompilera om den till MicroPython-firmware. Detta är en ganska komplicerad process, vilket är anledningen till att den här guiden kommer att fokusera på det andra alternativet. Om du dock är intresserad av detta tillvägagångssätt kan du läsa mer om det på dessa webbsidor:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

.mpy är en fil som lagrar kompilerad maskinprogramkod och kan länkas dynamiskt. Detta gör den mycket mer flexibel eftersom du inte behöver kompilera om hela MicroPython firmware.

Skriva tilläggsmodulen

Att skriva tilläggsmodulen är den enklaste delen av att utöka MicroPython. Jag kommer att visa dig hur du gör det genom att titta på ett enkelt exempel. Till att börja med skapar vi en ny katalog som heter factfib. Öppna ett nytt terminalfönster, cd:a till den nya katalogen och skapa en ny fil med namnet factfib.c. Om du har skapat katalogen på en Raspberry Pi under Dekstop ser kommandot ut så här.

cd ~/Desktop/factfib

touch factfib.c

Öppna factfib.c i din favorittextredigerare och ange följande kod:

#include "py/dynruntime.h

STATIC mp_int_t factorial_helper(mp_int_t x) {
   if (x < 1) {
      return 1;
   }
   return x*factorial_helper(x-1);
}

STATIC mp_obj_t factorial(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = factorial_helper(x);
   return rslt;
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);

STATIC mp_int_t fibonacci_helper(mp_int_t x) {
   if (x <= 0) {
      return 0;
   }
   else if (x == 1) {
      return 1;
   }
   else {
      return (fibonacci_helper(x-1) + fibonacci_helper(x-2));
   }
}

STATIC mp_obj_t fibonacci(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = fibonacci_helper(x);
   return mp_obj_new_int(rslt);
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci);

mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
   MP_DYNRUNTIME_INIT_ENTRY

   mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
   mp_store_global(MP_QSTR_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj));

   MP_DYNRUNTIME_INIT_EXIT
}

Låt mig förklara koden ovan: I MicroPython och Python är varje variabel ett objekt. I C representerar vi dem därför som mp_obj_t. Men ett C-program kan bara arbeta med variabler av C-typ, vilket är anledningen till att vi måste omvandla objektet till en C-typ. Detta kallas Marshalling och MicroPython tillhandahåller funktioner för att extrahera vilken C-typ som helst från ett objekt.

Den här webbsidan av Oliver Robson är till stor hjälp för Marshalling. https://mpy-c-gen.oliverrobson.tech/. Dessutom kan du hitta exempel på Marshalling i MicroPython-dokumentationen. https://docs.micropython.org/en/latest/develop/natmod.html. Jag rekommenderar att du läser den här artikeln om du behöver ytterligare förklaringar.

Bygg utbyggnaden

Vi bygger upp tillägget till en .mpy-fil med hjälp av en Makefile. Men vi måste ställa in några saker först. Se till att din terminal fortfarande är öppen i factfib katalog. Klona sedan MicroPython GitHub-förrådet.

sudo apt-get install build-essential libreadline-dev libffi-dev git pkg-config gcc-arm-none-eabi libnewlib-arm-none-eabi

git clone --recurse-submodules https://github.com/micropython/micropython.git

pip3 install 'pyelftools>=0.25'

Dessa installationer kommer att ta ett tag, men om du vill spara tid och vet vad du gör behöver du bara klona katalogerna /py och /tools från micropython-förrådet och lägga dem i en micropython i mappen factfib mapp (hoppa över det andra kommandot).

Medan installationen pågår kan vi använda tiden till att skriva Makefilen. Exekvera helt enkelt

touch Makefile

och öppna den nyskapade filen i en textredigerare.

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

Du kanske har lagt märke till att vi har satt ARCH variabeln till armv7m även om pico är armv6m. Vi måste göra detta eftersom armv6m arkitekturen (när jag skriver denna artikel) inte stöds av dynruntime.mk verktyg.

Men som tur är kan vi fortfarande kompilera ett fungerande tillägg för Pico. Vi behöver bara ändra dynruntime.mk filen, som bör finnas under micropython/py/dynruntime.mk. Öppna den och leta under Architecture configuration i armv7m efter variabeln CFLAGS (bör vara rad 64). Ändra -mcpu=cortex-m3 till -mcpu=cortex-m0 och spara filen.

ändra -mcpu=cortex-m3 till -mcpu=cortex-m0

När allt är installerat, kör du sedan

make

och faktafib.mpy filen kommer att skapas.

Installera och testa tillägget

Det sista steget är installation och testning. För detta använder vi det filsystem som Micropython skapar på Raspberry Pi Pico. Först måste vi flasha Pico med MicroPython firmware. det enklaste sättet att göra detta är att använda Thonny IDE (förinstallerad på Raspbian/Raspberry Pi OS).

Om du inte använder en Raspberry Pi för att programmera din Pico eller om du aldrig har använt MicroPython på Pico tidigare, se guiden Kom igång med MicroPython från Raspberry Pi. https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

Om du använder Windows bör du läsa detta blogginlägg om Raspberry Pi Pico och MicroPython på Windows. https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

Stäng Thonny efter att du har flashat din Pico med MicroPython-firmware och installera rshell. Vi använder rshell för att komma åt filsystemet från kommandoraden.

sudo pip3 install rshell
rshell --version

Detta skriver ut rhsell-versionen om allt är korrekt inställt. Koppla sedan ur pico och koppla in den igen (utan att hålla BOOTSEL-knappen intryckt) för att se till att inget försöker komma åt filsystemet. Nu måste vi utföra

rshell -p /dev/ttyACM0 --buffer-size 512

cp factfib.mpy /pyboard/factfib.mpy

För att testa installationen kan du använda antingen rshell eller Thonny IDE för att spara din .py filer på Pico, men jag kommer att visa hur man använder MicroPython-tolken på minicom.

Tryck därför först på Ctrl+c för att lämna rshell och sedan installera minicom via

sudo apt install minicom

Nu kan vi få tillgång till Picos REPL (Read-eval-print-slinga).

minicom -o -D /dev/ttyACM0

De tre avslutande vinkelparenteserna >>> indikerar att MicroPython-tolken körs. Nu kan vi importera vårt tillägg och arbeta med det som vanligt.

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

Vi gratulerar! Du har skapat ditt första MicroPython C-tillägg för Pico! Nästa steg är att lära dig hur man skriver och kompilerar mer komplexa moduler. Ett bra sätt att börja är att testa hur Marshalling av listor och dicts fungerar (se webbsidan Marshalling ovan).

Om du har några frågor om detta inlägg är du välkommen att skriva en kommentar.

8 Kommentarer

  1. PeterB den november 26, 2021 kl 8:29 f m

    Bortsett från felet i c-programmet fungerade det utmärkt. Tack.

    STATISKT mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj);
    mp_int_t rslt = factorial_helper(x);
    return mp_obj_new_int(rslt);
    }

    • raspi berry den november 27, 2021 kl 5:56 e m

      Tack för din feedback 🙂

  2. XMC.pl den juni 1, 2022 kl 3:31 f m

    Hur konfigurerar man joomla så att den kan hämta data från mysql?

  3. Lynggaard den januari 8, 2023 kl 8:09 e m

    Jag har försökt att göra ovanstående, men olika saker fungerar inte som beskrivet:

    - Jag följde instruktionerna ovan, skaffade micropython, modifierade makefilen enligt beskrivningen.
    - Jag gör det (men jag får en factfib.native.mpy-fil i stället för factfib.mpy).
    - Jag kopierar filen factfib.native.mpy till pico som factfib.mpy.
    (Jag försökte med rshell men fick ett "timed out or error in transfer to remote: b""", men jag kunde kopiera över den med Thonny)
    - Jag kan importera modulen (i thonny eller rshell - samma sak) utan fel.
    - Jag försöker köra "ff.fibonacci(5)", men då får jag:

    "Traceback (senaste anropet senast):
    Fil "", rad 1, i
    AttributeError: 'module'-objektet har inget attribut 'fibonacci'"

    Vad gör jag fel?

    • paopao69 den mars 22, 2023 kl 5:59 e m

      Fick du det att fungera till slut?

  4. Paulo den februari 9, 2024 kl 1:44 f m

    Nu kan du kompilera till armv6 så ändra bara den här raden i Makefile och du behöver inte ändra micropython-koden
    ARCH = armv6m

    Jag hade några fel vid kompileringen av factfib.c, var finns korrigeringarna:

    // saknar " i slutet.
    #include "py/dynruntime.h"

    // konvertera returvärdet till mp_obj_t
    STATISKT mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj);
    mp_int_t rslt = factorial_helper(x);
    return mp_obj_new_int(rslt);
    }

    • Adam den februari 9, 2024 kl 9:22 f m

      Tack för informationen, Paulo!

  5. Dr_Phil den maj 24, 2024 kl 7:36 f m

    Jag följde alla instruktioner och slutade med en byggd facfib.mpy-fil. Jag använde
    ARCH = armv6m
    När den laddas in i Pico får jag det här felet:

    ValueError: inkompatibel .mpy-fil

    Min Pico kräver följande mpy-värden:
    mpy-version: 6
    mpy underversion: 2
    mpy flaggor: -marsch=armv6m

    Kontroll av de två första bytena i filen ger version 6 och underversion 6.
    Finns det något sätt att rätta till detta?

Lämna en kommentar