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
相关产品推荐
相关产品推荐

