STM32F407软件Bootloader UART数据接收异常问题排查
STM32F407 Bootloader UART固件传输丢包问题解决
核心问题根源
STM32执行Flash擦除/写入时会占用CPU较长时间(尤其是扇区擦除),如果UART接收用的是轮询方式,或者在Flash操作时全局关闭了中断,PC发送的后续数据会因为MCU没法及时响应UART接收中断,导致接收缓冲区溢出丢包。
针对性解决方法
1. 修复Flash操作时的中断处理
检查Flash操作代码里有没有__disable_irq()这类全局关中断的调用,要是有,别直接全局关,改成临时保存中断状态,操作完再恢复,确保UART接收中断不被关闭。
示例修正:
// 原错误写法 __disable_irq(); FLASH_ProgramDoubleWord(flash_addr, data); __enable_irq(); // 修正后:只临时关中断,保留原有中断状态 uint32_t saved_primask = __get_PRIMASK(); __disable_irq(); FLASH_ProgramDoubleWord(flash_addr, data); __set_PRIMASK(saved_primask); // 恢复之前的中断配置
2. 改用UART DMA接收
把UART接收换成DMA模式,DMA能在CPU处理Flash操作时自动把UART数据存到缓冲区,完全不占用CPU,从根本上避免丢包。
示例代码片段:
uint8_t uart_dma_rx_buf[512]; // 开足够大的缓冲区 // 初始化DMA接收 HAL_UART_Receive_DMA(&huart1, uart_dma_rx_buf, BLOCK_SIZE); // DMA接收完成回调函数,处理固件块 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { // 处理刚收到的固件块 handle_firmware_block(uart_dma_rx_buf, BLOCK_SIZE); // 重启DMA接收下一块 HAL_UART_Receive_DMA(&huart1, uart_dma_rx_buf, BLOCK_SIZE); } }
3. 给上位机加ACK应答机制
修改Python上位机,每发一块固件就等MCU回ACK信号,确认MCU处理完当前块(包括Flash写入)后再发下一块,彻底避免MCU忙不过来的情况。
修正后的Python代码片段:
import serial ser = serial.Serial('COM3', 115200, timeout=3) firmware_data = open('main_app.bin', 'rb').read() block_size = 256 for idx in range(0, len(firmware_data), block_size): current_block = firmware_data[idx:idx+block_size] # 发送当前块 ser.write(current_block) # 等待MCU的ACK(假设ACK是0x06) ack = ser.read(1) while ack != b'\x06': ack = ser.read(1) if not ack: print("等待ACK超时,传输失败") ser.close() exit(1) print(f"第{idx//block_size +1}块发送完成,已收到ACK") ser.close()
4. 优化Flash操作耗时
- 提前擦除需要写入的扇区,别每写一块就擦一次(扇区擦除比单字写入慢得多)
- 按STM32F4的Flash最小编程单元(双字,8字节)批量写入,减少单次操作次数
示例Flash操作优化:
// 提前擦除主应用所在扇区(0x08004000对应扇区1) FLASH_Erase_Sector(FLASH_SECTOR_1, VOLTAGE_RANGE_3); // 按双字批量写入 for (uint32_t i=0; i<block_size; i+=8) { uint64_t write_data = *(uint64_t*)(block_buf + i); FLASH_ProgramDoubleWord(flash_base_addr + i, write_data); }
额外检查点
- 确认UART接收缓冲区大小足够,太小的话即使开中断也容易溢出
- 检查Bootloader里有没有在Flash写入时阻塞UART接收的逻辑(比如轮询接收时卡在Flash操作里)
- 可以开启UART溢出中断,方便排查是否真的发生了缓冲区溢出
内容的提问来源于stack exchange,提问作者dinajs
相关产品推荐
相关产品推荐

