This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430FR5994: Testing use of '#pragma PERSISTENT' writing to FRAM across Power Cycles, with the Debugger.

Part Number: MSP430FR5994

A little Program was intended to test the FRAM retention of a set of three PID Parameters, held in float format.

Note that the PID Parameters can be updated, via an upload Command, sent via a Serial Link.

By default, these were stored in RAM, so were lost after a Power Cycle.  So migration to FRAM storage, is desirable:

#include <msp430.h>
#include <stdint.h>
#include <stdbool.h>


typedef float float32_t;

#define P_GAIN_LOCAL 0.8
#define I_GAIN_LOCAL 2.0
#define D_GAIN_LOCAL 0.6

// Number of float32_t Values written.
#define WRITE_SIZE 3

// Specify for first PID Set...
#pragma PERSISTENT(FRAM_array_1)
float32_t FRAM_array_1[WRITE_SIZE] = {0};

// Definitions
struct
{
    float32_t p_gain_value;
    float32_t i_gain_value;
    float32_t d_gain_value;

} pid_gain = {P_GAIN_LOCAL, I_GAIN_LOCAL, D_GAIN_LOCAL};


float32_t sum_of_gains = 0.0;


/**
* main.c
*
* Test of use of #pragma PERSISTENT(FRAM_array_1) Directives,
* applied (for now) to one set of pid_gain Parameters.
* Wanted to survive Power Cycles.
*
*/
int main(void)
{
    WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer

    // Read PID gain values from FRAM Block, to load any
    // previously FRAM stored values.
    pid_gain.p_gain_value = FRAM_array_1[0];
    pid_gain.i_gain_value = FRAM_array_1[1];
    pid_gain.d_gain_value = FRAM_array_1[2];


    // The PID values obtained from FRAM...
    sum_of_gains = pid_gain.p_gain_value + pid_gain.i_gain_value + pid_gain.d_gain_value;

    // Loaded gain_value items
    pid_gain.p_gain_value = P_GAIN_LOCAL;
    pid_gain.i_gain_value = I_GAIN_LOCAL;
    pid_gain.d_gain_value = D_GAIN_LOCAL;

    // The updated PID values written...
    sum_of_gains = pid_gain.p_gain_value + pid_gain.i_gain_value + pid_gain.d_gain_value;

    // Write FRAM with float32_t gain_value items.
    FRAM_array_1[0] = pid_gain.p_gain_value;
    FRAM_array_1[1] = pid_gain.i_gain_value;
    FRAM_array_1[2] = pid_gain.d_gain_value;

    return 0;
}

The FRAM locations written (displayed via 'Expressions" in the CCS 9.1 Debugger), were reported at 0x4000, 0x4004 & 0x4008.  This is the base of FRAM, on the FR5994 Device.

The test PID Values appeared to be written fine, while single-stepping the Debugger.

After power cycling the Launchpad Development Kit, and running the Debugger again, the above Memory Locations were cleared.

As the Debugger has no knowledge that FRAM is being used for Variable Storage, when it loads the Program to debug it, the assumption is that the FRAM storage might have worked, but has been wiped out by the next invocation of the Debugger.  This after the required Power Cycle, for testing FRAM retention.

I'm sure the FRAM works, as "flashed" Code is retained, and works fine!

Is there a way to easily test the FRAM Data retention, with the Debugger?

  • Try going into Project Properties -> Debug -> MSP430 Flash Settings

    Listed on the right side of the window are the available Erase Options when a program is loaded during debug. However, also note that since you're using PERSISTENT the array will be initialized to zero every time you load the program into the chip. You may want to substitute PERSISTENT with NOINIT, as well as modifying the Erase Options to avoid having your PID values erased.

    7.9.25 The NOINIT and PERSISTENT Pragmas

    Project Properties Debug menu

  • Hi Andrew,

    Are you flashing code every time you start a debug session? As Seth noted, loading code will set the persistent variable back to 0.

    Can you try:

    1. Load code
    2. Disconnect the debugger
    3. Power cycle the device
    4. Reconnect the debugger
    5. Inspect the persistent variable. It should be what you last wrote to it.

    Evan

  • I'm using the USB Power Input, so wrapping a for (;;) loop (so I can re-run the Code), gives a JTAG Communication Error.  The only option is to Stop the prior Debug Session.  I'll try Seth's idea...

  • Unsure if done as intended - PERSISTENT changed for NOINIT.

    The addresses previously auto-selected in FRAM to be written (0x4000, 0x4004 & 0x4008) were changed to 0x1C00, 0x1C04 & 0x1C08.  These are not at base of FRAM, but base of RAM!

    Excluded from Memory Cleared, was just the 0x0C locations previously used at FRAM base from being cleared (i.e. 0x400C to 0x43FFF).

    Little doubt this will work in the Application if carefully coded.  But it would be nice to prove first.

  • You'll have to modify the linker file. For some reason, the default linker files I've seen don't have the section for NOINIT defined.

    In the lnk_msp430fr5994.cmd file near the top in the READ_WRITE_MEMORY group, find the line that contains:

    .TI.persistent : {}              /* For #pragma persistent            */

    Add a line below that looks almost identical except replace "persistent" with "noinit"

    .TI.noinit     : {} /* For #pragma noinit   */

    This will place your NOINIT in FRAM but also exclude it from the C environment initialization. If you start with a blank device that hasn't been programmed before, the NOINIT memory segment bytes will all be high (0xFF) until you set an initial value. In the float32_t data type, that data value would equate to NaN so you may need to place some checks in your code if that would cause issues in your system.

  • Edited lnk_msp430fr5994.cmd, as indicated.  Not a happy camper after rebuild (with NONAME).  Debugger won't enter to allow first 'step into'; only Stop seems to work.  May have been why 'noname' was left out of the Linker File!

    Reverted back to #pragma PERSISTENT, just to check that debugged (it did).  Thanks for the help, so far.

  • When you say NONAME, you're referring to NOINIT, right? If so, did you modify the line

    float32_t FRAM_array_1[WRITE_SIZE] = {0};

    and remove the assignment to {0}? The assignment can't be there for a NOINIT. It's not a compiler error, only a warning, #1518-D to be exact.

  • Long Week (4 Days * 10 Hours), was NOINIT.  Removing the '= {0}', and the clearing exclusion from FRAM, worked!  PID values read back fine from FRAM, across Power Cycle, with next debug session.

  • Feels good, right? FRAM is definitely a great technology, feels so strange to simply write to a variable like normal and it persists through resets, etc.

    Glad it worked out for you.

  • Yes, it really is much easier (and much longer lasting), than flashing.  Like the old Core-Store (they really were tiny Ferrite Beads), the destructive read is the liability, requiring immediate rewrite.  16MHz FR5994 MCU effectively runs at 8MHz.

    Have been trying the MPY32 Unit, with Q16.16 format.  To replace current float Math for PID Loops.  Really fast!

**Attention** This is a public forum