串口调试与printf重定向

📅 2026-09-04
#来源/立芯星球 #类型/专题提炼 #技术/UART #技术/DMA #技术/printf重定向

串口调试与printf重定向

串口是嵌入式最高频的调试与通信通道。本专题合并星球中关于串口接收架构、DMA 不定长接收、环形缓冲与 printf 的讨论。

一、接收架构总览:解析不要放在前端

串口接收不定长数据的基本架构共识:不在前端(中断/最高优先级层)解析数据,因为解析可能耗时过长、影响中断及时响应——前端只做数据分发(显式/隐式双 buffer),后端线程再做解包(看完串口相关视频我是这么理解的:串口接收不定长架构,Jack;架构全图见 软件架构层面来说: ADC:数据缓冲与解包: 以串口为例,Jack)。

双 buffer 的两种形态:显式双 buffer 用两个独立缓冲区,装满后切换并通知线程解包,通常配合 Normal 模式;隐式双 buffer 只有一个缓冲区,靠半满/全满中断加 circular 模式实现前后半段复用。解包又分原地解包(省拷贝但要求解包线程够快)与异地解包(DMA 提前搬走,对线程实时性要求低但费空间)。

配套实操 Demo:B 站视频中的"串口单字节不定长双 buffer 的 DMA 和 CPU 并行工作"工程可直接下载(B站视频中的串口单字节不定长双buffer的DMA和CPU并行工作Demo,Jack)。

二、不定长接收的三种打法

  1. 空闲中断 + 指针偏移法:不靠 DMA 定长中断;每次空闲中断产生就自动解析,但不要干预 DMA 本身,否则容易挂(这里不能靠dma中断去定长度进,Jack)。
  2. 无空闲中断的芯片:自己查询 DMA 剩余传输长度并结合时间断帧,既解决没有 idle 中断的问题,也节省中断资源(串口dma接收,不用idle的话,可以自己查询dma剩余传输长度结合时间来断帧,Jack;单片机的串口本身没有空闲中断怎,Jack)。
  3. 环形缓冲区 + 自定义协议帧:发送用定长 buffer + 串口 DMA 发出;接收用串口+环形缓冲实时收命令。协议帧结构为"包头识别符、包长度、净荷数据、包尾识别符"(老师,我们的串口dma加环形缓冲区主要是用于传输数据,Jack)。

环形缓冲的典型坑:ST 串口发送函数"最后一个字节先发出来",多半是环形缓冲区里上一包数据还没发完(我猜测,因为你的环形缓冲区没有,??? 提问 / Jack 答)。另外还有一种"一读多写"的串口策略模式可供多写入方场景参考(一读多写串口策略模式,Jack)。

三、实战复盘:收发切换导致的接收错位

半双工/收发复用场景下,只发送不接收阶段若 Rx 引脚仍保持 UART 复用输入,总线波形会被误解析成数据残留在接收寄存器,下次 enable DMA 时脏数据先入 buffer,造成数据整体后移。解法:按状态动态把 Rx 引脚在"复用输入 / 普通 DIO 输入"之间切换,不接收时彻底断开 UART 输入链路(最近在 RH850 平台调试 时,遇到一个比较典型的问题,zzz)。这条经验与 DMA 通用原则一致:DMA 只是被动搬运,先保证外设输入源干净。

四、USB 虚拟串口

单片机 USB 口可以枚举成串口当调试口用,但前提是系统完整跑起来;若枚举不成功,电脑端不会出现 COM 口(我想问一下,单片机的USB口,可以当串口使用吗?我之前USB口是用串口,用来调试,Jack)。

五、printf 重定向的三个实际问题

  • 中文乱码/无法输出:通常是编码与终端设置问题,解决方式之一见 printf无法输出中文的解决(Jack)。
  • 内存占用:标准库 printf 在重定位时会申请一块专用缓冲空间,自己实现过重定向就知道,资源紧张的项目要注意(print这种内存占用也大吗?,Jack)。
  • 优化等级破坏时序:调整编译优化等级后模拟 IIC 读不出温度——编译器会改变指令顺序、擅自循环展开,导致模拟时序变化。必须对驱动相关代码禁止优化,如 #pragma O0__attribute__((optnone)) 等(老师,调整优化等级后,我的温度读不出来了,用的模拟IIC,这怎么办,Jack)。这类问题在 printf 重定向的弱函数、以及所有靠精确时序的位操作驱动上都可能发生。

来源笔记