Külső C modulok a MicroPython számára¶
Amikor a MicroPython rendszerhez modulokat fejlesztesz, előfordulhat, hogy a Python környezet korlátaiba ütközöl, gyakran amiatt, hogy bizonyos hardvererőforrásokhoz nem lehet hozzáférni, vagy a Python sebességi korlátai miatt.
Ha a korlátaid nem oldhatók fel a A MicroPython sebességének maximalizálása szakaszban található javaslatokkal, akkor életképes megoldás lehet a modulod egy részének vagy egészének C-ben (és/vagy C++ nyelven, ha az a portodhoz implementálva van) történő megírása.
Ha a modulod gyakran elérhető hardver vagy könyvtárak elérésére, illetve azokkal való együttműködésre készült, fontold meg, hogy a MicroPython forrásfájában, hasonló modulok mellé implementálod, és pull requestként beküldöd. Ha azonban kevéssé ismert vagy zárt rendszereket célzol meg, érdemesebb lehet ezt a fő MicroPython repository-n kívül tartani.
Ez a fejezet leírja, hogyan fordíthatók le az ilyen külső modulok a MicroPython futtatható állományába vagy firmware-képébe. Mind a Make, mind a CMake build eszköz támogatott, és külső modul írásakor jó ötlet mindkét eszköz build fájljait hozzáadni, hogy a modul minden porton használható legyen. Egy adott port fordításakor azonban csak az egyik build módszert, vagy a Make-et, vagy a CMake-et kell használnod.
Egy másik megközelítés a Natív gépi kód .mpy fájlokban használata, amely lehetővé teszi egyedi C kód írását, amely egy .mpy fájlba kerül, és amely dinamikusan importálható egy futó MicroPython rendszerbe anélkül, hogy a fő firmware-t újra kellene fordítani.
Egy külső C modul felépítése¶
Egy MicroPython felhasználói C modul egy könyvtár, amely a következő fájlokat tartalmazza:
*.c/*.cpp/*.hforráskódfájlok a modulodhoz.Ezek jellemzően az implementált alacsony szintű funkcionalitást, valamint a MicroPython kötőfüggvényeket tartalmazzák, amelyek elérhetővé teszik a függvényeket és a modul(oka)t.
Jelenleg a legjobb referencia ezeknek a függvényeknek/moduloknak a megírásához az, hogy hasonló modulokat keresel a MicroPython fájban, és azokat használod példaként.
A
micropython.mktartalmazza az ehhez a modulhoz tartozó Makefile-részletet.A
$(USERMOD_DIR)elérhető amicropython.mkfájlban a modulkönyvtárad útvonalaként. Mivel ez minden C modulhoz újra van definiálva, amicropython.mkfájlban egy helyi make változóba kell kibontanod, pl.EXAMPLE_MOD_DIR := $(USERMOD_DIR)A
micropython.mkfájlnak hozzá kell adnia a moduljaid forrásfájljait aSRC_USERMOD_Cvagy aSRC_USERMOD_LIB_Cváltozóhoz. Az előbbitMP_QSTR_ésMP_REGISTER_MODULEdefiníciókra dolgozzák fel, az utóbbit nem (pl. segédfüggvények és olyan könyvtári kód, amely nem MicroPython-specifikus). Ezeknek az útvonalaknak tartalmazniuk kell a$(USERMOD_DIR)kibontott másolatát, pl.:SRC_USERMOD_C += $(EXAMPLE_MOD_DIR)/modexample.c SRC_USERMOD_LIB_C += $(EXAMPLE_MOD_DIR)/utils/algorithm.c
Hasonlóképpen, C++ forrásfájlokhoz használd a
SRC_USERMOD_CXXésSRC_USERMOD_LIB_CXXváltozókat. Ha assembly fájlokat szeretnél belefoglalni, használd aSRC_USERMOD_LIB_ASMváltozót.Ha egyedi fordítói opcióid vannak (mint a
-I, amellyel könyvtárakat adhatsz hozzá a header fájlok kereséséhez), ezeket C kódhoz aCFLAGS_USERMOD, C++ kódhoz pedig aCXXFLAGS_USERMODváltozóhoz kell hozzáadni.A
micropython.cmaketartalmazza az ehhez a modulhoz tartozó CMake konfigurációt.A
micropython.cmakefájlban az aktuális modul útvonalaként a${CMAKE_CURRENT_LIST_DIR}használható.A
micropython.cmakefájlodnak definiálnia kell egyINTERFACEkönyvtárat, és hozzá kell rendelnie a forrásfájljaidat, a fordítási definíciókat és az include könyvtárakat. A könyvtárat ezután ausermodcélhoz kell linkelni.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)
A teljes használati példát lásd alább.
Alapvető példa¶
A cexample modul egy függvényre és egy osztályra mutat példát. A cexample.add_ints(a, b) függvény összead két egész argumentumot, és visszaadja az eredményt. A cexample.Timer() típus olyan időzítőket hoz létre, amelyekkel mérhető az objektum példányosítása óta eltelt idő.
A modul megtalálható a MicroPython forrásfájban a példák könyvtárában, és van egy forrásfájlja, valamint egy Makefile-részlete, a fentebb leírt tartalommal:
micropython/
└──examples/
└──usercmodule/
└──cexample/
├── examplemodule.c
├── micropython.mk
└── micropython.cmake
A további magyarázatért lásd az ezekben a fájlokban lévő megjegyzéseket. A cexample modul mellett található egy cppexample is, amely ugyanígy működik, de a C és C++ kód MicroPython-ban való vegyítésének egy módját mutatja be.
A cmodule lefordítása a MicroPython-ba¶
Egy ilyen modul felépítéséhez fordítsd le a MicroPython-t (lásd a kezdeti lépéseket), 2 módosítást alkalmazva:
Állítsd be a
USER_C_MODULESbuild-időbeli kapcsolót úgy, hogy a belefoglalni kívánt modulokra mutasson. A Make-et használó portoknál ennek a változónak egy könyvtárnak kell lennie, amelyben automatikusan keresik a modulokat. A CMake-et használó portoknál ennek a változónak egy fájlnak kell lennie, amely tartalmazza a felépítendő modulokat. A részletekért lásd alább.Engedélyezd a modulokat a megfelelő C előfeldolgozó makró 1-re állításával. Erre csak akkor van szükség, ha a felépítendő modulok nem engedélyezettek automatikusan.
A MicroPython-nal érkező példamodulok felépítéséhez állítsd a USER_C_MODULES változót az examples/usercmodule könyvtárra Make esetén, vagy az examples/usercmodule/micropython.cmake fájlra CMake esetén.
Például így építhető fel a unix port a példamodulokkal:
cd micropython/ports/unix
make USER_C_MODULES=../../examples/usercmodule
Előfordulhat, hogy egyszer le kell futtatnod a make clean parancsot az elején, amikor új felhasználói modulokat foglalsz a build-be. A build kimenete megmutatja a megtalált modulokat:
...
Including User C Module from ../../examples/usercmodule/cexample
Including User C Module from ../../examples/usercmodule/cppexample
...
Egy CMake-alapú portnál, mint az rp2, ez kissé másképp néz ki (vedd figyelembe, hogy a CMake-et valójában a make hívja meg):
cd micropython/ports/rp2
make USER_C_MODULES=../../examples/usercmodule/micropython.cmake
Ismét, lehet, hogy először le kell futtatnod a make clean parancsot, hogy a CMake felvegye a felhasználói modulokat. A CMake build kimenete név szerint felsorolja a modulokat:
...
Including User C Module(s) from ../../examples/usercmodule/micropython.cmake
Found User C Module(s): usermod_cexample, usermod_cppexample
...
A legfelső szintű micropython.cmake tartalma használható annak szabályozására, hogy mely modulok legyenek engedélyezve.
A saját projektjeidnél kényelmesebb az egyedi kódot a fő MicroPython forrásfán kívül tartani, így egy tipikus projektkönyvtár-struktúra így néz ki:
my_project/
├── modules/
│ ├── example1/
│ │ ├── example1.c
│ │ ├── micropython.mk
│ │ └── micropython.cmake
│ ├── example2/
│ │ ├── example2.c
│ │ ├── micropython.mk
│ │ └── micropython.cmake
│ └── micropython.cmake
└── micropython/
├──ports/
... ├──stm32/
...
Make-kel való építéskor állítsd a USER_C_MODULES változót a my_project/modules könyvtárra. Például az stm32 port felépítése:
cd my_project/micropython/ports/stm32
make USER_C_MODULES=../../../modules
CMake-kel való építéskor a legfelső szintű micropython.cmake fájlnak – amely közvetlenül a my_project/modules könyvtárban található – include paranccsal kell betöltenie az összes elérhetővé tenni kívánt modult:
include(${CMAKE_CURRENT_LIST_DIR}/example1/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/example2/micropython.cmake)
Majd építsd fel a következővel:
cd my_project/micropython/ports/rp2
make USER_C_MODULES=../../../modules/micropython.cmake
A USER_C_MODULES változóhoz abszolút útvonalakat is megadhatsz.
A USER_C_MODULES változó által megadott összes modul (akár ebben a könyvtárban található Make használatakor, akár include paranccsal hozzáadva CMake használatakor) lefordításra kerül, de csak azok lesznek importálhatók, amelyek engedélyezve vannak. A felhasználói modulok általában alapértelmezetten engedélyezettek (ezt a modul fejlesztője dönti el), ebben az esetben nincs más teendő, mint beállítani a USER_C_MODULES változót a fentebb leírtak szerint.
Ha egy modul nincs alapértelmezetten engedélyezve, akkor a megfelelő C előfeldolgozó makrót kell engedélyezni. Ez a makrónév a modul forráskódjában a MP_REGISTER_MODULE sor keresésével található meg (általában a fő forrásfájl végén szerepel). Ezt a makrót egy #if X / #endif párnak kell körülvennie, és az X konfigurációs opciót a CFLAGS_EXTRA segítségével 1-re kell állítani, hogy a modul elérhetővé váljon. Ha nincs #if X / #endif pár, akkor a modul alapértelmezetten engedélyezett.
Például az examples/usercmodule/cexample modul alapértelmezetten engedélyezett, ezért a következő sor szerepel a forráskódjában:
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
Alternatívaként, ha ezt a modult alapértelmezetten letiltottá, de egy előfeldolgozó konfigurációs opcióval kiválaszthatóvá szeretnéd tenni, ez így nézne ki:
#if MODULE_CEXAMPLE_ENABLED MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule); #endif
Ebben az esetben a modul engedélyezhető a CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1 hozzáadásával a make parancshoz, vagy az mpconfigport.h vagy mpconfigboard.h szerkesztésével a következő hozzáadásával
#define MODULE_CEXAMPLE_ENABLED (1)
Vedd figyelembe, hogy a pontos módszer a porttól függ, mivel azok eltérő struktúrájúak. Ha nem helyesen történik, akkor le fog fordulni, de az importálás nem fogja megtalálni a modult.
Modulhasználat a MicroPython-ban¶
Miután beépítetted a MicroPython másolatodba, a modul mostantól Python-ban éppúgy elérhető, mint bármely más beépített modul, pl.
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
C dinamikus memóriafoglalás¶
A MicroPython a saját „Python heap”-jét használja a Memóriakezelés célokra, amely nem azonos a C könyvtárfüggvények, mint a malloc(), free() stb. által használt „C heap”-pel. Nem minden MicroPython port rendelkezik egyáltalán „C heap”-pel.
Az 1. és 2. szintű portok eltérő mértékben támogatják a C dinamikus memóriafoglalást egy „C heap”-en keresztül:
A unix, windows, esp32 és webassembly portok támogatják a C dinamikus memóriafoglalást.
Az rp2 port futásidőben nem tud memóriát foglalni, hacsak a firmware nem a
MICROPY_C_HEAP_SIZE=nbeállítással van felépítve, hogynbájt memóriát foglaljon le egy C heap számára. Ez a memória nem lesz elérhető a Python kód számára.Az alif, mimxrt, nrf, renesas-ra, samd és stm32 portok dinamikus C foglalást tartalmazó buildjei linkeléskor olyan hibákkal fognak elbukni, mint az
undefined reference to `malloc'. A MicroPython ezeken a portokon nem tartalmaz beépített támogatást a dinamikus C foglaláshoz. Bármilyen megoldás megköveteli egy C heap implementáció kézi hozzáadását az egyedi build-hez.A zephyr port jelenleg nem támogatja a felhasználói modulokkal való felépítést.
Python heap mint C heap¶
A C kód számára gyakorlatias lehet, ha helyette „Python heap” dinamikus foglalási függvényeket hív meg, mint a m_malloc(), m_malloc0() és m_free().
Erről a megközelítésről további információért lásd a MicroPython memória C kódból szakaszt.
C++ modulok¶
A legtöbb 1. és 2. szintű MicroPython port (és néhány 3. szintű is) támogatja a C++ felhasználói modulok felépítését a fentebb leírt C++-specifikus környezeti változók használatával.
A C++ és a MicroPython sikeres integrálása néhány további szempontot is magában foglal:
C++ dinamikus memóriafoglalás¶
A C++ programok (valamint a C++ szabványos könyvtár jellemzői) jellemzően dinamikus memóriafoglalást használnak. A C++ alapértelmezett memóriafoglalója (azaz a new és delete operátorok) jellemzően a C dinamikus memóriafoglalás tetejére épülő rétegként van implementálva.
Azoknál a MicroPython portoknál, amelyek nem tartalmaznak C dinamikus memóriafoglalási támogatást, a C++ dinamikus memóriafoglalás kétféleképpen támogatható:
Implementálj C dinamikus memóriafoglalást az egyedi build-edben.
Implementálj egy egyedi C++ memóriafoglalót az egyedi build-edben.
Linkelési szempontok¶
Mivel a MicroPython egy C-alapú projekt, minden olyan szimbólumot, amely a MicroPython-hoz vagy onnan linkelődik, extern "C" minősítéssel kell ellátni a C++ kódban.
Erősen ajánlott a examples/usercmodule/cppexample példában bemutatott minta követése, ahol a Python modul egy minimális C fájlban van implementálva, amely a C++ kódot burkolja.