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
