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 isstrobject, which is called bymy_lib.keyat the level ofMaixPy. Here,MP_OBJ_NEW_QSTR(MP_QSTR___name__)is used to generate astrobject with a value of__name__. You may have questions about where the__name__thiscvariable is defined. This is automatically generated by the tool during the compilation phaseVariables, in short, remember this can be written to generate a constantstr` 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 astrtype valuemy_lib, that is, usingmy_lib.__name__at the level ofMaixPyto get the resultmy_lib.Other constant objects: You can usemp_obj_new_xxx, such asintvariablemp_obj_new_int(10), function search inobj.hFunction: The corresponding value ofkey``hellohere is(mp_obj_t)&my_lib_func_hello_obj, which is a function object. Note that it is not aCfunction. As mentioned earlier, everything inpythonis 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_0to define theCfunction ofmy_lib_func_helloas the object ofmy_lib_func_hello_obj. Note that thehellofunction needs to return a valuemp_const_none, and note that it cannot returnNULLbecauseNULLNot a (MaixPy) object, this return value is the return value when thehello()function is called at theMaixPy` level
In addition to
MP_DEFINE_CONST_FUN_OBJ_0, which has no parameters, there are also1/2/3/nparameters, 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.cfile:
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.hfile and add
#define MODULE_MY_LIB_ENABLED (1)
- Open
components/micropython/CMakeLists.txtto 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_tobject, just like the previous definition ofmp_obj_module_t - Similarly, give this class object a
dictobject, as a member of this class, the member can be a constant or function or even anothertypeobject - Register this class object to the previous
my_libmodule
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_newmember, 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_printfvsprintkvsprintf:
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! !