Search code examples
cembeddedmicrocontrollerflash-memory

Flash programming causes unexpectedly reset


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:

enter image description here

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;
    }

}

Solution

  • 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);
        }
    }