资讯动态

STM32串口通信实战:从协议原理到中断/DMA传输与软件仿真波特率测量

发布时间:2026/10/19 5:19:05 来源:尧图企业网站定制
STM32串口通信实战从协议原理到中断/DMA传输与软件仿真波特率测量前言本文基于 STM32F103C8T6 最小系统板完整记录了串口通信实验的全过程涵盖串口协议基础、RS-232/RS-485 电平标准、CH340 USB 转 TTL 模块原理、PC 间串口文件传输、STM32 与上位机的基础通信、中断与 DMA 方式通信以及使用 Keil 软件仿真逻辑分析仪测量实际波特率。一、基础理论1.1 串口协议概述串口通信是指外设和计算机之间通过数据信号线按位进行传输数据的一种通信方式。其特点是通信线路简单只需一对传输线即可实现双向通信特别适合远距离通信。关键参数参数说明实验取值波特率每秒传输的二进制位数bps115200数据位一次传输的数据位数8位停止位用于表示单个数据包的结束1位校验位用于数据校验无校验一帧数据格式8N1起始位(1位低电平) 数据位(8位LSB先发) 停止位(1位高电平)共10位。1.2 RS-232、RS-485 与 TTL 电平区别标准电平标准逻辑0逻辑1传输距离抗干扰典型应用TTL0~5V0V3.3V/5V短板级弱芯片间通信RS-232±15V3~15V-3~-15V15m一般PC 串口、短距离RS-485差分信号ABAB1200m强工业现场、长距离核心区别TTL 电平单片机直接使用的电平0V 表示逻辑 03.3V/5V 表示逻辑 1仅适合板内或极短距离通信RS-232 电平采用负逻辑3V~~15V 表示逻辑 0-3V~~-15V 表示逻辑 1电平摆幅大抗干扰能力比 TTL 强RS-485 电平采用差分信号A 线和 B 线通过两根线的电压差判断逻辑抗共模干扰能力极强支持多节点总线和长距离传输1.3 CH340 USB转TTL模块工作原理CH340 是沁恒微电子推出的 USB 总线转接芯片可实现 USB 转串口功能。工作原理CH340 芯片通过 USB 接口与 PC 连接在 PC 端虚拟出一个 COM 口如 COM4芯片内部完成 USB 协议与 UART 协议的双向转换TXD 引脚输出 TTL 电平的串口数据RXD 引脚接收 TTL 电平数据PC 端应用程序操作虚拟 COM 口数据经 USB→CH340→TXD 输出到外部设备外部设备数据经 RXD→CH340→USB 上传到 PC接线要点交叉连接CH340 TXD → STM32 PA10(RX)CH340 RXD → STM32 PA9(TX)CH340 GND → STM32 GND必须接共地是通信的前提二、PC间串口文件传输实验2.1 实验 Setup两台笔记本电脑各通过一个 CH340 USB 转 TTL 模块用杜邦线交叉连接电脑 A CH340 TXD → 电脑 B CH340 RXD电脑 A CH340 RXD → 电脑 B CH340 TXD两端 GND 相连使用 SSCOM 串口助手进行文件传输。2.2 传输时间预估与实际对比理论计算公式传输时间秒 文件大小字节 × 10 位/字节 ÷ 波特率bps8N1 格式下每字节实际传输 10 位1 起始位 8 数据位 1 停止位以 115200 波特率为例理论速率 115200 ÷ 10 11520 字节/秒 ≈ 11.25 KB/s传输 1MB 文件理论时间 ≈ 1024KB ÷ 11.25KB/s ≈ 91 秒实际测试结果文件大小波特率理论时间实际时间实际速率效率512KB115200~46s~55s~9.3KB/s~83%1MB115200~91s~108s~9.5KB/s~84%2MB115200~182s~215s~9.5KB/s~85%实际时间大于理论时间的原因串口助手软件有协议开销如 XMODEM/YMODEM 协议的握手、校验、重传USB 转串口芯片有缓冲和调度延迟PC 端操作系统的调度延迟实际有效数据率通常为理论值的 80%~90%2.3 不接GND能否传输结论不能正常传输。原因分析串口通信是基于电压差来判断逻辑 0 和逻辑 1 的发送端的电平是相对于自身 GND 的如果不共地两端的 GND 之间可能存在电位差几伏甚至更高发送端输出的 3.3V 高电平在接收端看来可能是 0V 甚至负电压导致逻辑判断错误即使短距离内偶尔能通信也是不稳定的数据会大量出错GND 是信号的参考基准没有参考基准电压信号就没有意义三、任务一STM32基础串口通信3.1 CubeMX配置新建工程选择 STM32F103C8T6配置 RCCHSE 外部晶振配置 SYSDebug 选 Serial Wire配置 USART1Mode 选 Asynchronous波特率 1152008 位数据1 位停止无校验PA9 自动设为 USART1_TXPA10 自动设为 USART1_RX时钟配置HSE 8MHz → PLL ×9 → 72MHz系统时钟生成代码选择MDK-ARM3.2 连续发送 “hello windows!”在main.c的 while 循环中添加HAL_UART_Transmit(huart1, (uint8_t*)hello windows!\r\n, 16, HAL_MAX_DELAY); HAL_Delay(1000);不加延时的丢包现象如果去掉HAL_Delay(1000)连续不断发送上位机端会出现数据丢失。原因STM32 以 115200 bps 连续发送数据量约 11.5 KB/s串口助手的接收缓冲区有限如果 UI 刷新或保存不及时缓冲区溢出导致丢数据实际测试中连续发送约30秒后串口助手显示的字符数明显少于发送量3.3 扩展功能#暂停 / *继续需求上位机发送#后 STM32 暂停发送发送*后恢复发送。实现方案查询方式uint8_t tx_enable 1; // 发送使能标志 uint8_t rx_byte; // 接收字节 while (1) { // 查询是否收到数据 if(HAL_UART_Receive(huart1, rx_byte, 1, 0) HAL_OK) { if(rx_byte #) tx_enable 0; if(rx_byte *) tx_enable 1; } if(tx_enable) { HAL_UART_Transmit(huart1, (uint8_t*)hello windows!\r\n, 16, HAL_MAX_DELAY); } HAL_Delay(1000); }四、任务二中断与DMA方式通信4.1 任务二1串口中断方式重做问题背景查询方式下连续快速发送会丢字符多次发送会卡死。根因是阻塞式HAL_UART_Transmit和HAL_UART_Receive在中断上下文或高频调用时产生 ORE溢出错误。解决方案环形缓冲区 中断接收4.1.1 usart.c 修改/* USER CODE BEGIN 0 */ uint8_t rx_buf[128]; // 环形缓冲区 volatile uint8_t rx_head 0; // 写入指针 volatile uint8_t rx_tail 0; // 读取指针 uint8_t rx_byte; // 中断接收单字节 uint8_t tx_enable 1; // 发送使能标志 uint8_t echo_pending 0; // 有待回显的字节 uint8_t echo_byte; // 待回显字节 // 缓冲区是否有数据 uint8_t buf_has_data(void) { return rx_head ! rx_tail; } // 从缓冲区读取一个字节 uint8_t buf_read(void) { uint8_t data rx_buf[rx_tail]; rx_tail (rx_tail 1) % 128; return data; } // 中断接收完成回调 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { if(rx_byte #) tx_enable 0; else if(rx_byte *) tx_enable 1; else { // 存入环形缓冲区回显用 uint8_t next (rx_head 1) % 128; if(next ! rx_tail) { rx_buf[rx_head] rx_byte; rx_head next; } } // 重新开启中断接收 HAL_UART_Receive_IT(huart1, rx_byte, 1); } } /* USER CODE END 0 */在MX_USART1_UART_Init末尾开启中断接收/* USER CODE BEGIN USART1_Init 2 */ HAL_UART_Receive_IT(huart1, rx_byte, 1); /* USER CODE END USART1_Init 2 */4.1.2 main.c 主循环while (1) { // 1. 从接收缓冲区取数据暂存待回显 if(buf_has_data() !echo_pending) { echo_byte buf_read(); echo_pending 1; } // 2. 优先回显其次周期性发送 if(echo_pending) { HAL_UART_Transmit(huart1, echo_byte, 1, HAL_MAX_DELAY); echo_pending 0; } else if(tx_enable) { static uint32_t last_tick 0; if(HAL_GetTick() - last_tick 1000) { last_tick HAL_GetTick(); HAL_UART_Transmit(huart1, (uint8_t*)hello windows!\r\n, 16, HAL_MAX_DELAY); } } }中断方式的优势接收完全由中断处理主循环无需轮询不丢字符环形缓冲区隔离了中断上下文和主循环避免竞态连续快速发送不再卡死4.2 任务二3串口DMA方式连续发送4.2.1 DMA原理DMADirect Memory Access直接存储器访问允许外设和内存之间直接传输数据无需 CPU 干预。CPU 只需配置好 DMA 通道和传输参数数据传输由 DMA 控制器自动完成传输完成后触发中断通知 CPU。USART1_TX对应的DMA通道DMA1_Channel44.2.2 usart.c DMA初始化/* USER CODE BEGIN 0 */ uint8_t uart_tx_busy 0; // DMA发送忙标志 // DMA发送完成回调 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart-Instance USART1) { uart_tx_busy 0; } } // 手动初始化DMA1_Channel4USART1_TX void USART1_DMA_Init(void) { __HAL_RCC_DMA1_CLK_ENABLE(); hdma_usart1_tx.Instance DMA1_Channel4; hdma_usart1_tx.Init.Direction DMA_MEMORY_TO_PERIPH; hdma_usart1_tx.Init.PeriphInc DMA_PINC_DISABLE; hdma_usart1_tx.Init.MemInc DMA_MINC_ENABLE; hdma_usart1_tx.Init.PeriphDataAlignment DMA_PDATAALIGN_BYTE; hdma_usart1_tx.Init.MemDataAlignment DMA_MDATAALIGN_BYTE; hdma_usart1_tx.Init.Mode DMA_NORMAL; hdma_usart1_tx.Init.Priority DMA_PRIORITY_HIGH; HAL_DMA_Init(hdma_usart1_tx); __HAL_LINKDMA(huart1, hdmatx, hdma_usart1_tx); HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn); } /* USER CODE END 0 */在MX_USART1_UART_Init中调用/* USER CODE BEGIN USART1_Init 2 */ USART1_DMA_Init(); HAL_UART_Receive_IT(huart1, rx_byte, 1); /* USER CODE END USART1_Init 2 */4.2.3 STM32F1xx_IT.c 添加中断处理/* USER CODE BEGIN Includes */ #include main.h /* USER CODE END Includes */ /* USER CODE BEGIN EV */ extern DMA_HandleTypeDef hdma_usart1_tx; /* USER CODE END EV */ void DMA1_Channel4_IRQHandler(void) { HAL_DMA_IRQHandler(huart1.hdmatx); }4.2.4 main.c 主循环DMA发送while (1) { // 1. 从接收缓冲区取数据 if(buf_has_data() !echo_pending) { echo_byte buf_read(); echo_pending 1; } // 2. DMA空闲时发起发送 if(!uart_tx_busy) { if(echo_pending) { uart_tx_busy 1; HAL_UART_Transmit_DMA(huart1, echo_byte, 1); echo_pending 0; } else if(tx_enable) { static uint32_t last_tick 0; if(HAL_GetTick() - last_tick 1000) { last_tick HAL_GetTick(); uart_tx_busy 1; HAL_UART_Transmit_DMA(huart1, (uint8_t*)hello windows!\r\n, 16); } } } }DMA 方式的优势发送过程 CPU 完全不参与可以同时处理其他任务适合大数据量连续传输CPU 占用率极低115200 bps 下发送 16 字节仅需约 1.4 msDMA 自动完成五、任务三Keil软件仿真逻辑分析仪测量波特率5.1 配置软件仿真点击Options for Target魔术棒→Debug选项卡选择Use SimulatorParameter填写-pSTM32F103C8点击OK保存5.2 逻辑分析仪配置与波形观察CtrlF5 进入调试模式View → Analysis Windows → Logic Analyzer打开逻辑分析仪点击Setup→New输入PORTA.9USART1_TX 对应 PA9Display Type 设为Bit按 F5 运行仿真约 10 秒后停止点击Zoom In放大至 Grid 为 10 μs可清晰观察到标准 UART 帧波形波形时序分析空闲状态高电平正确起始位1位低电平正确数据位8位LSB first正确停止位1位高电平正确以字符h0x68 01101000为例波形电平变化与理论完全一致时序状态正确。5.3 光标测量与波特率计算勾选Cursor将蓝色光标移至起始位下降沿红色光标移至下一个跳变沿底部显示时间差即位时间。测量结果位时间 Δt ≈8.68 μs实际波特率1Δt18.68×10−6≈115207 bps \text{实际波特率} \frac{1}{\Delta t} \frac{1}{8.68 \times 10^{-6}} \approx 115207\ \text{bps}实际波特率Δt1​8.68×10−61​≈115207bps项目值设定波特率115200 bps实际测量波特率~115207 bps相对误差~0.006%实际波特率与设定值几乎完全一致误差在晶振公差和测量精度允许范围内说明 USART 波特率发生器配置正确时序状态完全正确。六、实验中遇到的问题与解决6.1 串口助手收不到数据排查过程波特率误设为 9600 → 修正为 115200收发模式误选 HEX → 修正为文本模式不知道串口号 → 设备管理器查看 CH340 对应的 COM 口接线错误 → TX/RX 必须交叉GND 必须共地6.2 回显丢字符现象回显时偶尔丢字符如 “hello windows!” 变成 “hello winows!”。原因中断回调中直接调用阻塞式HAL_UART_Transmit导致下一个字节的接收中断被延迟产生 ORE 溢出错误。解决改用环形缓冲区中断只存字节主循环负责发送隔离中断上下文和阻塞操作。6.3 连续发送卡死现象连续快速点击发送多次后STM32 完全无响应。原因ORE 溢出错误未清除USART 处于错误状态后续接收全部失败。解决环形缓冲区方案从根本上避免了 ORE必要时在错误回调中调用__HAL_UART_CLEAR_OREFLAG清除溢出标志。七、总结本次实验系统学习了串口通信的完整知识体系理论层面掌握了串口协议、RS-232/RS-485/TTL 电平区别、CH340 工作原理理解了共地对串口通信的重要性基础通信实现了 STM32 向上位机连续发送字符串以及#/*控制启停中断方式通过环形缓冲区 中断接收解决了丢字符和卡死问题理解了中断上下文与主循环的隔离原则DMA方式实现了 DMA 连续发送CPU 零拷贝适合大数据量传输软件仿真掌握了Keil逻辑分析仪的使用成功观察串口波形并测量了实际波特率核心收获串口通信看似简单但在实际工程中需要关注缓冲区设计、错误处理、中断优先级、DMA配置等多个细节。软件仿真是调试时序问题的有力工具。附录项目文件结构cubemx/ ├── 1/ # 任务一任务二(1) 中断方式 │ ├── Core/ │ │ ├── Inc/ │ │ │ ├── main.h │ │ │ └── usart.h # 函数声明、extern变量 │ │ └── Src/ │ │ ├── main.c # 主循环回显周期发送#/*控制 │ │ ├── usart.c # 环形缓冲区中断回调 │ │ └── stm32f1xx_it.c # 中断处理 │ └── MDK-ARM/ │ └── 1.uvprojx # Keil工程 └── 2/ # 任务二(3) DMA方式 任务三仿真 ├── Core/ │ ├── Inc/ │ │ └── usart.h │ └── Src/ │ ├── main.c # DMA发送主循环 │ ├── usart.c # DMA初始化发送完成回调环形缓冲 │ └── stm32f1xx_it.c # DMA1_Channel4_IRQHandler └── MDK-ARM/ └── 1.uvprojx本文基于STM32F103C8T6 HAL库 Keil5 STM32CubeMX完成所有代码均在硬件上验证通过。AI在本次项目中起到了在关键步骤提供教学的作用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑