Rozszerzenie C dla MicroPythona na Raspberry Pi Pico

To jest przewodnik, jak napisać i uruchomić podstawowe rozszerzenie C dla MicroPython na Raspberry Pi Pico. Omówię powody dla rozszerzenia MicroPython, jak napisać moduł rozszerzenia, jak go skompilować i jak zainstalować go na Pico. Jeśli chcesz przeczytać więcej na ten temat, odsyłam do https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Wstęp

Dlaczego warto rozszerzyć MicroPython?

Może być wiele powodów, dla których chciałbyś rozszerzyć MicroPythona. Ponieważ jest to okrojona wersja Pythona, MicroPython nie radzi sobie zbyt dobrze, jeśli chodzi o zadania wymagające dużych mocy obliczeniowych (podobnie jak Python). Z drugiej strony, język C jest naprawdę dobrze przystosowany do takich zadań. Rozszerzenie C dla Micropythona umożliwia wykonywanie tych obliczeń w języku C i wykorzystywanie ich wyników w MicroPythonie. Może to przyspieszyć niektóre procesy o rząd wielkości.

Drugim powodem może być chęć podłączenia sprzętu, który używa C API z poziomu MicroPythona. 

Typy rozszerzeń MicroPython C

Istnieją różne sposoby rozszerzania MicroPythona: zewnętrzne moduły C oraz natywny kod maszynowy w plikach .mpy.

Aby uruchomić zewnętrzny moduł w języku C, należy go przekompilować do firmware'u MicroPythona. Jest to dość skomplikowany proces, dlatego w tym poradniku skupimy się na drugiej alternatywie. Jeśli jednak jesteś zainteresowany tym podejściem, możesz przeczytać więcej na ten temat na tych stronach:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

.mpy jest plikiem, który przechowuje skompilowany natywny kod maszynowy i może być łączony dynamicznie. To czyni go znacznie bardziej elastycznym, ponieważ nie trzeba rekompilować całego firmware MicroPythona.

Napisać moduł rozszerzenia

Napisanie modułu rozszerzeń jest najprostszą częścią rozszerzania MicroPythona. Pokażę Ci, jak to zrobić, na prostym przykładzie. Na początek utworzymy nowy katalog o nazwie factfib. Otwórz nowe okno terminala, cd do nowego katalogu i utwórz nowy plik o nazwie faktfib.c. Jeśli utworzyłeś katalog na Raspberry Pi pod Dekstopem, polecenie wygląda tak.

cd ~/Desktop/factfib

touch factfib.c

Otwórz faktfib.c w swoim ulubionym edytorze tekstu i wprowadź następujący 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
}

Pozwól mi wyjaśnić powyższy kod: W MicroPythonie i Pythonie każda zmienna jest obiektem. Dlatego w C reprezentujemy je jako mp_obj_t. Ale program w języku C może pracować tylko ze zmiennymi typu C, dlatego musimy przekształcić obiekt w typ C. Nazywa się to Marshalling, a MicroPython dostarcza funkcji do wyodrębnienia dowolnego typu C z obiektu.

Ta strona autorstwa Olivera Robsona jest naprawdę pomocna w Marshallingu https://mpy-c-gen.oliverrobson.tech/. Ponadto możesz znaleźć przykłady Marshalling w dokumentacji MicroPythona https://docs.micropython.org/en/latest/develop/natmod.html. Radzę przeczytać ten artykuł, jeśli potrzebujesz dalszych wyjaśnień.

Zbuduj rozszerzenie

Budujemy rozszerzenie do pliku .mpy za pomocą pliku Makefile. Ale najpierw musimy skonfigurować kilka rzeczy. Upewnij się, że twój terminal jest wciąż otwarty w trybie factfib katalog. Następnie sklonuj repozytorium MicroPython GitHub.

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'

Te instalacje zajmą trochę czasu, jeśli chcesz go zaoszczędzić i wiesz co robisz, wystarczy sklonować katalogi /py i /tools z repozytorium micropythona i umieścić je w katalogu micropython folder wewnątrz factfib folder (pomiń drugie polecenie).

Podczas gdy instalacja jest uruchomiona, możemy wykorzystać ten czas na napisanie pliku Makefile. Po prostu wykonaj

touch Makefile

i otwórz nowo utworzony plik w edytorze tekstu.

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

Być może zauważyłeś, że ustawiliśmy ARCH zmienna do armv7m chociaż pico jest armv6m. Musimy to zrobić, ponieważ armv6m architektura jest (w momencie pisania tego artykułu) nie wspierana przez dynruntime.mk narzędzie.

Ale na szczęście wciąż możemy skompilować działające rozszerzenie dla Pico. Musimy tylko zmodyfikować dynruntime.mk plik, który powinien znajdować się pod micropython/py/dynruntime.mk. Otwórz go i pod Architecture configuration w armv7m znajdź zmienną CFLAGS (powinna być w linii 64). Zmień -mcpu=cortex-m3 do -mcpu=cortex-m0 i zapisać plik.

chnge -mcpu=cortex-m3 do -mcpu=cortex-m0

Następnie, gdy wszystko jest już zainstalowane, wykonaj

make

factfib.mpy zostanie utworzony plik.

Zainstaluj i przetestuj rozszerzenie

Ostatnim krokiem jest instalacja i testowanie. W tym celu wykorzystujemy system plików, który Micropython tworzy na Raspberry Pi Pico. Najpierw musimy sflashować Pico z firmware MicroPython. Najprostszym sposobem na to jest użycie Thonny IDE (preinstalowanego na Raspbian/Raspberry Pi OS).

Jeśli nie używasz Raspberry Pi do programowania Pico lub jeśli nigdy nie używałeś MicroPython na Pico przed, proszę odnieść się do rozpoczęcia z MicroPython przewodnik przez Raspberry Pi https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

Jeśli używasz systemu Windows należy sprawdzić ten blog Post o Raspberry Pi Pico i MicroPython w systemie Windows https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

Zamknij Thonny'ego po przepłukaniu Pico firmware'em MicroPython i zainstaluj rshell. Używamy rshell, aby uzyskać dostęp do systemu plików z linii poleceń.

sudo pip3 install rshell
rshell --version

Spowoduje to wydrukowanie wersji rhsell, jeśli wszystko jest poprawnie skonfigurowane. Następnie odłączamy pico i podłączamy je ponownie (bez trzymania przycisku BOOTSEL) aby upewnić się, że nic nie próbuje uzyskać dostępu do systemu plików. Teraz musimy wykonać

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

cp factfib.mpy /pyboard/factfib.mpy

Aby przetestować instalację, możesz użyć rshell lub Thonny IDE do zapisania twojej .py na Pico, ale zademonstruję jak używać interpretera MicroPython na minicomie.

Więc najpierw naciśnij Ctrl+c aby opuścić rshell, a następnie zainstalować minicom przez

sudo apt install minicom

teraz możemy uzyskać dostęp do Picos REPL (pętla Read-eval-print)

minicom -o -D /dev/ttyACM0

Trzy zamykające nawiasy kątowe >>> wskazują, że interpreter MicroPython jest uruchomiony. Teraz możemy zaimportować nasze rozszerzenie i pracować z nim jak zwykle.

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

Gratulacje! Stworzyłeś swoje pierwsze rozszerzenie MicroPython C dla Pico! Następnym krokiem jest nauczenie się pisania i kompilowania bardziej złożonych modułów. Dobrym sposobem na rozpoczęcie jest przetestowanie, jak działa Marshalling list i dicts (patrz strona Marshalling powyżej).

Jeśli masz jakieś pytania dotyczące tego wpisu, napisz komentarz.

Komentarzy: 8

  1. PeterB listopad 26, 2021 o 8:29 am

    Pomijając błąd w programie c, wszystko działa jak należy. Dzięki.

    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 mp_obj_new_int(rslt);
    }

  2. XMC.pl czerwiec 1, 2022 o 3:31 am

    Jak skonfigurować joomlę, aby mogła pobierać dane z mysql?

  3. Lynggaard styczeń 8, 2023 o 8:09 pm

    Próbowałem zrobić powyższe, ale różne rzeczy dont działają zgodnie z opisem:

    - Postępowałem zgodnie z instrukcjami powyżej, dostałem micropython, zmodyfikowałem makefile zgodnie z opisem
    - Robię make (ale dostaję plik factfib.native.mpy zamiast factfib.mpy)
    - Skopiowałem plik factfib.native.mpy do pico jako factfib.mpy
    (Próbowałem rshell ale dostałem "timed out lub error in transfer to remote: b"", ale mogłem skopiować go używając Thonny)
    - Mogę zaimportować moduł (w thonny lub rshell - to samo) bez błędów
    - Próbuję uruchomić "ff.fibonacci(5)", ale wtedy dostaję:

    "Traceback (most recent call last):
    Plik "", linia 1, w
    AttributeError: 'moduł' object has no attribute 'fibonacci'"

    Co robię źle?

    • paopao69 marzec 22, 2023 o 5:59 pm

      Czy w końcu udało ci się go uruchomić?

  4. Paulo luty 9, 2024 o 1:44 am

    Teraz możesz skompilować do armv6, więc po prostu zmień tę linię w pliku Makefile i nie musisz zmieniać kodu micropythona
    ARCH = armv6m

    Miałem kilka błędów podczas kompilacji factfib.c, gdzie są poprawki:

    // brakuje " na końcu.
    #include "py/dynruntime.h"

    // konwertuje wartość zwracaną na mp_obj_t
    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 mp_obj_new_int(rslt);
    }

    • Adam luty 9, 2024 o 9:22 am

      Dziękuję za informację, Paulo!

  5. Dr_Phil maj 24, 2024 o 7:36 am

    Postępowałem zgodnie ze wszystkimi instrukcjami i skończyłem ze zbudowanym plikiem facfib.mpy. Użyłem
    ARCH = armv6m
    Po załadowaniu do Pico pojawia się ten błąd:

    ValueError: niekompatybilny plik .mpy

    Mój Pico wymaga następujących wartości mpy:
    mpy wersja: 6
    podwersja mpy: 2
    flagi mpy: -march=armv6m

    Sprawdzenie pierwszych dwóch bajtów pliku daje wersję 6 i podwersję 6.
    Czy istnieje sposób, aby to poprawić?

Pozostaw komentarz