1. STM32F103C8T6多串口通信基础STM32F103C8T6作为经典的Cortex-M3内核微控制器内置了多达3个独立的USART模块。在实际项目中经常需要同时与多个外设进行串行通信比如同时连接蓝牙模块、GPS模块和调试终端。这时候就需要充分利用芯片的多串口资源。我刚开始接触多串口项目时遇到过数据错乱的问题。后来发现是因为没有处理好中断优先级导致高优先级串口的数据把低优先级串口的缓冲区冲掉了。这里分享下我的解决方案每个USART模块都有独立的时钟使能位USART1挂载在APB2总线最高72MHzUSART2/3挂载在APB1总线最高36MHz引脚分配建议如下表串口TX引脚RX引脚复用功能重映射USART1PA9PA10PB6/PB7USART2PA2PA3PD5/PD6USART3PB10PB11PC10/PC11提示使用重映射功能可以优化PCB布线但要注意GPIO时钟必须先使能2. 三串口并行配置实战2.1 硬件初始化关键步骤配置多串口时最容易遗漏的是时钟使能。我建议按照这个顺序操作先开启GPIO端口时钟再开启USART模块时钟最后配置复用功能以USART1为例的初始化代码片段// 使能时钟不能漏 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOA, GPIO_InitStructure); // 配置RX为浮空输入 GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure);2.2 中断优先级优化方案当三个串口同时工作时合理的NVIC配置至关重要。我的经验是将最关键的通信接口设为最高优先级如USART1用于调试确保子优先级不同避免抢占冲突在RTOS中要考虑任务调度的影响推荐的中断优先级配置// USART1配置较高优先级 NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // USART2配置中等优先级 NVIC_InitStructure.NVIC_IRQChannel USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; // USART3配置较低优先级 NVIC_InitStructure.NVIC_IRQChannel USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;3. 数据收发冲突规避策略3.1 双缓冲区的实现技巧直接操作寄存器容易引发竞争条件。我采用双缓冲区方案前台缓冲区中断服务程序专用后台缓冲区主程序处理专用以USART2为例的改进代码// 在头文件中定义双缓冲区 typedef struct { u8 buffer[2][USART_REC_LEN]; volatile u8 active_idx; u16 length[2]; } DoubleBuffer; // 中断服务程序中 void USART2_IRQHandler(void) { static u8 idx 0; u8 data USART2-DR; if(current_buffer-length[idx] USART_REC_LEN-1) { current_buffer-buffer[idx][current_buffer-length[idx]] data; } if(data \n) { // 帧结束标志 current_buffer-active_idx ^ 1; // 切换缓冲区 idx current_buffer-active_idx; current_buffer-length[idx] 0; // 重置新缓冲区 } }3.2 硬件流控的应用当传输大量数据时建议启用硬件流控USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_RTS_CTS;需要连接对应的RTS/CTS引脚通常是PA1/PA0用于USART24. LED控制案例实战解析4.1 状态机实现命令解析用状态机处理串口命令更可靠。下面是我在项目中验证过的方案typedef enum { CMD_IDLE, CMD_LED_ON, CMD_LED_OFF } LedCmdState; void ProcessUSARTCommands(void) { static LedCmdState state CMD_IDLE; switch(state) { case CMD_IDLE: if(strstr((char*)USART2_RX_BUF, LED_ON)) { state CMD_LED_ON; } break; case CMD_LED_ON: GPIO_ResetBits(GPIOB, GPIO_Pin_0); // LED亮 state CMD_IDLE; break; // 其他状态处理... } }4.2 多串口协同工作调试调试多串口系统时我总结了几点经验给每个串口分配不同颜色的LED指示灯使用逻辑分析仪同时捕捉多路串口信号在中断入口/出口加调试引脚电平翻转// 调试技巧测量中断响应时间 GPIO_SetBits(GPIOB, GPIO_Pin_1); // 进入中断时拉高 // ...中断处理代码... GPIO_ResetBits(GPIOB, GPIO_Pin_1); // 退出中断时拉低遇到最棘手的问题是USART3偶尔丢失数据后来发现是PB11引脚被意外配置成了输出模式。这个坑让我花了整整两天时间排查现在都会在初始化后二次检查GPIO配置。