FDS with SoftDevice reporting FDS_ERR_CRC_CHECK_FAILED on read

Hi,

I'm using SEGGER Embedded Studio to write code that writes BLE characteristics to flash storage (via FDS) when they're changed. When the device starts up after a reset, it will read these characteristics from FDS during initialization.

I have written a few functions in fds_support.c, shown here. (Most of it was adapted from either the NFC FDS example, or the FDS UART CLI example.)

#define FILE_ID 0x1111

static volatile bool m_fds_ready      = false;      /**< Flag used to indicate that FDS initialization is finished. */
static volatile bool m_pending_write  = false;      /**< Flag used to preserve write request during Garbage Collector activity. */
static volatile bool m_pending_update = false;      /**< Flag used to preserve update request during Garbage Collector activity. */

static uint32_t        m_pending_msg_size   = 0;    /**< Pending write/update request data size. */
static uint8_t const * m_p_pending_msg_buff = NULL; /**< Pending write/update request data pointer. */
static uint32_t        m_pending_msg_rec    = 0;    /**< Pending write/update request data record. */

static fds_record_desc_t  m_record_desc;            /**< Record descriptor. */
static fds_record_t       m_record;                 /**< Record description used for writes. */

static void prepare_record(void const * p_data, uint32_t len, uint32_t rec){
  m_record = (fds_record_t){
    .file_id = FILE_ID,
    .key     = rec,
    .data.p_data = p_data,
    .data.length_words = BYTES_TO_WORDS(len)
  };
} 

/**
 * @brief   Flash Data Storage(FDS) event handler.
 *
 * @details This function is used to handle various FDS events like end of initialization,
 *          write, update and Garbage Collection activity. It is used to track FDS actions
 *          and perform pending writes after the Garbage Collecion activity.
 *
 * @param[in] p_fds_evt Pointer to the FDS event.
 */
static void fds_evt_handler(fds_evt_t const * const p_fds_evt)
{
    ret_code_t err_code;

    //printf("FDS event %u with result %u.", p_fds_evt->id, p_fds_evt->result);

    switch (p_fds_evt->id)
    {
        case FDS_EVT_INIT:
            APP_ERROR_CHECK(p_fds_evt->result);
            m_fds_ready = true;
            break;

        case FDS_EVT_UPDATE:
            APP_ERROR_CHECK(p_fds_evt->result);
            NRF_LOG_INFO("FDS update success.");
            break;

        case FDS_EVT_WRITE:
            APP_ERROR_CHECK(p_fds_evt->result);
            NRF_LOG_INFO("FDS write success.");
            break;

        case FDS_EVT_GC:
            APP_ERROR_CHECK(p_fds_evt->result);
            NRF_LOG_INFO("Garbage Collector activity finished.");

            //Perform pending write/update.
            if (m_pending_write)
            {
                NRF_LOG_DEBUG("Write pending msg.", p_fds_evt->id, p_fds_evt->result);
                m_pending_write = false;
                err_code        = flash_write(m_p_pending_msg_buff, m_pending_msg_size, m_pending_msg_rec);
                APP_ERROR_CHECK(err_code);
            }
            else if (m_pending_update)
            {
                NRF_LOG_DEBUG("Update pending msg.", p_fds_evt->id, p_fds_evt->result);
                m_pending_update = false;
                err_code         = flash_update(m_p_pending_msg_buff, m_pending_msg_size, m_pending_msg_rec);
                APP_ERROR_CHECK(err_code);
            }
            break;

        default:
            break;
    }
}

ret_code_t flash_init(){
    ret_code_t err_code;

    // Register FDS event handler to the FDS module.
    err_code = fds_register(fds_evt_handler);
    VERIFY_SUCCESS(err_code);

    // Initialize FDS.
    err_code = fds_init();
    VERIFY_SUCCESS(err_code);

    // Wait until FDS is initialized.
    while (!m_fds_ready)
      ;

    return err_code;
}

ret_code_t flash_write(uint8_t const * p_buff, uint32_t size, uint32_t rec)
{
  prepare_record(p_buff, size, rec);
  ret_code_t err = fds_record_write(&m_record_desc, &m_record);
  if (err == FDS_ERR_NO_SPACE_IN_FLASH)
  {
      // If there is no space, preserve write request and call Garbage Collector.
      m_pending_write      = true;
      m_pending_msg_size   = size;
      m_p_pending_msg_buff = p_buff;
      m_pending_msg_rec    = rec;
      NRF_LOG_INFO("FDS has no free space left, Garbage Collector triggered!");
      err = fds_gc();
  }
  return err;
}

ret_code_t flash_update(uint8_t const * p_buff, uint32_t size, uint32_t rec){
  prepare_record(p_buff, size, rec);
  ret_code_t err = fds_record_update(&m_record_desc, &m_record);
  if (err == FDS_ERR_NO_SPACE_IN_FLASH)
  {
      // If there is no space, preserve write request and call Garbage Collector.
      m_pending_write      = true;
      m_pending_msg_size   = size;
      m_p_pending_msg_buff = p_buff;
      m_pending_msg_rec    = rec;
      NRF_LOG_INFO("FDS has no free space left, Garbage Collector triggered!");
      err = fds_gc();
  }
  return err;
}

ret_code_t flash_read(uint8_t* p_buff, uint32_t len, uint32_t rec){
  fds_record_desc_t desc = {0};
  fds_find_token_t  tok  = {0};
  ret_code_t err;

  while((err = fds_record_find(FILE_ID, rec, &desc, &tok)) == NRF_SUCCESS){
    fds_flash_record_t frec = {0};

    err = fds_record_open(&desc, &frec);
    if(err != NRF_SUCCESS){
      printf("error in fds_record_open: 0x%x\n", err);
      fds_record_close(&desc);
      return err;
    }

    memcpy(p_buff,
      frec.p_data,
      frec.p_header->length_words * sizeof(uint32_t));
    
    err = fds_record_close(&desc);
    return err;
  }

  return err;

}

ret_code_t flash_delete(uint32_t rec){
  fds_record_desc_t desc = {0};
  fds_find_token_t  tok  = {0};
  ret_code_t err = fds_record_find(FILE_ID, rec, &desc, &tok);
  if(err == NRF_SUCCESS){
    err = fds_record_delete(&desc);
  }

  return err;
}

Here's an example of what I do in a BLE characteristic handler, to write to flash.

uint8_t buffer[4];

//Some characteristics are 2 bytes - this moves the uint16_t into a uint8_t[]
static void int_to_buffer(uint16_t in, uint8_t *p_buff) {
	p_buff[0] = (uint8_t)((in >> 8) & 0xFF);
	p_buff[1] = (uint8_t)(in & 0xFF);
	p_buff[2] = 0;
	p_buff[3] = 0;
}

static void start_time_write_handler(uint16_t conn_handle, ble_lbs_t *p_lbs, uint16_t data) {
	start_time = data;

	ret_code_t err = flash_read(buffer, 2, FDS_KEY_START_TIME);
	int_to_buffer(data, buffer);
	if (err == FDS_ERR_NOT_FOUND)
		err = flash_write(buffer, 2, FDS_KEY_START_TIME);
	else
		err = flash_update(buffer, 2, FDS_KEY_START_TIME);
	if (err != NRF_SUCCESS)
		printf("Error in start_time_write_handler: %d\n", err);
}

And here's the initialization code that's supposed to read from flash. However, I always get FDS_ERR_CRC_CHECK_FAILED (0x860C) here when I'm trying to read back from FDS. What might I be doing wrong?

static void load_values_from_flash() {
	//Set up buffer
	memset(&buffer, 0x00, sizeof(buffer));
	//Set up size
	uint8_t size = 1;
	//Set up ret_code
	ret_code_t ret_code;

	//Load in values:
	//start time
	ret_code = flash_read(buffer, size, FDS_KEY_START_TIME);
	if (ret_code == NRF_SUCCESS)
		start_time = buffer_to_int(buffer);
	else {
		start_time = 360; //default value, in case it's not in FDS or there's an error
		if (ret_code == FDS_ERR_NOT_FOUND) {
			int_to_buffer(start_time, buffer);
			flash_write(buffer, size, FDS_KEY_START_TIME);
		} 
		else
			printf("Flash load error in start_time: %x\n", ret_code); //this is what I'm hitting, with 0x860C
	}
	int_to_buffer(start_time, buffer);
	ble_write_characteristic(m_conn_handle, m_lbs.start_time_char_handles.value_handle, buffer, size);
}

nRF5 SDK version 17.0.2
nRF52832 chip, SoftDevice S132
SEGGER Embedded Studio v4.18
Manjaro Linux

Related