It is very requisite for internet of thing (IoT) application to have a non-volatile memory, it could keep the status when the battery runs out.
In nRF51/52 series, the flash is called as "Persistent storage", the APIs of this kind are begin from pstorage.
There are examples and guild for how to exploit the pstorag, but I found those are not concise enough. So in here, I would demonstrate how to call the pstorage api in practice case.
Continue from my previous post, I want to add code of pstorage in that.
Add Below code about line 603 :
static void power_manage(void)
{
uint32_t err_code = sd_app_evt_wait();
APP_ERROR_CHECK(err_code);
}
#ifndef FALSE
#define FALSE (0)
#define TRUE (1)
#endif
#define UART_TX_BUF_SIZE (256)
#define UART_RX_BUF_SIZE (256)
#define APP_BLOCK_SIZE (32)
pstorage_handle_t m_app_storage_base_handle;
static uint8_t m_is_need_to_wait_pstorage_operation = FALSE;
static pstorage_block_t m_pstorage_operation_block_id = 0;
unsigned char m_pstorage_buffer[APP_BLOCK_SIZE];
void app_storage_save_data(void);
void app_storage_print_stored_data(void);
/**
* @brief UART events handler.
*/
void uart_events_handler(app_uart_evt_t * p_event)
{
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
{
unsigned char received_char;
UNUSED_VARIABLE(app_uart_get(&received_char));
if('~' == received_char)
{
app_storage_print_stored_data();
NVIC_SystemReset();
}
{
sprintf((char*)&m_pstorage_buffer[0],
" received char = %c\r\n", received_char);
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
/*
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
*/
uint32_t err_code;
err_code = pstorage_update(&block_handle, &m_pstorage_buffer[0],
APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
printf("storing str :\"%s\" into flash\r\n",
(char*)&m_pstorage_buffer[0]);
}/*store data to flash block*/
}
break;
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event->data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event->data.error_code);
break;
case APP_UART_TX_EMPTY:
break;
case APP_UART_DATA:
break;
default:
break;
}
}
/**
* @brief UART initialization.
*/
void uart_config(void)
{
uint32_t err_code;
const app_uart_comm_params_t comm_params =
{
RX_PIN_NUMBER,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_DISABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud38400
};
APP_UART_FIFO_INIT(&comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_events_handler,
APP_IRQ_PRIORITY_LOW,
err_code);
APP_ERROR_CHECK(err_code);
}/*uart_config*/
// Event Notification Handler.
static void app_storage_callback_handler(pstorage_handle_t * handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
if(handle->block_id == m_pstorage_operation_block_id)
m_is_need_to_wait_pstorage_operation = FALSE;
switch(op_code)
{
case PSTORAGE_STORE_OP_CODE:
if(NRF_SUCCESS != result)
printf("pstorage STORE ERROR callback received \r\n");
break;
case PSTORAGE_LOAD_OP_CODE:
if(NRF_SUCCESS != result)
printf("pstorage LOAD ERROR callback received \r\n");
break;
case PSTORAGE_CLEAR_OP_CODE:
if(NRF_SUCCESS != result)
printf("pstorage CLEAR ERROR callback received \r\n");
break;
case PSTORAGE_UPDATE_OP_CODE:
if(NRF_SUCCESS != result)
printf("pstorage UPDATE ERROR callback received \r\n");
break;
default:
printf("pstorage ERROR callback received \r\n");
break;
}
}/*app_storage_callback_handler*/
/**
* @brief pstorage initializing and block register
*/
void app_storage_init()
{
uint32_t err_code;
err_code = pstorage_init();
APP_ERROR_CHECK(err_code);
pstorage_module_param_t pstorage_param;
pstorage_param.block_size = APP_BLOCK_SIZE;
pstorage_param.block_count = 1;
pstorage_param.cb = app_storage_callback_handler;
err_code = pstorage_register(&pstorage_param, &m_app_storage_base_handle);
APP_ERROR_CHECK(err_code);
#if(0)
{
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
err_code = pstorage_clear(&block_handle, APP_BLOCK_SIZE);
APP_ERROR_CHECK(err_code);
while(FALSE != m_is_need_to_wait_pstorage_operation)
power_manage();
}/*clear data block*/
#endif
}/*pstorage_init_store_and_update*/
void app_storage_save_data(void)
{
uint32_t err_code;
#if(0)
{
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
err_code = pstorage_clear(&block_handle, APP_BLOCK_SIZE);
APP_ERROR_CHECK(err_code);
while(FALSE != m_is_need_to_wait_pstorage_operation)
power_manage();
}/*clear data block*/
#endif
{
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle, 0, &block_handle);
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
//err_code = pstorage_store(&block_handle, &m_pstorage_buffer[0],
// APP_BLOCK_SIZE, 0);
err_code = pstorage_update(&block_handle, &m_pstorage_buffer[0],
APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
while(FALSE != m_is_need_to_wait_pstorage_operation)
power_manage();
}/*store code block*/
}/*app_storage_save_data*/
void app_storage_print_stored_data(void)
{
uint32_t err_code;
unsigned char storedData[APP_BLOCK_SIZE];
pstorage_handle_t block_handle;
err_code = pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
APP_ERROR_CHECK(err_code);
err_code = pstorage_load(&storedData[0],
&block_handle, APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
{
printf("%s\r\n", &storedData[0]);
}
}/*app_storage_print_stored_data*/
/**@brief Function for application main entry.
*/
int main(void)
{
uint32_t err_code;
bool erase_bonds;
uart_config();
printf("Start...\r\n");
// Initialize.
timers_init();
buttons_leds_init(&erase_bonds);
ble_stack_init();
app_storage_init();
app_storage_print_stored_data();
sprintf((char*)&m_pstorage_buffer[0], "new boot string\n\r");
{
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
//err_code = pstorage_store(&block_handle, &m_pstorage_buffer[0],
// APP_BLOCK_SIZE, 0);
err_code = pstorage_update(&block_handle,
&m_pstorage_buffer[0], APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
while(FALSE != m_is_need_to_wait_pstorage_operation)
power_manage();
}/*store code block*/
app_storage_print_stored_data();
device_manager_init(erase_bonds);
gap_params_init();
You could use serial tool (like sscom..etc) to write data (a character) to pstorage.
for example, when I send 'a' via uart, the flash would be written the string" received char = a".
When you send '~' the nRF51/52 would reboot, you could find that the string in pstorage would be kept.
Here is the knacks for using pstorage:
零. The block size should be align of 4, of course.
一. The initialization function pstorage_init (in my case, within the function pstorage_init), should be called after function ble_stack_init. If you call it before ble_stack_init, the function pstorage_init might lead the system reboot ceaselessly. It is not necessary to call pstorage_init after ble_stack_init Immediately, you could call it after the advertise has been started.
二. Before calling the function pstorage_store, it is necessary to call function pstorage_clear, Or the storing action should not be done : the flash contain would not be update. The alternative way is to call function pstorage_update, which is the combination of pstorage_clear and pstorage_store.
三. When the functions pstorage_store/pstorage_update/pstorage_clear, it costs time to complete the action
after the functions have returned. In the main "thread", I adopt a global flag (m_is_need_to_wait_pstorage_operation) , while loop and function power_manage to wait the pstorage has been updated
indeed, that is like:
{
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle, 0, &block_handle);
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
//err_code = pstorage_store(&block_handle, &m_pstorage_buffer[0],
// APP_BLOCK_SIZE, 0);
err_code = pstorage_update(&block_handle, &m_pstorage_buffer[0],
APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
while(FALSE != m_is_need_to_wait_pstorage_operation)
power_manage();
}/*store code block*/
In the pstorage handler callback function :
// Event Notification Handler.
static void app_storage_callback_handler(pstorage_handle_t * handle,
uint8_t op_code,
uint32_t result,
uint8_t * p_data,
uint32_t data_len)
{
if(handle->block_id == m_pstorage_operation_block_id)
m_is_need_to_wait_pstorage_operation = FALSE;
switch(op_code)
{
case PSTORAGE_STORE_OP_CODE:
that could ensure the data be stored into pstorage.
But
inside the interrupt callback, calling power_manage would lead system hanging. Therefore, in the uart event handler callback, i do not call the power_manage function:
/**
* @brief UART events handler.
*/
void uart_events_handler(app_uart_evt_t * p_event)
{
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
{
unsigned char received_char;
UNUSED_VARIABLE(app_uart_get(&received_char));
if('~' == received_char)
{
app_storage_print_stored_data();
NVIC_SystemReset();
}
{
sprintf((char*)&m_pstorage_buffer[0],
" received char = %c\r\n", received_char);
pstorage_handle_t block_handle;
pstorage_block_identifier_get(&m_app_storage_base_handle,
0, &block_handle);
/*
m_pstorage_operation_block_id = block_handle.block_id;
m_is_need_to_wait_pstorage_operation = TRUE;
*/
uint32_t err_code;
err_code = pstorage_update(&block_handle, &m_pstorage_buffer[0],
APP_BLOCK_SIZE, 0);
APP_ERROR_CHECK(err_code);
printf("storing str :\"%s\" into flash\r\n",
(char*)&m_pstorage_buffer[0]);
}/*store data to flash block*/
}
break;
四. Do not update your pstorage too frequently, It would cost much electric power, and the written times is limited : it be written 10000 times before broken typically.