I am trying to write several bytes of data onto the STM32F410CBT3 Flash Memory Sector 4 (size of 64KB), and I chose this sector and assume it is safe to use as the code is approximately 30KB (which would probably reside in sectors 1 and 2). The microcontroller's clock speed is clocked at 100MHz (through PLL).
Here is my Flash Write code:
/* Programming in 16-bit words, so offset address of 0x04 should be enough */
#define FLASH_SECTOR4_BASEADDRESS ((uint32_t)0x8011000)
#define OFFSET ((uint8_t)0x04)
#define ADDR0 FLASH_SECTOR4_BASEADDRESS
#define ADDR1 ((uint32_t)(ADDR0 + OFFSET))
#define ADDR2 ((uint32_t)(ADDR1 + OFFSET))
#define ADDR3 ((uint32_t)(ADDR2 + OFFSET))
/* and so on... */
void FLASH_Init(void)
{
/* Only use FLASH Sector 4 for storing configuration/calibration data. s_EraseInit is stored as a static variable. This function called first because s_EraseInit needs to have values before any FLASH functions/routines are called. */
s_EraseInit.TypeErase = TYPEERASE_SECTORS;
s_EraseInit.Sector = FLASH_SECTOR_4;
s_EraseInit.NbSectors = 1;
s_EraseInit.VoltageRange = VOLTAGE_RANGE_4; /* Input voltage to mcu is around 3.3V */
}
void FLASH_Write(void)
{
/* Stop LPTIM1 interrupts prior to modifying FLASH region */
HAL_LPTIM_Counter_Stop_IT(&hlptim1);
/* Assign temp_x values of struct members stored globally. temp_x will be the variable used for the FlashProgram function */
uint16_t temp0 = GlobalStruct[0].Member1;
uint16_t temp1 = GlobalStruct[0].Member2;
uint16_t temp2 = GlobalStruct[0].Member3;
uint16_t temp3 = GlobalStruct[1].Member1;
uint16_t temp4 = GlobalStruct[1].Member2;
uint16_t temp5 = GlobalStruct[1].Member3;
uint16_t temp6 = GlobalStruct[2].Member1;
uint16_t temp7 = GlobalStruct[2].Member2;
uint16_t temp8 = GlobalStruct[2].Member3;
uint16_t temp9 = GlobalStruct[3].Member1;
uint16_t temp10 = GlobalStruct[3].Member2;
uint16_t temp11 = GlobalStruct[3].Member3;
/* Unlock FLASH peripheral and clear FLASH status register (error) flags */
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP|FLASH_FLAG_OPERR|FLASH_FLAG_WRPERR|FLASH_FLAG_PGAERR|FLASH_FLAG_PGSERR);
/* Mass erase FLASH sector 4 */
FlashStatus[12] = HAL_FLASHEx_Erase(&s_EraseInit, &s_SectorError);
/* Write into FLASH sector 4 and lock the FLASH peripheral after using it */
FlashStatus[0] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR0, temp0);
FlashStatus[1] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR1, temp1);
FlashStatus[2] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR2, temp2);
FlashStatus[3] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR3, temp3);
FlashStatus[4] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR4, temp4);
FlashStatus[5] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR5, temp5);
FlashStatus[6] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR6, temp6);
FlashStatus[7] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR7, temp7);
FlashStatus[8] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR8, temp8);
FlashStatus[9] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR9, temp9);
FlashStatus[10] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR10, temp10);
FlashStatus[11] = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, ADDR11, temp11);
HAL_FLASH_Lock();
/* Resume LPTIM1 interrupts after modifying FLASH region */
HAL_LPTIM_Counter_Start_IT(&hlptim1, LPTIM1_AUTORELOAD_NUMBER);
}
/*********************** Relevant code from the HAL Library *******************************/
/**
* @brief Program byte, halfword, word or double word at a specified address
* @param TypeProgram Indicate the way to program at a specified address.
* This parameter can be a value of @ref FLASH_Type_Program
* @param Address specifies the address to be programmed.
* @param Data specifies the data to be programmed
*
* @retval HAL_StatusTypeDef HAL Status
*/
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
HAL_StatusTypeDef status = HAL_ERROR;
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Check the parameters */
assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
if(status == HAL_OK)
{
if(TypeProgram == FLASH_TYPEPROGRAM_BYTE)
{
/*Program byte (8-bit) at a specified address.*/
FLASH_Program_Byte(Address, (uint8_t) Data);
}
else if(TypeProgram == FLASH_TYPEPROGRAM_HALFWORD)
{
/*Program halfword (16-bit) at a specified address.*/
FLASH_Program_HalfWord(Address, (uint16_t) Data);
}
else if(TypeProgram == FLASH_TYPEPROGRAM_WORD)
{
/*Program word (32-bit) at a specified address.*/
FLASH_Program_Word(Address, (uint32_t) Data);
}
else
{
/*Program double word (64-bit) at a specified address.*/
FLASH_Program_DoubleWord(Address, Data);
}
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* If the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}
About the code above, the global struct's members' are either 8-bit or 16-bit. I stopped the LPTIM1 interrupts before going into the FLASH Write sequence because I thought that the LPTIM1 interrupts could cause issues if it occurs exactly when the microcontroller is overwritting its Flash memory.
The Issue
I have been having HardFault errors on this code even though a really similar code (with the difference being the data written) works on a different STM32F410CBT3 microcontroller. I also matched the Linker Settings on MDK-ARM IROM1: Start 0x8000000, Size 0x1C000
, previously it was at IROM1: Start 0x8000000, Size 0x20000
. The common pattern that I am observing is, based on MDK-ARM's Memory Viewer, the Flash gets erased where ??
appears on all the memory block. Afterwards, it goes to 0xFF
again indicating that none of the blocks/sections were written. At this point, the code is already in its HardFault Handler. If the code supposedly works, the sectors would turn from ??
to whatever value was written to the Flash memory.
Regarding the HardFault Error, it sometimes shows as a Memory Manage Fault, where the IACCVIOL
(Instruction access violation flag) was raised, and SCB->MMFAR
not containing any address. On most times, it shows up as a Bus Fault, where SCB->BFAR = 0xFFFFFFFF
with the BFARVALID
flag high, and PRECISERR
flag was also raised. I looked through the STM32F410 Memory Map and the address 0xFFFFFFFF
points to a 512-Mbyte block 7 Cortex-M4's internal peripherals. On rare occasions, it shows as a Usage Fault, where the UNDEFINSTR
bit is high. Sometimes, the Flash Write sequence does work, but only if breakpoints were used.
What I tried
HAL_FLASH_Program
APIs. Based on the UsageFault error, I thought that the struct member access was too slow, so I declared those unsigned 16-bit tempX
variables to hold the struct members' values. The error still persists, and maybe, it is not the cause as the compiler would probably optimize that anyways. I also tried using delays and the error still persists.tempX
variable declarations to uint32_t
because I read somewhere that the input needs to be either 32-bits or 64-bits. I changed it to uint32_t
declarations and the error still persists.FLASH_TYPEPROGRAM_WORD
instead of FLASH_TYPEPROGRAM_HALFWORD
, and the error still persists. On the different microcontroller, I noticed that this should not have much effect, because if I were to write a 16-bit value (for ex. 0xAAAA
), in the Flash section itself, it would either appear as 0x0000AAAA
if FLASH_TYPEPROGRAM_WORD
was used, or 0xFFFFAAAA
if FLASH_TYPEPROGRAM_HALFWORD
was used because the left 16-bits were cleared to 0xFFFF
but not re-written to 0x0000
since only the least 16-bits were overwritten.0xFFFF
would cause an issue, but after I ensured that all the struct members were nonzero or nonFFFF, the error still persists. On the different microcontroller, I could also still write 0xFFFF
to the Flash without causing any errors.FlashStatus[x], x = 0...12
contains only HAL_OK
. The FLASH->SR
register usually shows nothing too (no error status).s_SectorError
usually has 0xFFFFFFFF
, indicating that all the sectors (Sector 4) was successfully erasedQuestion What am I missing here? Any help would be greatly appreciated. I also don't know how to debug this issue deeper and any tips on debugging this problem would be appreciated too. This is the Keil uVision's page on Fault Reporting Guide, which I used as a reference for identifying the HardFault Errors. Thank you!
I was looking through the HAL files and I made a mistake of using VOLTAGE_RANGE_4
or FLASH_VOLTAGE_RANGE_4
instead of FLASH_VOLTAGE_RANGE_3
. The HAL files showed that both options are for microcontrollers powered by a 2.7V - 3.6V, but the difference between the two options are the external Vpp for voltage range 4. Using FLASH_VOLTAGE_RANGE_3
actually fixed my issue, meaning that the original problem I had was caused by erasing the Flash memory incorrectly, and not because I programmed the Flash Memory incorrectly.
I initially chose VOLTAGE_RANGE_4
because I misunderstood the voltage range when reading this page found in the Reference Manual:
While this section below actually tells me which voltage range I should be using since the voltage range referred in the HAL libraries corresponds to FLASH_CR->PSIZE
bits:
It is still a mystery to me as to why different HardFault errors can be raised at each tries, and why erasing the Flash Memory needs to be done in a certain way (64-bit erase/32-bit erase/16-bit erase/8-bit erase) depending on the supply voltage. It is also weird how it can work sometimes, but not work most of the time with the wrong configuration.
User contributions licensed under CC BY-SA 3.0