This article explains how we wrote data to flash during program execution when developing the batteryless energy harvesting remote control. This remote control used an MCU from the Freescale KL25z series. The MkL25Z128VLK4 was used. Please be aware that, if done incorrectly, this can kill your MCU.
The starting point for flashing data is to define a memory region to store the data in. At this point it is necessary to look at the memory map for the device to see where the flash is located and if there are any protected/reserved regions in it. If we look at the Freescale MkL25z series reference manual we can see that the regions specified in the example code below are suitable to store the data.
/*------------------------------------------------------------------------------ Data stores for eink frame buffer and IR codes. Volatile to prevent compiler optimisation *------------------------------------------------------------------------------*/ volatile ir_command device_1[45] __attribute__((at(0x09000))); volatile ir_command device_2[45] __attribute__((at(0x0A000))); volatile ir_command device_3[45] __attribute__((at(0x0B000))); volatile ir_command device_4[45] __attribute__((at(0x0C000))); volatile char frame_buff_store[176][33] __attribute__((at(0x0D000))); volatile int page __attribute__((at(0x0C500))); volatile int device __attribute__((at(0x0C504))); volatile int signed tv_locat[6] __attribute__((at(0x0C800))); volatile int signed dvd_locat[6] __attribute__((at(0x0CF00)));
These regions have been defined as global variables. The regions usually have to be long word aligned.
In the energy harvesting remote control example we defined regions for the infra-red commands that the remote control could “learn”. We also defined regions for the frame buffer, for the page and for the device so that the e-ink display could be altered accordingly. We added regions for the Television and the DVD player locations, these used accelerometer and magnetometer values to determine where the remote was pointing. If pointing at the television, the device would automatically switch to the Television User Interface.
The next stage of the process is to test whether it is safe to flash the regions chosen. Memory View in Keil Debugger is excellent for this. Look at the 1 KB after the region defined for the data. This region should be blank and unchanging. If it is not it may be necessary to look at the scatter file to see what is next to it.
Once you are happy that you are not going to corrupt something by erasing the block of flash, it is time to erase the flash. For a successful erase to occur, and to ensure that the core does not lockup and reset, it is essential that nothing else is going to use the flash during these operations.
This is achieved by placing the code to execute the flash commands into the on-board RAM for execution. To do this you need to right-hand click on the file you are using to write data to flash, and selecting options. You then need to look at the memory assignment section at the bottom of the page and change the ‘Code / Const’ to a RAM region on your MCU. This will ensure that every time your application starts this section of code is copied into the RAM for execution.
The important registers for the MkL25Z series are the FCCOB registers and, as can be seen in the reference manual, these are numbered 0-B. The FSTAT register is needed to determine if the FCCOB registers are free to use.
/*------------------------------------------------------------------------------ Erase the flash block containing the sector *------------------------------------------------------------------------------*/ int flash_EraseSector(long int sector) { int temp1,temp2,temp3; //Dividing the sector address into the 3 bytes before starting to fill the FCCOB //registers reduces the risk of the operation being corrupted: temp1 = (sector << 8); temp1 = temp1 >> 24; temp2 = (sector << 16); temp2 = temp2 >> 24; temp3 = (sector << 24); temp3 = temp3 >> 24; //Wait until the command complete instruction is cleared indicate the FCCOB registers //are available: while(((FTFA->FSTAT)&(1UL << 7))==0x00); //Clear any previous errors from the last operation: if(!((FTFA->FSTAT)==0x80)) {FTFA->FSTAT = 0x30;} FTFA->FCCOB0 = 0x09; // 0x09 is the instruction code for erase FTFA->FCCOB1 = temp1; // Load the segment address into registers FTFA->FCCOB2 = temp2; FTFA->FCCOB3 = temp3; FTFA->FSTAT = 0x80; // Writing a 1 to bit 7 of the FSTAT register launches // Wait for command completion: while(((FTFA->FSTAT)&(1UL << 7))==0x00); return 1; }
The example code above shows exactly how we did the erase for the MkL25Z series. This function is called from another handler function also running directly from RAM to reduce the risk of core lockup. The handler function is shown below. A delay is required between calling the handler function and carrying out flash commands to prevent core lockup. In the example code below this was achieved with a simple for loop.
/*------------------------------------------------------------------------------ Write a page and device to flash memory. *------------------------------------------------------------------------------*/ void flash_memory_tv_loc(signed int ax,signed int ay,signed int az,signed int hx,signed int hy,signed int hz) { int j; for(j=0;j<10000;j++); //delay flash_EraseSector((int)&tv_locat); for(j=0;j<100;j++); flash_writeLongWord((int)&tv_locat[0],ax); flash_writeLongWord((int)&tv_locat[1],ay); flash_writeLongWord((int)&tv_locat[2],az); flash_writeLongWord((int)&tv_locat[3],hx); flash_writeLongWord((int)&tv_locat[4],hy); flash_writeLongWord((int)&tv_locat[5],hz); }
The final part of writing data to flash is the write itself. This is very similar to erasing but with a different instruction code. The example code shows how this is achieved for the MkL25Z series.
/*------------------------------------------------------------------------------ Write a long word to an erased flash block *------------------------------------------------------------------------------*/ int flash_writeLongWord(long int locat,long int value) { int temp1,temp2,temp3,temp4,temp5,temp6,temp7; temp1 = (locat << 8); temp1 = temp1 >> 24; temp2 = (locat << 16); temp2 = temp2 >> 24; temp3 = (locat << 24); temp3 = temp3 >> 24; temp4 = value >> 24; temp5 = (value << 8); temp5 = temp5 >> 24; temp6 = (value << 16); temp6 = temp6 >> 24; temp7 = (value << 24); temp7 = temp7 >> 24; while(((FTFA->FSTAT)&(1UL << 7))==0x00); if(!((FTFA->FSTAT)==0x80)) {FTFA->FSTAT = 0x30;} FTFA->FCCOB0 = 0x06; //0x06 is instruction code for write long word. FTFA->FCCOB1 = temp1; FTFA->FCCOB2 = temp2; FTFA->FCCOB3 = temp3; FTFA->FCCOB4 = temp4; FTFA->FCCOB5 = temp5; FTFA->FCCOB6 = temp6; FTFA->FCCOB7 = temp7; FTFA->FSTAT = 0x80; while(((FTFA->FSTAT)&(1UL << 7))==0x00); return 1; }
As can be seen, more of the FCCOB registers are required, when compared with an erase, because the location and the value must be specified.The instruction code for writing a long word is 0x06. The instruction code for an erase is 0x09. These can be looked up in the reference manual.
If you have problems with core lockup, check all the pointers for the functions/data you’re using to make sure they’re in RAM.
The next development step is available in the blog below.
[CTAToken URL = "https://community.arm.com/iot/embedded/b/embedded-blog/posts/infra-red-code-learning-and-repeating-for-the-batteryless-energy-harvesting-remote-control" target="_blank" text="Read next development step on infra-red code learning and repeating" class ="green"]
Dear All,
In the above article I read that "A delay is required between calling the handler function and carrying out flash commands to prevent core lockup."
Caould You better explain what is the cause of the core lockup when no delay is added?
I'm working with Kinetis MK66FN2M0VMD18 on a custom board, could You confirm that this workaround is needed also for my target?
Could You estimate the value of the delay to add before the Erase Sector Command and before Write Long Word Command?
Thanks for Your support.
Best Regards
Emanuele