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.
Hi vishwanath
myself deepak
Could you please suggest me
->how i can save the ADCvalues directly to flash memory
->then transfer the same data from flash to computer.
Hi Deepak,
I think you can do a continuous conversion. I believe you can give a starting address and it is used as a pointer for storing the data. I haven’t tested it myself. After every conversion, you can increment a copy of the pointer and send the data from that address to PC using UART.
Thanks vishwanath for your immediate response.
I would really try that will get back.
Could give your mail id so that i can ask few more question there.
what is WRITE_FROM_BUFFER and option?
There are two buffers of size 64. WRITE_FROM_BUFFER is for selecting one of the two buffers.
Excellent explanation, good examples. Good job!
Hi Yvon, thank you very much for the kind gesture.
Excellent, thanks a lot!
how to read flash memory for msp430f5152
Hello Parthiban, unfortunately I never worked on MSP430F5152. However, I am assuming it will be almost the same. For the smaller details, you can try having a look at the data sheet.
Thanks for the example. What is the purpose of addr_cpy
Hello Adam,
Thank you for your comment. Also, thank you for pointing that out. I might have kept addr_cpy for some debugging purpose, but it is not necessary. You can remove it from the code.
Your example works well on our prototypes. Unfortunately, in production it tends to drop data bits on 20% of the MCUs that we tested. (200 failures for a lot of 1000 units.) Any suggestions?
Hello Ben, thank you for checking out my article. Could you give me a little more information about your project? For example, what are you doing, how much data are you transferring, etc. I hope that will help us solve the issue.
Did you add a routine to check that the flash was actually written, and if not, try again?
Hello Travis. No, I didn’t add a routine to check. Do you have any suggestions in this regard? Thanks!
Hi Viswanath;
Beautifully explained.
I really appreciate you. Good Job 🙂
Hello Navneet,
Thank you for your kind gesture.
Hi Viswanath,
Two questions, Why you use the add:0x0E000, is this a segment?, How many bytes you can store there?
Thank for your help.
Hello Eduardo. Yes, 0x0E000 is a segment. From what I recollect, you can store 512 bytes in each segment. You would get more details from the datasheet. Cheers!
Hi , very helpful tutorial 😉
I have one question , in your flash_write part your define WRITE_FROM_BUFFER and WRITE_FROM_DUMP like variable , but what type of variable ? int? unsigned char ?
and for data_buffer [] ? it’s int , I think?
thank again
Anaelle
Hello Anaelle,
WRITE_FROM_BUFFER and WRITE_FROM_DUMP are preprocessor #define variables and are ‘char’ type.
The data_buffer[] is of int type.
For further information, do check the code in repository: https://github.com/vishwa91/e_steth
Regards.
thank you 🙂
i have some question about your void flash_read(int *addr, char option) fonction,
How do you use it in main fonction ?
Anaëlle
The function flash_read() writes data to one of the two data buffers defined in the main script. Does that answer your question?
Hello, i have a question. Is it possible to write 28800 uint8 type numbers in the MSP430G2553?
Hello. Did you find your answer? I think that depends on memory space. As log as you can allocate 28800 bytes of memory, you can store them.