AnsweredAssumed Answered

Problem with writing flash memory on STM32L4

Question asked by Ricardo Hassan on May 22, 2018
Latest reply on May 23, 2018 by Ricardo Hassan

Hi,

    I am trying to write a flash memory location on an STM32L496 and I can't get it to work.  From reading the manual, I understand that each location can only be written once, and it must be erased before it can be rewritten.  Also, the smallest piece of memory that can be erased is a page, which is 2K on this MCU.  So, I have written some code that reads a copy page I want to write to into RAM, modifies the copy to the desired state, erases the page, then writes the new version to the page using the standard programming API.  This is not working.  The first write works, but the second is returning a HAL_ERROR.  What am I doing wrong?  Any advice is appreciated. I will include a code snippet:

 

bool Nvm::Write(uint32_t destination, uint8_t* source, uint16_t length) {
uint32_t start_page_index = GetPage(destination);
uint32_t end_page_index = GetPage(destination + length);
uint32_t page_start = FLASH_BASE + FLASH_PAGE_SIZE * start_page_index;
page_start += (GetBank(destination) == FLASH_BANK_2) ? FLASH_BANK_SIZE : 0;
bool result = true;

if (start_page_index != end_page_index) {
result = false;
}

if (result == true) {
memcpy(ram_buffer_, (void*)page_start, FLASH_PAGE_SIZE); // make a ram copy of the entire flash page
uint32_t start_offset = destination - page_start;
memcpy(ram_buffer_ + start_offset, source, length); // make the desired chage

HAL_StatusTypeDef status = HAL_FLASH_Unlock();
if (status != HAL_OK) {
result = false;
}
}

if (result == true) {
result = Erase(GetBank(destination), start_page_index); // erase the page so it will accept a new write
}

if (result == true) {
  uint32_t write_location = page_start;
  uint64_t* write_source = (uint64_t*)ram_buffer_;
  uint32_t page_end = page_start + FLASH_PAGE_SIZE;
  for(; write_location < page_end; write_location += sizeof(uint64_t), write_source++) {
    HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,
                                                                                         page_start,
                                                                                         *write_source); // write the page with the new value
    if (status != HAL_OK) {
      //uint32_t error = HAL_FLASH_GetError();
      result = false;
      break;
    }
    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPTVERR);
  }
}

 

HAL_FLASH_Lock();
return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Erase a page of flash memory.
bool Nvm::Erase(uint32_t bank, uint32_t page) {
FLASH_EraseInitTypeDef erase_init_struct = {0};
uint32_t page_error = 0;
bool result = true;

erase_init_struct.TypeErase = FLASH_TYPEERASE_PAGES;
erase_init_struct.Banks = bank;
erase_init_struct.Page = page;
erase_init_struct.NbPages = 1;

if (HAL_FLASHEx_Erase(&erase_init_struct, &page_error) != HAL_OK) {
result = false;
}

return result;
}

 

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Gets the flash bank of the specified flash address - this is for our STM32L496
uint32_t Nvm::GetBank(uint32_t address) {
return (address < FLASH_BASE + FLASH_BANK_SIZE) ? FLASH_BANK_1 : FLASH_BANK_2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Gets the flash page of the specified flash address - this is for our STM32L496
uint32_t Nvm::GetPage(uint32_t address) {
return ((address - FLASH_BASE) / FLASH_PAGE_SIZE) % kPagesPerBank;
}

Outcomes