MicroPython vanjski C moduli

Pri razvoju modula za upotrebu s MicroPython-om možda ćete naići na ograničenja Python okruženja, često zbog nemogućnosti pristupa određenim hardverskim resursima ili zbog ograničenja brzine Python-a.

Ako se vaša ograničenja ne mogu riješiti prijedlozima iz Maksimiziranje brzine MicroPythona, pisanje dijela ili cijelog modula u jeziku C (i/ili C++ ako je implementiran za vaš port) je održiva opcija.

Ako je vaš modul namijenjen pristupu ili radu s uobičajeno dostupnim hardverom ili bibliotekama, razmotrite njegovo implementiranje unutar MicroPython izvornog stabla uz slične module i podnošenje kao pull request. Ako pak ciljate na rijetke ili vlasničke sustave, možda ima više smisla zadržati ovo izvan glavnog MicroPython repozitorija.

Ovo poglavlje opisuje kako prevesti takve vanjske module u MicroPython izvršnu datoteku ili sliku ugrađenog programa (firmware). Podržani su i Make i CMake alati za izgradnju, a pri pisanju vanjskog modula dobra je ideja dodati datoteke za izgradnju za oba alata kako bi se modul mogao koristiti na svim portovima. No pri prevođenju određenog porta trebat ćete koristiti samo jednu metodu izgradnje, ili Make ili CMake.

Alternativni pristup je upotreba Izvorni strojni kod u .mpy datotekama koji omogućuje pisanje prilagođenog C koda koji se smješta u .mpy datoteku, koja se može dinamički uvesti u pokrenuti MicroPython sustav bez potrebe za ponovnim prevođenjem glavnog ugrađenog programa (firmware).

Struktura vanjskog C modula

MicroPython korisnički C modul je direktorij sa sljedećim datotekama:

  • *.c / *.cpp / *.h datoteke izvornog koda za vaš modul.

    One će obično uključivati niskorazinsku funkcionalnost koja se implementira te MicroPython vezne funkcije koje izlažu funkcije i modul(e).

    Trenutno je najbolja referenca za pisanje ovih funkcija/modula pronaći slične module unutar MicroPython stabla i koristiti ih kao primjere.

  • micropython.mk sadrži Makefile fragment za ovaj modul.

    $(USERMOD_DIR) je dostupan u micropython.mk kao putanja do vašeg direktorija modula. Budući da se redefinira za svaki C modul, treba ga proširiti u vašem micropython.mk u lokalnu make varijablu, npr. EXAMPLE_MOD_DIR := $(USERMOD_DIR)

    Vaš micropython.mk mora dodati datoteke izvornog koda vaših modula u varijable SRC_USERMOD_C ili SRC_USERMOD_LIB_C. Prva će biti obrađena za MP_QSTR_ i MP_REGISTER_MODULE definicije, druga neće (npr. pomoćni i bibliotečni kod koji nije specifičan za MicroPython). Ove putanje trebaju uključivati vašu proširenu kopiju $(USERMOD_DIR), npr.:

    SRC_USERMOD_C += $(EXAMPLE_MOD_DIR)/modexample.c
    SRC_USERMOD_LIB_C += $(EXAMPLE_MOD_DIR)/utils/algorithm.c
    

    Slično, koristite SRC_USERMOD_CXX i SRC_USERMOD_LIB_CXX za C++ datoteke izvornog koda. Ako želite uključiti asemblerske datoteke, koristite SRC_USERMOD_LIB_ASM.

    Ako imate prilagođene opcije prevodioca (poput -I za dodavanje direktorija u kojima se traže datoteke zaglavlja), one se trebaju dodati u CFLAGS_USERMOD za C kod i u CXXFLAGS_USERMOD za C++ kod.

  • micropython.cmake sadrži CMake konfiguraciju za ovaj modul.

    U micropython.cmake možete koristiti ${CMAKE_CURRENT_LIST_DIR} kao putanju do trenutnog modula.

    Vaš micropython.cmake treba definirati INTERFACE biblioteku i povezati s njom svoje datoteke izvornog koda, definicije za prevođenje i direktorije za uključivanje. Biblioteka se zatim treba povezati s usermod ciljem.

    add_library(usermod_cexample INTERFACE)
    
    target_sources(usermod_cexample INTERFACE
        ${CMAKE_CURRENT_LIST_DIR}/examplemodule.c
    )
    
    target_include_directories(usermod_cexample INTERFACE
        ${CMAKE_CURRENT_LIST_DIR}
    )
    
    target_link_libraries(usermod INTERFACE usermod_cexample)
    

    Pogledajte u nastavku potpuni primjer upotrebe.

Osnovni primjer

Modul cexample pruža primjere za funkciju i klasu. Funkcija cexample.add_ints(a, b) zbraja dva cjelobrojna argumenta i vraća rezultat. Tip cexample.Timer() stvara mjerače vremena koji se mogu koristiti za mjerenje proteklog vremena od instanciranja objekta.

Modul se može pronaći u MicroPython izvornom stablu u direktoriju s primjerima te ima datoteku izvornog koda i Makefile fragment sa sadržajem opisanim gore:

micropython/
└──examples/
   └──usercmodule/
      └──cexample/
         ├── examplemodule.c
         ├── micropython.mk
         └── micropython.cmake

Pogledajte komentare u ovim datotekama za dodatno objašnjenje. Pored modula cexample nalazi se i cppexample koji radi na isti način, ali pokazuje jedan način miješanja C i C++ koda u MicroPython-u.

Prevođenje cmodule u MicroPython

Da biste izgradili takav modul, prevedite MicroPython (pogledajte getting started), primjenjujući 2 izmjene:

  1. Postavite zastavicu za vrijeme izgradnje USER_C_MODULES tako da pokazuje na module koje želite uključiti. Za portove koji koriste Make ova varijabla treba biti direktorij koji se automatski pretražuje za module. Za portove koji koriste CMake ova varijabla treba biti datoteka koja uključuje module koje treba izgraditi. Za detalje pogledajte u nastavku.

  2. Omogućite module postavljanjem odgovarajućeg makronaredbe C predprocesora na 1. Ovo je potrebno samo ako moduli koje gradite nisu automatski omogućeni.

Za izgradnju primjera modula koji dolaze s MicroPython-om, postavite USER_C_MODULES na direktorij examples/usercmodule za Make, ili na examples/usercmodule/micropython.cmake za CMake.

Na primjer, evo kako izgraditi unix port s primjerima modula:

cd micropython/ports/unix
make USER_C_MODULES=../../examples/usercmodule

Možda ćete morati jednom pokrenuti make clean na početku kada uključujete nove korisničke module u izgradnju. Izlaz izgradnje prikazat će pronađene module:

...
Including User C Module from ../../examples/usercmodule/cexample
Including User C Module from ../../examples/usercmodule/cppexample
...

Za port temeljen na CMake-u poput rp2, ovo će izgledati malo drugačije (imajte na umu da make zapravo poziva CMake):

cd micropython/ports/rp2
make USER_C_MODULES=../../examples/usercmodule/micropython.cmake

Ponovno, možda ćete najprije morati pokrenuti make clean kako bi CMake prepoznao korisničke module. CMake izlaz izgradnje navodi module po imenu:

...
Including User C Module(s) from ../../examples/usercmodule/micropython.cmake
Found User C Module(s): usermod_cexample, usermod_cppexample
...

Sadržaj micropython.cmake na najvišoj razini može se koristiti za upravljanje time koji su moduli omogućeni.

Za vlastite projekte praktičnije je držati prilagođeni kod izvan glavnog MicroPython izvornog stabla, pa će tipična struktura direktorija projekta izgledati ovako:

my_project/
├── modules/
│   ├── example1/
│   │   ├── example1.c
│   │   ├── micropython.mk
│   │   └── micropython.cmake
│   ├── example2/
│   │   ├── example2.c
│   │   ├── micropython.mk
│   │   └── micropython.cmake
│   └── micropython.cmake
└── micropython/
    ├──ports/
   ... ├──stm32/
      ...

Pri izgradnji pomoću Make-a postavite USER_C_MODULES na direktorij my_project/modules. Na primjer, izgradnja stm32 porta:

cd my_project/micropython/ports/stm32
make USER_C_MODULES=../../../modules

Pri izgradnji pomoću CMake-a micropython.cmake na najvišoj razini – koji se nalazi izravno u direktoriju my_project/modules – treba pomoću include uključiti sve module koje želite imati dostupne:

include(${CMAKE_CURRENT_LIST_DIR}/example1/micropython.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/example2/micropython.cmake)

Zatim izgradite pomoću:

cd my_project/micropython/ports/rp2
make USER_C_MODULES=../../../modules/micropython.cmake

Također možete navesti apsolutne putanje za USER_C_MODULES.

Svi moduli navedeni varijablom USER_C_MODULES (bilo pronađeni u ovom direktoriju pri upotrebi Make-a, bilo dodani putem include pri upotrebi CMake-a) bit će prevedeni, ali samo oni koji su omogućeni bit će dostupni za uvoz. Korisnički moduli obično su omogućeni prema zadanim postavkama (o tome odlučuje razvijatelj modula), u kojem slučaju nema ništa više za učiniti osim postaviti USER_C_MODULES kako je opisano gore.

Ako modul nije omogućen prema zadanim postavkama, tada se mora omogućiti odgovarajuća makronaredba C predprocesora. Ovo ime makronaredbe može se pronaći pretraživanjem retka MP_REGISTER_MODULE u izvornom kodu modula (obično se pojavljuje na kraju glavne datoteke izvornog koda). Ova makronaredba treba biti okružena parom #if X / #endif, a konfiguracijska opcija X mora biti postavljena na 1 pomoću CFLAGS_EXTRA kako bi modul postao dostupan. Ako ne postoji par #if X / #endif, tada je modul omogućen prema zadanim postavkama.

Na primjer, modul examples/usercmodule/cexample omogućen je prema zadanim postavkama pa u svom izvornom kodu ima sljedeći redak:

MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);

Alternativno, da bi se ovaj modul onemogućio prema zadanim postavkama, ali bio odabir putem konfiguracijske opcije predprocesora, bilo bi:

#if MODULE_CEXAMPLE_ENABLED
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
#endif

U tom se slučaju modul omogućuje dodavanjem CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1 u make naredbu, ili uređivanjem mpconfigport.h ili mpconfigboard.h kako bi se dodalo

#define MODULE_CEXAMPLE_ENABLED (1)

Imajte na umu da točna metoda ovisi o portu jer imaju različite strukture. Ako se ne učini ispravno, prevest će se, ali uvoz neće uspjeti pronaći modul.

Upotreba modula u MicroPython-u

Nakon što je izgrađen u vašu kopiju MicroPython-a, modulu se sada može pristupiti u Python-u baš kao i bilo kojem drugom ugrađenom modulu, npr.

import cexample
print(cexample.add_ints(1, 3))
# should display 4
from cexample import Timer
from time import sleep_ms

watch = Timer()
sleep_ms(1000)
print(watch.time())
# should display approximately 1000

Dinamičko zauzimanje memorije u jeziku C

MicroPython koristi vlastiti „Python heap” za Upravljanje memorijom, što nije isto kao „C heap” koji koriste funkcije C biblioteke malloc(), free() itd. Ne dolazi svaki MicroPython port s „C heap”-om.

Portovi razine 1 i 2 imaju različitu podršku za dinamičko zauzimanje C memorije putem „C heap”-a:

  • unix, windows, esp32 i webassembly portovi podržavaju dinamičko zauzimanje C memorije.

  • rp2 port neće uspjeti zauzeti nikakvu memoriju tijekom izvođenja osim ako je ugrađeni program (firmware) izgrađen s MICROPY_C_HEAP_SIZE=n kako bi se rezerviralo n bajtova memorije za C heap. Ova memorija neće biti dostupna za upotrebu Python kodu.

  • Izgradnje alif, mimxrt, nrf, renesas-ra, samd i stm32 portova koje uključuju dinamičko C zauzimanje neće uspjeti tijekom povezivanja s pogreškama poput undefined reference to `malloc'. MicroPython nema ugrađenu podršku za dinamičko C zauzimanje na ovim portovima. Svako rješenje zahtijeva ručno dodavanje implementacije C heap-a u prilagođenu izgradnju.

  • zephyr port trenutno ne podržava izgradnju s korisničkim modulima.

Python heap kao C heap

Za C kod može biti praktično da umjesto toga poziva funkcije dinamičkog zauzimanja „Python heap”-a poput m_malloc(), m_malloc0() i m_free().

Pogledajte MicroPython memorija iz C koda za više informacija o ovom pristupu.

C++ moduli

Većina MicroPython portova razine 1 i 2 (i neki razine 3) podržavaju izgradnju C++ korisničkih modula, koristeći C++-specifične varijable okruženja opisane gore.

Uspješno integriranje C++-a i MicroPython-a uključuje neka dodatna razmatranja:

Dinamičko zauzimanje memorije u jeziku C++

C++ programi (kao i značajke C++ standardne biblioteke) obično koriste dinamičko zauzimanje memorije. Zadani C++ alokator memorije (tj. operatori new i delete) obično je implementiran kao sloj iznad Dinamičko zauzimanje memorije u jeziku C.

Za MicroPython portove koji ne uključuju podršku za dinamičko zauzimanje C memorije, dinamičko zauzimanje C++ memorije može se podržati na jedan od dva načina:

  • Implementirajte dinamičko zauzimanje C memorije u svojoj prilagođenoj izgradnji.

  • Implementirajte prilagođeni C++ alokator u svojoj prilagođenoj izgradnji.

Razmatranja o povezivanju

Budući da je MicroPython projekt temeljen na jeziku C, svi simboli koji se povezuju s MicroPython-om ili iz njega moraju biti kvalificirani kao extern "C" u C++ kodu.

Snažno se preporučuje slijediti obrazac prikazan u examples/usercmodule/cppexample, gdje je Python modul implementiran u minimalnom omotaču C datoteke oko C++ koda.