MicroPythonin ulkoiset C-moduulit¶
Kun kehität moduuleja käytettäväksi MicroPythonin kanssa, saatat törmätä Python-ympäristön rajoituksiin, jotka johtuvat usein kyvyttömyydestä käyttää tiettyjä laitteistoresursseja tai Pythonin nopeusrajoituksista.
Jos rajoituksiasi ei voida ratkaista kohdassa MicroPythonin nopeuden maksimointi annetuilla ehdotuksilla, osan tai koko moduulisi kirjoittaminen C-kielellä (ja/tai C++-kielellä, jos se on toteutettu porttiisi) on toimiva vaihtoehto.
Jos moduulisi on suunniteltu käyttämään yleisesti saatavilla olevaa laitteistoa tai kirjastoja tai toimimaan niiden kanssa, harkitse sen toteuttamista MicroPythonin lähdekoodipuussa vastaavien moduulien rinnalla ja sen lähettämistä pull requestina. Jos kuitenkin kohdennat harvinaisiin tai suljettuihin järjestelmiin, voi olla järkevämpää pitää tämä pääasiallisen MicroPython-arkiston ulkopuolella.
Tässä luvussa kuvataan, miten tällaisia ulkoisia moduuleja käännetään MicroPython-suoritettavaan tiedostoon tai laiteohjelmiston levykuvaan. Sekä Make- että CMake-koontityökaluja tuetaan, ja ulkoista moduulia kirjoitettaessa on hyvä lisätä molempien työkalujen koontitiedostot, jotta moduulia voidaan käyttää kaikilla porteilla. Mutta tiettyä porttia käännettäessä tarvitset vain yhtä koontimenetelmää, joko Makea tai CMakea.
Vaihtoehtoinen lähestymistapa on käyttää Natiivi konekoodi .mpy-tiedostoissa-ominaisuutta, jonka avulla voit kirjoittaa mukautettua C-koodia, joka sijoitetaan .mpy-tiedostoon ja joka voidaan tuoda dynaamisesti käynnissä olevaan MicroPython-järjestelmään ilman, että pääasiallinen laiteohjelmisto on käännettävä uudelleen.
Ulkoisen C-moduulin rakenne¶
MicroPythonin käyttäjän C-moduuli on hakemisto, jossa on seuraavat tiedostot:
*.c/*.cpp/*.h-lähdekooditiedostot moduulillesi.Nämä sisältävät tyypillisesti toteutettavan matalan tason toiminnallisuuden sekä MicroPython-sidosfunktiot, jotka tuovat funktiot ja moduulit näkyviin.
Tällä hetkellä paras viite näiden funktioiden/moduulien kirjoittamiseen on etsiä vastaavia moduuleja MicroPython-puusta ja käyttää niitä esimerkkeinä.
micropython.mksisältää tämän moduulin Makefile-fragmentin.$(USERMOD_DIR)on saatavilla tiedostossamicropython.mkpolkuna moduulihakemistoosi. Koska se määritellään uudelleen jokaiselle C-moduulille, se tulisi laajentaa tiedostossasimicropython.mkpaikalliseksi make-muuttujaksi, esim.EXAMPLE_MOD_DIR := $(USERMOD_DIR)Tiedostosi
micropython.mktäytyy lisätä moduuliesi lähdetiedostot muuttujiinSRC_USERMOD_CtaiSRC_USERMOD_LIB_C. Edellinen käsitelläänMP_QSTR_- jaMP_REGISTER_MODULE-määrittelyjen varalta, jälkimmäistä ei (esim. apufunktiot ja kirjastokoodi, joka ei ole MicroPython-spesifistä). Näiden polkujen tulisi sisältää laajennettu kopiosi muuttujasta$(USERMOD_DIR), esim.:SRC_USERMOD_C += $(EXAMPLE_MOD_DIR)/modexample.c SRC_USERMOD_LIB_C += $(EXAMPLE_MOD_DIR)/utils/algorithm.c
Käytä vastaavasti muuttujia
SRC_USERMOD_CXXjaSRC_USERMOD_LIB_CXXC++-lähdetiedostoille. Jos haluat sisällyttää assembly-tiedostoja, käytä muuttujaaSRC_USERMOD_LIB_ASM.Jos sinulla on mukautettuja kääntäjäasetuksia (kuten
-Ilisätäksesi hakemistoja otsikkotiedostojen etsintään), nämä tulisi lisätä muuttujaanCFLAGS_USERMODC-koodia varten ja muuttujaanCXXFLAGS_USERMODC++-koodia varten.micropython.cmakesisältää tämän moduulin CMake-konfiguraation.Tiedostossa
micropython.cmakevoit käyttää muuttujaa${CMAKE_CURRENT_LIST_DIR}polkuna nykyiseen moduuliin.Tiedostosi
micropython.cmaketulisi määritelläINTERFACE-kirjasto ja liittää siihen lähdetiedostosi, käännösmäärittelyt ja include-hakemistot. Kirjasto tulisi sitten linkittää kohteeseenusermod.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)
Katso alta täydellinen käyttöesimerkki.
Perusesimerkki¶
Moduuli cexample tarjoaa esimerkkejä funktiosta ja luokasta. Funktio cexample.add_ints(a, b) laskee kaksi kokonaislukuargumenttia yhteen ja palauttaa tuloksen. Tyyppi cexample.Timer() luo ajastimia, joita voidaan käyttää mittaamaan kulunutta aikaa siitä, kun objekti instantioitiin.
Moduuli löytyy MicroPythonin lähdekoodipuusta examples-hakemistosta ja siinä on lähdetiedosto sekä Makefile-fragmentti, jonka sisältö on edellä kuvatun mukainen:
micropython/
└──examples/
└──usercmodule/
└──cexample/
├── examplemodule.c
├── micropython.mk
└── micropython.cmake
Katso näiden tiedostojen kommenteista lisäselityksiä. Moduulin cexample vieressä on myös cppexample, joka toimii samalla tavalla mutta näyttää yhden tavan sekoittaa C- ja C++-koodia MicroPythonissa.
Cmodule-moduulin kääntäminen MicroPythoniin¶
Tällaisen moduulin koontamiseksi käännä MicroPython (katso aloitusopas), soveltaen kahta muutosta:
Aseta koonti-ajan lippu
USER_C_MODULESosoittamaan moduuleihin, jotka haluat sisällyttää. Make-koontia käyttävissä porteissa tämän muuttujan tulisi olla hakemisto, josta moduuleja etsitään automaattisesti. CMakea käyttävissä porteissa tämän muuttujan tulisi olla tiedosto, joka sisällyttää koottavat moduulit. Katso lisätietoja alta.Ota moduulit käyttöön asettamalla vastaava C-esikäsittelijän makro arvoon 1. Tätä tarvitaan vain, jos koottavat moduulit eivät ole automaattisesti käytössä.
MicroPythonin mukana tulevien esimerkkimoduulien koontamiseksi aseta USER_C_MODULES hakemistoon examples/usercmodule Makelle, tai tiedostoon examples/usercmodule/micropython.cmake CMakelle.
Esimerkiksi näin koonnataan unix-portti esimerkkimoduulien kanssa:
cd micropython/ports/unix
make USER_C_MODULES=../../examples/usercmodule
Saatat joutua ajamaan make clean kerran alussa, kun sisällytät uusia käyttäjämoduuleja koontiin. Koontituloste näyttää löydetyt moduulit:
...
Including User C Module from ../../examples/usercmodule/cexample
Including User C Module from ../../examples/usercmodule/cppexample
...
CMake-pohjaiselle portille kuten rp2 tämä näyttää hieman erilaiselta (huomaa, että CMaken kutsuu itse asiassa make):
cd micropython/ports/rp2
make USER_C_MODULES=../../examples/usercmodule/micropython.cmake
Jälleen saatat joutua ajamaan make clean ensin, jotta CMake huomaa käyttäjämoduulit. CMaken koontituloste listaa moduulit nimeltä:
...
Including User C Module(s) from ../../examples/usercmodule/micropython.cmake
Found User C Module(s): usermod_cexample, usermod_cppexample
...
Ylätason micropython.cmake-tiedoston sisältöä voidaan käyttää sen hallintaan, mitkä moduulit ovat käytössä.
Omissa projekteissasi on kätevämpää pitää mukautettu koodi MicroPythonin pääasiallisen lähdekoodipuun ulkopuolella, joten tyypillinen projektihakemiston rakenne näyttää tältä:
my_project/
├── modules/
│ ├── example1/
│ │ ├── example1.c
│ │ ├── micropython.mk
│ │ └── micropython.cmake
│ ├── example2/
│ │ ├── example2.c
│ │ ├── micropython.mk
│ │ └── micropython.cmake
│ └── micropython.cmake
└── micropython/
├──ports/
... ├──stm32/
...
Makella koonnettaessa aseta USER_C_MODULES hakemistoon my_project/modules. Esimerkiksi stm32-portin koontaminen:
cd my_project/micropython/ports/stm32
make USER_C_MODULES=../../../modules
CMakella koonnettaessa ylätason micropython.cmake – joka löytyy suoraan hakemistosta my_project/modules – tulisi include-komennolla sisällyttää kaikki moduulit, jotka haluat olevan saatavilla:
include(${CMAKE_CURRENT_LIST_DIR}/example1/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/example2/micropython.cmake)
Koonnata sitten komennolla:
cd my_project/micropython/ports/rp2
make USER_C_MODULES=../../../modules/micropython.cmake
Voit myös määrittää absoluuttisia polkuja muuttujalle USER_C_MODULES.
Kaikki muuttujan USER_C_MODULES määrittämät moduulit (joko tästä hakemistosta löytyneet Makea käytettäessä tai include-komennolla lisätyt CMakea käytettäessä) käännetään, mutta vain ne, jotka ovat käytössä, ovat tuotavissa. Käyttäjämoduulit ovat yleensä oletusarvoisesti käytössä (tämän päättää moduulin kehittäjä), jolloin ei tarvitse tehdä muuta kuin asettaa USER_C_MODULES edellä kuvatulla tavalla.
Jos moduuli ei ole oletusarvoisesti käytössä, vastaava C-esikäsittelijän makro täytyy ottaa käyttöön. Tämän makron nimen löytää etsimällä riviä MP_REGISTER_MODULE moduulin lähdekoodista (se esiintyy yleensä päälähdetiedoston lopussa). Tämän makron tulisi olla #if X / #endif -parin ympäröimä, ja konfiguraatioasetus X täytyy asettaa arvoon 1 käyttäen CFLAGS_EXTRA-muuttujaa, jotta moduuli olisi saatavilla. Jos #if X / #endif -paria ei ole, moduuli on oletusarvoisesti käytössä.
Esimerkiksi moduuli examples/usercmodule/cexample on oletusarvoisesti käytössä, joten sen lähdekoodissa on seuraava rivi:
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule);
Vaihtoehtoisesti, jotta tämä moduuli olisi oletusarvoisesti pois käytöstä mutta valittavissa esikäsittelijän konfiguraatioasetuksen kautta, se olisi:
#if MODULE_CEXAMPLE_ENABLED MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule); #endif
Tässä tapauksessa moduuli otetaan käyttöön lisäämällä CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1 make-komentoon, tai muokkaamalla tiedostoa mpconfigport.h tai mpconfigboard.h lisäämällä
#define MODULE_CEXAMPLE_ENABLED (1)
Huomaa, että tarkka menetelmä riippuu portista, koska niillä on erilaiset rakenteet. Jos sitä ei tehdä oikein, koonti onnistuu mutta tuonti ei löydä moduulia.
Moduulin käyttö MicroPythonissa¶
Kun moduuli on koonnettu MicroPython-kopioosi, sitä voidaan nyt käyttää Pythonissa aivan kuten mitä tahansa muuta sisäänrakennettua moduulia, esim.
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-dynaaminen muistinvaraus¶
MicroPython käyttää omaa ”Python-kekoaan” Muistinhallinta-tarkoituksiin, mikä ei ole sama kuin C-kirjastofunktioiden malloc(), free() ym. käyttämä ”C-keko”. Kaikissa MicroPython-porteissa ei ole lainkaan ”C-kekoa”.
Tason 1 ja 2 porteilla on vaihteleva tuki C-dynaamiselle muistinvaraukselle ”C-keon” kautta:
unix-, windows-, esp32- ja webassembly-portit tukevat C-dynaamista muistinvarausta.
rp2-portti epäonnistuu varatakseen lainkaan muistia ajonaikaisesti, ellei laiteohjelmistoa ole koonnettu asetuksella
MICROPY_C_HEAP_SIZE=nvaratakseenntavua muistia C-kekoa varten. Tämä muisti ei ole Python-koodin käytettävissä.alif-, mimxrt-, nrf-, renesas-ra-, samd- ja stm32-porttien koonnit, jotka sisältävät dynaamisen C-varauksen, epäonnistuvat linkitysaikana virheillä kuten
undefined reference to `malloc'. MicroPythonissa ei ole sisäänrakennettua tukea dynaamiselle C-varaukselle näillä porteilla. Mikä tahansa ratkaisu vaatii C-keon toteutuksen manuaalista lisäämistä mukautettuun koontiin.zephyr-portti ei tällä hetkellä tue koontamista käyttäjämoduulien kanssa.
Python-keko C-kekona¶
C-koodin voi olla käytännöllistä kutsua sen sijaan ”Python-keon” dynaamisia varausfunktioita kuten m_malloc(), m_malloc0() ja m_free().
Katso MicroPython-muisti C-koodista saadaksesi lisätietoja tästä lähestymistavasta.
C++-moduulit¶
Useimmat tason 1 ja 2 MicroPython-portit (ja jotkin tason 3) tukevat C++-käyttäjämoduulien koontamista käyttäen edellä kuvattuja C++-spesifisiä ympäristömuuttujia.
C++:n ja MicroPythonin onnistunut integrointi sisältää joitakin lisänäkökohtia:
C++-dynaaminen muistinvaraus¶
C++-ohjelmat (kuten myös C++-standardikirjaston ominaisuudet) käyttävät tyypillisesti dynaamista muistinvarausta. C++:n oletusmuistinvaraaja (eli operaattorit new ja delete) on tyypillisesti toteutettu kerroksena C-dynaaminen muistinvaraus-keon päälle.
MicroPython-porteille, jotka eivät sisällä C-dynaamisen muistinvarauksen tukea, C++-dynaaminen muistinvaraus voidaan tukea jommallakummalla kahdesta tavasta:
Toteuta C-dynaaminen muistinvaraus mukautetussa koonnissasi.
Toteuta mukautettu C++-varaaja mukautetussa koonnissasi.
Linkitysnäkökohdat¶
Koska MicroPython on C-pohjainen projekti, kaikki symbolit, jotka linkittyvät MicroPythoniin tai siitä pois, täytyy määritellä extern "C" C++-koodissa.
On vahvasti suositeltavaa noudattaa kohdassa examples/usercmodule/cppexample esitettyä mallia, jossa Python-moduuli on toteutettu minimaalisena C-tiedoston kääreenä C++-koodin ympärille.