资讯动态

STM32F429标准库串口通信实战:从GPIO复用到环形缓冲区与DMA

发布时间:2026/10/16 3:37:29 来源:尧图企业网站定制
简介这份资源面向STM32嵌入式开发初学者与需要快速搭建串口通信原型的工程师提供基于STM32F429标准库的完整串口通信工程示例帮助解决GPIO复用配置、USART初始化、阻塞与非阻塞收发、中断与DMA传输以及错误回调处理等常见问题。压缩包共295个文件约17.11MB包含45个c源文件、46个h头文件、62个o目标文件、61个crf浏览信息文件以及uvprojx工程、sct分散加载、axf可执行文件、map映射和lst列表等覆盖从源码到编译产物的完整工程结构。目前已有1029人学习下载。工程中可看到stm32f4xx_tim、rcc、rtc、adc、flash、fmc、can等标准外设驱动便于读者对照理解串口与各外设的协同使用同时提供HAL_UART_Transmit、HAL_UART_Receive及中断、DMA、Abort等接口的调用范例并预留错误回调与RS-232、RS-485、MODBUS等协议参数调整空间适合作为调试与二次开发的参考模板。1. 串口通信在 STM32F429 上到底该怎么跑通很多人第一次拿到 STM32F429 标准库的串口例程烧进去发现串口助手一片空白或者只收到乱码第一反应是“板子坏了”。其实十有八九是时钟树没配对、GPIO 复用没打开、或者波特率算错了。这份资源就是围绕 STM32F429 标准库的串口通信展开的从 GPIO 复用配置、USART 初始化、中断收发到环形缓冲区把一条完整链路拆开讲清楚。它适合刚接触 F429 标准库、想用串口做调试输出或设备通信的开发者也适合从 HAL 库转回标准库、需要一份能直接抄的参考实现的人。串口通信看着简单但要把收发做稳里面有不少细节值得抠。2. 工程结构与串口外设初始化从时钟到引脚2.1 目录结构先看清楚拿到一份标准库工程别急着打开 main.c。先看目录心里有个数。典型的 STM32F429 标准库工程长这样Project/ ├── CMSIS/ # 内核支持文件 ├── FWlib/ │ ├── inc/ # 标准库头文件 │ └── src/ # 标准库源文件 ├── User/ │ ├── main.c # 主逻辑 │ ├── stm32f4xx_it.c # 中断服务函数 │ └── usart.c / usart.h # 串口配置 ├── Startup/ │ └── startup_stm32f429_439xx.s └── Output/ # 编译输出FWlib是 ST 官方标准库User放自己的代码Startup是启动文件。串口相关的配置一般单独抽成usart.c方便复用。我一般会把串口初始化、发送函数、接收中断都放在这个文件里main.c只负责调用。2.2 时钟使能和 GPIO 复用F429 的 USART1 挂在 APB2 上USART2/3 挂在 APB1 上。以 USART1 为例PA9 是 TXPA10 是 RX。初始化第一步是开时钟// 使能 USART1 和 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);注意 F4 系列 GPIO 挂在 AHB1 上不是 APB2这点和 F1 不一样。很多人从 F103 转过来习惯性写RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE)结果 GPIO 根本没时钟串口自然没输出。接着配置 GPIO 复用GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; // 复用模式 GPIO_InitStructure.GPIO_OType GPIO_OType_PP; // 推挽 GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_UP; // 上拉 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); // 关键一步把引脚复用到 USART1 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);GPIO_PinAFConfig这行最容易漏。F4 的引脚功能比 F1 多必须显式指定复用映射不写这行引脚就是普通 GPIO串口收发都没反应。2.3 USART 参数配置与波特率GPIO 配好之后配置 USART 本身USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 115200; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE);波特率 115200 是最常用的调试速率。标准库会根据 APB2 时钟自动算分频但前提是系统时钟配置正确。如果SystemInit里时钟没跑到 180MHz或者外部晶振频率和代码里写的不一致波特率就会偏串口助手收到的就是乱码。常见做法是先确认system_stm32f4xx.c里的 PLL 参数和板子晶振匹配再用示波器或者串口助手验证。提示如果串口助手收到乱码先查时钟配置再查波特率最后查串口助手本身的波特率设置。顺序别反。3. 收发链路拆解中断、环形缓冲与重定向3.1 发送一个字节和字符串标准库发送很简单void USART_SendByte(USART_TypeDef* USARTx, uint8_t data) { while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) RESET); USART_SendData(USARTx, data); } void USART_SendString(USART_TypeDef* USARTx, char* str) { while (*str) { USART_SendByte(USARTx, *str); } }USART_FLAG_TXE是发送数据寄存器空标志等它置位再写数据否则会覆盖上一个字节。发送字符串就是循环调用单字节发送。这个写法是阻塞的发大量数据时会占 CPU调试输出够用高速通信就得换 DMA。3.2 接收中断配置接收一般用中断不然主循环要一直轮询。配置 NVICNVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // 使能接收非空中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);然后在stm32f4xx_it.c里写中断服务函数void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { uint8_t data USART_ReceiveData(USART1); // 把数据丢进环形缓冲区 RingBuffer_Put(rx_buffer, data); USART_ClearITPendingBit(USART1, USART_IT_RXNE); } }中断里只做一件事把数据存进缓冲区赶紧退出。不要在中断里做解析、打印、延时否则容易丢字节。3.3 环形缓冲区实现环形缓冲区是串口接收的标配解决“中断来得快、主循环处理慢”的问题#define BUF_SIZE 256 typedef struct { uint8_t buffer[BUF_SIZE]; volatile uint16_t head; volatile uint16_t tail; } RingBuffer; void RingBuffer_Put(RingBuffer* rb, uint8_t data) { uint16_t next (rb-head 1) % BUF_SIZE; if (next ! rb-tail) // 缓冲区未满 { rb-buffer[rb-head] data; rb-head next; } // 满了就丢弃或者置错误标志 } uint8_t RingBuffer_Get(RingBuffer* rb, uint8_t* data) { if (rb-head rb-tail) return 0; // 空 *data rb-buffer[rb-tail]; rb-tail (rb-tail 1) % BUF_SIZE; return 1; }head和tail都用volatile修饰因为一个在中断里改一个在主循环里改。缓冲区大小 256 是常见值数据量大可以加到 512 或 1024。主循环里轮询RingBuffer_Get拿到完整帧再解析。3.4 printf 重定向到串口调试时最方便的是把printf重定向到串口。标准库工程里加这段#include stdio.h int fputc(int ch, FILE* f) { USART_SendByte(USART1, (uint8_t)ch); return ch; }然后在 Keil 里勾选Use MicroLIB否则printf可能不工作。IAR 下需要改__write或者用printf的库配置。重定向之后printf(count %d\r\n, count)就能直接在串口助手里看到。注意printf是阻塞发送在中断里调用会导致死锁或者丢数据。中断里只存缓冲区打印放到主循环。4. 避坑与排查串口不通时先查这几处4.1 现象串口助手完全没数据原因GPIO 时钟没开、复用没配、USART 没使能或者 TX/RX 接反。F429 的 PA9 是 TX要接串口模块的 RXPA10 是 RX接 TX。接反了就是没数据。解决用万用表量 TX 引脚发送时应该有电压跳动。没有跳动就查时钟和复用配置。接反了就换线。4.2 现象收到乱码原因波特率不匹配、系统时钟不对、晶振频率和代码不一致。常见的是板子用 8MHz 晶振代码里按 25MHz 算 PLL系统时钟偏了波特率也跟着偏。解决确认HSE_VALUE宏和实际晶振一致确认 PLL 参数算出来的系统时钟是 180MHz。用示波器量 TX 引脚发 0x55 时波形周期应该是 1/115200 秒的整数倍。4.3 现象偶尔丢数据原因中断里处理太慢或者缓冲区太小。中断里做printf、做浮点运算、做延时都会导致下一个字节来的时候 RXNE 还没清数据被覆盖。解决中断里只存缓冲区主循环处理。缓冲区加到 512 或 1024。如果还丢考虑用 DMA 接收。4.4 现象printf 没输出原因没勾Use MicroLIB或者fputc没包含stdio.h或者串口没初始化就调用了printf。解决Keil 里勾选 MicroLIB确认fputc函数签名正确确认USART_Cmd已经 ENABLE。4.5 现象中断进不去原因NVIC 没配置、USART_IT_RXNE没使能、中断服务函数名字写错。F429 的 USART1 中断函数名必须是USART1_IRQHandler写错一个字母就进不去。解决对照启动文件里的中断向量表确认函数名一致。用调试器在中断函数里打断点看能不能命中。5. 二次开发从能跑到好用5.1 加一层协议解析裸串口只能收发字节实际项目里一般要加帧头、长度、校验。我一般会定义一个简单协议typedef struct { uint8_t header; // 0xAA uint8_t cmd; uint8_t len; uint8_t data[64]; uint8_t checksum; // 累加和 } ProtocolFrame;主循环里从环形缓冲区取字节状态机解析等帧头、读命令、读长度、读数据、校验。校验不过就丢弃重新等帧头。这样即使丢了一个字节也能快速重新同步。5.2 用 DMA 替代中断接收数据量大或者波特率高的时候中断接收会占 CPU。F429 的 USART1 支持 DMA 接收配置好 DMA 流之后串口收到的数据直接搬到内存CPU 完全不用管。常见做法是 DMA 加空闲中断DMA 负责搬数据空闲中断负责标记一帧结束。这样既高效又不会丢字节。5.3 发送用 DMA 加环形队列发送也可以用 DMA。把要发的数据放进发送环形队列DMA 自动搬运。发送完成中断里检查队列还有没有数据有就继续发。这样printf也不会阻塞主循环。5.4 调试习惯从那以后我每次调串口都强制走一遍先量 TX 引脚有没有波形再看波特率对不对最后看数据内容。这三步能解决九成以上的串口问题。另外串口助手的波特率、数据位、停止位、校验位要和代码里完全一致别只改代码不改助手。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑