uart_periodic_tx 模块 API 手册#
概述#
uart_periodic_tx 是一个可选的原生模块,用硬件定时器周期性发送已经准备好的完整 UART 帧。定时器回调只执行原生 UART 写操作,不执行 Python 代码,也不在回调中分配 GC 内存。
适用于需要稳定周期发送固定长度或有限长度数据帧的场景,例如每 50 ms 发送一次状态帧。应用程序在普通 Python 上下文中调用 update() 准备下一帧;模块会复制数据并在后续定时器触发时发送。
备注
该模块是 UART 周期发送器,不是通用硬件定时任务框架,不能在定时器中调度任意 Python 函数,也不能直接用于网络、SPI 或 I2C 传输。
启用模块#
该功能默认关闭。编译固件前,在源码根目录执行构建环境对应的配置命令:
# 使用 k230-builder 时
k230 make menuconfig
# 直接本机编译时
make menuconfig
打开以下选项后重新编译固件:
CanMV Micropython Components Configurations
Enable UART periodic TX module
未启用该选项的固件中导入模块会报 ImportError。
导入模块#
from machine import FPIOA, UART
from uart_periodic_tx import UARTPeriodicTx
快速开始#
以下示例将 UART3 的 TX 映射到 IO50,并每 50 ms 发送一帧。应根据开发板和板级配置选择未被 REPL 或其他系统功能占用的 UART 与引脚。
from machine import FPIOA, UART
from uart_periodic_tx import UARTPeriodicTx
import time
fpioa = FPIOA()
fpioa.set_function(50, FPIOA.UART3_TXD)
tx = UARTPeriodicTx(
UART.UART3,
0,
50,
max_len=64,
baudrate=115200,
bits=UART.EIGHTBITS,
parity=UART.PARITY_NONE,
stop=UART.STOPBITS_ONE,
repeat_last=True,
)
try:
tx.update(b"\xA5\x5A\x00\xFF\x3C\xC3\xFF\x0D")
tx.start()
time.sleep_ms(1000)
tx.update(b"\xA5\x5A\x01\xFE\x3C\xC3\xFF\x0D")
time.sleep_ms(1000)
finally:
tx.deinit()
必须在调用 start() 前完成 FPIOA 的 UART TX 引脚映射。update() 会复制传入的缓冲区,因此调用返回后修改原始 bytearray 不会修改已经发布的帧。
UARTPeriodicTx 类#
构造函数#
UARTPeriodicTx(
uart_id,
timer_id,
period=50,
*,
max_len=64,
baudrate=115200,
bits=UART.EIGHTBITS,
parity=UART.PARITY_NONE,
stop=UART.STOPBITS_ONE,
repeat_last=True,
)
创建周期发送器。构造函数会分配三组发送缓冲区,但在调用 start() 前不会占用 UART 或硬件定时器。
参数 |
说明 |
|---|---|
|
UART 硬件编号,例如 |
|
硬件定时器编号。不能使用软件定时器编号 |
|
发送周期,单位为 ms,默认 |
|
单帧最大长度,默认 |
|
UART 波特率,默认 |
|
数据位,使用与 |
|
校验方式,例如 |
|
停止位,例如 |
|
是否在没有新的 |
period 可以作为第三个位置参数传入,其余 UART 配置参数必须使用关键字参数。
update 方法#
tx.update(data)
复制并发布一帧完整数据,供硬件定时器在后续触发时发送。
参数
data:支持缓冲区协议的数据,例如bytes、bytearray或memoryview。长度不能超过max_len。
异常
ValueError:对象已释放,或数据长度超过max_len。OSError(EBUSY):三组缓冲区暂时都不可写。可在普通 Python 上下文中稍后重试;不要在machine.Timer回调中调用该方法。
update() 不会等待当前帧发送完成,也不保证新帧恰好在下一次硬件触发时出现。如果定时器回调已经取走当前缓冲区,新帧会在其后的触发中生效。repeat_last=True 时,定时器持续发送最近一次成功发布的完整帧;repeat_last=False 时,该帧完整写入一次后等待下一次 update()。
start 方法#
tx.start()
申请硬件定时器和 UART,并开始周期发送。调用前至少应成功调用一次 update(),否则定时器触发会记为跳过发送。
同一个 timer_id 不能同时被 machine.Timer 或另一个 UARTPeriodicTx 使用。资源冲突会抛出 OSError(EBUSY)。
stop 方法#
tx.stop()
停止周期发送并释放硬件定时器和 UART 的运行时资源,但保留对象和已分配的发送缓冲区。之后可以再次调用 start()。
deinit 方法#
tx.deinit()
停止发送并释放全部原生资源和缓冲区。调用后对象不能再次使用。建议在 try / finally 中调用,确保异常路径也能释放硬件定时器。
active 方法#
tx.active()
返回布尔值,表示硬件定时器是否正在运行。
stats 方法#
sent, short_write, errors, skipped = tx.stats()
返回累计统计元组:
返回值 |
说明 |
|---|---|
|
UART 写入返回完整帧长度的次数。 |
|
UART 写入了部分帧的次数。 |
|
UART 写入返回错误的次数。 |
|
未发送的定时器触发次数,例如 UART 正在被另一个周期发送器写入、尚未发布可发送帧,或 |
统计值在对象生命周期内累计,stop() 和再次 start() 不会清零。
资源与时序约束#
UARTPeriodicTx持有用于发送的原生 UART 驱动。运行期间不要通过machine.UART.write()发送同一 UART,也不要重新配置或释放同一 UART。多个
UARTPeriodicTx可以使用同一 UART,但 UART 配置必须完全相同。两个发送时刻重叠时,其中一个触发会被跳过并计入skipped。需要可预测时序时,应为一个 UART 只创建一个周期发送器。定时器触发由硬件定时器驱动,避免了 Python VM、GC 和 Python 回调带来的发送触发延迟;实际在线路上完成一帧仍受波特率、帧长度和 UART 驱动状态影响。
帧的串行发送时间应明显小于
period。若帧过长、波特率过低或 UART 忙,可能出现short_write、errors或skipped。repeat_last=True时,逻辑分析仪会看到最近成功update()的帧被连续发送。repeat_last=False时,每次成功update()最多产生一次完整帧发送;没有新数据的触发会计入skipped。
完整回环验证示例见 UARTPeriodicTx 硬件定时发送。
