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.

NVS is not operating as expected when organised - Data is cleared unintentionally/overwritten

Part Number: CC1350

Tool/software: TI-RTOS

I have a node set up that requires both 'pairing' to a network and calibration, and as a result has a couple of structs to hold this information and store it to NV memory in case the device is restarted. The NVS is organised through use of an additional typedef containing the two structs.

If I only calibrate the device it will recall this data correctly for the foreseeable future. However, if I pair the device and then perform a reset it will remember the pairing but clear any record it has of the calibration data. Following another restart it then not remember the pairing information any more. 

I haven't got a clue what could be going on here or what I'm doing wrong. Here are some snippets of code that may help:

Setting up the structs to manage the data and NVS

typedef struct deviceParams{
    uint8_t pairState;
    uint32_t receiverAddress;
    uint32_t doorId;
    // What else?
} deviceParams;

/* This struct is used to organised items in memory */
typedef struct nvs_data{
    deviceParams nvsDeviceParams;
    calParams nvsCalData;
} nvs_data;

extern calParams calData;
deviceParams deviceData;

calData is used from another file:

typedef struct calParams{
    uint16_t margin;
    int16_t closedBx;
    int16_t closedBy;
    uint8_t saveState;
} calParams;

calParams calData;

The code I use to recall the data on startup:

    int16_t status;

    /***** Check the pairing status and load in any relevant data  *****/
    status = NVS_read(nvsHandle, offsetof(nvs_data, nvsDeviceParams), &deviceData, sizeof(deviceData));
    if(status != NVS_STATUS_SUCCESS){
        System_abort("NVS read error");
    }
    if(deviceData.pairState == 1){
        // Node has been previously paired
        // Retrieve the receiver's address and store
        nodeRadioTask_setReceiverAddr(deviceData.receiverAddress);
        startupIndicatorLeds |= (1<<Board_PIN_LEDPAIR);
    }else{
        deviceData.pairState = 0;
        startupIndicatorLeds |= (1<<Board_PIN_LEDMNTN);
    }

    /***** Check if there is existing calData *****/
    // LED red = no cal on record, green = good
    status = NVS_read(nvsHandle, offsetof(nvs_data, nvsCalData), &calData, sizeof(calData));
    if(status != NVS_STATUS_SUCCESS){
        System_abort("NVS read error");
    }

    if(calData.saveState != 0xCC){
        // Set default calibration values if it's the very first run
        sensorCtrl_action(SENSOR_ACTION_CAL_INIT); // This sets the a couple of initial values if they've never been set before
        status = NVS_write(nvsHandle, offsetof(nvs_data, nvsCalData), &calData, sizeof(calData), NVS_WRITE_ERASE);
        if(status != NVS_STATUS_SUCCESS){
            System_abort("NVS write error");
        }
    }

    // Indicate if Cal has been done
    if(calData.closedBx == calData.closedBy){
        startupIndicatorLeds |= (1<<Board_PIN_LEDPWRR);
    }else{
        // Data should already be loaded into the struct from NVmem
        startupIndicatorLeds |= (1<<Board_PIN_LEDPWRG);
    }

The device 'pairs' when it received an ACK with the receiver's address:

        if (events & NODE_EVENT_NEW_ACK_PACKET) {
            // Respond to the AckPacket - adjust timing, etc.

            // Was this ACK a result of an NJR packet?
            if(networkJoinRequestActive){
                // Save our pairing status locally
                deviceData.pairState = 1;
                // Also store the Receiver's address in NV mem (update device data struct)
                deviceData.receiverAddress = latestAckPacket.address;

                NVS_write(nvsHandle, offsetof(nvs_data, nvsDeviceParams), &deviceData, sizeof(deviceData), NVS_WRITE_ERASE);
                if(maintenanceModeActive)
                    UART_write(uart, "\n\rNode paired\n\r", 15);
                // Turn off pair LED
                //ledCtrl_action(Board_PIN_LEDPAIR, LED_ACTION_3SEC_TOGGLE);

                networkJoinRequestActive = 0;
            }
        }

When calibration is complete is stores the calibration data:

        /* Conclude the calibration process */
        if (events & NODE_EVENT_CALIBRATE_OFF) {
            ledCtrl_action(Board_PIN_LEDMNTN, LED_ACTION_OFF);
            // store data to NVmem
            NVS_write(nvsHandle, offsetof(nvs_data, nvsCalData), &calData, sizeof(calData), NVS_WRITE_ERASE);
            calModeActive = 0;
        }

Can anyone shed some light on what I might be doing wrong here?

  • Remind me: Is this with using the Stack or Easylink?
  • This is using Easylink, more specifically it's built on the rfWsnDmNode example.
  • I assume you have checked/ read back the NV entry after a pairing and a calibration and checked that this is as expected.

    I haven't played too much with the NVS driver so forgive me if this is a basic question: How do you secure that

    NVS_write(nvsHandle, offsetof(nvs_data, nvsCalData), &calData, sizeof(calData), NVS_WRITE_ERASE);

    does not overwrite

    NVS_write(nvsHandle, offsetof(nvs_data, nvsDeviceParams), &deviceData, sizeof(deviceData), NVS_WRITE_ERASE)

    and the other way around?

    --
    After which operation is the structure in NV corrupted/ overwritten? Do you have some way to check if the data you read back from NV is corrupted?
  • Yes, I have done Write/Reads to verify the contents, and also through reset I have checked this too.

    using the 'offfsetof()' macro I find the offset of the struct member within the struct. I use this to avoid having to manually change the read/write offset if I add another member to the nvs_data struct.

    I assumed (perhaps wrongly) that NVS_WRITE_ERASE flag pertained only to the size of data that I was writing and note the entire page, but perhaps this could be erasing more than I expected.

    The driver documentation states: 

    If NVS_WRITE_ERASE is set in the flags passed to NVS_write(), the affected destination flash sectors will be erased prior to the start of the write operation.

    But I actually found another description in the NVS example documentation:

    The NVS_WRITE_ERASE flag is used with the NVS_write() API to ensure the flash region is erased prior to performing a write. This flag will erase NVS_Attrs.sectorSize bytes of memory.

    This sounds a bit more like it's erasing the entire assigned NVS memory, and not just the bytes that I aim to write.

    I'll try removing the flag and see if that fixes the issue.

    There are only a couple places the data could be overwritten, so I will throw in another read of the calData before and after the deviceData has been written to see if that is a cause. When the calData is uninitialised, it automatically initialises/re-writes the memory on startup. I suppose doing this could affect the deviceData and cause it to appear to have vanished on the next restart.

    Thanks,
    Craig

  • Note that erasing flash is only possible on a page by page bases (that's how flash works). So I suspect that you erase the full page before writing in either the calibration or joining data basically erasing the information you had from before.
  • I hadn't realised it was sector-only erase, good to know for future.
    Removing the NVS_WRITE_ERASE flag seems to have resolved the issue. Thanks for your help!
  • I've having new NVS issues, which may/or may not be related. I've made a new thread here: e2e.ti.com/.../665671