目录
创建LVGL模块
创建lv_mod文件夹,在里面创建lv_mod.h和lv_mod.c文件,在lv_mod.h文件中引入头文件:
#pragma once
#include "py/builtin.h"
#include "py/runtime.h"
#include "py/obj.h"
在lv_mod.c文件中引入lv_mod.h文件。
然后开始创建模块,代码如下:
// 1. 创建模块字典 MP_QSTR_ __name__
STATIC const mp_rom_map_elem_t lvgl_globals_table[] = { //lvgl_globals_table[]元素数组名称可以自己定义。
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_lvgl) }, //设置模块的名称lvgl
};
// 2. 将元素数组转换成字典对象
STATIC MP_DEFINE_CONST_DICT(lvgl_globals, lvgl_globals_table); // 将元素数组lvgl_globals_table 转换成字典对象lvgl_globals
// 3. 定义模块
const mp_obj_module_t mp_mod_lvgl = {
.base = { &mp_type_module }, // 类型指向(模块的类型恒定为 &mp_type_module)
.globals = (mp_obj_dict_t *)&lvgl_globals, // 指向字典
};
// 4. 注册模块到系统
MP_REGISTER_MODULE(MP_QSTR_lvgl, mp_mod_lvgl);
创建完成后,我们需要在main文件夹中的CMakeLists.txt文件下,将源文件和头文件加入到编译队列中,SRCS是存放源文件的地方,INCLUDE_DIRS存放头文件。
此外,还需要在CMakeLists.txt文件中,定义模块文件夹
这样,我们就创建好了一个模块。
编译,烧入后,我们可以在thonny中输入help("modules")命令,查看我们的模块
lvgl就是我们创建的模块。
在模块中添加方法
首先要先进行定义,代码如下:
// 1. 定义 C方法
STATIC mp_obj_t lv_print(){ //在lvgl模块中,定义print方法,"print()"中"()"里面表示定义的参数,这里是空的表示为定义0个参数
printf("Test function \n");
return mp_const_none; //表示 MicroPython 中不返回值
}
然后将定义的C方法转换成Micro Python的方法对象,代码如下:
// 2. 转换成 Mieropython 方法对象
MP_DEFINE_CONST_FUN_OBJ_0(lv_print_obj,lv_print); // 0个参数
之后需要在创建模块字典下对创建的方法进行定义。
注意,方法名称要和上面的一致。
完成后即可烧入到thonny中,利用micropython环境进行调用。
在模块中创建添加类型
定义类型的字典,代码如下:
// 1. 定义类型的字典
STATIC const mp_rom_map_elem_t lv_obj_locals_dict_table[] = {};
把定义的类型的字典转换成对象:
// 2. 把字典转换成对象
STATIC MP_DEFINE_CONST_DICT(lv_obj_locals_dict, lv_obj_locals_dict_table);
定义类型结构:
// 3. 定义类结构
STATIC const mp_obj_type_t lv_type_obj = {
{ &mp_type_type }, // 类类型定义
.name = MP_QSTR_obj, // 类名
.locals_dict = (mp_obj_dict_t *)&lv_obj_locals_dict, // 类成员字典
.make_new = lv_obj_make_new, // 构造函数
.parent = NULL,
};
这里构造函数lv_obj_make_new需要我们自己创建,下面是定义构造函数的方法:
// 4. 定义构造函数(类型,按位置传递参数数量,按字典传递参数熟练,参数列表)
STATIC mp_obj_t lv_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args){
// 可以传入一个参数,i也可以不传入参数
// 传入得参数就是父级标签
printf("调用了构造方法,传入%d个参数\n", n_args);
// 检查参数(按位置参数的数量,按字典参数的数量,最参数要求,最大参数要求,是否允许按字典传值)
mp_arg_check_num(n_args, n_kw, 0, 1, false);
// m_malloc(sizeof(lv_obj_t));
lv_obj_t *self = m_new_obj(lv_obj_t); // 开辟类空间(SELF)
self->base.type = type; // 制定数据类型(非常重要)
// self->base.type = &lv_type_obj;
if(n_args >0 && &lv_type_obj == mp_obj_get_type(args[0])){
printf("确定是父级对象\n");
self->parent = (lv_obj_t *)args[0];
}
return MP_OBJ_FROM_PTR(self); // 返回对象
}
定义数据类型:
// 5. 定义数据类型
struct _lv_obj_t{
mp_obj_base_t base; // 必须以这个开头
int16_t x;
int16_t y;
uint32_t width;
uint32_t height;
lv_obj_t *parent;
};
值得注意的是需要在模块字典中对类进行定义并且要加上静态常量的设置。
以上,一个类就在模块中添加好了。
在模块的类中添加方法
上面我们是从模块中直接添加方法,除此之外我们也可以在我们定义的类中添加方法,流程是一样的,下面是一个获取位置的方法。
我创建一个lvdiv.c文件,此文件作为一个类在lv_mod.c文件中被定义。我们要在lv_mod.h文件中进行相关的定义,代码如下:
// 给_lv_div_t 定义个新名字
typedef struct _lv_div_t lv_div_t;
// 定义DIV结构体
struct _lv_div_t{
mp_obj_base_t base; // 每个micropython 类必须以这个开头
int16_t x;
int16_t y;
uint32_t width;
uint32_t height;
lv_div_t *parent;
};
extern const mp_obj_type_t lv_type_div; // 全局声明
首先和上面添加方法的流程一样,对获取位置的方法进行定义,我这里取名为lv_div_position
STATIC mp_obj_t lv_div_position(size_t n_args, const mp_obj_t *args){} // 定义名称为lv_div_position,并且是可变参数类型(按位置传递的参数数量, 参数列表)
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_position_obj,1,3,lv_div_position); // "MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN" 宏将 "lv_div_position" 定义为一个可变参数函数。(转换后的对象,最少入参数量,最多入参数量,方法名)
在函数的实现中,我们首先从参数中获取指向 "lv_div_t" 结构体的指针 "self"。然后,我们检查传递给函数的参数数量。如果参数数量大于 1,说明我们需要设置位置信息。根据参数的数量和类型,我们可以设置 "lv_div_t" 结构体中的 "x" 和 "y" 值。
如果参数数量为 1,并且该参数为整数类型,则将其设置为 "x" 值。如果参数数量为 1,并且该参数为元组类型,则将元组的第一个元素设置为 "x" 值,第二个元素设置为 "y" 值。如果参数数量为 1,并且该参数为列表类型,则将列表的第一个元素设置为 "x" 值,第二个元素设置为 "y" 值。
如果参数数量大于 2,并且前两个参数都是整数类型,则将第一个参数设置为 "x" 值,第二个参数设置为 "y" 值。
如果参数数量为 0 或者参数数量大于 3,则函数会抛出 "ValueError" 异常。否则,函数将返回一个元组对象,它包含 "lv_div_t" 结构体中的 "x" 和 "y" 值。
代码如下:
STATIC mp_obj_t lv_div_size(size_t n_args, const mp_obj_t *args){
lv_div_t *self = (lv_div_t *)args[0];
if(n_args>1){
// 设置
// 1 个int
// 2 个 int
// 1 元组
if(2 == n_args ){ // 表示传入一个参数
if(&mp_type_int == mp_obj_get_type(args[1])){ // 只设置 x
self->width = mp_obj_get_int(args[1]);
}else if(&mp_type_tuple == mp_obj_get_type(args[1])){
mp_obj_tuple_t *t = args[1];
if(t->len==1){ // 只传入了一个值
self->width = mp_obj_get_int(t->items[0]);
}else if(t->len>1){
self->width = mp_obj_get_int(t->items[0]);
self->height = mp_obj_get_int(t->items[1]);
}
}
}else if(n_args>2){
if(&mp_type_int == mp_obj_get_type(args[1]) && &mp_type_int == mp_obj_get_type(args[2])){
self->width = mp_obj_get_int(args[1]);
self->height = mp_obj_get_int(args[2]);
}
}
return MP_OBJ_FROM_PTR(self);
}else{
mp_obj_t pos_table[2]; // 创造元组的数组
pos_table[0] = mp_obj_new_int(self->width); // 设置元组元素
pos_table[1] = mp_obj_new_int(self->height);
mp_obj_tuple_t *pos = mp_obj_new_tuple(2,pos_table); // 创造一个元组
return MP_OBJ_FROM_PTR(pos);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_size_obj,1,3,lv_div_size);
之后别忘记在lvdiv.c文件的字典下对方法进行定义。
此外,我还在lvdiv.c这个类文件中定义了对x,y赋值等方法,lvdiv.c文件中的具体代码如下:
#include "lv_mod.h"
// 1. 分别获得 x,y , width ,height
// 2. positon, size
// 3. box(x, y, w, h)
// STATIC mp_obj_t lv_div_px(mp_obj_t self_in, mp_obj_t value){
// MP_DEFINE_CONST_FUN_OBJ_2
STATIC mp_obj_t lv_div_px(size_t n_args, const mp_obj_t *args){
lv_div_t *self = (lv_div_t *)args[0];
if(n_args>1 && &mp_type_int == mp_obj_get_type(args[1])){
// 赋值
self->x = mp_obj_get_int(args[1]);
}
return mp_obj_new_int(self->x);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_px_obj,1,2,lv_div_px);
STATIC mp_obj_t lv_div_py(size_t n_args, const mp_obj_t *args){
lv_div_t *self = (lv_div_t *)args[0];
if(n_args>1 && &mp_type_int == mp_obj_get_type(args[1])){
// 赋值
self->y = mp_obj_get_int(args[1]);
}
return mp_obj_new_int(self->y);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_py_obj,1,2,lv_div_py);
/**
* @brief 设置位置
* 传入两个或者一个参数表设置,不传参表示获取
* (x, y)
* (x)
* ((x,y))
* ([x,y])
* () =>(x,y)
*/
STATIC mp_obj_t lv_div_position(size_t n_args, const mp_obj_t *args){
lv_div_t *self = (lv_div_t *)args[0];
if(n_args>1){
// 设置
// 1 个int
// 2 个 int
// 1 元组
if(2 == n_args ){ // 表示传入一个参数
if(&mp_type_int == mp_obj_get_type(args[1])){ // 只设置 x
self->x = mp_obj_get_int(args[1]);
}else if(&mp_type_tuple == mp_obj_get_type(args[1])){
mp_obj_tuple_t *t = args[1];
if(t->len==1){ // 只传入了一个值
self->x = mp_obj_get_int(t->items[0]);
}else if(t->len>1){
self->x = mp_obj_get_int(t->items[0]);
self->y = mp_obj_get_int(t->items[1]);
}
}else if(&mp_type_list == mp_obj_get_type(args[1])){ // 列表
mp_obj_list_t *t = args[1];
if(t->len==1){ // 只传入了一个值
self->x = mp_obj_get_int(t->items[0]);
}else if(t->len>1){
self->x = mp_obj_get_int(t->items[0]);
self->y = mp_obj_get_int(t->items[1]);
}
}
}else if(n_args>2){
if(&mp_type_int == mp_obj_get_type(args[1]) && &mp_type_int == mp_obj_get_type(args[2])){
self->x = mp_obj_get_int(args[1]);
self->y = mp_obj_get_int(args[2]);
}
}
return MP_OBJ_FROM_PTR(self);
}else{
mp_obj_t pos_table[2]; // 创造元组的数组
pos_table[0] = mp_obj_new_int(self->x); // 设置元组元素
pos_table[1] = mp_obj_new_int(self->y);
mp_obj_tuple_t *pos = mp_obj_new_tuple(2,pos_table); // 创造一个元组
return MP_OBJ_FROM_PTR(pos);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_position_obj,1,3,lv_div_position);
STATIC mp_obj_t lv_div_size(size_t n_args, const mp_obj_t *args){
lv_div_t *self = (lv_div_t *)args[0];
if(n_args>1){
// 设置
// 1 个int
// 2 个 int
// 1 元组
if(2 == n_args ){ // 表示传入一个参数
if(&mp_type_int == mp_obj_get_type(args[1])){ // 只设置 x
self->width = mp_obj_get_int(args[1]);
}else if(&mp_type_tuple == mp_obj_get_type(args[1])){
mp_obj_tuple_t *t = args[1];
if(t->len==1){ // 只传入了一个值
self->width = mp_obj_get_int(t->items[0]);
}else if(t->len>1){
self->width = mp_obj_get_int(t->items[0]);
self->height = mp_obj_get_int(t->items[1]);
}
}
}else if(n_args>2){
if(&mp_type_int == mp_obj_get_type(args[1]) && &mp_type_int == mp_obj_get_type(args[2])){
self->width = mp_obj_get_int(args[1]);
self->height = mp_obj_get_int(args[2]);
}
}
return MP_OBJ_FROM_PTR(self);
}else{
mp_obj_t pos_table[2]; // 创造元组的数组
pos_table[0] = mp_obj_new_int(self->width); // 设置元组元素
pos_table[1] = mp_obj_new_int(self->height);
mp_obj_tuple_t *pos = mp_obj_new_tuple(2,pos_table); // 创造一个元组
return MP_OBJ_FROM_PTR(pos);
}
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(lv_div_size_obj,1,3,lv_div_size);
STATIC const mp_rom_map_elem_t lv_div_locals_dict_table[] ={
{ MP_ROM_QSTR(MP_QSTR_DEFAULT_WIDTH), MP_ROM_INT(200)},
{ MP_ROM_QSTR(MP_QSTR_px), MP_ROM_PTR(&lv_div_px_obj)},
{ MP_ROM_QSTR(MP_QSTR_py), MP_ROM_PTR(&lv_div_py_obj)},
{ MP_ROM_QSTR(MP_QSTR_position), MP_ROM_PTR(&lv_div_position_obj)},
{ MP_ROM_QSTR(MP_QSTR_sizet), MP_ROM_PTR(&lv_div_size_obj)},
};
// 转换成字典对象
STATIC MP_DEFINE_CONST_DICT(lv_div_locals_dict, lv_div_locals_dict_table);
// 构造函数
STATIC mp_obj_t lv_div_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args){
// 检查参数数量是否正确
mp_arg_check_num(n_args, n_kw, 0, 1, false);
// 开始构造对象
lv_div_t *self = m_new_obj(lv_div_t); // *
self->base.type = &lv_type_div; // 必须设置返回值的类型 // *
// 父级对象
if(n_args>0 && mp_obj_get_type(args[0]) == &lv_type_div){
printf("确认是父级\n");
self->parent = (lv_div_t *)args[0];
}
return MP_OBJ_FROM_PTR(self); // *
}
// 定义 DIV 对象都类型
const mp_obj_type_t lv_type_div = {
{ &mp_type_type}, // 必须以这个开头
.name = MP_QSTR_div, // 类的名字(注意大小写)
.locals_dict = (mp_obj_dict_t *)&lv_div_locals_dict, // 添加类成员字典
.make_new = lv_div_make_new, // 构造函数
};