【免费下载链接】TobudOS
TobudOS 是面向物联网领域开发的实时操作系统,早期版本基于腾讯自研的物联网操作系统TencentOS Tiny,2020年由腾讯捐赠到开放原子开源基金会进行孵化,2023年正式更名为TobudOS,TobudOS具有低功耗,低资源占用,模块化,安全可靠等特点,可有效提升物联网终端产品开发效率,提供精简的 RTOS 内核,内核组件可裁剪可配置,可快速移植到多种主流 MCU (如 STM32 全系列) 及模组芯片上。而且,基于 RTOS 内核提供了丰富的物联网组件,内部集成主流物联网协议栈(如 CoAP/MQTT/TLS/DTLS/LoRaWAN/NB-IoT 等),可助力物联网终端设备及业务快速接入物联网云平台。
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 位定时器关键字参数:
| 参数 | 取值 | 说明 |
|---|---|---|
mode | TimerWiPy.ONE_SHOT | 定时器只运行一次,直到配置的通道周期到期 |
TimerWiPy.PERIODIC | 定时器按配置的通道频率周期性运行 | |
TimerWiPy.PWM | 在引脚上输出 PWM 信号 | |
width | 16或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 的引脚映射如下:
| 引脚 | 通道 |
|---|---|
GP24 | Timer 0 通道 A |
GP25 | Timer 1 通道 A |
GP9 | Timer 2 通道 B |
GP10 | Timer 3 通道 A |
GP11 | Timer 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:达到占空比数值时执行回调。
参数:
| 参数 | 取值 | 说明 |
|---|---|---|
priority | 1–7 的整数 | 中断优先级,数值越大优先级越高 |
handler | 可调用对象 | 中断触发时被调用的回调函数(可选) |
trigger | TimerWiPy.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")。
必须遵守的核心约束:
- IRQ 内禁止分配内存:中断处理程序中无法从堆分配内存(堆分配不可重入)。因此回调里不要对 list 做 append、不要插入字典、不要使用浮点运算(浮点对象需要分配)。
- 异常信息受限:在中断里抛出的异常几乎不携带有用信息,可调用
micropython.alloc_emergency_exception_buf(100)预先分配紧急异常缓冲区,便于 ISR 出错时输出堆栈跟踪:
import micropython micropython.alloc_emergency_exception_buf(100)- 回调保持短小:ISR 应尽量短而简单,只做必须立即处理的事情,其余工作交给主循环;避免循环、避免访问非中断设备的慢速 I/O(磁盘、UART、
print等)、绝不能阻塞等待事件。 - 与主程序共享数据:使用预分配的
bytearray、array或整型/布尔变量与主循环通信;修改共享数据时考虑临界区(如machine.disable_irq()/machine.enable_irq(state)保护读-改-写操作,参见 machine.rst 的disable_irq/enable_irq说明)。 - 用
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 等),可助力物联网终端设备及业务快速接入物联网云平台。
相关推荐
TobudOS 嵌入式 MicroPython 硬件定时器 machine.Timer 实战指南:从 API 到内核实现
TobudOS 嵌入式 MicroPython 硬件定时器 machine.Timer 实战指南:从 API 到内核实现 TobudOS 在 component
TobudOS MicroPython GPIO 编程指南:machine.Pin 类完整解析与实战
TobudOS MicroPython GPIO 编程指南:machine.Pin 类完整解析与实战 导读 machine.Pin 是 MicroPython
TobudOS 上的 MicroPython machine.SPI 指南:硬件 SPI 与 SoftSPI 的构造、初始化与读写实战
TobudOS 上的 MicroPython machine.SPI 指南:硬件 SPI 与 SoftSPI 的构造、初始化与读写实战 本文基于 compone
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考