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

STM32L072KZ双Bank烧录异常:Bank2写入0x08018100后失败

STM32L072KZ Bank2 Flash写入故障排查

问题概述

  • MCU:STM32L072KZ(Category5,192KB双Bank),当前应用运行在Bank1
  • 固件更新流程:UART DMA+中断接收HEX格式固件,按1500字节块处理,已完成地址、CRC、校验和及真实数据提取,调用businessRules_updateFirmware实现Bank2(起始地址0x08018000)写入
  • 故障:可正常写入0x08018000~0x080180F0,无法写入0x08018100及之后地址
  • 已尝试两种代码实现,均未解决问题

初始版本代码

uint32_t flashAddress = 0x08018000;

void businessRules_updateFirmware(void)
{
  uint8_t buffer[FIRMWARE_PACKAGE_SIZE];
  uint32_t pageError;

  if (protocol_feedFirmwareBuffer(buffer))
  {
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();

    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_BSY);
    FLASH_EraseInitTypeDef eraseInitStruct;
    eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    eraseInitStruct.PageAddress = flashAddress;
    eraseInitStruct.NbPages = 1;

    if (HAL_FLASHEx_Erase(&eraseInitStruct, &pageError) != HAL_OK)
    {
      HAL_FLASH_Lock();
      HAL_FLASH_OB_Lock();
      return;
    }

    uint8_t binaryData[FIRMWARE_PACKAGE_SIZE / 2];

    hexStringToBytes((const char*)buffer, binaryData, FIRMWARE_PACKAGE_SIZE);

    for (uint32_t i = 0; i < FIRMWARE_PACKAGE_SIZE / 2; i += 4)
    {
      uint32_t data = *(uint32_t*)&binaryData[i];

      if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flashAddress , data) != HAL_OK)
      {
        HAL_FLASH_Lock();
        HAL_FLASH_OB_Lock();
        return;
      }

      flashAddress += 4;
    }

    HAL_FLASH_Lock();
    HAL_FLASH_OB_Lock();
  }
}


uint8_t hexCharToValue(char hexChar)
{
  if (isdigit(hexChar))
    return hexChar - '0';
  else
    return tolower(hexChar) - 'a' + 10;
}

void hexStringToBytes(const char *hexString, uint8_t *byteArray, size_t byteArraySize)
{
  for (size_t i = 0; i < byteArraySize / 2; ++i)
  {
    if (2 * i + 1 >= strlen(hexString))
    {
      byteArray[i] = 0x00;
      break;
    }

    byteArray[i] = (hexCharToValue(hexString[2 * i]) << 4) | hexCharToValue(hexString[2 * i + 1]);
  }
}

尝试修改后的代码

void businessRules_updateFirmware(void)
{

    uint32_t pageAddress;
    uint32_t pageError;
    uint8_t buffer[FIRMWARE_PACKAGE_SIZE];

    bool dataIsReady = false;
    dataIsReady = protocol_feedFirmwareBuffer(buffer);

    if (dataIsReady)
    {
        uint8_t binaryData[FIRMWARE_PACKAGE_SIZE / 2];
        hexStringToBytes((char *)buffer, binaryData, FIRMWARE_PACKAGE_SIZE / 2);

        pageAddress = protocol_getAddressToFirmware();

        HAL_FLASH_Unlock();
        HAL_FLASH_OB_Unlock();


        FLASH_EraseInitTypeDef eraseInitStruct;
        eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
        eraseInitStruct.PageAddress = pageAddress;
        eraseInitStruct.NbPages = 1;

        if(HAL_FLASHEx_Erase(&eraseInitStruct, &pageError) != HAL_OK){
            HAL_FLASH_Lock();
            HAL_FLASH_OB_Lock();
            return;
        }

        for(uint32_t i = 0; i < FIRMWARE_PACKAGE_SIZE / 2; i += 4){

            uint32_t data = (binaryData[i] << 0) | (binaryData[i + 1] << 8) | (binaryData[i + 2] << 16) | (binaryData[i + 3] << 24);

            if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, pageAddress + i , data) != HAL_OK){
                HAL_FLASH_Lock();
                HAL_FLASH_OB_Lock();
                return;
            }

        }

        pageAddress += FIRMWARE_PACKAGE_SIZE / 2;
        protocol_setAddressToFirmware(pageAddress);

        HAL_FLASH_OB_Lock();
        HAL_FLASH_Lock();
    }

}

uint8_t hexCharToValue(char hexChar) {
    if (isdigit(hexChar))
        return hexChar - '0';
    else
        return tolower(hexChar) - 'a' + 10;
}

void hexStringToBytes(const char *hexString, uint8_t *byteArray, size_t byteArraySize) {
    for (size_t i = 0; i < byteArraySize; ++i) {
        byteArray[i] = (hexCharToValue(hexString[2 * i]) << 4) | hexCharToValue(hexString[2 * i + 1]);
    }
}

排查方向及解决方案

1. Flash写保护(WRP)检查

STM32L0的Option Byte中包含Flash写保护区域设置,需确认Bank2的0x08018100及之后地址未被标记为写保护:

  • 使用STM32CubeProgrammer读取Option Byte,查看WRP相关字段
  • 若存在写保护,重新配置Option Byte解除对应区域保护

2. HEX数据处理越界问题

修改后的hexStringToBytes函数未检查HEX字符串长度,当输入HEX块长度不足时,会访问超出字符串的内存,导致binaryData出现随机值,进而引发Flash编程失败:

  • 恢复初始版本中的边界检查逻辑,确保仅处理有效HEX字符:
    void hexStringToBytes(const char *hexString, uint8_t *byteArray, size_t byteArraySize) {
        size_t hexLen = strlen(hexString);
        for (size_t i = 0; i < byteArraySize; ++i) {
            if (2 * i + 1 >= hexLen) {
                byteArray[i] = 0x00;
                break;
            }
            byteArray[i] = (hexCharToValue(hexString[2 * i]) << 4) | hexCharToValue(hexString[2 * i + 1]);
        }
    }
    

3. Flash页擦除逻辑修正

STM32L0的Flash页大小为2KB(0x800),0x08018100仍属于Bank2的第24页(0x08018000~0x080187FF)。需确保擦除的是页起始地址:

  • 计算页起始地址:pageAddress = (protocol_getAddressToFirmware() & ~0x7FF);(0x7FF是2KB页掩码)

4. 字节序与Flash编程匹配

STM32采用小端存储,Flash编程时HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD)会将32位数据的低字节写入目标地址,需确保数据拼接顺序与HEX文件字节顺序一致:

  • 正确的32位数据拼接方式:
    uint32_t data = ((uint32_t)binaryData[i+3] << 24) | ((uint32_t)binaryData[i+2] << 16) | ((uint32_t)binaryData[i+1] << 8) | binaryData[i];
    
  • 或直接使用memcpy避免字节序错误:
    uint32_t data;
    memcpy(&data, &binaryData[i], sizeof(uint32_t));
    

5. 中断与Flash操作冲突

Flash擦除/编程为耗时操作,若此时UART DMA或高优先级中断抢占总线,可能导致操作失败。在Flash操作期间关闭全局中断:

void businessRules_updateFirmware(void)
{
    // ... 其他代码 ...
    HAL_FLASH_Unlock();
    HAL_FLASH_OB_Unlock();

    __disable_irq(); // 关闭全局中断

    // Flash擦除、编程代码

    __enable_irq(); // 恢复全局中断

    HAL_FLASH_Lock();
    HAL_FLASH_OB_Lock();
    // ... 其他代码 ...
}

6. 错误标志完整清除

初始版本未清除所有Flash错误标志,残留的错误标志可能导致后续编程失败:

__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); // 清除所有Flash错误标志

内容的提问来源于stack exchange,提问作者Wave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:33:12