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);
}