MicroPython pyb.UART 串口编程完全指南:Pyboard 全系串口配置、流式读写与 RTS/CTS 硬件流控深度解析
2026/9/20 8:42:24 网站建设 项目流程

MicroPython pyb.UART 串口编程完全指南:Pyboard 全系串口配置、流式读写与 RTS/CTS 硬件流控深度解析

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

本篇技术指南以 MicroPython 官方文档中pyb.UART类为核心,系统讲解在 Pyboard(含 Pyboard Lite 与 Pyboard D)上如何使用 UART/USART 双工串行通信:从构造器与板级引脚映射、init()全参数配置(波特率、数据位、校验、停止位、超时、流控、接收缓冲),到流式读写 API 与 RTS/CTS 硬件流控的完整行为语义。结合仓库源码 machine_uart.c 与官方测试用例 uart.py,读者将掌握可落地的串口编程方案,并理解底层实现约束(如波特率精度、缓冲阈值、流控引脚极性)。

UART 基础:双线双工、字符宽度与适用场景

pyb.UART实现标准的 UART/USART 双工串行通信协议。物理层面仅由两条信号线构成:

  • RX(接收):接收对端数据;
  • TX(发送):发送数据到对端。

通信的基本单元是字符(character),注意它不等同于 Python 字符串中的"字符"。一个 UART 字符的宽度可以是8 位或 9 位(结合校验位使用时则支持 7 位或 8 位),这决定了每帧携带的信息量与最高传输速率之间的权衡。

UART 是嵌入式系统中最基础的外设之一,典型应用包括:与 PC 上位机通信(配合 USB 转串口)、连接 GPS/GSM 模块、传感器调试日志输出、板间通信等。在 MicroPython 中,UART 对象实现了标准流(stream)接口,因此可以像操作文件一样进行读写,并可与selectasyncio等高层机制配合。

快速上手:创建与初始化 UART 对象

标准用法如下(与 pyb.UART 文档 保持一致):

from pyb import UART uart = UART(1, 9600) # 以给定波特率创建并初始化 uart.init(9600, bits=8, parity=None, stop=1) # 使用给定参数重新初始化

参数取值范围:

  • bits:7、8 或 9;
  • parityNone(无校验)、0(偶校验 even)或1(奇校验 odd);
  • stop(停止位):1 或 2。

重要限制:当parity=None时,仅支持 8 位和 9 位;启用校验位后,仅支持 7 位和 8 位。换言之,9 位数据帧无法与校验位共存——因为 9 位数据 + 1 位校验会超出帧结构。

构造器与板级引脚映射

UART(bus, ...)在指定总线上构造 UART 对象。若不提供额外参数,对象仅被创建而不初始化(保留该总线上次初始化时的设置,如有);若传入额外参数,则立即按init()的语义初始化。

Pyboard(V1/V1.1)引脚映射

总线位置(TX, RX)(PAx, PAx) 映射
UART(4)XA(X1, X2)(PA0, PA1)
UART(1)XB(X9, X10)(PB6, PB7)
UART(6)YA(Y1, Y2)(PC6, PC7)
UART(3)YB(Y9, Y10)(PB10, PB11)
UART(2)(X3, X4)(PA2, PA3)

Pyboard Lite 引脚映射

Pyboard Lite 仅支持UART(1)UART(2)UART(6)

总线位置(TX, RX)映射
UART(1)XB(X9, X10)(PB6, PB7)
UART(6)YA(Y1, Y2)(PC6, PC7)
UART(2)(X1, X2)(PA2, PA3)

Pyboard D 引脚映射

Pyboard D 仅支持UART(1)UART(2)UART(3)UART(4)

总线位置(TX, RX)映射
UART(4)XA(X1, X2)(PA0, PA1)
UART(1)YA(Y1, Y2)(PA9, PA10)
UART(3)YB(Y9, Y10)(PB10, PB11)
UART(2)(X3, X4)(PA2, PA3)

特别注意:Pyboard D 的UART(1)位于YA,与 Pyboard / Pyboard Lite 上UART(1)位于 XB、UART(6)位于 YA 的布局不同。移植代码到 Pyboard D 前务必核对引脚。

构造时传入不存在的总线标识会抛出ValueError,这在官方测试 tests/ports/stm32/uart.py 中得到验证:UART(-1, 9600)UART(0, 9600)UART(100, 9600)UART("doesnotexist", 9600)均触发ValueError

init() 参数详解:从波特率到接收缓冲

init()的完整签名:

UART.init(baudrate, bits=8, parity=None, stop=1, *, timeout=0, flow=0, timeout_char=0, read_buf_len=64)

参数语义:

  • baudrate:时钟速率(波特率)。
  • bits:每字符位数,7、8 或 9。
  • parityNone0(偶)、1(奇)。
  • stop:停止位数,1 或 2。
  • flow:流控类型,可为0UART.RTSUART.CTSUART.RTS | UART.CTS(见下文"硬件流控"章节)。
  • timeout:等待写入/读取第一个字符的超时时间,单位毫秒。
  • timeout_char:写入或读取时相邻字符之间的等待超时,单位毫秒。
  • read_buf_len:接收缓冲区的字符长度,0表示禁用缓冲。

波特率精度与最低波特率

init()在无法将波特率设置在目标值5% 误差范围内时会抛出异常。最低可达波特率由总线所在的外设时钟频率决定:

  • UART(1)UART(6)挂接在APB2总线上,默认总线频率下最低波特率约1300
  • 其余 UART 挂接在APB1总线上,默认频率下最低波特率约650

若需要更低的波特率,可使用 pyb.freq 降低对应总线时钟频率(注意这会影响同总线上的其他外设)。

超时与缓冲的底层实现细节(源码视角)

从 machine_uart.c 的参数解析代码可以印证文档行为并补充若干实现事实:

  • read_buf_len默认值为64,且被标记为 legacy 参数;实际分配缓冲时会在用户指定值上+1size_t len = args.read_buf_len.u_int + 1; // +1 to adjust for usable length of buffer),以保证环形缓冲可用的最大长度,打印输出时也会做-1修正;
  • timeout_char并非任意值都生效:源码强制其不小于13000 / baudrate + 2(毫秒),这是保证两个字符之间中断调度可完成的最短间隔,低于该值会被自动抬高;
  • 参数表中还可见较新的rxbuf关键字参数会覆盖 legacy 的read_buf_len
  • 在 STM32H7 系列上还额外支持invert=0INV_TX/INV_RX信号极性反转(见 machine_uart.c),文档所列 API 之外的进阶能力。

deinit() 与对象状态查看

  • UART.deinit():关闭 UART 总线,释放外设;
  • 打印 UART 对象会输出其完整配置,如UART(1, baudrate=9600, bits=8, parity=None, stop=1, flow=0, timeout=1000, timeout_char=0, rxbuf=63)(实现于 machine_uart.c,实际显示内容取决于该对象是否已启用)。

流式读写 API:read / readline / readinto / write

UART 对象是一个流对象(stream),读写采用标准流方法:

uart.read(10) # 读取 10 个字符,返回 bytes 对象 uart.read() # 读取当前所有可用字符 uart.readline() # 读取一行(以换行符结尾) uart.readinto(buf) # 读取并存入给定缓冲区 uart.write('abc') # 写入 3 个字符

各方法语义(严格对应 pyb.UART 文档):

  • read([nbytes]):指定nbytes时最多读取该字节数;若缓冲区已有足够数据则立即返回,否则等待数据到齐或超时。不指定nbytes时读取尽可能多的数据,在timeout到期后返回。返回bytes对象,超时返回None
  • readline():读取以换行符结尾的一行;已存在完整行则立即返回,超时后无论是否含换行都返回已收集数据。无数据且超时返回None
  • readinto(buf[, nbytes]):读入buf,指定nbytes时最多读该字节数,否则最多读len(buf)字节。返回实际读入字节数,超时返回None
  • write(buf):写入缓冲区。7/8 位字符时每字节即一个字符;9 位字符时每个字符占用两个字节(小端序),buf必须包含偶数个字节。返回写入字节数;超时且未写入任何字节时返回None

9 位字符的读写对称性read时若为 9 位字符,每字符占两字节,nbytes必须为偶数,实际字符数为nbytes/2write时同样要求偶数字节数。9 位模式常用于需要传输带标志位帧(如 9 位地址帧多机通信)的场景。

单字符与查询方法

uart.readchar() # 读取 1 个字符,以整数形式返回 uart.writechar(42) # 写入 1 个字符(整数) uart.any() # 返回等待读取的字符数
  • readchar():返回读到的字符(整数);超时返回-1
  • writechar(char)char为要写入的整数,返回None。若启用了 CTS 流控,见下文特殊说明。
  • any():返回缓冲区中等待读取的字节数(可能为 0),可用于轮询式接收,避免阻塞。
  • sendbreak():在总线上发送一个中断条件(break condition)——将总线拉低约13 位时长,返回None。常用于唤醒对端设备或触发线路状态中断。

版本兼容性提醒

流式方法readwrite等自 MicroPythonv1.3.4起引入;更早版本使用uart.senduart.recv,在新版本中已不再适用。

RTS/CTS 硬件流控深度解析

流控常量与可用总线

UART.RTSUART.CTS常量用于init(flow=...)选择流控类型,可单独或组合使用(UART.RTS | UART.CTS)。在源码中它们被定义为硬件寄存器位值(machine_uart.c 中的UART_HWCONTROL_RTS/UART_HWCONTROL_CTS)。

支持硬件流控的引脚(下文"目标"指与 Pyboard 相连的对端设备):

Pyboard V1 / V1.1UART(2)UART(3)支持 RTS/CTS。

  • UART(2)(TX, RX, nRTS, nCTS) = (X3, X4, X2, X1) = (PA2, PA3, PA1, PA0)
  • UART(3)(TX, RX, nRTS, nCTS) = (Y9, Y10, Y7, Y6) = (PB10, PB11, PB14, PB13)

Pyboard Lite:仅UART(2)支持:

  • (TX, RX, nRTS, nCTS) = (X1, X2, X4, X3) = (PA2, PA3, PA1, PA0)

连接方式与信号极性

init(flow=...)指定 RTS 和/或 CTS 时,相关引脚被配置为流控用途:

  • nRTS:低电平有效(active low)输出;
  • nCTS:低电平有效输入,内部上拉使能

接线规则是交叉连接:

  • Pyboard 的nCTS连接目标的nRTS
  • Pyboard 的nRTS连接目标的nCTS

CTS:目标设备控制 Pyboard 的发送

启用 CTS 流控后,write()的行为:

  • 调用UART.write(buf)时,只要nCTSFalse(低有效,表示对端繁忙),发送即暂停
  • 若在超时时间内整个缓冲区未能发完,则发生超时。方法返回已写入的字节数,用户可据此续发剩余数据;
  • 发生超时时,UART 中会残留一个正在等待nCTS的字符,该字符的字节数包含在返回值中

writechar()的特殊行为:

  • 若在nCTSFalse时调用writechar(),方法会等待,若目标未及时拉高nCTS则超时并抛出OSError 116(ETIMEDOUT);
  • 一旦目标拉高nCTS,该字符立即被发送。

RTS:Pyboard 控制目标设备的发送

启用 RTS 流控后的行为取决于是否使用接收缓冲:

使用缓冲(read_buf_len> 0)

  • 入站字符进入缓冲区;
  • 当缓冲区写满后,下一个到达的字符会使nRTS变为False(通知目标暂停发送);从缓冲区读取字符后nRTS恢复为True

文档给出一个精确的计数示例:设缓冲长度为N字节。缓冲满后再到达一个字符,nRTS被置False,此时any()返回计数N。当字符被读出后,这个"多出来"的字符会被放入缓冲区,并在后续any()调用中计入结果。

不使用缓冲(read_buf_len== 0)

  • 每到达一个字符,nRTS立即变为False,直到该字符被读出为止——即逐字符流控,适合对延迟敏感、由应用立即消费的接收场景。

流控使用的时机建议

硬件流控适合两类典型场景:一是与无缓冲/小缓冲传感器或模块通信时防止数据丢失;二是高速率(如 921600 及以上)长距离传输时避免软件层来不及取走数据。若对端不支持 RTS/CTS,务必保持flow=0(默认)。

从源码到测试:pyb.UART 的实现与验证路径

实现位置

  • Python 层封装pyb模块中的 UART 由 machine_uart.c 实现(STM32 移植版),其头文件与底层 HAL 适配见 uart.h 与 uart.c;
  • REPL 串口:Pyboard 的 USB VCP 之外的调试串口由 main.c 初始化,例如pyb_uart_repl_obj使用MICROPY_HW_UART_REPL_BAUD波特率、timeout_char=2,并将 UART 挂接到 REPL(uart_attach_to_repl),这解释了为何某些板载 UART(1) 默认被 REPL 占用(测试 uart.py 中对 STM32WB 平台直接SKIP的原因)。

官方测试要点

tests/ports/stm32/uart.py 覆盖了本文所述核心行为:

  • 非法总线标识触发ValueError
  • 构造、init()变参重初始化、print(uart)输出规范化(波特率取整到偶数以跨 MCU 一致);
  • any()/write()/writechar()/sendbreak()的返回值验证;
  • 非阻塞模式(timeout=0)下的写行为与read(100)返回None
  • rxbuf=8/rxbuf=0与 legacyread_buf_len=4/read_buf_len=0的缓冲配置切换。

对于连接了真实硬件回环(TX-RX 短接)的板子,可自行验证read/readinto/readline的完整读写闭环。

实战要点总结

  1. 选对总线:优先使用UART(1)/UART(6)(APB2,默认最低波特率 1300);需要更低波特率时先调低总线时钟(pyb.freq),再验证init()是否在 5% 误差内成功。
  2. 帧格式匹配:对端为 8N1(8 数据位、无校验、1 停止位)时用init(9600);9 位地址帧模式记得读写时按"每字符 2 字节"处理且保证nbytes为偶数。
  3. 超时设计timeout管首字符,timeout_char管字符间隔;接收侧推荐轮询any()或使用带rxbuf的缓冲读取,避免read()长时间阻塞。
  4. 流控接线:交叉连接 nRTS↔nCTS,注意均为低电平有效;CTS 会导致write()返回部分写入数、writechar()可能抛OSError 116,代码需容忍并处理。
  5. 兼容性:老代码中的send/recv需改写为write/read;Pyboard D 的引脚布局与 V1 不同,跨板移植必须重新核对 引脚映射。

【免费下载链接】micropythonMicroPython - a lean and efficient Python implementation for microcontrollers and constrained systems项目地址: https://gitcode.com/gh_mirrors/mi/micropython

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询