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

Errors writing to flash memory multiple times in flash_fds example

I'm trying to modify the flash_fds example to write a start time when a button is pressed, then an end time when the button is released.  I get the time over TWI from an RTC and I've verified my functions to get the time and to write the time are both working.  So basically the flow of the program goes: 

1.) initialize FDS

2.)Enter the function buttonPress() which waits for a button to be pressed then calls the writeStartTime() function when pressed

3.)Write the time to flash memory when a button is pressed

(What I want to add) 4.)Write the end time to flash memory when the button is released

The program runs as I have it currently, but adding a second function, writeEndTime() (commented out in line 95)  within the button press function breaks the program.  When I run the program with writeEndTime() included the call to writeStartTime() works and the first time is written to memory, but then I get "fatal error" and "system reset" afterwards.  Below are the important areas of code.

void writeEndTime()
{
   #define FILE_ID         0x0005  /* The ID of the file to write the records into. */
   #define RECORD_KEY_1    0x5555  /* A key for the first record. */

   ret_code_t rc = NULL;

   uint8_t* rx_data = clockTime(); //get the pointer to the array that holds the clockTime

   static char intermediate[20] = {0}; //writing to the aligned variable breaks the alignment but memcpy doesn't.  So add data to an intermediate var then memcpy it to the aligned variable and write it to memory.
  __ALIGN(4) static char m_timeStart[20] = {0};
   
    
  //generate a string that holds the start time from the array
  
  snprintf(intermediate, sizeof(intermediate), "E: %x:%x:%x %x/%x/%x", rx_data[2], rx_data[1], rx_data[0], rx_data[5], rx_data[4], rx_data[6]);
  memcpy(m_timeStart, intermediate, sizeof(intermediate));

  //add this line to see what was written to timeStart.  After first run can also check what was written through getTime fn.
  //NRF_LOG_INFO("time: %c%c%c%c written to memory!", m_timeStart[0],m_timeStart[1],m_timeStart[2],m_timeStart[3]); 


   fds_record_t        record;
   fds_record_desc_t   record_desc;


   // Set up record.

   record.file_id           = FILE_ID;
   record.key               = RECORD_KEY_1;
   record.data.p_data       = &m_timeStart;
   /* The following calculation takes into account any eventual remainder of the division. */
   record.data.length_words = (sizeof(m_timeStart) + 3) / 4;
   rc = fds_record_write(&record_desc, &record);
}


void writeStartTime()
{
   #define FILE_ID         0x0005  /* The ID of the file to write the records into. */
   #define RECORD_KEY_1    0x5555  /* A key for the first record. */

   ret_code_t rc = NULL;

   uint8_t* rx_data = clockTime(); //get the pointer to the array that holds the clockTime

   static char intermediate[20] = {0}; //writing to the aligned variable breaks the alignment but memcpy doesn't.  So add data to an intermediate var then memcpy it to the aligned variable and write it to memory.
  __ALIGN(4) static char m_timeStart[20] = {0};
   
    
  //generate a string that holds the start time from the array
  
  snprintf(intermediate, sizeof(intermediate), "S: %x:%x:%x %x/%x/%x", rx_data[2], rx_data[1], rx_data[0], rx_data[5], rx_data[4], rx_data[6]);
  memcpy(m_timeStart, intermediate, sizeof(intermediate));

 
   fds_record_t        record;
   fds_record_desc_t   record_desc;


   // Set up record.

   record.file_id           = FILE_ID;
   record.key               = RECORD_KEY_1;
   record.data.p_data       = &m_timeStart;
   /* The following calculation takes into account any eventual remainder of the division. */
   record.data.length_words = (sizeof(m_timeStart) + 3) / 4;
   rc = fds_record_write(&record_desc, &record);

}

void buttonPress()
{

  nrf_gpio_cfg_output(LED); 
  nrf_gpio_cfg_input(Button, NRF_GPIO_PIN_PULLUP); 
                                                   
  

   nrf_gpio_pin_set(LED); 


   while (true) //general run loop
   {
      
      if(nrf_gpio_pin_read(Button) == 0) //when the button is pressed 
      {
        
        nrf_gpio_pin_clear(LED); //turn on the LED 
        writeStartTime(); //write the string with starting time then enter the while loop 
        
        while(nrf_gpio_pin_read(Button) == 0) 
        {};
        
        //writeEndTime();
        nrf_gpio_pin_set(LED);
        break;
      }
   } 
}


int main(void) //when clock time is called it can cause an error if placed after log_init
{
    ret_code_t rc;


#ifdef SOFTDEVICE_PRESENT
    ble_stack_init();
#else
    clock_init();
#endif

    timer_init();
    log_init();
    cli_init();

    
    NRF_LOG_INFO("FDS example started.")


    /* Register first to receive an event when initialization is complete. */
    (void) fds_register(fds_evt_handler);

    NRF_LOG_INFO("Initializing fds...");

    rc = fds_init();
    APP_ERROR_CHECK(rc);

    /* Wait for fds to initialize. */
    wait_for_fds_ready();

    fds_stat_t stat = {0};

    rc = fds_stat(&stat);
    APP_ERROR_CHECK(rc);
    
    buttonPress(); 
    cli_start();

    /* Enter main loop. */
    for (;;)
    {
        if (!NRF_LOG_PROCESS())
        {
            power_manage();
        }
        cli_process();
        delete_all_process();
    }
}

I think I'm not understanding something in the flow of events for writing to flash memory through the flash_fds example since I know I can write once, but the second write breaks it.  Is there something that I need to do or reset between calls for functions that write to flash memory to allow the program to write to memory twice?

I also think there might be an issue with the cli interface.  I'm not sure what exactly it does in the flash fds example other than provide the cli to give the program commands, but commenting it out gives a variety of errors, so if anyone could explain what it is doing to setup the flash_fds that would be very helpful as well, thanks!

  • Hi,

    I do not spot the issue immediately.  You write that you get "fatal error" in the log. That is typically from an APP_ERROR_CHECK, and then you can get the file name, line number and error code by using a debug build. If using SES, simply select "Debug" in the build configuration dropdown. If not, define "DEBUG and "DEBUG_NRF" in your project, build and test again. Then we will hopefully know a lot more.

  • Okay I ran the test again and got: <error> app: ERROR 8 [NRF_ERROR_INVALID_STATE].

    It came during line 92, but I noticed that commenting out the while loop just causes the error to happen when the program tries to break the while loop. If I remove the rest of the code in the loop, the error just happens at whatever line comes after the writeStartTime() call.  Is there a way to find the exact line the error came from? 

    Another thing I noticed was that if I comment out everything but the single writeStartTime() function that a normal build works properly, but in debug mode I still get the invalid state error.  I stepped through the writeStartTime() function and got a stopped by vector catch error at line 68, then within the fds.c library after the queue start.  I think that cli effects the queue, so maybe that is related?

    I also wasn't sure how the if and while conditions would work during debug mode since they typically happen after button presses, and I wasn't sure how the program would proceed at these lines if I tried to step over the nrf_gpio_pin_read function, so let me know if this could cause an issue. 

    Thanks for the help and please let me know if there's any other info that may be useful!

  • Hi,

    jake11212 said:
    It came during line 92, but I noticed that commenting out the while loop just causes the error to happen when the program tries to break the while loop. If I remove the rest of the code in the loop, the error just happens at whatever line comes after the writeStartTime() call.  Is there a way to find the exact line the error came from? 

    Can you share your code that matched the line numbers, or translate to the line number in the code snippet in your original post if that contains the relevant part?

    The line number you will see here is the line of the APP_ERROR_CHECK. And so the really interesting is what produced the value that you use as in put to the APP_ERROR_CHECK (typically that would be a function call no the proceeding line or similar).

    We should clarify this first as understanding the cause of a detected error is typically a very good (and simple) way to find issues.

    jake11212 said:
    Another thing I noticed was that if I comment out everything but the single writeStartTime() function that a normal build works properly, but in debug mode I still get the invalid state error.  I stepped through the writeStartTime() function and got a stopped by vector catch error at line 68, then within the fds.c library after the queue start.  I think that cli effects the queue, so maybe that is related?

    That could be. I do not know enough yet to say much though.

    jake11212 said:
    I also wasn't sure how the if and while conditions would work during debug mode since they typically happen after button presses, and I wasn't sure how the program would proceed at these lines if I tried to step over the nrf_gpio_pin_read function, so let me know if this could cause an issue

    That should not matter much. The difference between a debug build and a release build is mostly: No optimizations, more error logging and asserts.

  • Update:  I solved this issue by adding a 100ms delay after the write functions.  Basically the next command was being executed before the write event had finished, which led to an interruption that was causing the error.  I found it when I noticed only the beginning of what I wanted to write was successfully written to memory.

    The code I used was nrf_delay_ms(100).  Hope this is helpful!

  • Thanks for letting us know. Note that a better approach, which is to wait for the FDS_EVT_WRITE or FDS_EVT_UPDATE event before scheduling a new write. That way you will not wait longer than necessary (also busy waiting with nrf_delay_ms() is fine in some situations, but you do typically not want to do that often for long times in areal product as it is jus ta busy wait, spending CPU time and power).

Related