USART — 串口轮询收发(LL 库)
HAL vs LL:PUYA 固件库提供 HAL 和 LL 两套 API。
- HAL 库:封装度高,可移植性强,适合快速开发
- LL 库:接近寄存器操作,代码精简效率高,适合性能敏感场景 本文使用 LL 库版本。
固件库: PY32F0xx_Firmware V1.5.0
芯片: PY32F030(PY32F002A / F003 同库可参考)
源文件:PUYA/PY32F0xx_Firmware_V1.5.0/Projects/PY32F030-STK/Example_LL/USART/USART_HyperTerminal_Polling_Init/Src/main.c
功能简介
本示例实现串口“超级终端”式的轮询(Polling)通信:上电后先向电脑发送一段欢迎语,再接收你从键盘敲入的 12 个字符,原样回传,最后点亮 LED 表示测试通过。
与 HAL 版(用 HAL_UART_Transmit / HAL_UART_Receive 一行搞定)不同,LL 版没有封装好的“发 N 字节”函数,需要你自己用 LL_USART_TransmitData8() 逐字节发送,并用 LL_USART_IsActiveFlag_TXE() / LL_USART_IsActiveFlag_RXNE() 轮询标志位。这正是 LL 的典型特征:给你最底层的积木,组合方式由你决定。
硬件准备
- PY32F030 开发板
- USB 转串口线(或板载 ST-Link VCP),连接到 USART1 的 TX(PA2) / RX(PA3)
- 电脑端串口助手(如 PuTTY、串口调试助手),波特率 9600 / 8N1
- 板载绿 LED(用于测试通过指示)
完整代码
|
|
代码分段讲解
1. 时钟与 GPIO 复用
LL 里 GPIO 和 USART 的时钟分属不同的“组”:GPIO 在 LL_IOP_GRP1,USART1 在 LL_APB1_GRP2。这和 HAL 的 __HAL_RCC_xxx_CLK_ENABLE() 宏对应,但名字更直白地反映了总线分组。PA2/PA3 要设为 LL_GPIO_MODE_ALTERNATE 并指定 Alternate = LL_GPIO_AF1_USART1,才能把引脚接到 USART1。
2. 参数配置:结构体 + Init
LL 的 LL_USART_Init() 同样接收结构体,这一点和 HAL 的 UART_InitTypeDef 很像——LL 并不是完全不用结构体,而是把“一次性参数”用结构体批量写入,把“运行时动作”拆成独立函数。
3. 发送:手动轮询 TXE
HAL 一句 HAL_UART_Transmit() 内部帮你做了“等待 TXE→写 DR→等待 TC”的循环;LL 把这段循环直接暴露给你:
|
|
好处是你可以精确控制发送节奏、插入其它操作,甚至做“发送一半去干别的事”这类 HAL 难以表达的逻辑。
4. 接收:手动轮询 RXNE
同理,接收就是等 RXNE(接收寄存器非空)后 LL_USART_ReceiveData8() 读一个字节。注意 LL 没有“超时”概念,若对方不发数据,这里的 while 会永远等下去——真实项目务必加超时或改用中断/DMA。
实验现象
- 打开串口助手(9600 / 8N1),复位开发板。
- 先收到欢迎语:
USART Hyperterminal communication based on polling ... Enter 12 characters ...。 - 在串口助手输入恰好 12 个字符并发送,开发板原样回传这 12 个字符。
- 最后收到
Example Finished,板载绿 LED 点亮,表示流程成功。
常见问题
-
串口助手收不到任何字符?
- 确认 TX/RX 交叉连接(板子 TX 接 USB 转串口的 RX)。
- 确认波特率 9600、8 位数据、1 停止位、无校验、无流控,与代码一致。
- 确认
APP_SystemClockConfig()里系统时钟确实是 8MHz(LL_Init1msTick(8000000)),否则波特率实际值会偏离。
-
输入不足 12 字符程序就“卡住”?
- LL 轮询接收没有超时,必须收满 12 字节才继续。这是示例故意为之;真实项目建议加超时或改用中断。
-
想重定向 printf?
- LL 版需要自己实现
_write()/fputc()并在里面调用LL_USART_TransmitData8();HAL 版则常配合HAL_UART_Transmit做重定向(参见 HAL 版文档)。
- LL 版需要自己实现
小结
- LL 串口收发 = 结构体配参数 + 独立函数做动作 + 手动轮询标志位。
- 相比
HAL_UART_Transmit/Receive,LL 代码更长,但每一步都看得见、改得动,没有隐藏的超时/状态机。 - 性能敏感或资源紧张时,LL 能省下 HAL 那一层封装开销。
拓展应用
- 给轮询接收加超时计数,避免“永远等待”。
- 改用
LL_USART_EnableIT_RXNE()+ 中断回调函数,实现不阻塞的串口接收(参考USART_HyperTerminal_IT_Init)。 - 用 DMA 搬运大量数据(参考
USART_HyperTerminal_DMA_Init),CPU 几乎零参与。 - 把发送函数封装成
printf风格的可变参数函数,方便打日志。