五路串口各自测都正常,一起跑就偶发丢帧,加栈、拉主频都不见效。结论先给:问题多半在接收路径——字节中断扛不住并发,改用 DMA 搬运加空闲中断切帧,再用环形缓冲把收数与处理解耦。本文给出 STM32F103 的实现与 512KB Flash 环形存储方案。
现场一台电力数据采集器,五路串口同时接着 485 电表、GPRS 模块、Zigbee 汇聚节点、红外抄表口和调试口。单路测试都正常,五路一起跑,一天下来总有几十帧对不上。第一反应是 CPU 不够快,于是把主频拉满、任务栈加大,丢帧照旧。结论先给:多串口丢帧很少是算力问题,绝大多数出在接收路径——用字节中断逐字节收数,中断一密集就丢;正确的做法是 DMA 搬运加空闲中断切帧,再用环形缓冲把"收"和"处理"解耦。本文按 ST 的电力数据采集器案例讲这套结构,顺带把 512KB Flash 的本地存储怎么做成不掉数的环形区讲清楚。
电力数据采集器的需求看起来零散,其实只有四条:快速接收外围采集的数据并处理、支持多种数据传输方式、被采集的数据要保存一定期限、处理后的数据能显示。ST 在应用实例里给出的方案是 STM32F103ZDT6:512KB Flash 存数据与字库,5 路串口接不同通道,通信外设全部带 DMA,另外还有双看门狗兜底。
| 需求 | 落地手段 | 关键约束 |
|---|---|---|
| 快速接收并处理 | 串口 DMA 搬运 + 空闲中断切帧 | 中断里不做解析,只投递 |
| 多种传输方式 | 5 路串口分别接 485、GPRS、Zigbee、红外与调试口 | 各口波特率与帧长差异大 |
| 数据保存一定期限 | 片内 512KB Flash 分页环形存储 | 擦写寿命、掉电完整性 |
| 处理后显示 | LCD 只刷统计值,不刷原始帧 | 刷屏不能占用接收中断 |
这张表里最容易被低估的是最后一条。显示模块刷新一次动辄几毫秒到几十毫秒,如果它和接收共用一个任务,屏幕一刷,串口缓冲区就开始堆积;堆到溢出,就是现场看到的"偶尔丢一帧"。
同一路串口,三种接收方式的差别不在代码长短,而在中断次数。以 9600bps、每帧 20 字节为例,一帧数据在线上持续约 20ms;改用 115200bps 后只有约 1.7ms。字节中断方式下,这 1.7ms 里要进 20 次中断,每次都要保存现场、读寄存器、写缓冲,五路同时来就是 100 次——中断嵌套一深,最先被牺牲的就是最后到达的那个字节。
| 接收方式 | 每帧中断次数 | CPU 占用 | 适用场景 |
|---|---|---|---|
| 主循环轮询 | 0 | 取决于轮询频率,容易漏字节 | 单路低速、实时性要求低 |
| 接收中断逐字节 | 等于字节数 | 高,随波特率线性上升 | 偶发短帧、低波特率 |
| DMA + 空闲中断 | 1 | 极低,搬运不占内核 | 多路并发、不定长帧 |
DMA 的价值不只是"省中断",而是把接收从"CPU 参与"变成"内存直写"。串口每收到一个字节,DMA 控制器自己把它搬到指定缓冲区,CPU 完全不介入;只有当一帧结束、总线空闲时,才由空闲中断通知一次。对不定长帧来说,这个"空闲"就是天然的帧边界——不用靠超时去猜。
实现上有三个细节必须做对。第一,空闲中断的判断要"读状态寄存器再读数据寄存器",顺序反了标志清不掉,中断会反复进。第二,DMA 要配成循环模式,缓冲区做成 2 的整数次幂大小的环形区,写指针永远不回头擦数据,只覆盖最老的。第三,DMA 计数寄存器记录的是"剩余可搬字节数",当前写位置要用缓冲区大小减去它来算,不能直接拿计数值当长度。
#define RX_SIZE 256 /* 必须是 2 的幂 */
static uint8_t rx_buf[RX_SIZE];
static uint16_t rx_tail; /* 消费者读位置 */
/* USART1_RX 对应 DMA1 通道 5;其余串口按 RM0008 请求映射分配 */
void uart1_dma_init(void)
{
DMA1_Channel5->CPAR = (uint32_t)&USART1->DR;
DMA1_Channel5->CMAR = (uint32_t)rx_buf;
DMA1_Channel5->CNDTR = RX_SIZE;
DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_EN;
USART1->CR3 |= USART_CR3_DMAR; /* 使能串口 DMA 接收 */
}
/* 空闲中断:只算长度并投递,不在中断里解析报文 */
void USART1_IRQHandler(void)
{
if (USART1->SR & USART_SR_IDLE) {
(void)USART1->SR; /* 先读 SR */
(void)USART1->DR; /* 再读 DR 才能清 IDLE */
uint16_t head = RX_SIZE - DMA1_Channel5->CNDTR;
ring_push(rx_tail, head); /* 把 [tail, head) 段入队 */
rx_tail = head;
}
}
这段代码里最值钱的是 RX_SIZE - CNDTR 这一句。DMA 循环模式下写指针会回绕,直接拿 CNDTR 当长度,缓冲区回绕的那一帧就会被算成超长帧,接收方按长度校验时把它当噪声丢掉——现象就是"每隔一段时间丢一帧",而且丢的间隔很规律,容易误判成电磁干扰。
五路串口不是平均分配,而是按帧的特点分工:实时性要求高的给独立缓冲加高优先级,数据量大的给大缓冲,调试口则要能被随时静音,避免日志把业务口挤掉。
| 端口 | 角色 | 帧特点 | 处理策略 |
|---|---|---|---|
| USART1 | 调试 / 本地维护 | 不定长文本 | 独立缓冲,可整体关闭 |
| USART2 | RS-485 电表 | 定长请求 + 变长应答 | 一问一答,超时重试 |
| USART3 | GPRS 上行 | 长帧、突发 | 大缓冲 + 整帧校验后再解析 |
| UART4 | Zigbee 汇聚 | 短帧、高频 | 只做入队与校验,解析下放主循环 |
| UART5 | 红外抄表 | 低速、偶发 | 低优先级,允许排队 |
分工的判据是"谁可以等"。红外抄表一天抄几次,排队几秒无所谓;485 电表是问答式,超时窗口只有几百毫秒;GPRS 上行一包可能几百字节,最怕缓冲不够被截断。把这三类混在同一个缓冲里,等于让最慢的拖住最快的。
采集器要存"一定期限"的数据,用片内 Flash 是成本最低的方案。STM32F103 高密度型号的 512KB Flash 被划分为 256 页、每页 2KB(参考手册 RM0008),按页擦除、按半字写入。关键是别把它当文件系统用,做成定长记录的环形区就够了:写满一圈就擦最老的一页,永远只往前写。
| 字段 | 长度 | 作用 |
|---|---|---|
| 记录序号 | 4 字节 | 上电扫描时定位最新记录 |
| 时间戳 | 4 字节 | 秒级时标,来自 RTC |
| 通道与长度 | 2 字节 | 标明来源串口与数据长度 |
| 数据 | 变长,上限固定 | 原始帧或压缩后的值 |
| CRC16 与有效标志 | 4 字节 | 最后写入,标志置位才算有效 |
掉电保护靠写入顺序:先写数据区和 CRC,最后才写有效标志。上电扫描时只认标志已置位、且 CRC 校验通过的记录,最后那条写了一半的记录自然被忽略。这个顺序不能颠倒,否则掉电瞬间会留下一条"标志有效但数据残缺"的记录,解析时直接把后面的数据带偏。
还有一个寿命问题。ST 的 Flash 仿真应用笔记给出的保证值是每页约 10000 次擦写;按 256 页轮流擦,一天写满 10 页、用五年计算,擦写次数还在安全范围内。但如果把配置参数、校准系数这类"改一次写一次"的数据也放进同一个环形区,寿命会被频繁改写的那几页先耗光——参数区要单独划分,并做双备份轮换。
LCD 只显示统计量:各口收帧数、校验错误数、最新一条数据、存储剩余天数。原始帧不进显示路径,屏幕刷新放在主循环里按固定节拍做,中断里只置标志。这样即使屏幕刷新偶发变慢,串口缓冲也只是多堆几帧,不会丢字节。这条经验在低端屏上尤其明显——并口屏刷一屏的耗时,足够 115200bps 的串口再收进上百个字节。
第一项是并发压力:五路同时满速灌数据,连续跑 72 小时,比对发送端与接收端的帧计数,差值必须为零。第二项是掉电:在写入过程中反复断电,每次上电后检查记录序号是否连续、有没有出现 CRC 通过但内容错乱的记录。第三项是误码:人为加入噪声,确认校验失败的帧被丢弃而不是被当有效数据存进 Flash。第四项是看门狗配合:让某个任务故意卡死,确认双看门狗能在不影响已存数据的前提下把系统拉回来。
四项里最容易省掉的是第一项。很多项目只做单路测试就交付,现场五路一开才暴露问题,而这时候改的是架构,代价最高。
一句话总结:多串口采集器的稳定性不靠更快的 CPU,而靠三件事——DMA 搬运加空闲中断把一帧的中断次数压到 1 次,环形缓冲把收数与处理解耦,Flash 分页环形区用"最后写有效标志"的顺序保住掉电完整性;三件事做到,五路并发也只是缓冲里多几帧而已。
本文基于 ST《STM32 应用实例》(2008 年 9 月全国巡回研讨会资料)中的电力数据采集器案例,并结合 STM32F103 高密度系列参考手册 RM0008 的 Flash 页结构与 ST 的片内 Flash 仿真应用笔记整理优化。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应