2019-12-16 12:04:01 +00:00
|
|
|
.. _cmodules:
|
|
|
|
|
2018-12-12 05:50:55 +00:00
|
|
|
MicroPython external C modules
|
|
|
|
==============================
|
|
|
|
|
|
|
|
When developing modules for use with MicroPython you may find you run into
|
|
|
|
limitations with the Python environment, often due to an inability to access
|
|
|
|
certain hardware resources or Python speed limitations.
|
|
|
|
|
|
|
|
If your limitations can't be resolved with suggestions in :ref:`speed_python`,
|
2020-10-08 15:44:55 +01:00
|
|
|
writing some or all of your module in C (and/or C++ if implemented for your port)
|
|
|
|
is a viable option.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
If your module is designed to access or work with commonly available
|
|
|
|
hardware or libraries please consider implementing it inside the MicroPython
|
|
|
|
source tree alongside similar modules and submitting it as a pull request.
|
|
|
|
If however you're targeting obscure or proprietary systems it may make
|
|
|
|
more sense to keep this external to the main MicroPython repository.
|
|
|
|
|
|
|
|
This chapter describes how to compile such external modules into the
|
|
|
|
MicroPython executable or firmware image.
|
|
|
|
|
2019-12-16 12:04:01 +00:00
|
|
|
An alternative approach is to use :ref:`natmod` which allows writing custom C
|
|
|
|
code that is placed in a .mpy file, which can be imported dynamically in to
|
|
|
|
a running MicroPython system without the need to recompile the main firmware.
|
|
|
|
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
Structure of an external C module
|
|
|
|
---------------------------------
|
|
|
|
|
|
|
|
A MicroPython user C module is a directory with the following files:
|
|
|
|
|
2020-10-08 15:44:55 +01:00
|
|
|
* ``*.c`` / ``*.cpp`` / ``*.h`` source code files for your module.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
These will typically include the low level functionality being implemented and
|
|
|
|
the MicroPython binding functions to expose the functions and module(s).
|
|
|
|
|
|
|
|
Currently the best reference for writing these functions/modules is
|
|
|
|
to find similar modules within the MicroPython tree and use them as examples.
|
|
|
|
|
|
|
|
* ``micropython.mk`` contains the Makefile fragment for this module.
|
|
|
|
|
|
|
|
``$(USERMOD_DIR)`` is available in ``micropython.mk`` as the path to your
|
|
|
|
module directory. As it's redefined for each c module, is should be expanded
|
|
|
|
in your ``micropython.mk`` to a local make variable,
|
|
|
|
eg ``EXAMPLE_MOD_DIR := $(USERMOD_DIR)``
|
|
|
|
|
2020-10-08 15:44:55 +01:00
|
|
|
Your ``micropython.mk`` must add your modules source files relative to your
|
2018-12-12 05:50:55 +00:00
|
|
|
expanded copy of ``$(USERMOD_DIR)`` to ``SRC_USERMOD``, eg
|
|
|
|
``SRC_USERMOD += $(EXAMPLE_MOD_DIR)/example.c``
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
If you have custom compiler options (like ``-I`` to add directories to search
|
|
|
|
for header files), these should be added to ``CFLAGS_USERMOD`` for C code
|
|
|
|
and to ``CXXFLAGS_USERMOD`` for C++ code.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2021-02-24 12:57:53 +00:00
|
|
|
* ``micropython.cmake`` contains the CMake configuration for this module.
|
|
|
|
|
|
|
|
In ``micropython.cmake``, you may use ``${CMAKE_CURRENT_LIST_DIR}`` as the path to
|
|
|
|
the current module.
|
|
|
|
|
|
|
|
Your ``micropython.cmake`` should define an ``INTERFACE`` library and associate
|
|
|
|
your source files, compile definitions and include directories with it.
|
|
|
|
The library should then be linked to the ``usermod`` target.
|
|
|
|
|
|
|
|
.. code-block:: cmake
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2018-12-12 05:50:55 +00:00
|
|
|
See below for full usage example.
|
|
|
|
|
|
|
|
|
2020-01-01 11:51:42 +00:00
|
|
|
Basic example
|
2018-12-12 05:50:55 +00:00
|
|
|
-------------
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
This simple module named ``cexample`` provides a single function
|
|
|
|
``cexample.add_ints(a, b)`` which adds the two integer args together and returns
|
2020-10-29 06:32:39 +00:00
|
|
|
the result. It can be found in the MicroPython source tree
|
|
|
|
`in the examples directory <https://github.com/micropython/micropython/tree/master/examples/usercmodule/cexample>`_
|
|
|
|
and has a source file and a Makefile fragment with content as descibed above::
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
micropython/
|
|
|
|
└──examples/
|
|
|
|
└──usercmodule/
|
|
|
|
└──cexample/
|
|
|
|
├── examplemodule.c
|
2021-02-24 12:57:53 +00:00
|
|
|
├── micropython.mk
|
|
|
|
└── micropython.cmake
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2021-02-24 12:57:53 +00:00
|
|
|
|
|
|
|
Refer to the comments in these files for additional explanation.
|
2020-10-21 10:13:47 +01:00
|
|
|
Next to the ``cexample`` module there's also ``cppexample`` which
|
|
|
|
works in the same way but shows one way of mixing C and C++ code
|
|
|
|
in MicroPython.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
Compiling the cmodule into MicroPython
|
|
|
|
--------------------------------------
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
To build such a module, compile MicroPython (see `getting started
|
|
|
|
<https://github.com/micropython/micropython/wiki/Getting-Started>`_),
|
|
|
|
applying 2 modifications:
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
- an extra ``make`` flag named ``USER_C_MODULES`` set to the directory
|
|
|
|
containing all modules you want included (not to the module itself).
|
|
|
|
For building the example modules which come with MicroPython,
|
|
|
|
set ``USER_C_MODULES`` to the ``examples/usercmodule`` directory.
|
|
|
|
For your own projects it's more convenient to keep custom code out of
|
|
|
|
the main source tree so a typical project directory structure will look
|
|
|
|
like this::
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
my_project/
|
|
|
|
├── modules/
|
|
|
|
│ └──example1/
|
|
|
|
│ ├──example1.c
|
2021-02-24 12:57:53 +00:00
|
|
|
│ ├──micropython.mk
|
|
|
|
│ └──micropython.cmake
|
2020-10-21 10:13:47 +01:00
|
|
|
│ └──example2/
|
|
|
|
│ ├──example2.c
|
2021-02-24 12:57:53 +00:00
|
|
|
│ ├──micropython.mk
|
|
|
|
│ └──micropython.cmake
|
|
|
|
│ └──micropython.cmake
|
2020-10-21 10:13:47 +01:00
|
|
|
└── micropython/
|
|
|
|
├──ports/
|
|
|
|
... ├──stm32/
|
|
|
|
...
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
with ``USER_C_MODULES`` set to the ``my_project/modules`` directory.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2021-02-24 12:57:53 +00:00
|
|
|
A top level ``micropython.cmake`` - found directly in the ``my_project/modules``
|
|
|
|
directory - should ``include`` all of your modules.
|
|
|
|
|
|
|
|
.. code-block:: cmake
|
|
|
|
|
|
|
|
include(${CMAKE_CURRENT_LIST_DIR}/example1/micropython.cmake)
|
|
|
|
include(${CMAKE_CURRENT_LIST_DIR}/example2/micropython.cmake)
|
|
|
|
|
|
|
|
|
|
|
|
- all modules found in this directory (or added via ``include`` in the top-level
|
|
|
|
``micropython.cmake`` when using CMake) will be compiled, but only those which are
|
|
|
|
explicitly enabled will be available for importing. Enabling a module is done
|
|
|
|
by setting the preprocessor define from its module registration to 1.
|
|
|
|
|
|
|
|
For example if the source code defines the module with
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
.. code-block:: c
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, MODULE_CEXAMPLE_ENABLED);
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
then ``MODULE_CEXAMPLE_ENABLED`` has to be set to 1 to make the module available.
|
|
|
|
This can be done by adding ``CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1`` to
|
|
|
|
the ``make`` command, or editing ``mpconfigport.h`` or ``mpconfigboard.h``
|
|
|
|
to add
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
.. code-block:: c
|
2019-04-13 12:57:22 +01:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
#define MODULE_CEXAMPLE_ENABLED (1)
|
2019-04-13 12:57:22 +01:00
|
|
|
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
Note that the exact method depends on the port as they have different
|
|
|
|
structures. If not done correctly it will compile but importing will
|
|
|
|
fail to find the module.
|
2019-04-15 06:01:15 +01:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
To sum up, here's how the ``cexample`` module from the ``examples/usercmodule``
|
|
|
|
directory can be built for the unix port:
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
.. code-block:: bash
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
cd micropython/ports/unix
|
|
|
|
make USER_C_MODULES=../../examples/usercmodule CFLAGS_EXTRA=-DMODULE_CEXAMPLE_ENABLED=1 all
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
The build output will show the modules found::
|
2018-12-12 05:50:55 +00:00
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
...
|
|
|
|
Including User C Module from ../../examples/usercmodule/cexample
|
|
|
|
Including User C Module from ../../examples/usercmodule/cppexample
|
|
|
|
...
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
|
2021-02-24 12:57:53 +00:00
|
|
|
For a CMake-based port such as rp2, this will look a little different:
|
|
|
|
|
|
|
|
.. code-block:: bash
|
|
|
|
|
|
|
|
cd micropython/ports/rp2
|
|
|
|
make USER_C_MODULES=../../examples/usercmodule all
|
|
|
|
|
|
|
|
|
|
|
|
The CMake build output lists the modules by name::
|
|
|
|
|
|
|
|
...
|
|
|
|
Including User C Module(s) from ../../examples/usercmodule/micropython.cmake
|
|
|
|
Found User C Module(s): usermod_cexample, usermod_cppexample
|
|
|
|
...
|
|
|
|
|
|
|
|
|
|
|
|
Note that the ``micropython.cmake`` files define ``DMODULE_<name>_ENABLED=1`` automatically.
|
|
|
|
The top-level ``micropython.cmake`` can be used to control which modules are enabled.
|
|
|
|
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
Or for your own project with a directory structure as shown above,
|
|
|
|
including both modules and building the stm32 port for example:
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
.. code-block:: bash
|
|
|
|
|
|
|
|
cd my_project/micropython/ports/stm32
|
2020-10-21 10:13:47 +01:00
|
|
|
make USER_C_MODULES=../../../modules \
|
|
|
|
CFLAGS_EXTRA="-DMODULE_EXAMPLE1_ENABLED=1 -DMODULE_EXAMPLE2_ENABLED=1" all
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
Module usage in MicroPython
|
|
|
|
---------------------------
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
Once built into your copy of MicroPython, the module
|
|
|
|
can now be accessed in Python just like any other builtin module, e.g.
|
2018-12-12 05:50:55 +00:00
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
2020-10-21 10:13:47 +01:00
|
|
|
import cexample
|
|
|
|
print(cexample.add_ints(1, 3))
|
2018-12-12 05:50:55 +00:00
|
|
|
# should display 4
|