Reputation: 475
I'm working with a MCF51EM256 Freescale microcontroller and I've some problems with the flash programming.
In order to make my software persistant I'm trying to store some variables in the secondary flash memory to recover from unexpected shutdowns.
Sometimes, when I test my work shutting down the MCU, it is constantly reset.
I have to store this struct:
// kWh or kVARh Energy accumulator type
typedef struct {
uint32 Ea_ps; // Energy must be stored in kWh or kVARh
uint32 Ea_ng; // All fields must contain POSITIVE values!
uint32 Er_q1;
uint32 Er_q2;
uint32 Er_q3;
uint32 Er_q4;
}kWh_EnergyAcc32;
And these are my functions:
// This function stores in Flash a given kWh_EnergyAcc64 structure.
void Save_Flash_kWhEnergyAcc(long addr, kWh_EnergyAcc32* Acc) {
// kWhEnergyAcc struct needs 32 bytes in Flash
Flash_Burst(addr, 1, &(Acc->Ea_ps));
Flash_Burst(addr + 4, 1, &(Acc->Ea_ng));
Flash_Burst(addr + 8, 1, &(Acc->Er_q1));
Flash_Burst(addr + 12, 1, &(Acc->Er_q2));
Flash_Burst(addr + 16, 1, &(Acc->Er_q3));
Flash_Burst(addr + 20, 1, &(Acc->Er_q4));
}
*
// This functions erase a flash sector in external flash
void EraseFlashSector(long startAddr) {
// Sector size: 1 kB
uint32 eraseData = 0xFFFFFFFF;
Flash_Cmd((uint32)startAddr, (uint16)1, (uint32*)&eraseData, 0x40);
}
*
// This function initializes a given Ws_EnergyAcc64 structure with the
// stored values in Flash.
void Init_Flash_kWhEnergyAcc(long addr, kWh_EnergyAcc32* Acc) {
Acc->Ea_ps = *(uint32*)addr;
addr = addr + 4;
Acc->Ea_ng = *(uint32*)addr;
addr = addr + 4;
Acc->Er_q1 = *(uint32*)addr;
addr = addr + 4;
Acc->Er_q2 = *(uint32*)addr;
addr = addr + 4;
Acc->Er_q3 = *(uint32*)addr;
addr = addr + 4;
Acc->Er_q4 = *(uint32*)addr;
addr = addr + 4;
}
*
And flash programming functions:
#define FLASH_MASS_ERASE_CMD 0x41
#define FLASH_ERASE_CMD 0x40
#define FLASH_PROGRAM_CMD 0x20
#define FLASH_BURST_CMD 0x25
#if (SYSTEM_CLOCK/2) > 12800000 /* 12.8 MHz */
#define FLASH_CLOCK (UINT8)(( (SYSTEM_CLOCK/3200000) -1) | 0x40)
#else
#define FLASH_CLOCK (unsigned char)( (SYSTEM_CLOCK/400000) -1)//<200KHz
#endif
/* Macros to call the function using the different features */
#define Flash_Burst(Address, Size, DataPtr) \
Flash_Cmd((UINT32)Address, (UINT16)Size, (UINT32*)DataPtr, FLASH_BURST_CMD)
UINT8 /*far*/
Flash_Cmd(UINT32 FlashAddress,
UINT16 FlashDataCounter,
UINT32 *pFlashDataPtr,
UINT8 FlashCommand)
{
/* Check to see if FACCERR or PVIOL is set */
if (FSTAT &0x30)
{
/* Clear Flags if set*/
FSTAT = 0x30;
}
if (FlashDataCounter)
{
do
{
/* Wait for the Last Busrt Command to complete */
while(!(FSTAT&FSTAT_FCBEF_MASK)){};/*wait until termination*/
/* Write Data into Flash*/
(*((volatile unsigned long *)(FlashAddress))) = *pFlashDataPtr;
FlashAddress += 4;
pFlashDataPtr++;
/* Write Command */
FCMD = FlashCommand;
/* Put FCBEF at 1 */
FSTAT = FSTAT_FCBEF_MASK;
asm (NOP);
asm (NOP);
asm (NOP);
/* Check if Flash Access Error or Protection Violation Error are Set */
if (FSTAT&0x30)
{
/* If so, finish the function returning 1 to indicate error */
return (1);
}
}while (--FlashDataCounter);
}
/* wait for the last command to complete */
while ((FSTAT&FSTAT_FCCF_MASK)==0){};/*wait until termination*/
/* Return zero to indicate that the function executed OK */
return (0);
}
My main program looks like:
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long magic_word_addr = 0x20800;
main() {
uint32 bad_magic_word = 0x12345678;
uint32 ok_magic_word = 0x87654321;
magic_word = *(uint32*)magic_word_addr;
if (isFirstExecution() || (magic_word == bad_magic_word) || (magic_word == -1) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
EraseFlashSector(magic_word_addr);
Flash_Burst(magic_word_addr, 1, &bad_magic_word);
EraseFlashSector(PhR_ABS_kWh_addr);
Save_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
EraseFlashSector(magic_word_addr);
Flash_Burst(magic_word_addr, 1, &ok_magic_word);
}
}
Can anyone see what I'm doing wrong? Why my micro sometines is constantly reset when I turn off de power supply to test the persistance? There is a way to catch this fatal exception which causes the reset in my micro?
First I thought it might be caused by an error writing any flash address, during the shutdown, which after is not able to read properly but I tried to use a "magic word" written to a known location at the end of my flash write to check if the flash write had already finished and seems that was not the problem.
Edit: MCF51EM256 Reference Manual
Edit 2: This is the memory map of my micro:
Edit 3:
I've included the FLASH_CLOCK definition in Flash programming functions
I've also included this function to check inconsistent values:
int testIntegrity(kWh_EnergyAcc32 Acc) {
if (Acc.Ea_ps == -1 || Acc.Ea_ng == -1 || Acc.Er_q1 == -1 || Acc.Er_q2 == -1 || Acc.Er_q3 == -1 || Acc.Er_q4 == -1)
return 0;
else return 1;
}
Now, this function is called after initializing values and the leds never turn on.
Note: (Acc->Ea_ps == -1) is the same that (Acc->Ea_ps == 0xffffffff)
Edit 4:
The code for my function SetFlashSectorToZero:
void SetFlashSectorToZero(long addr){
uint32 resetValue = 0x00000000;
int endSector = addr + 1024;
while (addr <= endSector) {
Flash_Burst(addr, 1, &resetValue);
addr = addr + 4;
}
}
Upvotes: 2
Views: 1205
Reputation: 16223
I don't know if you solved your problem with our suggestions, but I think your code could be smaller in the following way:
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long magic_word_addr = 0x203FC;
main() {
uint32 ok_magic_word = 0x87654321;
magic_word = *(uint32*)magic_word_addr;
if (isFirstExecution() || (magic_word != ok_magic_word)) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
EraseFlashSector(PhR_ABS_kWh_addr);
Save_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
Flash_Burst(magic_word_addr, 1, &ok_magic_word);
}
}
or
// kWh or kVARh Energy accumulator type
typedef struct {
uint32 Ea_ps; // Energy must be stored in kWh or kVARh
uint32 Ea_ng; // All fields must contain POSITIVE values!
uint32 Er_q1;
uint32 Er_q2;
uint32 Er_q3;
uint32 Er_q4;
uint32 magic_word;
}kWh_EnergyAcc32;
static kWh_EnergyAcc32 PhR_ABS_kWh_AccStr;
static long PhR_ABS_kWh_addr = 0x20000;
static long *magic_word_addr = (uint32 *)(PhR_ABS_kWh_addr-sizeof(uint32));
#define OK_MAGIC_WORD 0x87654321
main() {
if (isFirstExecution() || (*magic_word != OK_MAGIC_WORD)) {
EraseFlashSector(PhR_ABS_kWh_addr);
// Writes 0's in all addresses of the flash sector (1024 bytes)
SetFlashSectorToZero(PhR_ABS_kWh_addr);
}
Init_Flash_kWhEnergyAcc(PhR_ABS_kWh_addr, &PhR_ABS_kWh_AccStr);
if (testIntegrity(PhR_ABS_kWh_AccStr) == 0) {
// Turn on LEDs to show a message
ShowMsgLED(255, 0, 0);
}
while (1) {
getValuesFromSensors(&PhR_ABS_kWh_AccStr);
processValues(&PhR_ABS_kWh_AccStr);
PhR_ABS_kWh_AccStr.magic_word = OK_MAGIC_WORD;
EraseFlashSector(PhR_ABS_kWh_addr);
Flash_Burst(PhR_ABS_kWh_addr, sizeof(kWh_EnergyAcc32)/4, &PhR_ABS_kWh_AccStr);
}
}
Upvotes: 1
Reputation: 214169
You cannot execute the flash programming code from the same flash array that is currently getting programmed. According to the manual for this specific device, it contains dual flash controllers just for the purpose of allowing you to program one flash array while programming the other.
So you have to ensure that the flash programming code is allocated in a separate array. You might even need two complete duplicates of the flash programming code.
Failing to do this will cause any random behavior.
You have to configure the flash pre-scaler clock by writing to the FxCDIV register. An incorrectly configured flash clock will typically cause things like program hang-up or CPU reset. The clock needs to be within 150-200kHz. Important: you need margins, so have to take your clock inaccuracy in account, and also the inaccuracy that comes from your bus clock not being evenly divisible with whatever constant you picked.
Needless to say, you cannot have any interrupts running during flash programming, in case they reside inside the flash array being programmed, or in case they try to access constants/call code inside that flash array.
Upvotes: 2