2017年1月3日 星期二

nRF51/52 : Pstorage (Flash , Non-volatile Memory) Operation


    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.