news 2026/10/12 1:35:31

TobudOS 上的 MicroPython 硬件定时器:machine.TimerWiPy 类完整使用指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TobudOS 上的 MicroPython 硬件定时器:machine.TimerWiPy 类完整使用指南

【免费下载链接】TobudOS

TobudOS 是面向物联网领域开发的实时操作系统,早期版本基于腾讯自研的物联网操作系统TencentOS Tiny,2020年由腾讯捐赠到开放原子开源基金会进行孵化,2023年正式更名为TobudOS,TobudOS具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率,提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如 STM32 全系列) 及模组芯片上。而且,基于 RTOS 内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入物联网云平台。

项目地址:https://gitcode.com/openatomfoundation/TobudOS
点击查看免费下载

MicroPython 移植到 TobudOS 之后,machine模块提供了基于 TobudOS 内核定时器(tos_timer_*)实现的硬件定时器能力。本文以官方文档 machine.TimerWiPy.rst 为骨架,系统讲解该非标准定时器类的构造、初始化、通道(TimerChannel)与 PWM 输出、回调中断约束,并结合 machine_timer.c 与 tos_timer.h 源码,说明它在 TobudOS 内核中的真实落地方式。读完本文,你将能在 TobudOS + MicroPython 环境下熟练创建周期/单次定时器、配置定时器通道与 PWM 信号,并写出符合中断上下文约束的安全回调代码。

一、TimerWiPy 是什么:一类非标准的定时器实现

硬件定时器(Hardware Timer)负责处理周期与事件的计时。定时器是 MCU/SoC 中最灵活、也最异构的外设——不同型号差异极大,因此 MicroPython 官方只定义了基线行为:按给定周期重复执行回调,或延迟一段时间后执行一次回调;具体板卡可以在此基础上定义更多非标准行为(但这类行为不可移植到其他板卡)。

machine.TimerWiPy正是这样一类非标准 Timer 实现:它在 WiPy 板卡上直接以machine.Timer的名称可用,文档中命名为TimerWiPy,仅仅是为了与更通用的 machine.Timer 类(通用实现,见 machine.Timer.rst)相区分。

需要特别说明的是:在 TobudOS 仓库中,MicroPython 移植端口(components/language/micropython/port/)把定时器对象注册为通用的machine.Timer(见 modmachine.c 中的{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }),并从 WiPy 文档继承了ONE_SHOT/PERIODIC常量与回调模型。因此本文既适用于理解 WiPy 上的TimerWiPy用法,也适用于 TobudOS 移植端口下machine.Timer的日常开发。

二、构造定时器对象:TimerWiPy(id, ...)

from machine import Timer tim = Timer(0) # 创建 id 为 0 的定时器对象 tim = Timer(-1) # id 为 -1 时构造一个虚拟定时器(若板卡支持)
  • id:定时器硬件编号,不同板卡支持的定时器个数不同。传入-1时构造虚拟定时器(virtual timer,若板卡支持)。从文档语义看,虚拟设备(负数 id)本质上仍是真实硬件中断之上的薄封装,回调约束与物理设备一致(参见 machine.rst 中machine_callbacks一节)。
  • 构造时也可以直接传入后续init的关键字参数,一步完成初始化。

从 TobudOS 移植源码看,构造过程在 machine_timer.c 的machine_timer_make_new中完成:对象内嵌了一个 TobudOS 内核定时器控制块k_timer_t,默认mode = TOS_OPT_TIMER_ONESHOT、period = 1000(毫秒)、callback = None;如果构造时传入了参数,则会立即调用初始化流程启动定时器。

三、初始化定时器:init(mode, *, width=16)

tim.init(Timer.PERIODIC) # 周期 16 位定时器 tim.init(Timer.ONE_SHOT, width=32) # 单次 32 位定时器

关键字参数:

参数取值说明
modeTimerWiPy.ONE_SHOT定时器只运行一次,直到配置的通道周期到期
TimerWiPy.PERIODIC定时器按配置的通道频率周期性运行
TimerWiPy.PWM在引脚上输出 PWM 信号
width16或32(位)计数宽度。对于低于 5Hz 的极低频率(或很大的周期),应使用 32 位定时器;32 位模式仅适用于ONE_SHOT和PERIODIC两种模式

设计要点:

  • 低频场景必须选 32 位:16 位计数在很低的频率下很快溢出,官方文档明确要求低于 5Hz(或周期很大)时使用width=32。
  • PWM 模式没有 32 位:width=32与PWM不能组合使用。
  • 在 WiPy 的通道模型下,init只负责设定工作模式与位数,真正的频率/周期、PWM 占空比等参数由后续channel()配置。

TobudOS 移植端口的 init 差异

需要指出:TobudOS 移植端口的machine_timer_init_helper(machine_timer.c)采用通用machine.Timer风格,支持的关键字参数更多:

  • mode:默认TOS_OPT_TIMER_PERIODIC,仅接受TOS_OPT_TIMER_ONESHOT/TOS_OPT_TIMER_PERIODIC,传入其他值会抛出ValueError: invalid timer mode;
  • callback:到期回调(可调用对象),默认None;
  • tick_hz:默认 1000,用于周期换算的时基;
  • period:定时器周期(毫秒),默认极大值0xffffffff;
  • freq:定时器频率(Hz),与period二选一。

其中周期换算逻辑为:若给出freq(Hz),则period = 1000 / freq(毫秒);否则period = period * 1000 / tick_hz。换算结果会被钳制在1到0x40000000之间,超出上限会抛出ValueError: period too large(machine_timer.c)。只有传入可调用 callback 时,该端口才会真正调用tos_timer_start启动内核定时器(machine_timer.c)——这意味着在 TobudOS 上,无回调的定时器创建后不会开始走时。

四、停止并释放:deinit()

tim.deinit()

deinit()反初始化定时器:停止定时器,并禁用定时器外设。在 TobudOS 移植实现中,machine_timer_deinit直接调用machine_timer_disable(machine_timer.c),后者调用内核tos_timer_destroy(&self->timer)销毁定时器、清空回调引用并将init标志复位(machine_timer.c)。重复调用init()之前,实现也会先执行一次machine_timer_disable,保证旧配置被彻底清理(machine_timer.c)。

五、配置定时器通道:channel(channel, **, freq, period, polarity=POSITIVE, duty_cycle=0)

定时器通道(TimerChannel)用于利用定时器产生或捕获信号。TimerChannel对象通过Timer.channel()方法创建:

# 仅传入通道标识:返回已初始化的通道对象(若不存在则返回 None) ch = tim.channel(Timer.TIMER_A) # 传入通道标识 + 配置参数:初始化并返回一个新的 TimerChannel ch = tim.channel(Timer.TIMER_A, freq=1000) # 1kHz ch = tim.channel(Timer.TIMER_B, period=1000) # 周期 1000 微秒(1ms) ch = tim.channel(Timer.TIMER_A | Timer.TIMER_B, freq=10) # 32 位定时器必须用 A|B

参数说明:

  • channel(通道标识):若定时器宽度为 16 位,则必须是TIMER.A或TIMER.B;若宽度为 32 位,则必须是TIMER.A | TIMER.B(两个通道合并使用)。
  • freq/period(仅限关键字参数):
    • freq:设置频率,单位 Hz;
    • period:设置周期,单位微秒(注意:这里与machine.Timer.init的毫秒单位不同);
    • ⚠️二者必须二选一,绝不能同时给出。
  • polarity:仅适用于PWM模式,定义占空比极性,默认TimerWiPy.POSITIVE。
  • duty_cycle:仅适用于PWM模式。语义上是百分比(0.00–100.00),但由于 WiPy 不支持浮点数,实际取值必须落在0–10000的整数区间:10000表示 100.00%、5050表示 50.50%,依此类推。

通道的工作模式继承自创建它的Timer对象(即由init(mode=...)决定)。

PWM 模式下的引脚自动分配

当通道处于 PWM 模式时,对应引脚会被自动分配,因此无需通过Pin类手动配置引脚的复用功能(alternate function)。WiPy 上支持 PWM 的引脚映射如下:

引脚通道
GP24Timer 0 通道 A
GP25Timer 1 通道 A
GP9Timer 2 通道 B
GP10Timer 3 通道 A
GP11Timer 3 通道 B

关于 TobudOS 端口的现状

从当前仓库移植实现看,TobudOS 端口的machine.Timer对象方法表(machine_timer.c)目前仅包含init、deinit、callback及常量ONE_SHOT/PERIODIC,尚未实现channel()与TimerChannel/ PWM 能力。也就是说,上表所述通道与 PWM 用法属于 WiPy 板卡专有能力(文档也明确提示这类行为不可移植到其他板卡);在 TobudOS 端口上,请使用通用machine.Timer的init(period=..., freq=..., callback=...)完成周期/单次定时,PWM 输出可另经machine.Pin(见 machine_pin.c 的init/value/irq等方法)驱动 GPIO 实现。

六、TimerChannel:通道回调与参数读写

TimerChannel对象用于配合定时器产生/捕获信号,由Timer.channel()创建。

timerchannel.irq(*, trigger, priority=1, handler=None)

设置通道中断回调,行为强烈依赖定时器通道的工作模式:

  • 模式为TimerWiPy.PERIODIC:回调按配置的频率/周期周期性执行;
  • 模式为TimerWiPy.ONE_SHOT:定时器到期时回调只执行一次;
  • 模式为TimerWiPy.PWM:达到占空比数值时执行回调。

参数:

参数取值说明
priority1–7 的整数中断优先级,数值越大优先级越高
handler可调用对象中断触发时被调用的回调函数(可选)
triggerTimerWiPy.TIMEOUT工作模式为PERIODIC或ONE_SHOT时使用
TimerWiPy.MATCH工作模式为PWM时使用

该方法返回一个回调对象。

通道参数读写

ch.freq(500) # 设置通道频率为 500 Hz print(ch.freq()) # 读取通道频率(Hz) ch.period(2000) # 设置通道周期为 2000 微秒 print(ch.period()) # 读取通道周期(微秒) ch.duty_cycle(5050) # 设置 PWM 占空比为 50.50% print(ch.duty_cycle())
  • timerchannel.freq([value]):获取或设置通道频率(Hz)。
  • timerchannel.period([value]):获取或设置通道周期(微秒)。
  • timerchannel.duty_cycle([value]):获取或设置 PWM 信号占空比,语义为百分比(0.00–100.00),但由于 WiPy 不支持浮点,取值必须在 0–10000 区间:10000= 100.00%、5050= 50.50%。

七、常量

TimerWiPy.ONE_SHOT # 定时器单次运行模式 TimerWiPy.PERIODIC # 定时器周期运行模式

在 TobudOS 移植实现中,这两个常量直接映射到 TobudOS 内核定时器选项(machine_timer.c):

  • Timer.ONE_SHOT=TOS_OPT_TIMER_ONESHOT=0x0001u;
  • Timer.PERIODIC=TOS_OPT_TIMER_PERIODIC=0x0002u。

其语义定义见 tos_timer.h:单次定时器只运行一次到期,周期定时器到期后按周期重复运行。内核定时器还维护TIMER_STATE_UNUSED / STOPPED / RUNNING / COMPLETED状态机,其中COMPLETED只在TOS_OPT_TIMER_ONESHOT场景出现(tos_timer.h)。

八、定时器回调的硬约束:中断上下文与 ISR 规则

文档明确要求参考 machine.rst 中的machine_callbacks说明:machine 模块中函数与类方法使用的所有回调都应视为在中断上下文(interrupt context)中执行。这一点对物理设备(id ≥ 0)与虚拟设备(负数 id)都成立。更完整的约束见 isr_rules.rst("Writing interrupt handlers")。

必须遵守的核心约束:

  1. IRQ 内禁止分配内存:中断处理程序中无法从堆分配内存(堆分配不可重入)。因此回调里不要对 list 做 append、不要插入字典、不要使用浮点运算(浮点对象需要分配)。
  2. 异常信息受限:在中断里抛出的异常几乎不携带有用信息,可调用micropython.alloc_emergency_exception_buf(100)预先分配紧急异常缓冲区,便于 ISR 出错时输出堆栈跟踪:
import micropython micropython.alloc_emergency_exception_buf(100)
  1. 回调保持短小:ISR 应尽量短而简单,只做必须立即处理的事情,其余工作交给主循环;避免循环、避免访问非中断设备的慢速 I/O(磁盘、UART、print等)、绝不能阻塞等待事件。
  2. 与主程序共享数据:使用预分配的bytearray、array或整型/布尔变量与主循环通信;修改共享数据时考虑临界区(如machine.disable_irq()/machine.enable_irq(state)保护读-改-写操作,参见 machine.rst 的disable_irq/enable_irq说明)。
  3. 用micropython.schedule绕过限制:需要做更重的处理时,可在 ISR 中调用micropython.schedule(callback, arg)将回调排队到堆未锁定的时刻执行,此时可以创建对象、使用浮点;但要注意高频率中断下队列可能无限增长,最终抛出RuntimeError。

TobudOS 移植实现的调度式回调

一个值得注意的实现细节:TobudOS 移植端口的定时器回调machine_timer_handler(machine_timer.c)内部调用mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)),即把 Python 回调投递到 MicroPython 调度队列,在 VM 主循环中执行,而不是直接在中断上下文中运行 Python 代码;单次模式回调后会将running标志清零。同时该端口还提供callback()方法(machine_timer.c),传入None时停止内核定时器并清空回调,传入可调用对象时设置回调,若定时器未在运行则重启。这为用户提供了一种相对安全的"软中断"式回调体验,但编写回调时仍应遵循上述 ISR 约束,避免阻塞主循环。

九、源码视角:TobudOS 定时器在内核中的底层映射

TobudOS 端口的machine.Timer是一个桥接层:machine_timer_obj_t内嵌内核定时器控制块k_timer_t(machine_timer.c),所有 Python 操作最终都落到 TobudOS 内核 API 上。

启动链路(machine_timer_enable,machine_timer.c):

period_ticks = tos_millisec2tick(self->period) # 毫秒 → 系统 tick tos_timer_create(&self->timer, period_ticks, period_ticks, machine_timer_handler, self, self->mode)

即周期毫秒数先经tos_millisec2tick(声明见 tos_time.h)换算为内核 tick,再调用tos_timer_create(tmr, delay, period, callback, cb_arg, opt)创建定时器(API 签名见 tos_timer.h)。创建失败时抛出OSError: can't create timer。tos_timer_create的实现见 tos_timer.c,其校验period/delay有效性,返回值K_ERR_TIMER_INVALID_PERIOD/K_ERR_TIMER_INVALID_DELAY/K_ERR_NONE。

k_timer_t控制块结构(tos_timer.h)包含回调函数指针cb、回调参数cb_arg、剩余到期时间expires、首次延迟delay、周期period、选项opt与状态state,并挂在k_tick_list上由内核 tick 驱动。整套机制对应 TobudOS 的软件定时器服务(soft_timer_*),与TOS_CFG_TIMER_EN配置开关相关。

定时器在 TobudOS 任务模型中的运行方式

TobudOS 上的 MicroPython 以独立任务形式运行:示例 micropython_demo.c 中,application_entry创建名为micropython的任务(优先级 3、默认栈 4KB),任务入口mp_entry循环调用mp_main()(micropython_demo.c)。你创建的machine.Timer就运行在这个 MicroPython 任务上下文里,其回调经mp_sched_schedule回到该任务的主循环执行;而定时器本身的触发由 TobudOS 内核 tick/定时器服务保障,不占用应用任务的阻塞等待。

十、综合示例:从周期闪烁到单次延时

在 TobudOS + MicroPython 环境中,一个可直接参考的完整流程如下:

import machine import micropython import time micropython.alloc_emergency_exception_buf(100) # 便于 ISR 内异常定位 # 1. 周期定时器:每 500ms 打印一次 tim = machine.Timer(0) def periodic_cb(t): print("tick, period(ms):", t.period if hasattr(t, 'period') else 'n/a') tim.init(mode=machine.Timer.PERIODIC, period=500, callback=periodic_cb) time.sleep_ms(2000) # 2. 单次定时器:1 秒后触发一次 tim2 = machine.Timer(1) def one_shot_cb(t): print("one shot fired") tim2.init(mode=machine.Timer.ONE_SHOT, period=1000, callback=one_shot_cb) time.sleep_ms(1500) # 3. 停止并释放 tim.deinit() tim2.deinit()

说明:上述示例采用 TobudOS 移植端口machine.Timer.init的period(毫秒)/freq(Hz)/callback关键字参数,这是当前仓库端口实际支持的写法。PERIODIC周期任务适合做轮询、采样、心跳上报等场景;ONE_SHOT适合做超时保护、延时后动作。若后续端口补充了channel()/ PWM 能力,可再按本文第五、六节的通道模型配置 PWM 输出与irq(trigger=...)回调。

结语

machine.TimerWiPy文档定义了硬件定时器的通道模型与回调约束,其中ONE_SHOT/PERIODIC两种工作模式、width=16/32的位数选择、channel()与TimerChannel的freq/period/polarity/duty_cycle参数,以及"回调在中断上下文执行、IRQ 内禁止分配内存"的硬性约束,构成了使用定时器的完整知识框架。在 TobudOS 仓库中,这一框架通过 machine_timer.c 映射到内核 tos_timer.h / tos_timer.c 的软件定时器服务:Python 侧的init/deinit/callback依次对应tos_timer_create / tos_timer_destroy / tos_timer_start|stop,周期单位经tos_millisec2tick换算为内核 tick。理解这层映射,你就能在 TobudOS 上写出既符合 MicroPython 规范、又贴合底层内核行为的定时器程序。

【免费下载链接】TobudOS

TobudOS 是面向物联网领域开发的实时操作系统,早期版本基于腾讯自研的物联网操作系统TencentOS Tiny,2020年由腾讯捐赠到开放原子开源基金会进行孵化,2023年正式更名为TobudOS,TobudOS具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率,提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如 STM32 全系列) 及模组芯片上。而且,基于 RTOS 内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入物联网云平台。

项目地址:https://gitcode.com/openatomfoundation/TobudOS
点击查看免费下载
上一篇:终极指南:让2008-2018年老款Mac免费运行最新macOS的完整教程
下一篇:终极Parabolic视频下载器:免费开源跨平台下载解决方案完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:30:31

基于ESP32与IMU的DIY跳跃传感器:从原理到实战

1. 从一个“不起眼的小玩意”说起:DIY jump sensor 到底能做什么第一次听到“DIY jump sensor”这个词,很多人脑子里冒出来的画面可能是健身房里的专业弹跳测试仪,或者是运动员身上贴满传感器的高科技装备。其实完全不是那么回事。所谓 jump …

作者头像 李华
网站建设 2026/10/12 1:29:14

Apache Beam Python SDK 中的 Reify 变换:显式化时间戳与窗口信息

【免费下载链接】beam Apache Beam is a unified programming model for Batch and Streaming data processing. 项目地址: https://gitcode.com/gh_mirrors/beam18/beam 点击查看 免费下载 导读:在 Apache Beam 的流式与批处理统一编程模型中&#xff…

作者头像 李华