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.

MSP430FR5739 Data save on Power Reset

Other Parts Discussed in Thread: MSP430FR5739, MSP430WARE

Would like to know how to go about saving RAM data to FRAM location in which the FRAM data would be retained during a power reset or power off condition. The data size is just a few bytes received via serial communication. Once received I would like to send the data to a storage area that would be retained during a loss of power or reset. I have the serial code piece working and storing the data in RAM but need to know how to store the data away.  I'm a little confused on how to do this on the MSP430FR5739 controller, can I simply create a pointer to a FRAM location and copy the data over, or do I need to manipulate the FRAM controller to perform the saving of data.

Any help would be much appreciated. 

  • Hi Obie,

    FRAM is very easy to write to - it is basically like you said, you can just directly write to the FRAM location - no extra handling with the controller is needed (this is a big difference from FLASH). Please see the MSP430FR5739 code example MSP430FR57xx_FRAMWrite.c for a simple code that demonstrates this (In CCS you can find this by going to View > TI Resource Explorer and in MSP430ware navigating to Devices > MSP430FR57xx > Code Examples > MSP430FR573x).

    Now, please note that because FRAM is so easy to write to, you must take care that you are not going to overwrite any of your main application code. To help combat this, I'd make a few additional suggestions:

    Rather than simply using a pointer defined for a specific absolute address, I would actually define an array in my code and tell the linker to place this array in FRAM. Here's how you can do this:

    1. modify your linker file to create a new section within GROUP(READ_WRITE_MEMORY) - you can see in your lnk_msp430fr5739.cmd file that already there is .cio and .sysmem - add your own section (you could call it something like .myVars or something). We are putting it in the READ_WRITE_MEMORY group because this is variable data that you are storing - if it was constants that you were setting up, you could instead store them in the READ_ONLY_MEMORY group.
      1.        GROUP(READ_WRITE_MEMORY)
               {
                  .cio        : {}                   /* C I/O BUFFER                      */
                  .sysmem     : {}                   /* DYNAMIC MEMORY ALLOCATION AREA    */
                  .myVars	  : {} 	//space allocated to store variables in FRAM
               } ALIGN(0x0200), RUN_START(fram_rw_start)
    2. Back in your C file, you can tell the compiler that you want your array to be placed in this section that you just created, instead of in RAM. You can do this by using the DATA_SECTION pragma (for more info see www.ti.com/lit/pdf/slau132 )
      1. #pragma DATA_SECTION(myArray, ".myVars")
        unsigned char myArray[10];

    Now you can just put data directly into this array in FRAM without using any absolute address, you can just use something like myArray[i] = data; to write a value into it just like a normal variable. You don't even have to pass your data through RAM, you could write it straight into the array when doing your serial communication if you'd like. You could place multiple variables all in the same .myVars section, just use the #pragma DATA_SECTION when declaring each one. The reason this is helpful is that it makes sure the linker will set aside enough space in FRAM for you to store your desired data - it will set an area aside for it when it is placing your application code in FRAM, so you know that this space is safe to write data into and won't overwrite any of your code.

    One last tip: before going to production, you should also enable the MPU to put appropriate permissions on your different areas of FRAM so that you can't accidentaly overwrite your code area if you write off the end of your array or something. See the MPU code examples and the MPU section of the user's guide, as well as other posts on this forum for more information on setting up the MPU.

    Regards,

    Katie

  • Katie,

    Thanks for the quick response, detailed explanation and suggestions. Will give it shot.

    Obie

  • Katie Pier said:
    Here's how you can do this:

    Note: This is a CCS specific solution.

  • Katie,

    Did as you explained and it works fine, Thanks. Now that I have it working would it be possible to define or set my .myVars to use the last512k of the  FRAM section. My FRAM map shows it runs from 


    FRAM : origin = 0xC200, length = 0x3D80

    What I'm asking is if I can specify a region/segment where myVars will always be located or could be located. Like the last 512 bytes (0x3B80-0x3D80).

    Obie

  • Thanks for the note, and I am using CCS.

    Obie

  • Hi Obie,

    The way that you can set a specific address for this section to always be located is to modify the linker file again. Where it currently says:

    FRAM : origin = 0xC200, length = 0x3D80

    You could put instead:

        //FRAM                    : origin = 0xC200, length = 0x3D80
        FRAM					: origin = 0xC200, length = 0x3B80	//FRAM without last 512 bytes
        MY_FRAM				: origin = 0xFD80, length = 0x0200	//section for .myVars

    Now you have a separate section called MY_FRAM that is the last 0x200 (512) bytes of the FRAM area, starting at 0xFD80.

    Now you would go back down to the bottom of the linker file where you set up .myVars before. Instead of having it in the GROUP(READ_WRITE_MEMORY) like you did before, you need to remove it there and put it instead below the GROUP(ALL_FRAM), so it will go here:

           GROUP(EXECUTABLE_MEMORY)
           {
              .text       : {}                   /* CODE                              */
           } ALIGN(0x0200), RUN_START(fram_rx_start)
        } > FRAM
    
        .myVars	  : {} 	> MY_FRAM //space allocated to store variables in FRAM
    
        .jtagsignature : {} > JTAGSIGNATURE   /* JTAG SIGNATURE                    */
        .bslsignature  : {} > BSLSIGNATURE    /* BSL SIGNATURE                     */
        .jtagpassword                         /* JTAG PASSWORD                     */

    And you place .myVars in the MY_FRAM section you made above. Now .myVars will always be in the last 512 bytes of your FRAM.

    Personally, I do not like doing this as well as putting your data in the READ_WRITE_MEMORY section in ALL_FRAM like we did before and allowing the linker to decide the address where it should be. Here is the reason why - you can see in the linker file that there are 3 values fram_rw_start, fram_ro_start, and fram_rx_start, that the linker defines in order to help you create your MPU settings. Keeping data with the same read/write/execute permissions grouped together inside the FRAM allows you to set up the MPU with correct permissions on each segment - and you can only make 3 segments with the MPU. Therefore, you want to keep all of your read/write permission data together in one place, so that you can set only that portion of FRAM to have read/write permissions and lock down all the rest of it (like your code space).

    If you keep your data within the groups defined in the linker file, then when you are done with code development and want to set up your MPU, you can find in your .map file the values for fram_rw_start, fram_ro_start, and fram_rx_start, and simply plug these addresses in for your MPU settings, making it easy - the linker has already done all the work of figuring out exactly what size each of the sections needs to be and rounded it to the correct alignment for the granularity of the MPU.

    On the other hand, if you have created your own FRAM section at the end, this is outside of the automatic grouping that the linker was doing, so you may not have all of your read/write data in one place, making it where you aren't going to be able to set up the MPU to protect these sections correctly - you could have some read/write data at 0xC200 and other read/write data at your 0xFB80 address that you defined.

    Further, if you use an array or structure to store your data, then you should not need to know the absolute address where it is defined because you can simply access it using a pointer to the array or structure.

    Regards,

    Katie

  • Katie,

    I configured my FRAM exactly as you explained and everything is working fine other then the loss of data when I power reset the micro. It appears all data in my FRAM storage area is set back to zero. Is there something I'm missing that I need to configure to maintain and hold data during a power loss. This happens with or without the debug interface attached, with the MSP430-FET430 attached I can see the data is reset to zeroes once I command the CPU to restart

    FYI, My code,

    linker cmd sections

    GROUP(ALL_FRAM)
        {
           GROUP(READ_WRITE_MEMORY)
           {
              .cio        : {}                   /* C I/O BUFFER                      */
              .sysmem     : {}                   /* DYNAMIC MEMORY ALLOCATION AREA    */
              .myNVvars   : {}                   /* Storage area for NV data          */
           } ALIGN(0x0200), RUN_START(fram_rw_start)
    
           GROUP(READ_ONLY_MEMORY)
           {
              .cinit      : {}                   /* INITIALIZATION TABLES             */
              .pinit      : {}                   /* C++ CONSTRUCTOR TABLES            */
              .init_array : {}                   /* C++ CONSTRUCTOR TABLES            */
              .mspabi.exidx : {}                 /* C++ CONSTRUCTOR TABLES            */
              .mspabi.extab : {}                 /* C++ CONSTRUCTOR TABLES            */
              .const      : {}                   /* CONSTANT DATA                     */
           } ALIGN(0x0200), RUN_START(fram_ro_start)
    
           GROUP(EXECUTABLE_MEMORY)
           {
              .text       : {}                   /* CODE                              */
           } ALIGN(0x0200), RUN_START(fram_rx_start)
        } > FRAM
    

    My definition in main.c

    #pragma DATA_SECTION(ubDataStorage, ".myNVvars")
    unsigned char ubDataStorage[16]; 
    
    Any thoughts would be greatly appreciated

    Obie
  • Hi Obie,

    How are you viewing the variables after a power cycle? Have you told CCS to load symbols only, and you connect to the part with the debugger then to view these? If the part gets reprogrammed your data will be lost. http://processors.wiki.ti.com/index.php/MSP430_-_Connecting_to_a_running_target

    One other thing to look into is using the PERSISTENT pragma in CCS to make sure that your array isn't getting zero-initialized at startup (making you lose your data). In EABI uninitialized variables are zero-initialized by default - this pragma tells it not to do that to this variable. See section 5.11.9 in the C/C++ compiler guide for more details on the PERSISTENT and NOINIT pragmas: www.ti.com/lit/pdf/slau132

    Regards,

    Katie

  • Let me suggest a mechanism for this.

    Put the array into read-only data area (should be done by declaring it const).

    Now before writing to it, change the MCU so that the RO area becomes writable (while still protecting the code).
    Then access the variables. Since they are const, you cannot directly write to them. You need to do some pointer casting to remove the const qualifier.

    If data is an int, something like

    *((int*)((void*)&data)) = x; should do it. You may get a ‘qualifier lost’ warning that you can ignore. (if it is an array, remove the "&" as a poitne ris already a reference to the data)

    Then change the MCU back to protect the RO area again.

    The data should survive any restart because it is stored along all the other constants. It does not, however, survive reprogramming, so be sure the debugger doesn’t reprogram the target on each debugging run.

**Attention** This is a public forum