How to add a MaixPy module in C
Preliminary knowledge
Everything is an object in python
You need to know what module, type, function, and class are, what are the relationships and differences
- module (module)
In MaixPy
, the functions of each category are put into a module,
For example, the built-in uos
, usys
, machine
,
In addition, our own new file, such as test.py
, can also be a module,
We use modules like this:
import uos
import machine
import test
In C source code, it is
mp_type_module
- type (type)
Used to represent a basic type, it can contain some methods or variables
In C source code, it is
mp_type_type
- class (class)
A class is actually a type
, for example
class A:pass
print(type(A))
Will output
<class'type'>
When A
is instantiated
class A:pass
a = A()
print(type(a))
Will output
<class'A'>
Indicates that a
is an instance (object) of A
Defining a class in C is actually defining a
mp_type_type
Add module in C
Our goal is to realize that the following code can be used at the MaixPy
level:
import my_lib
print(my_lib.__name__)
my_lib.hello()
Create a new folder in the components/port/src
directory, for example, name it my_lib
Then create a new my_lib.c
file under the my_lib
folder
Edit my_lib.c
to add code
Define a module:
#include "obj.h"
const mp_obj_module_t my_lib_module = {
.base = {&mp_type_module },
.globals = (mp_obj_dict_t*)&mp_module_my_lib_globals_dict,
};
Here my_lib_module
is the defined my_lib
module object,
mp_type_module
indicates that it is a module,
mp_module_my_lib_globals_dict
is the global variables and functions of the module. It is a dict
object, which has our own definitions. It has not been defined yet
Define module global variables
STATIC mp_obj_t hello()
{
mp_printf(&mp_plat_print, "hello from my_lib");
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(my_lib_func_hello_obj, my_lib_func_hello);
STATIC const mp_map_elem_t my_lib_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_my_lib) },
{MP_OBJ_NEW_QSTR(MP_QSTR_hello), (mp_obj_t)&my_lib_func_hello_obj },
};
STATIC MP_DEFINE_CONST_DICT (
mp_module_my_lib_globals_dict,
my_lib_globals_table
);
Here defines a set of key-value pair arrays, key-value pair values, mp_map_elem_t
is defined as follows:
typedef struct _mp_map_elem_t {
mp_obj_t key;
mp_obj_t value;
} mp_map_elem_t;
- The first value is
key
, and the type isstr
object, which is called bymy_lib.key
at the level ofMaixPy
. Here,MP_OBJ_NEW_QSTR(MP_QSTR___name__)
is used to generate astr
object with a value of__name__
. You may have questions about where the__name__
thisc
variable is defined. This is automatically generated by the tool during the compilation phaseVariables, in short, remember this can be written to generate a constant
str` object and save it in the firmware. - The second value is a number, and the type is an object, which can be
str/function/int/float/tuple/list/dict
, etc., in the following way:str
: Here is also the definition of astr
type valuemy_lib
, that is, usingmy_lib.__name__
at the level ofMaixPy
to get the resultmy_lib
.Other constant objects
: You can usemp_obj_new_xxx
, such asint
variablemp_obj_new_int(10)
, function search inobj.h
Function
: The corresponding value ofkey``hello
here is(mp_obj_t)&my_lib_func_hello_obj
, which is a function object. Note that it is not aC
function. As mentioned earlier, everything inpython
is an object. A function object is used, and then the address is cast tomp_obj_t
. This function object uses the macro definition ofMP_DEFINE_CONST_FUN_OBJ_0
to define theC
function ofmy_lib_func_hello
as the object ofmy_lib_func_hello_obj
. Note that thehello
function needs to return a valuemp_const_none
, and note that it cannot returnNULL
becauseNULL
Not a (
MaixPy) object, this return value is the return value when the
hello()function is called at the
MaixPy` level
In addition to
MP_DEFINE_CONST_FUN_OBJ_0
, which has no parameters, there are also1/2/3/n
parameters, as well as keywords with keywords. Please refer to the source code to learn by analogy
Then use the MP_DEFINE_CONST_DICT
macro definition to turn the key-value pair of my_lib_globals_table
into a dict
object that can be understood at the MaixPy
level (mp_map_elem_t
is only understandable at the C
level) mp_module_my_lib_globals_dict
, this object is also the previous step Used when defining modules in
At this point, the definition of a module is complete. At the level of MaixPy
, in theory, the following statement can be used to use
import my_lib
print(my_lib.__name__)
my_lib.hello()
But we haven't compiled
Add the module to the firmware and compile it
- Add at the end of the
my_lib.c
file:
MP_REGISTER_MODULE(MP_QSTR_my_lib, my_lib_module, MODULE_MY_LIB_ENABLED);
This line of code registers the module, but whether it is compiled into the firmware depends on whether the macro definition of MODULE_MY_LIB_ENABLED
is defined as 1
in mpconfigport.h
- So we open the
mpconfigport.h
file and add
#define MODULE_MY_LIB_ENABLED (1)
- Open
components/micropython/CMakeLists.txt
to edit
Find the place where there is ############## Add source files ###############
Add after
append_srcs_dir(MPY_PORT_SRCS "port/src/my_lib")
At this point, the project will compile the folder my_lib
to the firmware
Then python project.py rebuild
can compile the firmware, because there are new files, you must use the rebuild
command instead of build
, pay attention to the compilation prompt, if there is an error, pay attention to modify
Add a type to the module
A my_lib
module was previously defined, now we want to define a class in my_lib
, called A
, as follows
import my_lib
a = my_lib.A()
print(a.add(1, 2))
I only talk about the general idea here, and then provide a sample, you can understand it if you are smart
- Define a
mp_obj_type_t
object, just like the previous definition ofmp_obj_module_t
- Similarly, give this class object a
dict
object, as a member of this class, the member can be a constant or function or even anothertype
object - Register this class object to the previous
my_lib
module
The definition of mp_obj_type_t
object and member definition can refer to the implementation in port/src/standard_lib/machine/machine_i2c.c
When defining
mp_obj_type_t
, there is amake_new
member, this function is used to create a new object function will be called, such asa = my_lib.A(); a.add(1,2)
If you don't create a new object and call the class method or variable directly, this function will not be calledA.var_a
For example, we define a const mp_obj_type_t my_lib_A_type ...
Then add this object to my_lib_globals_table
in my_lib/my_lib.c
and map it to key
A
{MP_ROM_QSTR(MP_QSTR_A), MP_ROM_PTR(&my_lib_A_type) },
Note when writing firmware in C language
mp_printf
vsprintk
vsprintf
:
Because IDE
uses the serial communication protocol, do not directly use the printk
or printf
function to print messages at the C
level, must use the mp_printf
function to print, otherwise it will cause the IDE
to run When receiving unintelligible data and disconnecting! !
Of course, you can use printk
for debugging, because this function will not trigger a system interrupt and can be called in the interrupt function, but it is only used for debugging and must be deleted when the code is actually submitted! !