This article is about using the flash memory in MSP430. This will not teach you C programming, using MSP430 or flash memory basics. They are well documented on the internet. This article will help all of us who are struggling to use the flash memory.
I started off a project on MSP430G2553(Now on, I will refer it as micro). Soon I realised that I needed to store a large amount of data. I needed to store 4096 bytes. However, there was only 256 bytes of EEPROM space but 16KB flash memory. Initially, I was reluctant to use the flash but had no other option. So I started off. The trouble is that flash memory is not easy to handle. But not that difficult either. Reading is straight forward. There will be a pointer to the flash space and you just need to reference that pointer. However, the trouble starts with writing data.
The flash is arranged as segments A, B, C, D and other segments. A, B, C, D are 64 byte segments and the others are 512 byte segments. A in particular is locked as it contains information about calibration. When you wish to write to a segment, you need to erase it first. Even if you want to store one single byte, you need to erase the whole segment.
Let us first look at the flash erase function
// __DINT() is in IAR workbench
void flash_erase(int *addr)
{
_DINT(); // Disable interrupts. This is important, otherwise,
// a flash operation in progress while interrupt may
// crash the system.
while(BUSY & FCTL3); // Check if Flash being used
FCTL2 = FWKEY + FSSEL_1 + FN3; // Clk = SMCLK/4
FCTL1 = FWKEY + ERASE; // Set Erase bit
FCTL3 = FWKEY; // Clear Lock bit
*addr = 0; // Dummy write to erase Flash segment
while(BUSY & FCTL3); // Check if Flash being used
FCTL1 = FWKEY; // Clear WRT bit
FCTL3 = FWKEY + LOCK; // Set LOCK bit
_EINT();
}
Initially, the interrupts need to be disabled. After that, the flash must be configured in erase mode. The information about the registers can be obtained from the datasheet. FWKEY is the password for the flash memory and should be included for every action. After configuring the registers, a dummy write must be done to the given address. This will erase the whole segment. Once the erase is complete, the memory should be locked and interrupts enabled again.
Once a segment is erased, data can be written to it. In the following example, I have used two global registers, data_buffer and data_dump which hold 64 16 bit integers each.
void flash_write(int *addr, char option)
{
_DINT(); // Disable interrupts(IAR workbench).
int i = 0;
FCTL2 = FWKEY + FSSEL_1 + FN0; // Clk = SMCLK/4
FCTL3 = FWKEY; // Clear Lock bit
FCTL1 = FWKEY + WRT; // Set WRT bit for write operation
if(option == WRITE_FROM_BUFFER)
for (i=0; i<64; i++)
*addr++ = data_buffer[i]; // copy value to flash
else if(option == WRITE_FROM_DUMP)
for (i=0; i<64; i++)
*addr++ = data_dump[i]; // copy value to flash
FCTL1 = FWKEY; // Clear WRT bit
FCTL3 = FWKEY + LOCK; // Set LOCK bit
_EINT();
}
The code is self explanatory. Though I have used data buffers, there shouldn’t be any problem writing single data. Note that each segment is 512 bytes and hence it can hold 256 integers(each integer in msp430 is 2 bytes). Hence, once a segment is erased, we can perform 4 buffer writes. Also note that addresses 0xfe00-0xffff hold interrupt vectors. Hence don’t use the segment which will overwrite these. This will result in the microcontroller malfunctioning after restart since the interrupt vector will get erased and the microcontroller will not know where to start from.
With the above two functions, let us look at a simple example where we save 256 integers
void main(){
// Assume all initiations are done.
// flash_erase erases a segment
// flash_write writes 64 values
// get_value() is any function which returns some value
// print() is a generic function which prints the value given as input
int *addr = (int *)0x0E000 // Address of the flash memory segment starting
int *addr_cpy; // A copy of the address
int buffer[64]; // data buffer
int i, j;
flashs_erase(addr);
for(j=0; j<4; j++){
for(i=0; i<64; i++){
buffer[i] = get_value(); // Save a value.
}
flash_write(addr + j*64, buffer, WRITE_FROM_BUFFER); // Write the values to the flash
}
// Now print all the values
for(i=0; i<256; i++)
print(addr[i]);
}
That ends our article on using flash memory. Please feel free to comment. I would be more than happy to learn from you.