You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:59:51