Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
MicroPython
micropython 中 Timer回调函数BUG——解决经验分享
发布于 2019-08-26 13:22:55 浏览:1960
订阅该版
论坛上很少见有经验分享的贴子,有分享应该是件很快乐的事:lol C 代码中使用定时器或者串口通信时经常会使用到回调函数,然后查找micropyhton中居然也有回调函数的使用。查找源码发现Timer模块中有回调函数的,于是编写测试程序,如果测试通过,那么使用python编程会带来极大的方便。 注:使用Timer模块时需要先开启STM32的相关定时器设备,可在Env中设置开启。 编写的测试源码如下: from pyb import LED #自己增加的LED模块 from machine import Timer led1 = LED(1) led2 = LED(2) led3 = LED(3) led1.on() led2.on() led3.on() def led1_toggle(Timer1): led1.toggle() def led2_toggle(Timer2): led2.toggle() def led3_toggle(Timer3): led3.toggle() tim11 = Timer(11) tim13 = Timer(13) tim14 = Timer(14) tim11.init(1,1000,led1_toggle) tim13.init(1,2000,led2_toggle) tim14.init(1,3000,led3_toggle) 预期实验现象:LED1,LED2,LED3分别以1秒,2秒,3秒的周期闪烁 实际现象:只有LED闪烁,但是闪烁的频率不是1秒,感觉是Timer11,Timer13,Timer14都按照指定周期调用回调函数,但是全部都调用 led1_toggle 函数。 实际现象与预期现象不相符合,所以就必须查找问题在哪??? 查找步骤: 1.调试STM32的源码,发现Timer11,Timer13,Timer14都能进入中断,说明定时器工作是正常。 2.找到回到函数的调用执行函数 位于 machine_timer.c 中的 timer_event_handler 函数,self->timeout_cb 实际上存储的是定时器器回调函数的入口地址。奇怪的是每次进入该函数后self->timeout_cb的值都没有变化,说明都是执行的是同一个回调函数。 3.后来查找原因发现是timer_event_handler 函数中的 machine_timer_obj_t *self = timer_self;这一句惹的祸。 timer_self是一个全局变量,在machine_timer_init 函数中被赋值,源码如下: if (timer_self && timer_self != self) { // TODO add multi-timer device support error_check(result == RT_EOK, "Only supports one timer device work"); } else { timer_self = self; } 这段代码就是BUG的根源所在,第一次执行将 定时器对象地址 (self)赋值给 timer_self,之后再进来 timer_self 的值都不会改变,所以Timer11.init,Timer13.init,Timer14.init 执行后都指向Timer11的对象地址,故都调用Timer11的回调函数。 BUG原因找到了就等于成功可80%,接下来就考虑修改BUG了。 修改思路如下:将 timer_self 定义为一个数组,存放不同Timer对象结构体地址。进入timer_event_handler后根据设备ID取出对象数据再执行。我觉得方向是应该没错的。 修改源码: 1.将static machine_timer_obj_t *timer_self = RT_NULL; 更改为 #define MAX_TIMER 17 //STM32 定时器数量 static machine_timer_obj_t *timer_self[MAX_TIMER] = {RT_NULL}; 2. 修改machine_timer_make_new 函数,在函数增加一句 self->timer_device->device_id = device_id-1; //利用设备ID作为索引,找到定时器对象首地址 3. 将machine_timer_deinit 函数中的 timer_self = RT_NULL; 改为 timer_self[self->timer_device->device_id] = RT_NULL; 4. 将 timer_event_handler 函数中的machine_timer_obj_t *self = timer_self; 改为 machine_timer_obj_t *self = timer_self[dev->device_id]; 5.将 machine_timer_init 函数 代码段 if (timer_self && timer_self != self) { // TODO add multi-timer device support error_check(result == RT_EOK, "Only supports one timer device work"); } else { timer_self = self; } 改为: if (timer_self[self->timer_device->device_id] && timer_self[self->timer_device->device_id] != self) { // TODO add multi-timer device support error_check(result == RT_EOK, "Only supports one timer device work"); } else { timer_self[self->timer_device->device_id] = self; } 猜想:我觉得这段代码,作者的本意应该是想同一个定时器设备只打开一次。。。 6.重新编写,下载固件后执行python测试代码 结果实际现象与预期现象一致,大功告成!!! micropython能够执行回调函数,的确是一大幸事。提高了模块代码的通用性。下一步目标将模块回调函数的写法搞明白,用于自建的模块中。 将修改后的 machine_timer.c 文件源码贴出来,红色为修改部分: /* * This file is part of the MicroPython project, [http://micropython.org/](http://micropython.org/) * * The MIT License (MIT) * * Copyright (c) 2019 ChenYong ([chenyong@rt-thread.com](mailto:)) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include
#include
#include
#include "py/obj.h" #include "py/runtime.h" #include "modmachine.h" #include "mphalport.h" #ifdef MICROPYTHON_USING_MACHINE_TIMER #include
#include
#include "machine_timer.h" #define MAX_TIMER 17 typedef struct _machine_timer_obj_t { mp_obj_base_t base; rt_device_t timer_device; mp_obj_t timeout_cb; uint8_t timerid; uint32_t timeout; rt_bool_t is_repeat; rt_bool_t is_init; } machine_timer_obj_t; const mp_obj_type_t machine_timer_type; STATIC void error_check(bool status, const char *msg) { if (!status) { nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, msg)); } } STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_timer_obj_t *self = self_in; mp_printf(print, "Timer(%p; ", self); mp_printf(print, "timerid=%d, ", self->timerid); mp_printf(print, "period=%d, ", self->timeout); mp_printf(print, "auto_reload=%d)", self->is_repeat); } STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); char timer_dev_name[RT_NAME_MAX] = {0}; int device_id = 0; // check arguments mp_arg_check_num(n_args, n_kw, 1, 1, true); device_id = mp_obj_get_int(args[0]); // find timer device rt_snprintf(timer_dev_name, sizeof(timer_dev_name), "timer%d", device_id); self->timer_device = rt_device_find(timer_dev_name); if (self->timer_device == RT_NULL || self->timer_device->type != RT_Device_Class_Timer) { nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "Timer(%s) don't exist", timer_dev_name)); } // initialize timer device self->base.type = &machine_timer_type; self->timerid = device_id; self->timeout = 0; self->timeout_cb = RT_NULL; self->is_repeat = RT_TRUE; self->is_init = RT_FALSE; self->timer_device->device_id = device_id-1; // return constant object return MP_OBJ_FROM_PTR(self); } static machine_timer_obj_t *timer_self[MAX_TIMER] = {RT_NULL}; STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { machine_timer_obj_t *self = self_in; rt_err_t result = RT_EOK; if (self->is_init == RT_TRUE) { result = rt_device_close(self->timer_device); error_check(result == RT_EOK, "Timer device close error"); self->is_init = RT_FALSE; timer_self[self->timer_device->device_id] = RT_NULL; } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); STATIC rt_err_t timer_event_handler(rt_device_t dev, rt_size_t size) { machine_timer_obj_t *self = timer_self[dev->device_id]; mp_sched_schedule(self->timeout_cb, MP_OBJ_FROM_PTR(self)); return RT_EOK; } STATIC mp_obj_t machine_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { machine_timer_obj_t *self = (machine_timer_obj_t *)args[0]; rt_bool_t result = RT_EOK; int mode = 0; enum { ARG_mode, ARG_period, ARG_callback, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_INT, {.u_int = 1} }, { MP_QSTR_period, MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_callback, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t dargs[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, dargs); if (2 == n_args) { self->timeout = dargs[0].u_int; } else if (3 == n_args) { self->is_repeat = dargs[ARG_mode].u_int; self->timeout = dargs[ARG_period].u_int; } else if (4 == n_args) { self->is_repeat = dargs[ARG_mode].u_int; self->timeout = dargs[ARG_period].u_int; self->timeout_cb = dargs[ARG_callback].u_obj; } else { mp_raise_ValueError("invalid format"); } error_check(self->timeout > 0, "Set timeout value error"); if (self->is_init == RT_FALSE) { // open timer device result = rt_device_open(self->timer_device, RT_DEVICE_OFLAG_RDWR); error_check(result == RT_EOK, "Timer device open error"); } if (self->timeout_cb != RT_NULL) { // set callback timer if (timer_self[self->timer_device->device_id] && timer_self[self->timer_device->device_id] != self) { // TODO add multi-timer device support error_check(result == RT_EOK, "Only supports one timer device work"); } else { timer_self[self->timer_device->device_id] = self; } result = rt_device_set_rx_indicate(self->timer_device, timer_event_handler); error_check(result == RT_EOK, "Timer set timout callback error"); } // set timer mode mode = self->is_repeat ? HWTIMER_MODE_PERIOD : HWTIMER_MODE_ONESHOT; result = rt_device_control(self->timer_device, HWTIMER_CTRL_MODE_SET, &mode); error_check(result == RT_EOK, "Timer set mode error"); if (self->timeout) { rt_hwtimerval_t timeout_s; rt_size_t len; timeout_s.sec = self->timeout / 1000; // second timeout_s.usec = self->timeout % 1000; // microsecond len = rt_device_write(self->timer_device, 0, &timeout_s, sizeof(timeout_s)); error_check(len == sizeof(timeout_s), "Timer set timout error"); } self->is_init = RT_TRUE; //return mp_const_none; return MP_OBJ_FROM_PTR(self); } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); STATIC const mp_rom_map_elem_t machine_timer_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&machine_timer_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_timer_init_obj) }, { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(RT_FALSE) }, { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(RT_TRUE) }, }; STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); const mp_obj_type_t machine_timer_type = { { &mp_type_type }, .name = MP_QSTR_Timer, .print = machine_timer_print, .make_new = machine_timer_make_new, .locals_dict = (mp_obj_t) &machine_timer_locals_dict, }; #endif // MICROPYTHON_USING_MACHINE_TIMER
查看更多
11
个回答
默认排序
按发布时间排序
我夏了夏天
认证专家
2019-08-26
Life isn't about finding yourself, life is about creating yourself.
楼主有心了,赞!! 楼主可以给 micropython 提交 PR 哦,一起来完善 micropython 软件包 :handshake
我夏了夏天
认证专家
2019-08-26
Life isn't about finding yourself, life is about creating yourself.
提个 PR,让该驱动的作者和其他开发者也能来测试测试你的修改呗
雁山情缘
2019-08-26
这家伙很懒,什么也没写!
>提个 PR,让该驱动的作者和其他开发者也能来测试测试你的修改呗 --- 惭愧,惭愧,没提交过,不知道怎么搞呢,有空研究看看相关规范怎么搞。
tyustli
2019-08-26
这家伙很懒,什么也没写!
>惭愧,惭愧,没提交过,不知道怎么搞呢,有空研究看看相关规范怎么搞。 ... --- 向 RT-Thread 贡献代码: [https://www.rt-thread.org/document/site/development-guide/github/github/](https://www.rt-thread.org/document/site/development-guide/github/github/)
雁山情缘
2019-08-26
这家伙很懒,什么也没写!
>向 RT-Thread 贡献代码: >https://www.rt-thread.org/document/site/development-guide/github/github/ ... --- 谢谢
我夏了夏天
认证专家
2019-08-26
Life isn't about finding yourself, life is about creating yourself.
>惭愧,惭愧,没提交过,不知道怎么搞呢,有空研究看看相关规范怎么搞。 ... --- 既然都研究这么深了,不提交一把多可惜
雁山情缘
2019-08-27
这家伙很懒,什么也没写!
>既然都研究这么深了,不提交一把多可惜 --- 已经提交:lol
mengde0532
2019-08-27
这家伙很懒,什么也没写!
看作者代码的意思,好像原来就是想只支持一个定时器回调
雁山情缘
2019-08-27
这家伙很懒,什么也没写!
>看作者代码的意思,好像原来就是想只支持一个定时器回调 --- 是的,他的注释也是这样写的,"Only supports one timer device work" 我觉得仅支持一个,不够用啊。:lol 我还增加一个callback的方法,可以在程序中随时更改回调函数和注销回调函数。已经提交了,希望作者能采纳吧:)
我夏了夏天
认证专家
2019-08-27
Life isn't about finding yourself, life is about creating yourself.
>已经提交 --- 赞赞赞 ;P
撰写答案
登录
注册新账号
关注者
0
被浏览
2k
关于作者
雁山情缘
这家伙很懒,什么也没写!
提问
10
回答
46
被采纳
0
关注TA
发私信
相关问题
1
请问rt-thread有没有移植micropython呢
2
micropython import 文件名的方式执行脚本问题
3
第一篇:Micropython 的起源和发展
4
第二篇:RT-Thread Micropython 简介
5
第三篇:RT-Thread Micropython 快速入门
6
第四篇:Micropython DIY 项目汇总
7
第五篇:Micropython 教程和资源
8
第六篇: RT-Thread MicroPython 学习经验和学习路线
9
RT-Thread MicroPython 最新开发板固件汇总【已失效】
10
有Mpy专门的板块啦~
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
【24嵌入式设计大赛】基于RT-Thread星火一号的智慧家居系统
2
RT-Thread EtherKit开源以太网硬件正式发布
3
如何在master上的BSP中添加配置yml文件
4
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
5
RT-Thread 发布 EtherKit开源以太网硬件!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
16
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
6
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部