2025-10-27 2:05 AM
Hello! I am working on a custom OTA bootloader system on a STM32G473RBT6 MCU in a custom board I have been using maybe for 3 years with no problem. My board is connected to a Raspberry Pi over RS485 network. My idea is this:
The problem is that the bootloader only succeeds if the following condition met (basically full flash erase):
However, when I try to upload a new firmware over the existing one, the bootloader cannot erase the pages "starting from bank 2 (application space)" HAL_FLASHEx_Erase even returns HAL_OK but when I check the addresses they are not filled with 0xFF, it cannot even erase a single page. Here "starting from" situation is the actual weird part because as you can see, the storage area takes space from both bank-1 and bank-2. When new code arrives, the header contains the flash size required. I designed a 52k program and I can successfully erase and write 52K program to storage when erase area starts from bank 1 (checked with STM32CubeProgrammer all the 52KB filled with 0x55 as designed). You probably think MCU is in single bank mode, but it is not. My code checks if FLASH_OPTR_DBANK defined as well as FLASH->OPTR & FLASH_OPTR_DBANK. Using single-bank mode or leaving a dummy blank page from the end of bank one might be a solution but I wonder what is wrong here.
So, my questions are, how can I erase page(s) from bank-2 in STM32G473RB microcontroller? Is there anything I am doing wrong here? Thank you very much in advance!
SUMMARY:
In dual bank mode STM32G473RBT6 cannot erase pages (although HAL_FLASHEx_Erase returns HAL_OK) from bank-2 only. I can erase pages from bank-2 only if I start erasing from bank-1 and go to bank-2.
Below are the flash_erase, flash_write and flash_copy functions I use if it helps:
#define FLASH_PROGRAM_ALIGNMENT 8
HAL_StatusTypeDef flash_erase_region(uint32_t start_address, uint32_t length)
{
HAL_StatusTypeDef status = HAL_OK;
FLASH_EraseInitTypeDef EraseInit;
uint32_t PageError = 0;
uint32_t first_page, num_pages, bank;
uint32_t start = start_address;
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
#if defined(FLASH_OPTR_DBANK)
// Check if dual-bank mode is active
uint32_t optcr = FLASH->OPTR;
uint8_t dual_bank = (optcr & FLASH_OPTR_DBANK) ? 1 : 0;
#else
uint8_t dual_bank = 0;
#endif
if (dual_bank) {
// 64 KB per bank, 2 KB per page → 32 pages per bank
const uint32_t bank_size = 64 * 1024;
const uint32_t page_size = 2 * 1024;
uint32_t end = start_address + length;
while (start < end) {
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
bank = (start < (FLASH_BASE + bank_size)) ? FLASH_BANK_1 : FLASH_BANK_2;
first_page = (start - ((bank == FLASH_BANK_1) ? FLASH_BASE : (FLASH_BASE + bank_size))) / page_size;
// Compute how many pages remain in this bank before possibly crossing boundary
uint32_t bank_end = (bank == FLASH_BANK_1) ? (FLASH_BASE + bank_size) : (FLASH_BASE + 2 * bank_size);
uint32_t bytes_this_bank = (end < bank_end) ? (end - start) : (bank_end - start);
num_pages = (bytes_this_bank + page_size - 1) / page_size;
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInit.Banks = bank;
EraseInit.Page = first_page;
EraseInit.NbPages = num_pages;
status = HAL_FLASHEx_Erase(&EraseInit, &PageError);
if (status != HAL_OK) break;
start += bytes_this_bank;
}
} else {
// Single bank (default, 2KB pages)
const uint32_t page_size = 2 * 1024;
first_page = (start - FLASH_BASE) / page_size;
num_pages = (length + page_size - 1) / page_size;
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInit.Banks = FLASH_BANK_1;
EraseInit.Page = first_page;
EraseInit.NbPages = num_pages;
status = HAL_FLASHEx_Erase(&EraseInit, &PageError);
}
for(uint32_t addr = start_address; addr < start_address + length; addr += 4){
if(*((uint32_t *)addr) != 0xFFFFFFFF){
status = HAL_ERROR;
break;
}
}
HAL_FLASH_Lock();
__enable_irq();
return status;
}
static HAL_StatusTypeDef flash_write_buffer(uint32_t addr, const uint8_t *buf, uint32_t len)
{
HAL_StatusTypeDef status = HAL_OK;
uint32_t aligned_len = (len + (FLASH_PROGRAM_ALIGNMENT - 1)) & ~(FLASH_PROGRAM_ALIGNMENT - 1);
uint8_t tmp[FLASH_PROGRAM_ALIGNMENT];
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
for (uint32_t offset = 0; offset < aligned_len; offset += FLASH_PROGRAM_ALIGNMENT) {
/* prepare a 64-bit value to program */
memset(tmp, 0xFF, sizeof(tmp));
uint32_t copy = (offset + FLASH_PROGRAM_ALIGNMENT <= len) ? FLASH_PROGRAM_ALIGNMENT : (len - offset);
if (copy) memcpy(tmp, buf + offset, copy);
uint64_t data64;
memcpy(&data64, tmp, sizeof(data64));
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + offset, data64);
if (status != HAL_OK){
//status = HAL_FLASH_GetError();
break;
}
}
HAL_FLASH_Lock();
__enable_irq();
return status;
}
static HAL_StatusTypeDef flash_copy_region(uint32_t dst, uint32_t src, uint32_t len)
{
/* dst must be erased before calling this function */
HAL_StatusTypeDef status = HAL_OK;
const uint32_t chunk = CHUNK_SIZE;
uint8_t buf[chunk];
uint32_t remaining = len;
uint32_t rptr = src;
uint32_t wptr = dst;
uint32_t check_erase = len;
while(check_erase--){
if(*((uint8_t *)wptr+check_erase)!=0xFF){
return HAL_BUSY;
}
}
while (remaining) {
uint32_t toread = (remaining > chunk) ? chunk : remaining;
memcpy(buf, (uint8_t *)rptr, toread);
status = flash_write_buffer(wptr, buf, toread);
if (status != HAL_OK) return status;
rptr += toread; wptr += toread; remaining -= toread;
}
return status;
}