简介
笔者最近初学K210上的视觉处理模块,但发现K210 C SDK的资料比较少。后来发现了开源项目MaxiPy,它是个学习K210 C SDK极佳例子,遂直接读其源码,不仅收获到底层SDK使用方法,还了解到用C写Python模块的方法。
在硬件上跑一行Python语句,底层其实都是调用C代码。那么C底层和Python之间到底怎样对应起来呢?此文告诉你如何基于MaxiPy这个项目,用C语言编写Python底层,按本文步骤操作下来,开发Python底层模块就像填空题一样简单!
学习目标
- 创建一个叫做
my_lib
的Python模块 - 在
my_lib
中创建一个C函数,使得在Python程序中调用hello
时,能打印字符串hello world
- 创建一个类
A
,实现一个类方法A.add
进行两数相加
加入你自己的函数
我们希望实现以下功能:
1 | >>> from my_lib import hello |
具体步骤如下
假设编译环境已经配置好,用VSCode从打开Maxipy工程根目录,或者终端进入到Maxipy工程根目录,直接vim编辑操作
创建
my_lib
模块所在的文件夹1
mkdir -p components/micropython/port/src/my_lib
因为
my_lib
和hello
是我们需要在Python中创建的新的模块名和函数名, 在components/micropython/core/py/makeqstrdata.py
中的static_qstr_list
中,加入这两个名字:1
2
3
4
5static_qstr_list = [
"hello", # Python底层函数名
"my_lib", # Python低层模块名
...
]在这个脚本加入这两个名字后,在最后一步的
python project.py rebuild
会在编译时自动调用这个脚本,创建一个头文件qstrdefs.generated.h
,C源代码需要用到这个文件的定义。也就是说,makeqstrdata.py
这个脚本文件帮我们节省了C的头文件定义工作。创建C源代码
components/micropython/port/src/my_lib/my_lib.c
,这里大部分比较固定,完整源代码+注释如下,从第一步看到第六步即可。其中大部分都比较固定,我们需要新增一个函数,更改1.1,1.2和3.1即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
///////////////////////////////////////////////////////////////////////
// I 模块内的函数定义
///////////////////////////////////////////////////////////////////////
// 1.1 定义业务函数
// 注1:my_lib_func_hello函数需要返回一个值mp_const_none,
// 注2:不能返回NULL,因为NULL不是一个(MaixPy)对象,这个返回值
// 也就是MaixPy层面调用hello()函数时的返回值
STATIC mp_obj_t my_lib_func_hello()
{
mp_printf(&mp_plat_print, "hello from my_lib\\n");
return mp_const_none;
}
// 1.2 将刚才写的函数转换为MaixPy对象
// 注1:MP_DEFINE_CONST_FUN_OBJ_0宏定义将my_lib_func_hello这个C函数定义为
// my_lib_func_hello_obj这个对象
// 注2:除了MP_DEFINE_CONST_FUN_OBJ_0即没有参数之外,还有1/2/3/n个参数,
// 以及带关键字参数,这些请翻阅源码举一反三学习
MP_DEFINE_CONST_FUN_OBJ_0(my_lib_func_hello_obj, my_lib_func_hello);
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// II 模块内的类定义
///////////////////////////////////////////////////////////////////////
// 定义对象 A
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// III my_lib模块注册
///////////////////////////////////////////////////////////////////////
// 3.1 定义该模块包含的类和函数,将上面自己定义的类、函数都添加到下表
// C语言层面定义一个结构体的数组,将名字(key)和对象(value)关系起来
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_ROM_QSTR(MP_QSTR_A), MP_ROM_PTR(&my_lib_A) },
{MP_OBJ_NEW_QSTR(MP_QSTR_hello), (mp_obj_t)&my_lib_func_hello_obj},
};
// 3.2 C dict转MaixPy dict 【固定不动】
// 解释:使用MP_DEFINE_CONST_DICT宏定义将my_lib_globals_table这个键值对
// 变成MaixPy层面能理解的dict对象(mp_map_elem_t只是C层面能理解)
// my_lib_globals_dict,这个对象也被上一步中定义模块的时候使用
STATIC MP_DEFINE_CONST_DICT( my_lib_globals_dict, my_lib_globals_table);
// 3.3 【固定不动】
const mp_obj_module_t my_lib_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&my_lib_globals_dict,
};
// 3.4 【固定不动】
// 注册当前模块,是否编译到固件取决两点:mpconfigport.h和CMakeLists.txt
MP_REGISTER_MODULE(MP_QSTR_my_lib, my_lib_module, MODULE_MY_LIB_ENABLED);如上面代码的3.4,代码是写好了,要想把这个功能编译并加入固件,还需要编辑两个文件
5.1 在components/micropython/port/include/mpconfigport.h
中添加
1 |
5.2 在components/micropython/CMakeLists.txt
中添加
1 | append_srcs_dir(MPY_PORT_SRCS "port/src/my_lib") |
编译、下载
1
2
3cd projects/maixpy_k210_minimum # 选用的是最小版
python project.py rebuild # 编译
kflash -b 1500000 -p /dev/cu.usbserial-14310 build/maixpy.bin # 烧录测试
使用串口通信,MacOS上方法为
1
screen /dev/cu.usbserial-14310 115200
接着出现如下现象:
1
2
3
4...
>>> from my_lib import hello
>>> hello()
hello world from my_lib当运行
hello()
出现hello world from my_lib
说明新加入一个模块成功了
学习完上面的7步,你已经可以完整的写一个底层函数,编译并烧录固件到硬件上了。
加入自定的类
假设需要在my_lib
里实现类A
,并实现类方法A.add
1 | import my_lib |
具体方法:只需要操作上面的3和4步即可
第3步变为: 在components/micropython/core/py/makeqstrdata.py
中的static_qstr_list
中,加入A
和 add
两个名字:
1 | static_qstr_list = [ |
第4步变为:类代码,我们就放在第二部分
1 | /////////////////////////////////////////////////////////////////////// |
注意:
- 类方法add的两个参数计算时需要使用
mp_obj_get_int
将参数从Python对象转为C类型,如果不使用这个函数直接计算,抑或是参数转移,会导致错误结果
参考资料
https://wiki.sipeed.com/soft/maixpy/zh/course/advance/add_c_module.html