On the MSP430FR2633 I needed to store an unsigned long in FRAM. The approach and examples suggested this was a straightforward task. The catch is that I wanted to avoid losing the contents when reprogramming the chip. This post is a description of what I needed to do to save variables to FRAM with the NOINIT pragma.
The following is based on CCS 8.0.
Step 1: Declare the variable
When using the FR2633, If you do not care what happens to the contents on re-program, saving variables in FRAM is this easy:
#pragma PERSISTENT(var) unsigned long var=0; SYSCFG0 = FRWPPW | DFWP; // Program FRAM write enable var++; // Increment the variable (aka, treat it like any other variable) SYSCFG0 = FRWPPW | PFWP | DFWP; // Program FRAM write protected (not writable)
Lines 01 and 02 set up the variable "var." The pragma PERSISTENT tells the compiler that it needs to locate the variable "var" in a specific memory space. The variable initialization is similar to initializing a variable in RAM. (In this example, it is set to 0.) A linker command file defines that memory space. I'll get to that file later. Lines 04 enables writes to FRAM, while line 06 disables the writes. Line 05 can be whatever you want. Accessing variables that exist in FRAM is no different than variables in RAM. In this example, I am just incrementing the value each time the program runs.
Locking and unlocking the FRAM is a quasi-optional step. The idea is that if something terrible happens, the locked FRAM is not accidentally overwritten. It would be effortless to have an array that overruns into another memory location.
The PERSISTENT pragma works out of the box, but it has a gotcha. Each time you re-load the code, it gets wiped out. You might be asking, what is the point of that? Well, the upside is that FRAM stores data when the processor goes into its deepest sleep (LPM5 I think). So you could dump variables stored in static RAM into FRAM. (Or on the MSP430, just keep them there.) There are some power usage costs. Saving to FRAM takes more energy than RAM. Also, there needs to be 10us after coming out of some sleep modes before you can save again to FRAM. Also, you need to configure wait states if the clock is running faster than 8 MHz. I guess that for most applications those are not serious tradeoffs.
The issue I have is that I'm going to be storing a value that needs to persist whenever I overwrite the code. No problem, I thought. I can just use the pragma called NOINIT instead.
#pragma NOINIT(var) unsigned long var;
Unlike PERSISTENT, NOINIT does not initialize the variable. Instead, you can get an address, and that is about it. Sort of. It turns out that in CCA v8, the example project I loaded, has a default set to erase "main memory." I naively thought that the description excluded FRAM, but it appears not to be the case.
Step 2: Change Erase Options
I found changing to “Replace written memory locations, retain unwritten memory locations” or “Erase and download necessary segments only (Differential download)” kept the FRAM intact. I would like to understand each of these options in more detail, but that will have to wait.
Step 3: Editing the Linker Command File
This step really irritated me. TI has gone out of their way to hide this critical change. In the application note "MSP430TM FRAM Technology – How To and Best Practices", they outline how to "properly" use FRAM on an MSP430. Except, the document really does not. At least in the case of preserving FRAM contents during re-programming. In section 5.1, it describes NOINIT and PERSISTENT. However, it brushes off a key step for NOINIT with the following comment.
"Requiring a minor modication of the linker command file is such functionlity is required." What?!
If you study the document closer, it infers the changes needed to be made to the "linker command file." However, it never clearly states WHAT. Also, the disturbing aspect is that it is a single line of code. In most of the forum references, the closest correct answer I found was, "...read the linker user's manual."
The "linker command file" is generated by CCS automatically for your processor. In my case, it is the “lnk_msp430fr2633.cmd.” Inside appears to be the memory map the linker uses to put variables into a memory location. Search for ".TI.noinit," to find it in the RAM definitions.
Comment that line out and them scroll back up to the line marked "SECTION." There if you look at the structure, it is groups called things like "READ_WRITE_MEMORY" and "READ_ONLY_MEMORY."
Here I added the line at 137. It tells the linker to place "NOINIT" variables into FRAM and that they should be READ and WRITE capable. That's it.
In summary, you need to declare the variable (well documented), need to change the debug erase options (no documentation), and change two lines in the linker command file (obstructed documentation.) From there, my code works. I can RESET the processor, power cycle it, and re-load the code. Each time, the contents of the FRAM location are saved.
#include <msp430.h>
#pragma NOINIT(Port_event)
unsigned long Port_event;
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
P1OUT &= ~BIT7;
P1DIR |= BIT7;
PM5CTL0 &= ~LOCKLPM5; // release power-on high z mode
// Add the variable Port_event in FRAM to record the button event
SYSCFG0 = FRWPPW | DFWP; // Program FRAM write enable
Port_event++; // Record the port event in FRAM
if (Port_event > 0)
P1OUT |= BIT7;
SYSCFG0 = FRWPPW | PFWP | DFWP; // Program FRAM write protected (not writable)
__bis_SR_register(LPM4_bits | GIE);
}
That said, it's time to move on to the next MSP430 peripheral I need for my project. Like the other 3, I've configured so far, the examples and documentation are missing critical information.
Top Comments