本教程说明 如何在带协议栈的BLE工程中使用flash操作。
教程基于sdk9.0的 uart工程
xxx\NordicSemiconductor\nRF_Examples\9.0.0\ble_peripheral\ble_app_uart
Nordic的SDK将flash操作封装成了一个pstorage模块。 模块提供了很好用的flash操作接口。
使用flash前需要调用 pstorage_init函数来初始化pstorage模块。
然后就可以调用pstorage_register函数来注册自己要使用的block数量和block_size(最好四字节对齐)
之后就可以使用pstorage_block_identifier_get 函数来或得你要操作的block_num (0-申请的block数量) 的地址。然后在使用获得的地址调用pstorage_load , pstorage_store 等操作
这里需要注意的是 涉及到存储的操作,比如store 和updata因为模块内部处理都需要时间,而这些函数内部又不会复制你传进来的buff所以,如果你只用了一个buff的话,连续调用两个updata是会出错的。你需要等待上次的操作完成才能再使用这个buff。
用代码来解释下。
错误的使用方式:
Buff:里面放的是要存储的数据
Pstorage_update(dest1,buff);
修改buff数据。
Pstorage_updata(dest2,buff);
看过内部源码的都知道updata的是分两次操作的,首先赋值原来的flash数据到swap区然后,然后擦除之前放数据的page,之后再将原来的数据修改后写会。所以这个过程需要时间。未在上次操作完成就又使用了buff,会导致前面一次的flash写操作出问题。
正确的方式:
Buff:里面放的是要存储的数据
Pstorage_update(dest1,buff);
等待上面操作完成。
修改buff数据。
Pstorage_updata(dest2,buff);
或者:
Buff1:里面放的是要存储到dest1的数据
Buff2:里面放的是要存储到dest2的数据
Pstorage_update(dest1,buff);
Pstorage_updata(dest2,buff);
我们在uart的demo上来实验flash的使用。
我们通过手机向设备发送八个字节的数据,设备受到后先存储在flash中然后在flash的回调函数中判断是否存储完成,如果完成就设置标志。 最后在主函数中判断这个标志,如果标志被置位代表存储操作完成,我们就可以调用读操作来读出flash中的数据了
下面介绍代码的编写:
PS:UART例子中要先关流控
首先在main.c中#include”pstorage.h”
然后我们要 初始化,再注册自己要使用的flash大小。
添加flash相关的代码和变量。
pstorage_handle_t block_id;//定义全局变量,flash存储的地址索引
uint8_t my_flag = 0;//定义标志标量。Main函数中会根据标志来 //读flash
uint8_t my_buff[8]={0}; //全局数组用来存放手机发过来的数据, //然后写到flash中
//定义flash操作完成后的回调函数,
static void my_cb(pstorage_handle_t * handle,uint8_t op_code,uint32_t result,
uint8_t * p_data, uint32_t data_len)
{
switch(op_code)
{
case PSTORAGE_UPDATE_OP_CODE:
if (result == NRF_SUCCESS)
{
my_flag = 1; //当flash update完成后置位标志。 Main函数中便可以读flash数据了
}
else
{
// Update operation failed.
}
break;
}
}
//main中添加flash的初始化和注册代码
int main(void)
{
uint32_t err_code;
bool erase_bonds;
uint8_t start_string[] = START_STRING;
pstorage_module_param_t module_param;
module_param.block_count = 1; // 申请了一个块
module_param.block_size = 16; //块大小为16(最小要求是16,但是后面的处理我们都只处理手机发过来的前8字节)
module_param.cb = my_cb;
pstorage_init(); //初始化
pstorage_register(&module_param, &g_block_id);//注册申请
// Initialize.
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
uart_init();
buttons_leds_init(&erase_bonds);
………..
……….
}.
然后我们需要在手机发送数据过来的时候将数据更新到flash中。
在 ble_nus.c中的ble_nus_on_ble_evt函数中有对手机写事件的处理。处理是在函数on_write中做的,我们在这个函数中添加自己的代码,红色部分为添加的代码
static void on_write(ble_nus_t * p_nus, ble_evt_t * p_ble_evt)
{
ble_gatts_evt_write_t * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;
if ( (p_evt_write->handle == p_nus->rx_handles.cccd_handle)&& (p_evt_write->len == 2))
{
………………………….
}
else if ( (p_evt_write->handle == p_nus->tx_handles.value_handle)&& (p_nus->data_handler != NULL))
{ //屏蔽掉之前的处理函数,添加flash 更新数据操作。
//p_nus->data_handler(p_nus, p_evt_write->data, p_evt_write->len);
pstorage_handle_t dest_block_id;
uint8_t len = p_evt_write->len>8?8:p_evt_write->len;
memcpy(my_buff, p_evt_write->data, len);
pstorage_block_identifier_get(&g_block_id, 0, &dest_block_id); //因为只注册了一个flash块,所以其实这个函数可以不调用的。
pstorage_update(&dest_block_id, my_buff, 8, 0);
}
else
{
// Do Nothing. This event is not relevant for this service.
}
}
之后就是main函数中判断标志然后读flash再打印出来的代码
部分代码如下:
Main(){
…………….
…………….
// Enter main loop.
for (;;)
{
power_manage();
if ( my_flag == 1 ){
printf("\r\nread flash: ");
my_flag = 0;
pstorage_handle_t dest_block_id;
uint8_t buff[8];
//因为只注册了一个flash块,所以其实这个函数可以不调用的。 pstorage_block_identifier_get(&g_block_id,0,&dest_block_id);
pstorage_load(buff, &dest_block_id, 8, 0);
for(int i = 0; i< 8; i++){
printf("%d ",buff[i]);
}
printf("\r\n");
}
}
}
最后最重要的一点是 对 sys_evt事件的处理,当flash操作完成后,sd会上抛给app 相应的sys_evt事件。(类似sd会上抛给APP BLE的事件)
因为pstorage的实现是基于状态机的。比如我们上面使用的update,它的实现是分步做的,先擦除交换区,然后将就数据写到交换区,然后修改再写回。每个时刻下一步要做的事都由当前的操作返回后的状态机决定。 所以pstorage需要获得sys_evt(flash的操作返回)然后进行下一步的处理。至于这些处理pstorage内部都是做好的。我们只要将事件处理函数添加到代码中就可以了。
首先要注册 sys_evt 的事件派发函数
在main.c中的 ble_stack_init函数最后添加注册代码
static void ble_stack_init(void)
{
…………………
…………………
err_code=softdevice_sys_evt_handler_set(sys_evt_dispatch);
APP_ERROR_CHECK(err_code);
}
然后在定义这个函数
static void sys_evt_dispatch(uint32_t sys_evt)
{
//这个函数时在pstorage模块中实现的
pstorage_sys_event_handler(sys_evt);
}
最后编译工程就可以操作了,手机连上设备后,找到tx特征值(可以写的那个),然后写8个数据过去