This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

flash not completely written with fstorage

I am using a custom board based on nRF52840. Application is working perfectly, and then a new feature was requested. I need to count the number of minutes the application has been running, up to a small number.

I used the example fstorage code to find the end of flash, backed up a page and initialized fstorage to use that page. I use app_timer to set a 60-second timer, and when it fires I use nrf_fstorage_write to write 32 bits of zero to the previously erased flash. 

When running with the debugger the 32-bit word is always written to zeros. But when I "free run" the board from power-up, and then later (minutes later) attach the debugger and have a look, I see that where I have written zeros the words in flash are not entirely zero. 

This is not a crisis, as *some* of the bits are written to zero and I only need know on the next boot whether the words are non-0xffffffff. But I am bothered and a little puzzled. 

The code, simplified, is essentially as below. Note that I just extracted this from my code and have not actually run this extract. If someone wishes to do so I can update the code to make it runnable. 

NRF_FSTORAGE_DEF(nrf_fstorage_t fstorage) =
{
   .end_addr = 0x100000,
   .start_addr = 0xfd000,
   .evt_handler = fstorage_evt_handler,
};


static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
{
    if (p_evt->result != NRF_SUCCESS)
    {
        NRF_LOG_INFO("--> Event received: ERROR while executing an fstorage operation.");
        return;
    }

    switch (p_evt->id)
    {
        case NRF_FSTORAGE_EVT_WRITE_RESULT:
        {
            NRF_LOG_INFO("\n--> Event received: wrote %d bytes at address 0x%x.",
                         p_evt->len, p_evt->addr);
        } break;

        default:
            break;
    }
}

uint32_t g_minutes_used = 0;
uint32_t g_minutes_left = MAX_RUNTIME_MINUTES;


static void timer_runtime_event_handler(void* p_context)
{
   UNUSED_PARAMETER(p_context);

   /*
   ** this handler is called every minute.
   */


   const uint32_t zero_value = 0;

   nrf_fstorage_write(&fstorage, (uint32_t)&my_flash_start[g_minutes_used], &zero_value, sizeof(zero_value), NULL);

   NRF_LOG_INFO("\n    Wrote zero to 0x%x. minutes left = %d.",(uint32_t)&my_flash_start[g_minutes_used],g_minutes_left);


}


#define  MAX_RUNTIME_MINUTES  250

int fstorage_init(void)
{
    ret_code_t rc;

    NRF_LOG_INFO("\nfstorage example started.");

    nrf_fstorage_api_t * p_fs_api;

#ifdef SOFTDEVICE_PRESENT
    NRF_LOG_INFO("\nSoftDevice is present.");
    NRF_LOG_INFO("\nInitializing nrf_fstorage_sd implementation...");
    /* Initialize an fstorage instance using the nrf_fstorage_sd backend.
     * nrf_fstorage_sd uses the SoftDevice to write to flash. This implementation can safely be
     * used whenever there is a SoftDevice, regardless of its status (enabled/disabled). */
    p_fs_api = &nrf_fstorage_sd;
#else
    NRF_LOG_INFO("\nSoftDevice not present.");
    NRF_LOG_INFO("\nInitializing nrf_fstorage_nvmc implementation...");
    /* Initialize an fstorage instance using the nrf_fstorage_nvmc backend.
     * nrf_fstorage_nvmc uses the NVMC peripheral. This implementation can be used when the
     * SoftDevice is disabled or not present.
     *
     * Using this implementation when the SoftDevice is enabled results in a hardfault. */
    p_fs_api = &nrf_fstorage_nvmc;
#endif
    /*
    ** initialize flash storage code
    */
    rc = nrf_fstorage_init(&fstorage, p_fs_api, NULL);
    APP_ERROR_CHECK(rc);


    /*
    ** count how many minutes we've used so far
    */
    uint32_t ix;

    for (ix = 0; ix < MAX_RUNTIME_MINUTES;ix++)
    {
       if (0xffffffff == my_flash_start[ix])
       {
          g_minutes_used = ix; // index into flash
          g_minutes_left = MAX_RUNTIME_MINUTES - g_minutes_used ; // number of minutes left
          break;
       }
    }

    /*
    ** check to see if we've used all our minutes
    */
    if (ix >= MAX_RUNTIME_MINUTES)
    {
       g_minutes_used = MAX_RUNTIME_MINUTES;
       g_minutes_left = 0;
    }

    NRF_LOG_INFO("\nStartup: minutes used = %d.",g_minutes_used);
    NRF_LOG_INFO("\nStartup: minutes left = %d.",g_minutes_left);


    /*
    ** create the & start the runtime timer
    */
    uint32_t err_code;

    #define TICKS_ONE_MINUTE   APP_TIMER_TICKS(60000)


    err_code = app_timer_create(&m_runtime_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                timer_runtime_event_handler);

    err_code = app_timer_start(m_runtime_timer_id,TICKS_ONE_MINUTE,NULL);

}

  • I am sorry to hear that Rob. 

    If the project is canceled I guess you no longer need to investigate this issue. I will close the ticket then. 

    Thanks for the update. 

    Best regards
    Torbjørn

  • Yeah - the software portion of the project was working perfectly, except for this minor bug which did not affect the functioning of the application. But, as with many projects, a lot of management mistakes were made and the project died thanks to them. This is my life, as a consultant. I am always rushing to get as much of the software done (and get paid!) as soon as possible, because so many projects are canceled. 

    Maybe one day the project will rise from the ashes and I'll get to address this issue. 

    Or, just for fun if I'm ever between contracts, I might pull out an eval card and get you the promised example. 

  • Hi Rob

    Being a regular employees definitely has it's advantages, yet I remember a couple of projects I've worked on that in the end didn't lead to much either... (I always get paid though, no worries there ;) )

    Don't worry about the example. I don't think this is a commonly reported issue, and as we are slowly migrating over to a completely different software architecture with NCS it is unlikely that you would see it again in the future. 

    If you do need to return to this project we can follow it up then. 

    Best regards
    Torbjørn

Related