一、背景
NRF52832 内部 Flash 的存储官方提供了两种方式,一种是 FStorage 方式,另一种是在 FStorage 基础上的 FDS 方式。
1.1 FStorage方式
FStorage 是一个用于读取、写入和擦除持久闪存中数据的模块。该模块定义了一个异步接口来访问闪存,并使用 读、写和(page)擦除 操作。通过对注册事件处理程序的回调,通知应用程序的操作结果。
FStorage 方式是一个低级库,旨在为闪存提供一个简单的、原始的接口。如果需要一个具有 更新、搜索 功能的更高级别 API 来存储记录和文件,可以看第二种方式 FDS 数据存储方式。
1.2 Flash区域
FStorage 方式不保证不同的存储应用实例在非重叠的闪存区域上运行,因此需要用户自己确保对该区域的使用不会影响其他功能,也就是说用户自己保证不将数据内容叠加到同个区域进行存储。
下图提供了关于 Flash 中存储数据的 SDK 模块使用了芯片上的 Flash 区域情况。
1.3 注意
当程序运行了蓝牙协议栈,Flash 操作的成功与协议的时间限制和硬件的特性联系在一起。使用过于激进的扫描或广播间隔,或运行多个连接,会影响 Flash 操作的成功。
在大多数情况下,nrf_storage_sd
将在没有问题的情况下与无线电协议同时处理 Flash 操作。如果出现超时错误,请尝试减少正在写入的数据大小(在一个 nrf_storage_write
调用中)。否则,尝试修改 nrf_storage_max_write_size
和增量 nrf_storage_max
重试。如果问题仍然存在,请参考软件规范文档来确定更合适的扫描和广播间隔。
二、移植文件
注意:以下出现缺失common.h文件错误,去除即可。uint8改为uint8_t或unsigned char或自己宏定义
链接:https://pan.baidu.com/s/1rWA9cKgN4XFTb9y6VEpkDQ 提取码:e7lk
将 board_flash_fstorage.c 和 board_flash_fstorage.h 两个文件加入工程的Application文件夹下
2.1 board_flash_fstorage.c
/*********************************************************************
* INCLUDES
*/
#include "nrf_fstorage.h"
#include "nrf_fstorage_sd.h"
#include "nrf_soc.h"
#include "nrf_log.h"
#include "app_error.h"
#include "board_flash.h"
static void fstorageCallbackFunc(nrf_fstorage_evt_t *pFstorageEvent);
static uint32 getflashEndAddress(void);
static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle);
/*********************************************************************
* LOCAL VARIABLES
*/
NRF_FSTORAGE_DEF(nrf_fstorage_t s_fstorageHandle) =
{
/* Set a handler for fstorage events. */
.evt_handler = fstorageCallbackFunc,
/* These below are the boundaries of the flash space assigned to this instance of fstorage.
* You must set these manually, even at runtime, before nrf_fstorage_init() is called.
* The function nrf5_flash_end_addr_get() can be used to retrieve the last address on the
* last page of flash available to write data. */
.start_addr = 0x3e000,
.end_addr = 0x3ffff,
};
/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
@brief Fstorage读写内存初始化
@param 无
@return 无
*/
void Fstorage_FlashInit(void)
{
ret_code_t errCode;
nrf_fstorage_api_t *pFstorageApi;
pFstorageApi = &nrf_fstorage_sd;
errCode = nrf_fstorage_init(&s_fstorageHandle, pFstorageApi, NULL); // Flash处理的开始地址和结束地址初始化
APP_ERROR_CHECK(errCode);
(void)getflashEndAddress(); // 获取地址,判断为可写地址大小
}
/**
@brief Fstorage读写内存操作
@param flashAddr -[in] 闪存地址
@param readWriteFlag -[in] 读写操作标志
@param pData -[in&out] 指向需要操作的数据
@param dataLen -[in] 数据长度
@return 无
*/
void Fstorage_FlashContrl(uint32 flashAddr, uint8 readWriteFlag, uint32 *pData, uint8 dataLen)
{
ret_code_t errCode;
if(readWriteFlag == FSTORAGE_READ) // 读取数据
{
errCode = nrf_fstorage_read(&s_fstorageHandle, flashAddr, pData, dataLen);
APP_ERROR_CHECK(errCode);
}
else // 写入数据
{
errCode = nrf_fstorage_erase(&s_fstorageHandle, flashAddr, 1, NULL); // 只能写入位值1,不能写入位值0,所以先擦后写
errCode = nrf_fstorage_write(&s_fstorageHandle, flashAddr, pData, dataLen, NULL);
APP_ERROR_CHECK(errCode);
waitForFlashReady(&s_fstorageHandle); // 等待写完
}
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief Fstorage事件回调函数
@param pFstorageEvent -[in] Fstorage事件
@return 无
*/
static void fstorageCallbackFunc(nrf_fstorage_evt_t *pFstorageEvent)
{
if(pFstorageEvent->result != NRF_SUCCESS)
{
NRF_LOG_INFO("--> Event received: ERROR while executing an fstorage operation.");
return ;
}
switch(pFstorageEvent->id)
{
case NRF_FSTORAGE_EVT_WRITE_RESULT:
NRF_LOG_INFO("--> Event received: wrote %d bytes at address 0x%x.", pFstorageEvent->len, pFstorageEvent->addr);
break;
case NRF_FSTORAGE_EVT_ERASE_RESULT:
NRF_LOG_INFO("--> Event received: erased %d page from address 0x%x.", pFstorageEvent->len, pFstorageEvent->addr);
break;
default:
break;
}
}
/**
@brief 检索Flash上可用于写入数据的地址
@param 无
@return 无
*/
static uint32 getflashEndAddress(void)
{
uint32 const bootloaderAddr = NRF_UICR->NRFFW[0];
uint32 const pageSz = NRF_FICR->CODEPAGESIZE;
uint32 const codeSz = NRF_FICR->CODESIZE;
return (bootloaderAddr != 0xFFFFFFFF ? bootloaderAddr : (codeSz * pageSz));
}
/**
@brief 等待写入完成
@param pFstorageHandle -[in] Fstorage句柄
@return 无
*/
static void waitForFlashReady(nrf_fstorage_t const *pFstorageHandle)
{
while(nrf_fstorage_is_busy(pFstorageHandle)) // While fstorage is busy, sleep and wait for an event.
{
sd_app_evt_wait();
}
}
/****************************************************END OF FILE****************************************************/
2.2 board_flash_fstorage.h
#ifndef _BOARD_FLASH_H_
#define _BOARD_FLASH_H_
/*********************************************************************
* INCLUDES
*/
#include "common.h"
/*********************************************************************
* DEFINITIONS
*/
#define FSTORAGE_READ 0x00
#define FSTORAGE_WRITE 0x01
#define CUSTOM_FSTORAGE_ADDR 0x3e000
/*********************************************************************
* API FUNCTIONS
*/
void Fstorage_FlashInit(void);
void Fstorage_FlashContrl(uint32 flashAddr, uint8 readWriteFlag, uint32 *pData, uint8 dataLen);
#endif /* _BOARD_FLASH_H_ */
三、API调用
需包含头文件 board_flash_fstorage.h
Fstorage_FlashInit
功能 | 初始化Flash读写模块 |
---|---|
函数定义 | void Fstorage_FlashInit(void) |
参数 | 无 |
返回 | 无 |
Fstorage_FlashContrl
功能 | Flash读写操作 |
---|---|
函数定义 | void Fstorage_FlashContrl(uint32 flashAddr, uint8 readWriteFlag, uint32 *pData, uint8 dataLen) |
参数 | flashAddr:进行读写的地址 readWriteFlag:读写标记,0-读,1-写 pData:读写的数据 dataLen:写入数据长度 |
返回 | 无 |
四、使用例子
1)添加头文件
#include "board_flash_fstorage.h"
2)添加初始化代码(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函数中)
加入 Fstorage_FlashInit()
int main(void)
{
bool erase_bonds;
/*-------------------------- 外设驱动初始化 ---------------------------*/
// Initialize.
log_init(); // 日志驱动初始化
timers_init(); // 定时器驱动初始化(在此加入自定义定时器)
Fstorage_FlashInit(); // 初始化Flash读写模块
/*-------------------------- 蓝牙协议栈初始化 ---------------------------*/
power_management_init();
ble_stack_init(); // 协议栈初始化
gap_params_init();
gatt_init();
advertising_init(); // 广播初始化
services_init(); // 服务初始化
conn_params_init(); // 连接参数初始化
peer_manager_init();
/*-------------------------- 开启应用 ---------------------------*/
// Start execution.
NRF_LOG_INFO("Template example started.");
advertising_start(erase_bonds); // 开启广播
application_timers_start(); // 定时器应用开启(在此开启自定义定时器)
Fstorage_FlashContrl(CUSTOM_FSTORAGE_ADDR, FSTORAGE_READ, (uint32 *) &s_totalConfigData, sizeof(s_totalConfigData));
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
3)在开启应用部分 读取数据
/*-------------------------- 开启应用 ---------------------------*/
// Start execution.
Fstorage_FlashContrl(CUSTOM_FSTORAGE_ADDR, FSTORAGE_READ, (uint32 *) &s_totalConfigData, sizeof(s_totalConfigData));
advertising_start(erase_bonds); // 开启广播
application_timers_start(); // 定时器应用开启(在此开启自定义定时器)
• 由 Leung 写于 2020 年 2 月 28 日
• 参考:青风电子社区