cancel
Showing results for 
Search instead for 
Did you mean: 

Write data to flash when power supply drops

ATurp.11
Associate II

Hello,

I am using a STM32G473VET6 on my board, and I want to save a few data bytes in Flash when the 24V power supply drops. I have more than 400ms available between the detection by the MCU and the 3.3V dropping.

When the ADC triggers this event, the associated ISR try to write the data in Flash. The HAL functions don't seem to return any kind of error, but the only thing I can see inside the memory is a set of 0x0 at the programmed addresses.

But there is something else strange : if my function is called in another way, from code, the write in Flash is working successfully.

If I manually set the content of my @data, it works

It is like a cannot retrieve the content of @data from the ISR.

@data is declared as a static uint64_t array of my class

Here, the function called from the ADC ISR when a power down event occurs :

bool Config::writeFlash(){
 
	uint32_t pageError;
	static FLASH_EraseInitTypeDef pEraseInit;
	pEraseInit.Banks = FLASH_BANK_2;
	pEraseInit.NbPages = 1;
	pEraseInit.Page = 3;
	pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
 
	//data is uint64_t array
	//currentConfig is the struct I want to save in Flash
	memcpy(data, (void*) &currentConfig, sizeof(TerrariumConfig));
 
	HAL_StatusTypeDef status = HAL_FLASH_Unlock();
	SET_BIT(FLASH->SR, FLASH_SR_PGSERR);
 
	status = HAL_FLASHEx_Erase(&pEraseInit, &pageError);
 
 
	for(unsigned int i = 0; i < 1 + sizeof(TerrariumConfig)/sizeof(uint64_t); i++)
	{
		//write the data, with 64bit alignment 
		status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addressDataFlash + i*8, data[i]);
 
	}
 
	FLASH_FlushCaches();
	HAL_FLASH_Lock();
 
	return true;
 
}

 The write attempt occurs only once. Once done, ADC ISR are disabled.

To sum up, I can use the function above to write in flash when it is called from main program, but from ADC ISR, it doesn't work

Do you have any idea of the problem ?

Thank you ! =)

22 REPLIES 22
ATurp.11
Associate II

My code comes from HAL lib examples. What could be less standard than the lib provided by ST for a 2020 microcontroller ?

I added this line because the HAL seems to forget to set this bit before to start programming in Flash.

SET_BIT(FLASH->SR, FLASH_SR_PGSERR);

I will try with the FLASH_x, functions, like you said... but where is the doc for them ?

What is the difference with the HAL_FLASH functions ?

EDIT:

HAL_FLASH_Program() calls internally FLASH_Program_DoubleWord()

So I don't have hope that will change something to my issue

EDIT EDIT :

FLASH_Program_DoubleWord() is private. So, try to call it is definitively not the right way to solve it

ATurp.11
Associate II

Something else to make the problem clearer :

If I try to call the following function (one line different from the first post), in my ISR, it works !

bool Config::saveConfigFlash(){
 
	uint32_t pageError;
	FLASH_EraseInitTypeDef pEraseInit;
	pEraseInit.Banks = FLASH_BANK_2;
	pEraseInit.NbPages = 1;
	pEraseInit.Page = 127;
	pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
 
	//data is uint64_t array
	//currentConfig is the struct I want to save in Flash
 
	static uint64_t data[1 + sizeof(TerrariumConfig)/sizeof(uint64_t)];
	//memcpy(data, (void*) &currentConfig, sizeof(TerrariumConfig));
	strcpy((char*)data, "my data");
 
	HAL_StatusTypeDef status = HAL_FLASH_Unlock();
 
	SET_BIT(FLASH->SR, FLASH_SR_PGSERR);
	status = HAL_FLASHEx_Erase(&pEraseInit, &pageError);
 
 
	for(unsigned int i = 0; i < 1 + sizeof(TerrariumConfig)/sizeof(uint64_t); i++)
	{
		//write the data, with 64bit alignment
		status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addressDataFlash + i*8, data[i]);
	}
 
	FLASH_FlushCaches();
	HAL_FLASH_Lock();
 
	return true;
 
}

It is like a cannot retrieve my real data

I write you need add

if(status ...) not only fake status =

and locate what dont work. and in HAL

__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGSERR | FLASH_FLAG_PGPERR | FLASH_FLAG_BSY);

helps better.

EDIT:

and yes memcpy use with uint64 mayby is bad idea... your calculate size for data different as for memcopy, then memcopy check oversized copy over data and do nothing.

Hello,

Could you please develop what you said about memcpy ? I am not sure to understand what you wrote

static uint64_t data[1 + sizeof(TerrariumConfig)/sizeof(uint64_t)];

for example sizeof terrarium is 57 bytes , then this create aaray data with (1+57/8) eq 8 elements u64

then

memcpy(data, (void*) &currentConfig, sizeof(TerrariumConfig));

determine type data and try copy 57 u64 from source, but dest is only 8 this i mean is compared in lib and func do nothing, return error.

Clearing FLASH_FLAG_BSY bit is again a nonsense. It cannot be cleared and it doesn't need to be cleared. A good example of why the reference manual must be read even if using the HAL bloatware.

As for array size:

https://stackoverflow.com/questions/2745074/fast-ceiling-of-an-integer-division-in-c-c

memcpy(destination, source, N);

N is a number of bytes, we don't care if destination is an uint64_t* or whatever

ATurp.11
Associate II

I am still stuck on this problem which drives me crazy.

The STM32 receives instructions from UART. One of these allows to save the data in Flash, just like I try to do :

//run in main context
void Communication::readFrames() {
 
	//other stuff before
 
 
       INSTRUCTION_SET cmd = (INSTRUCTION_SET) mListComFrame[mIndexListComFrame].cmd;
 
        if(cmd == SAVE_STM32_CONFIG){
 
		Config::saveConfigFlash();
 
	}
 
	
 
         //other stuf
}

It is exactly the same function as the first post, and it works. Unbelievable !

Why this does not work anymore from my interrupt ? (Or if I set a flag from the ISR triggered by ADC's watchdog, and then write in Flash from main)

According to your link @Piranha​ , I have changed the function to this, but without solving the problem :

bool Config::saveConfigFlash(){
 
	uint32_t pageError;
	FLASH_EraseInitTypeDef pEraseInit;
	pEraseInit.Banks = FLASH_BANK_2;
	pEraseInit.NbPages = 1;
	pEraseInit.Page = 127;
	pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
 
	//data is uint64_t array
	//currentConfig is the struct I want to save in Flash
	const uint8_t lenData = sizeof(TerrariumConfig)/sizeof(uint64_t) + (sizeof(TerrariumConfig) % sizeof(uint64_t) != 0);
	
	uint64_t* data = (uint64_t*) &currentConfig;
 
	HAL_StatusTypeDef status = HAL_FLASH_Unlock();
 
	SET_BIT(FLASH->SR, FLASH_SR_PGSERR);
	status = HAL_FLASHEx_Erase(&pEraseInit, &pageError);
 
 
	for(unsigned int i = 0; i < lenData; i++)
	{
		//write the data, with 64bit alignment
		status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addressDataFlash + i*8, *data);
		data++;
	}
 
	HAL_FLASH_Lock();
 
	return true;
 
}

To make it 100% clear: does this saveConfigFlash() function erase the flash page, or it does not?

If it still does erase, after all discussions above - this is self-inflicted trouble.

> What could be less standard than the lib provided by ST for a 2020 microcontroller ?

A lib provided by ST for a 2021 microcontroller =)

-- pa

Realy strange, sorry for memcpy i mean sizet is used here, but yes c++ say bytes here =)

Go back to scope where you place toggle pin. Try toggle before erase after erase and on every for write.

Too good is debug this with simulate power down without real power down and trace erase and flash func returned status.