STM32F103 USART3配置三重关:时钟、复用、中断优先级详解

发布时间:2026/9/12 5:16:56
STM32F103 USART3配置三重关:时钟、复用、中断优先级详解 简介本资源是一份面向嵌入式初学者与STM32F10x开发者的串口通信实战项目聚焦UART3模块的中断式收发实现解决实际开发中串口配置复杂、中断响应不及时、收发不同步等典型问题。压缩包共172个文件含29个C源文件.c与29个头文件.h构成完整工程框架29个编译中间文件.o、28个依赖文件.d及调试相关文件.axf、.hex、.map、.lst另有Keil工程文件.uvproj、.uvopt、启动汇编.s和硬件配置说明.txt总大小4.26MB结构规范适合作为学习参考或工程移植基础。已有427人学习下载资源包含可直接编译运行的完整工程涵盖UART3初始化函数、中断服务程序、收发封装接口及主循环测试逻辑并体现GPIO重映射、RCC时钟配置、NVIC优先级设置等关键细节助读者深入理解STM32底层通信机制与中断驱动编程范式。1. STM32串口3实验不是换个USART编号就能跑通它卡在时钟、引脚复用和中断优先级三道关上你解压STM32_串口实验.rar打开 Keil 工程把usart3_init()函数复制进自己项目烧写后发现串口助手收不到任何数据——这不是代码写错了而是 STM32F10x 系列中 USART3 的物理实现比 UART1/UART2 更“挑剔”。它不接在 APB2高速总线上而挂在 APB1低速总线它的 TX/RX 引脚PB10/PB11默认是普通 GPIO必须手动开启重映射更关键的是它的中断向量号IRQn在 NVIC 中排位靠后若主程序开了 SysTick 或其他高优先级中断USART3 接收中断可能被持续屏蔽。这个实验表面是“串口3”实则是检验你对 STM32 时钟树配置、AFIO 复用控制、NVIC 分组策略的实战理解。适合已能用 USART1 实现基本通信、正尝试扩展多串口外设如连接 GPS 模块 485 转换器 调试日志通道的嵌入式开发者。2. 从寄存器级确认 USART3 的硬件约束为什么 PB10/PB11 必须重映射且不能用 PA14/PA152.1 查手册确认 USART3 的真实引脚映射与复用条件STM32F103C8T6 数据手册第 29 页明确列出USART3 的 TX 只能映射到 PB10默认或 PC10部分重映射RX 只能映射到 PB11默认或 PC11部分重映射。PA14/PA15 是 SWD 调试接口引脚硬编码为调试功能不可用于 USART3——这是新手常踩的坑误以为“所有带 AF 功能的引脚都能自由分配”。而 PB10/PB11 默认为 GPIO 模式需通过 AFIO-PCRC 寄存器使能重映射否则即使配置了 USART3_CR10x2000UE1TX 引脚也无波形输出。2.2 配置 APB1 时钟并验证 USART3 时钟使能USART3 属于 APB1 总线外设其时钟由 RCC-APB1ENR 的第 18 位USART3EN控制。常见错误是只开 RCC-APB2ENR 的 IOPBENGPIOB 时钟却遗漏 APB1ENR 的 USART3EN。以下是最小初始化代码Keil MDK基于标准外设库// 1. 开启 GPIOB 和 USART3 时钟 RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_USART3, ENABLE); // 2. 配置 PB10/PB11 为复用推挽输出TX和浮空输入RX GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Pin GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; // TX 必须 AF_PP GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStruct); // 3. 关键启用 USART3 的部分重映射Partial Remap // 注意不是 Full RemapFull Remap 将 TX/RX 移至 PC10/PC11但需额外检查 PC 时钟 AFIO-MAPR ~AFIO_MAPR_USART3_REMAP; // 清除全重映射位 AFIO-MAPR | AFIO_MAPR_USART3_REMAP_PARTIAL; // 设置部分重映射PB10/PB11 生效 // 4. 初始化 USART3 参数波特率 1152008N1 USART_InitTypeDef USART_InitStruct; USART_InitStruct.USART_BaudRate 115200; USART_InitStruct.USART_WordLength USART_WordLength_8b; USART_InitStruct.USART_StopBits USART_StopBits_1; USART_InitStruct.USART_Parity USART_Parity_No; USART_InitStruct.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStruct.USART_Mode USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART3, USART_InitStruct); USART_Cmd(USART3, ENABLE);提示AFIO-MAPR寄存器操作必须在RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO, ENABLE)之后执行否则写入无效。标准库中常被忽略的一步是显式开启 AFIO 时钟RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_AFIO, ENABLE);2.3 验证引脚复用是否生效用示波器抓 PB10 波形烧写后用示波器探头接触 PB10发送单字节如A应看到起始位低电平、8 位数据LSB 先、停止位高电平组成的完整 UART 帧。若无波形立即检查三点RCC_APB1ENR的USART3EN位是否为 1读寄存器确认AFIO_MAPR的USART3_REMAP_PARTIAL是否已置位非清零状态GPIOB-CRH寄存器中 PB10 对应的 4bit 是否为0b1010AF_PP 模式。3. 用中断方式可靠接收数据解决“只收第一个字节”和“接收缓冲区溢出”的双重陷阱3.1 USART3 中断服务函数的正确结构必须清除 RXNE 标志 处理溢出USART3 的接收中断触发条件是RXNE读数据寄存器非空标志但若未及时读取USART3-DR下次接收会触发ORE溢出错误导致后续数据丢失。标准库中USART_GetITStatus(USART3, USART_IT_RXNE)返回真时必须立刻执行USART_ReceiveData(USART3)否则ORE会被置位且RXNE不再触发。以下是安全的中断处理模板// 在 stm32f10x_it.c 中定义 extern uint8_t rx_buffer[64]; extern uint16_t rx_head, rx_tail; void USART3_IRQHandler(void) { uint8_t temp; USART_TypeDef* USARTx USART3; // 1. 检查 RXNE 中断接收完成 if (USART_GetITStatus(USARTx, USART_IT_RXNE) ! RESET) { temp USART_ReceiveData(USARTx); // 关键必须读 DR 清除 RXNE // 2. 检查 ORE 错误若未及时读 DRORE 会置位 if (USART_GetFlagStatus(USARTx, USART_FLAG_ORE) ! RESET) { USART_ClearFlag(USARTx, USART_FLAG_ORE); // 清除 ORE 标志 // 此处可加错误计数器记录溢出次数 } // 3. 存入环形缓冲区避免阻塞 ISR if ((rx_head 1) % 64 ! rx_tail) { // 检查缓冲区未满 rx_buffer[rx_head] temp; rx_head (rx_head 1) % 64; } } }注意USART_ClearFlag(USARTx, USART_FLAG_ORE)必须在读取DR后调用因为读DR操作本身会清除RXNE但ORE需显式清除。若省略此步ORE将持续置位RXNE中断被屏蔽。3.2 NVIC 配置为 USART3 分配足够高的抢占优先级USART3 的 IRQn 是USART3_IRQn值为 39在stm32f10x.h中定义。若系统中同时使用 SysTickIRQn15、EXTI0IRQn6等中断需确保 USART3 的抢占优先级高于它们否则接收中断可能被延迟甚至丢弃。配置示例使用标准库NVIC_InitTypeDef NVIC_InitStruct; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2bit 抢占 2bit 响应 NVIC_InitStruct.NVIC_IRQChannel USART3_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 1; // 抢占优先级 1高于 SysTick 的 0不SysTick 固定为 0 NVIC_InitStruct.NVIC_IRQChannelSubPriority 0; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStruct);关键点SysTick 中断的抢占优先级固定为 0最高无法修改。因此 USART3 的抢占优先级必须设为 0 或 1。若设为 1则当 SysTick 执行时USART3 中断会被挂起若设为 0则与 SysTick 同级依赖响应优先级决定谁先执行。实践中将 USART3 设为PreemptionPriority0是稳妥选择。3.3 环形缓冲区管理避免因主循环处理慢导致丢包主循环中需轮询rx_head和rx_tail提取有效数据。以下为安全读取函数uint8_t USART3_ReadByte(uint8_t *data) { if (rx_head rx_tail) return 0; // 缓冲区空 *data rx_buffer[rx_tail]; rx_tail (rx_tail 1) % 64; return 1; } // 主循环中调用 uint8_t byte; while (USART3_ReadByte(byte)) { // 处理 byte例如解析协议帧 if (byte \n) { /* 完整命令结束 */ } }4. 用串口调试助手验证通信解决 CH340 驱动识别失败、COM 端口消失、波特率错乱三大现象4.1 Windows 下 CH340 驱动安装与端口确认STM32_串口实验.rar中的硬件板通常使用 CH340G 芯片做 USB 转串口。Windows 10/11 默认可能拒绝加载未签名驱动需手动操作下载官方 CH340 驱动v3.5.2022.12.14 或更新版解压后右键“以管理员身份运行”CH341SER.EXE插入开发板打开设备管理器 → “端口COM 和 LPT”确认出现USB-SERIAL CH340 (COMx)若显示“未知设备”或“感叹号”右键 → “更新驱动程序” → “浏览我的计算机” → 选择驱动文件夹中的CH341SER.INF。提示Ubuntu 用户需加载ch341内核模块sudo modprobe ch341并添加 udev 规则赋予串口权限/etc/udev/rules.d/99-ch340.rules中添加SUBSYSTEMusb, ATTR{idVendor}1a86, ATTR{idProduct}7523, MODE0666。4.2 串口调试助手参数设置匹配 STM32 实际波特率与帧格式使用 XCOM、SSCOM 或 Tera Term 时必须严格匹配 STM32 初始化参数波特率115200若代码中设为 9600则此处必须同步改数据位8停止位1校验位None流控None。常见错误是调试助手设为“16 进制发送”但 STM32 程序按 ASCII 处理导致发送0x41显示为A而助手显示41误判为通信失败。4.3 排查“发送正常但接收无响应”的硬件链路若调试助手能收到 STM32 发送的数据但发送指令后 STM32 无反应按顺序排查检查项方法预期结果TX/RX 线是否反接用万用表测开发板 CH340 的 TXD 引脚接 STM32 的 RX是否连到 PB11CH340_TXD ↔ PB11CH340_RXD ↔ PB10CH340 供电是否稳定测 CH340 VCC 引脚电压应为 5V 或 3.3V看板子设计波动 ±0.2V 会导致通信异常PB11 是否被外部电路拉低断开所有外设仅留 CH340 连接测 PB11 对地电阻应 10kΩ浮空输入状态5. 进阶技巧用 DMA 实现零 CPU 占用接收 自动帧识别解决长数据流粘包问题5.1 配置 USART3 与 DMA1 Channel3 的绑定关系USART3_RX 固定使用 DMA1 的 Channel3见 RM0008 第 127 页需手动关联。关键步骤开启 DMA1 时钟RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_DMA1, ENABLE)配置 DMA 初始化结构体指定外设地址为USART3-DR内存地址为rx_dma_buffer设置DMA_DIR_PeripheralSRC外设到内存DMA_MemoryInc_Enable内存地址自增必须关闭 USART3 的 RXNE 中断否则 DMA 与中断同时触发会导致数据错乱。DMA_InitTypeDef DMA_InitStruct; DMA_DeInit(DMA1_Channel3); DMA_InitStruct.DMA_PeripheralBaseAddr (uint32_t)USART3-DR; DMA_InitStruct.DMA_MemoryBaseAddr (uint32_t)rx_dma_buffer; DMA_InitStruct.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStruct.DMA_BufferSize 128; DMA_InitStruct.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode DMA_Mode_Circular; // 循环模式持续接收 DMA_InitStruct.DMA_Priority DMA_Priority_High; DMA_InitStruct.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel3, DMA_InitStruct); // 使能 DMA 传输完成中断非半传输 DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, ENABLE); // TC Transfer Complete // 启动 DMA DMA_Cmd(DMA1_Channel3, ENABLE); // 最后一步使能 USART3 的 DMA 接收请求 USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);5.2 在 DMA 传输完成中断中解析帧头帧尾DMA 设置为 Circular 模式后不会自动停止需在DMA1_Channel3_IRQHandler中检测帧边界。假设协议以0xAA开头、0x55结尾volatile uint16_t dma_rx_index 0; void DMA1_Channel3_IRQHandler(void) { if (DMA_GetITStatus(DMA1_IT_TC3) ! RESET) { DMA_ClearITPendingBit(DMA1_IT_TC3); // 扫描最近 128 字节查找 0xAA → ... → 0x55 模式 for (uint16_t i 0; i 128 - 1; i) { if (rx_dma_buffer[i] 0xAA rx_dma_buffer[(i127)%128] 0x55) { // 提取完整帧长度可变需协议定义 parse_frame(rx_dma_buffer[i], 128); break; } } } }技巧dma_rx_index可通过DMA_GetCurrDataCounter(DMA1_Channel3)获取当前写入位置避免全缓冲区扫描提升效率。5.3 用 CubeMX 快速生成 USART3DMA 工程的避坑指南若用 STM32CubeMX 生成代码务必注意在 Pinout Configuration → Connectivity → USART3 中勾选 Asynchronous并点击右侧 DMA Settings → 添加 RX 请求模式选 Circular不要勾选 Global interrupt即禁用 RXNE 中断否则生成的HAL_UART_RxCpltCallback()会与 DMA 冲突在 Project Manager → Code Generator 中勾选 Generate peripheral initialization as a pair of .c/.h files避免 HAL 库覆盖你的 DMA 配置。实际测试中DMA 方式下 CPU 占用率从中断方式的 30% 降至 0.5%特别适合需要同时处理 ADC 采样、PWM 输出、I2C 传感器读取的复合型项目。本文还有配套的精品资源点击获取