STM32 F3 Option Byte Retention
We have a product with a STM32F373CC where we calibrate the RTC in Production and store the calibration factor in Data0 and Data1 of the Option Bytes (OBR).
Tracing through the (legacy) code via my debugger, I can see the OBR values being written correctly. However, when I restart the micro, those values seem to be reset. The reference manual says it's meant to be non-volatile, but this doesn't seem to be the case.
What might we be doing wrong?
Here's our function that writes the code. At the end of the function, the OBR Data0 and Data1 fields show the correct values, and the nData0 and nData1 fields show the inverted values. We deliberately don't re-load the FLASH_OBR register because we don't want to cause a reset.
FLASHteResult FLASH_eWriteOptionBytes( tuFLASH_OBR uFLASH_OBR, uint32 lwFLASH_WRPR )
{
tuOBR uOBR;
FLASHteResult eResult;
uint32 lwIRQ;
uint16 *pwDst;
uint16 *pwSrc;
//erase OBR
memset( (void *)&uOBR, 0xFF, sizeof( uOBR ) );
//set the read protection option
if( uFLASH_OBR.LEVEL_2_PROT)
{
//uOBR.OB_RDP = OB_RDP_Level2; //DANGER - THIS CAN'T BE UNDONE
return( FLASHkeR_Failed );
}
else if( uFLASH_OBR.LEVEL_1_PROT )
{
uOBR.OB_RDP = OB_RDP_Level1;
}
else
{
uOBR.OB_RDP = OB_RDP_Level0;
}
//copy the USER flags
uOBR.abReg[2] = uFLASH_OBR.abReg[1];
//copy the data
uOBR.Data0 = uFLASH_OBR.Data0;
uOBR.Data1 = uFLASH_OBR.Data1;
//write protection
uOBR.WRP0 = lwFLASH_WRPR;
//disable interrupts (flash can't be used during erase anyway)
lwIRQ = ARM_lwPushInterrupts();
//unlock the flash
eResult = nFLASH_eUnlock();
if( eResult != FLASHkeR_Ok )
goto END;
//now unlock the option register
FLASH_OPTKEYR = 0x45670123;
FLASH_OPTKEYR = 0xCDEF89AB;
if( !FLASH_CR.OPTWRE )
{
eResult = FLASHkeR_Failed;
goto END; //jump if failed to unlock
}
//erase the option area
{
static const tuFLASH_CR kuFLASH_CR =
{{
.OPTER = TRUE,
.OPTWRE = TRUE
}};
FLASH_CR.lwReg = kuFLASH_CR.lwReg;
}
//request erase
FLASH_CR.STRT = 1;
//waf completion
while( FLASH_SR.BSY );
//finished erasing
FLASH_CR.lwReg = 0;
//unlock the option register again
FLASH_OPTKEYR = 0x45670123;
FLASH_OPTKEYR = 0xCDEF89AB;
if( !FLASH_CR.OPTWRE )
{
eResult = FLASHkeR_Failed;
goto END; //jump if failed to unlock
}
//write the flash
{
static const tuFLASH_CR kuFLASH_CR =
{{
.OPTPG = TRUE,
.OPTWRE = TRUE
}};
FLASH_CR.lwReg = kuFLASH_CR.lwReg;
}
//unlock the option register again
FLASH_OPTKEYR = 0x45670123;
FLASH_OPTKEYR = 0xCDEF89AB;
if( !FLASH_CR.OPTWRE )
{
eResult = FLASHkeR_Failed;
goto END; //jump if failed to unlock
}
//write the option bytes
for( pwDst = (uint16*)&OBR, pwSrc = (void*)&uOBR; pwDst < (uint16*)(&OBR + 1); pwDst++, pwSrc++)
{
//16 bits at a time
*pwDst = *pwSrc;
//waf completion
while( FLASH_SR.BSY );
}
//finished writing
FLASH_CR.lwReg = 0;
//relock
nFLASH_vLock();
#if 0 //avoid doing a reload as this causes a reset (see section 3.5.5 of the F3 Reference Manual)
//force reload
{
static const tuFLASH_CR kuFLASH_CR =
{{
.FORCE_OPTLOAD = TRUE
}};
FLASH_CR.lwReg = kuFLASH_CR.lwReg;
}
#endif
END:
//no more flash activity
FLASH_CR.lwReg = 0;
//restore interrupts
ARM_vPopInterrupts( lwIRQ );
return( FLASHkeR_Ok );
}As things stand, I'm going to re-write to NOT use the options bytes, as we need to get product out the door.
Here's the relevant section of the schematic. The battery is currently sitting at 3.13V.
