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.

MSP430FR2422: Understanding LPM3.5 and how to save/restore variables via FRAM

Part Number: MSP430FR2422
Other Parts Discussed in Thread: MSP-FET

Hi,

I wanted to take a closer look at the newer FRAM generation of MSP430 and decided to use the MSP430FR2422...

My project looks like this:
A LoRa module (SX1278) is connected to an MSP430FR2422 via SPI (UCA0 in SPI mode). The MSP430 is clocked by DCO (1MHz) and the RTC by VLO (10kHz). No external crystal is connected.
Everything should run for months on batteries. the main power source are 3x AAA Batteries with a LDO. 
So I decided to use the low power mode 3.5 (LPM3.5) and am currently waking up the MSP430 every 10 seconds with the RTC as a test to send a LoRa data packet of 6 bytes.
Later, these 10 seconds will of course be set to a higher cycle time.

In LPM3.5 I measured a power consumption of 200nA (0,2uA) which is fantastic. 
The wake-up via the RTC interrupt is also working perfectly.
A LoRa receiver receives successfully the packets of 6 Bytes from the MSP430 (in the period of 10sec).

The problem is, that only the data of the first packet are correct. 
Already the 2nd data packet has incorrect data. These look completely random to me.
 
I am aware that the RAM content is lost in LPM3.5.

Within a LoRa data packet (6 byte payload) there is a counter that is supposed to count the packets that have already been sent.
After the LPM3.5, the MSP430FR2422 is reset and the counter value is lost or may be reinitialized after the restart...
There is also a fixed, unique device ID. 
This is also completely wrong after a restart. 

In general, I now ask myself the question of how to correctly "save" the counter value (variables / char arrays).
Now several questions:

1. Do I have to write the entire RAM content to FRAM before switching to LPM3.5 (RAM --> FRAM)? And later, after waking up / resetting, restore the RAM content from FRAM (FRAM-RAM)? Or is it enough if I only back up the data that is really relevant to me? Is there a (simple) routine I could use to back up the entire RAM?
Another consideration: If my application only uses 200 bytes of RAM, then I can only store this in FRAM, right? Or does it somehow corrupt the RAM content after waking up.


2. If I store char variables / arrays with #pragma persistent(x) in the FRAM under CCS, then these are already in the FRAM and no longer have to be saved, right? After a reset / waking up from LPM3.5, the counter value shouldn't have been reset?

3. Under CCS 11 I use the MSP-FET for debugging. This has always worked well so far, but not with the LPM3.5. Is this normal or is there a setting missing somewhere? A breakpoint in the RTC ISR is not taken. And they don't see that the application restarts either. A breakpoint in the if statement for querying the status of the interrupt vector (to decide wake-up or cold start) is also not taken.

I would be very grateful for any hints or advice!
I can't go any further on my own...

I'll post my main.c with the main routine here.
You might spot an error right away.

I used the unmodified, auto-generated file from CCS as the linker file.
(Maybe this still needs to be adjusted to make the FRAM work?)

Many thanks and best regards
Stefan

#include "msp430.h"
#include "stdint.h"
#include "SX1278.h"

#define FIXED_PACKET_LENGTH     (6)
#define SENSOR_ID               (0x34)

// function prototypes
void initGpio(void);

#pragma PERSISTENT(LoRaPacketCount)
unsigned long LoRaPacketCount = 0;
#pragma PERSISTENT(SensorNodeID)
unsigned char SensorNodeID = SENSOR_ID;

#pragma PERSISTENT(LoRa_packet)
volatile unsigned char LoRa_packet[FIXED_PACKET_LENGTH] = {0x34, 0xFF, 0xFF, 0x00, 0x00, 0x00};          // payload of the lora packet

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;               // Stop WDT
    initGpio();                             // Configure GPIO

    // disabling the SVS (Supply Voltage Supervisor) --> not needed
    PMMCTL0_H = PMMPW_H;
    PMMCTL0_L &= ~SVSHE;

    // First determine whether we are coming out of an LPMx.5 or a regular RESET.
    if (SYSRSTIV == SYSRSTIV_LPM5WU)        // When woken up from LPM3.5, reinit
    {
        // If MCU wakes up from LPM3.5, re-init and then return to LPM3.5 again.
        LoRaPacketCount++;               
        LoRa_packet[0] = SENSOR_ID;      // unique SensorNode ID
        LoRa_packet[1] = 0xFF;           // DS1820 temp (LSB)
        LoRa_packet[2] = 0xFF;           // DS1820 temp (MSB)
        LoRa_packet[3] = (uint8_t)(LoRaPacketCount & 0x000000FF);           // 24bit counter (LSB)
        LoRa_packet[4] = (uint8_t)((LoRaPacketCount & 0x0000FF00) >> 8);    // 24bit counter
        LoRa_packet[5] = (uint8_t)((LoRaPacketCount & 0x00FF0000) >> 16);   // 24bit counter (MSB)

        // send the packet via LoRa Module
        SX1278_sendData(&LoRa_packet[0]);
        SX1278_sleep();
        __enable_interrupt();               // The RTC interrupt should trigger now...
    }
    else
    {
        // Device powered up from a cold start.
        // It configures the device and puts the device into LPM3.5

        P2OUT |= BIT0;                            // NSS high
        __delay_cycles(75);                       // Wait for SX1278 to initialize

        init_SX1278(FIXED_PACKET_LENGTH);
        // send the packet via LoRa Module
        SX1278_sendData(&LoRa_packet[0]);
        SX1278_sleep();

        // Configure RTC
        // Interrupt and reset happen every 1024/10000 * 100 = 10 sec.
        RTCMOD = 100-1;
        RTCCTL = RTCSS__VLOCLK | RTCSR |RTCPS__1024;
        RTCCTL |= RTCIE;
    }

    // Enter LPM3.5 mode with interrupts enabled. Note that this operation does
    // not return. The LPM3.5 will exit through a RESET event, resulting in a
    // re-start of the code.

    PMMCTL0_H = PMMPW_H;                    // Open PMM Registers for write
    PMMCTL0_L |= PMMREGOFF;                 // and set PMMREGOFF

    // It is recommended to turn off the LPM3.5 switch to avoid unnecessary leakage (see user guide page 88)
    // set LPM5SW = 0 in the PM5CTL0 register
    PM5CTL0 &= ~LPM5SW;

    __bis_SR_register(LPM3_bits | GIE);
    __no_operation();

    return 0;
}

void initGpio(void)
{
    // caution: there are pullup resistors for the I2C funtionality (SDL, SDA)
    // keep in mind to leave the Port Pins high to reduce current consumption
    // port configuration will be remaining in low power states
    P1DIR = 0xFF; P2DIR = 0xFF;
    P1REN = 0xFF; P2REN = 0xFF;
    P1OUT = 0xFF; P2OUT = 0xFF;

    // --- SPI configuration (SX1278 is connected at UCA0) ---
    // MOSI = P1.4
    // MISO = P1.5
    // SCLK = P1.6
    // CSn  = P2.0 (chip enabled is controlled manually by GPIO)
    P1SEL0 |= BIT4 | BIT5 | BIT6;             // set 3-SPI pin as second function

    UCA0CTLW0 |= UCSWRST;                     // **Put state machine in reset**
    UCA0CTLW0 |= UCMST|UCSYNC|UCMSB;   // 3-pin, 8-bit SPI master
                                              // Clock polarity high, MSB
    UCA0CTLW0 |= UCSSEL__SMCLK;               // SMCLK
    UCA0BR0 = 0x02;                           // SMCLK / 2 = 500kHz, fBitClock = fBRCLK/UCBRx
    UCA0BR1 = 0;                              //
    UCA0MCTLW = 0;                            // No modulation
    UCA0CTLW0 &= ~UCSWRST;                    // **Initialize USCI state machine**
    // --- end of SPI configuration ---

    // Disable the GPIO power-on default high-impedance mode
    // to activate previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;
}

#pragma vector = RTC_VECTOR
__interrupt void RTC_ISR(void)
{
    switch(__even_in_range(RTCIV, RTCIV_RTCIF))
    {
        case RTCIV_NONE : break;            // No interrupt pending
        case RTCIV_RTCIF:                   // RTC Overflow
            break;
        default:          break;
    }
}

  • PERSISTENT() puts the data in (Program) FRAM, but doesn't remove the Write Protection. To write to it (including "++") you need to turn WP off with something like:

    > SYSCFG0 = FRWPPW | DFWP;  // PFWP = 0 to write-enable Program FRAM

    To turn it back on, use something like

    > SYSCFG0 = FRWPPW | DFWP | PFWP;  // PFWP = 1

    (You might be able to do something more refined with FRWPOA and the linker .map file, but I suggest you "start small".)

    [Ref User Guide (SLAU445I) Table 1-29]

  • Hello,

    Also please take a look at the Compute Through Power Loss (CTPL) library in the FRAM Utilities. It was made for this type of scenario and can help you with your application by being able to resume your application were it left off instead of full reset that is done for LPM3.5 wakeup. 

    Also keep in mind response time as it takes some time to wakeup from LPM3.5. 

**Attention** This is a public forum