在mm32f3270上为MicroPython启用Timer模块

2024-02-28 13:32

本文主要是介绍在mm32f3270上为MicroPython启用Timer模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在mm32f3270上为MicroPython启用Timer模块

文章目录

  • 在mm32f3270上为MicroPython启用Timer模块
    • Requirements
    • Implementation
    • Application

昨天阅读micropython的材料时,特别注意了这个名字的写法,官方正式使用的是“MicroPython",确实有两个字母是大写的。我在这个项目的开发过程中一直秉承继承原汁原味的原生风格,自然也要使用“正确”的名字。后面的笔记中,均使用“MicroPython”这个名字。

Requirements

MicroPython的开发者官网文档中描述了对Timer模块的规范定义:https://docs.micropython.org/en/latest/library/machine.Timer.html

Timer的实例化传参直接用的id号,并且支持一个“-1”的id号表示创建虚拟定时器。“-1”的id号是可选项。我打算仅实现硬件对应的id。软件定时器意思不大。

Timer暴露给用户的全部是通过callback实现的,我看到esp的实现里有value函数,但标准实现中没有,我认为这个意思不大,也就不实现了。

Timer就是基本定时器,周期计数而已,用户在初始化实例的时候,指定唤醒频率即可直接启动,连start函数都没有。但有两种唤醒模式,分别对应两个累成员变量:Timer.ONE_SHOT和Timer_PERIODIC。

Timer相对于别的模块,一个实现难点是,增加的 callback 的机制。但 callback 函数是在 python 代码中传入的,怎么能在 C 语言层面上硬件中断服务程序中被调用呢。通过阅读stm32、mimxrt 和 esp 的移植,发现了一个关键的函数 mp_sched_schedule()函数,用于在定时器中断到来之时,实现在C语言层面上将python的程序注入到python内核的主线程参与调度的做法。目前只找到了一篇文章对 mp_sched_schedule() 函数的机制简要说明,见《MicroPython中Python与C代码是如何交互的》

mimxrt 的 Timer 基于 PIT 实现,固定使用一个 PIT,但是使用了其中的多通道。在 MM32 的实现上,具有的多通道的定时器(tim_16b、tim_32b、tim_adv)都用来作为PWM输出和编码器输入了,我最终选择使用仅具有计数功能的 tim_basic,但需要使用两个Instance。这里少不了还要实现一个 find_timer() 的函数以及一组预先分配好的conf结构体。

Implementation

使用 ports/mimxrt/machine_timer.c作为模板,完成mm32的Timer模块的实现。

STATIC mp_obj_t machine_timer_obj_init_helper () 函数中的实例化参数清单

    static const mp_arg_t allowed_args[] ={{ MP_QSTR_mode,         MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} },{ MP_QSTR_callback,     MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },{ MP_QSTR_period,       MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} },{ MP_QSTR_tick_hz,      MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },{ MP_QSTR_freq,         MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },};

Timer 对象的类型定义,要存放好硬件相关信息,同时能保存这些动态配置信息

typedef struct
{void   * callback;uint32_t period;uint32_t tick_hz;uint32_t freq;uint32_t mode;
} machine_timer_conf_t;typedef struct
{mp_obj_base_t    base;      // object base class.TIM_BASIC_Type * timer_port;IRQn_Type        timer_irqn;uint32_t         timer_id;machine_timer_conf_t *conf;
} machine_timer_obj_t;

这里的 callback 搞了个无差别指针,后面用强制类型转换骗过编译器吧。后来通过实际调试发现问题,改用“mp_obj_t callback”。

把machine_timer_obj_t的实例化放在machine_timer.c中吧,因为没有涉及引脚相关的定义,就不放在 machine_pin_board_pins.c 中了。

machine_timer_conf_t timer_conf[MACHINE_TIMER_NUM];const machine_timer_obj_t timer0 = {.base = {&machine_timer_type}, .timer_port = TIM6, .timr_irqn = TIM6_IRQn, .timer_id = 0u, .conf = &timer_conf[0]};
const machine_timer_obj_t timer1 = {.base = {&machine_timer_type}, .timer_port = TIM7, .timr_irqn = TIM7_IRQn, .timer_id = 1u, .conf = &timer_conf[1]};const machine_timer_obj_t * machine_timer_objs[] =
{&timer0 ,&timer1 ,
};

再实现 timer_find()函数:

const machine_timer_obj_t *timer_find(mp_obj_t user_obj)
{/* 如果传入参数本身就是一个Timer的实例,则直接送出这个Timer。 */if ( mp_obj_is_type(user_obj, &machine_timer_type) ){return user_obj;}/* 如果传入参数是一个Timer ID号,则通过索引在Timer清单中找到这个通道,然后送出这个通道的实例化对象。 */if ( mp_obj_is_small_int(user_obj) ){uint8_t timer_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);if ( timer_idx < MACHINE_TIMER_NUM ){return machine_timer_objs[timer_idx];}}mp_raise_ValueError(MP_ERROR_TEXT("Timer doesn't exist"));
}

突然发现,对于硬件模块,在 make_new 函数中安排开模块时钟挺好的,对应配引脚复用功能也不错。通常make_new函数中会调用init_helper函数,init_helper通常还会被init直接调用,暴露给用户。但实际上,init函数不仅仅是作为首次配置,更多是修改模块硬件属性,在运行时配置。因此,像开时钟、配引脚这种一次性的操作,放到make_new里更合适。

对于Timer,不涉及到引脚。涉及到引脚也不能在这里写死。目前的类对象结构体中,都已经包含了引脚复用功能的选项,而且要考虑动态复用的可能性,所以不会再pin_init.c或者make_new中一次性写死,而是放在init_helper中,每次在使用init配置类功能,都重新配置一遍引脚。至于访问时钟,我直接在clock_init.c中全部开启,也浪费不了多少电。

mp_obj_t machine_timer_obj_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, 1, MP_OBJ_FUN_ARGS_MAX, true);const machine_timer_obj_t *timer = timer_find(args[0]);if ( (n_args >= 1) || (n_kw >= 0) ){mp_map_t kw_args;mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来,单独封装成kw_args。 */machine_timer_obj_init_helper(timer, n_args - 1, args + 1, &kw_args);}return (mp_obj_t)timer;
}

其余函数都是常规实现。

特别需要注意中断服务程序的设计,这也是Timer模块实现的一个难点。

static void machine_timer_irq_handler(uint32_t timer_id)
{const machine_timer_obj_t * self = machine_timer_objs[timer_id];uint32_t flags = TIM_BASIC_GetInterruptStatus(self->timer_port);if (0u != (flags & TIM_BASIC_STATUS_UPDATE_PERIOD) ){if (self->conf->callback){mp_sched_schedule(self->conf->callback, MP_OBJ_FROM_PTR(self));}}TIM_BASIC_ClearInterruptStatus(self->timer_port, flags);if (self->conf->mode == TIMER_MODE_ONE_SHOT){TIM_BASIC_Stop(self->timer_port);}
}
void TIM6_IRQHandler(void) { machine_timer_irq_handler(0); }
void TIM7_IRQHandler(void) { machine_timer_irq_handler(1); }

要在于调用 mp_sched_schedule() 函数。若要使用 mp_sched_schedule() 函数,需要在mpconfigport.h文件中启用 MICROPY_ENABLE_SCHEDULER。

#define MICROPY_ENABLE_SCHEDULER            (1) /* enable scheduler components, using in irq. */

关于 mp_sched_schedule() 函数,其定义在 py/scheduler.c 文件中:

bool MICROPY_WRAP_MP_SCHED_SCHEDULE(mp_sched_schedule)(mp_obj_t function, mp_obj_t arg) {mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION();bool ret;if (!mp_sched_full()) {if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) {MP_STATE_VM(sched_state) = MP_SCHED_PENDING;}uint8_t iput = IDX_MASK(MP_STATE_VM(sched_idx) + MP_STATE_VM(sched_len)++);MP_STATE_VM(sched_queue)[iput].func = function;MP_STATE_VM(sched_queue)[iput].arg = arg;MICROPY_SCHED_HOOK_SCHEDULED;ret = true;} else {// schedule queue is fullret = false;}MICROPY_END_ATOMIC_SECTION(atomic_state);return ret;
}

从源代码中可以看出,这个函数要求在第一个参数传入回调的函数,第二个参数传入回调函数的参数。在 machine_timer_irq_handler() 函数中,将make_new中存入的callback和timer对象本身作为参数传入python的调度器。这个要特别注意,在python中,也要定义格式相同的函数作为回调函数传入。在callback内部,可以通过传入的参数访问到timer对象实例的所有资源。

Application

终于到验证环节了,我经历了一下午各种猜各种试各种调,终于搞出来一个能那得出手的测试用例了。

from machine import Pin
from machine import Timer
import utimeled0 = Pin('PA1', mode=Pin.OUT_PUSHPULL)
led1 = Pin('PA2', mode=Pin.OUT_PUSHPULL)led0_flag = False
led1_flag = Falsedef t0_callback(self):global led0_flagif led0_flag:led0.low()else:led0.high()led0_flag = not led0_flagt0 = Timer(0, mode=Timer.PERIODIC, callback=t0_callback, period=500)while True:utime.sleep_ms(200)if led1_flag:led1.low()else:led1.high()led1_flag = not led1_flag

两个小灯以不同的频率交替闪烁,成了。

这篇关于在mm32f3270上为MicroPython启用Timer模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755639

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

Jenkins构建Maven聚合工程,指定构建子模块

一、设置单独编译构建子模块 配置: 1、Root POM指向父pom.xml 2、Goals and options指定构建模块的参数: mvn -pl project1/project1-son -am clean package 单独构建project1-son项目以及它所依赖的其它项目。 说明: mvn clean package -pl 父级模块名/子模块名 -am参数

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

python内置模块datetime.time类详细介绍

​​​​​​​Python的datetime模块是一个强大的日期和时间处理库,它提供了多个类来处理日期和时间。主要包括几个功能类datetime.date、datetime.time、datetime.datetime、datetime.timedelta,datetime.timezone等。 ----------动动小手,非常感谢各位的点赞收藏和关注。----------- 使用datet

C8T6超绝模块--EXTI

C8T6超绝模块–EXTI 大纲 控制流程结构体分析EXTI实现按键 具体案例 控制流程 这里是流程框图,具体可以去看我STM32专栏的EXTI的具体分析 结构体分析 typedef struct {uint32_t EXTI_Line; // 中断/事件线EXTIMode_TypeDef EXTI_Mode; // EXTI 模式EXTITrigger_TypeDef EXTI_

1、创建多模块的maven springboot项目

现在的java的项目都是多模块的,这次也跟个风。 目标:实现下述结构 项目AcedBoot, 子模块:         aced-api 对外提供接口,         aced-web 给前端提供接口,         aced-service 服务层,         aced-dao 数据底层,包含数据库mapper和实体类entity,         aced-commo

Vue2电商项目(二) Home模块的开发;(还需要补充js节流和防抖的回顾链接)

文章目录 一、Home模块拆分1. 三级联动组件TypeNav2. 其余组件 二、发送请求的准备工作1. axios的二次封装2. 统一管理接口API----跨域3. nprogress进度条 三、 vuex模块开发四、TypeNav三级联动组件开发1. 动态展示三级联动数据2. 三级联动 动态背景(1)、方式一:CSS样式(2)、方式二:JS 3. 控制二三级数据隐藏与显示--绑定styl

PrestaShop免费模块/插件/扩展/工具下载

PrestaShop免费模块/插件/扩展/工具下载 PrestaShop免费模块 适用于您的电子商务网站的PrestaShop模块 现有超过3,000个PrestaShop模块可帮助您自定义在线商店,增加流量,提高转化率并建立客户忠诚度。 使您的电子商务网站成功! 下载(超过142+之多的PrestaShop官网认证的免费模块) 标签PrestaShop免费, PrestaShop免费工

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现