一、背景
NRF52832 內(nèi)部 Flash 的存儲(chǔ)官方提供了兩種方式钮蛛,一種是 FStorage 方式洒忧,另一種是在 FStorage 基礎(chǔ)上的 FDS 方式。
1.1 FStorage方式
FStorage 是一個(gè)用于讀取咏尝、寫(xiě)入和擦除持久閃存中數(shù)據(jù)的模塊。該模塊定義了一個(gè)異步接口來(lái)訪(fǎng)問(wèn)閃存啸罢,并使用 讀编检、寫(xiě)和(page)擦除 操作。通過(guò)對(duì)注冊(cè)事件處理程序的回調(diào)扰才,通知應(yīng)用程序的操作結(jié)果允懂。
FStorage 方式是一個(gè)低級(jí)庫(kù),旨在為閃存提供一個(gè)簡(jiǎn)單的衩匣、原始的接口蕾总。如果需要一個(gè)具有 更新粥航、搜索 功能的更高級(jí)別 API 來(lái)存儲(chǔ)記錄和文件,可以看第二種方式 FDS 數(shù)據(jù)存儲(chǔ)方式生百。
1.2 Flash區(qū)域
FStorage 方式不保證不同的存儲(chǔ)應(yīng)用實(shí)例在非重疊的閃存區(qū)域上運(yùn)行递雀,因此需要用戶(hù)自己確保對(duì)該區(qū)域的使用不會(huì)影響其他功能,也就是說(shuō)用戶(hù)自己保證不將數(shù)據(jù)內(nèi)容疊加到同個(gè)區(qū)域進(jìn)行存儲(chǔ)置侍。
下圖提供了關(guān)于 Flash 中存儲(chǔ)數(shù)據(jù)的 SDK 模塊使用了芯片上的 Flash 區(qū)域情況映之。
1.3 注意
當(dāng)程序運(yùn)行了藍(lán)牙協(xié)議棧拦焚,F(xiàn)lash 操作的成功與協(xié)議的時(shí)間限制和硬件的特性聯(lián)系在一起蜡坊。使用過(guò)于激進(jìn)的掃描或廣播間隔,或運(yùn)行多個(gè)連接赎败,會(huì)影響 Flash 操作的成功秕衙。
在大多數(shù)情況下,nrf_storage_sd
將在沒(méi)有問(wèn)題的情況下與無(wú)線(xiàn)電協(xié)議同時(shí)處理 Flash 操作僵刮。如果出現(xiàn)超時(shí)錯(cuò)誤据忘,請(qǐng)嘗試減少正在寫(xiě)入的數(shù)據(jù)大小(在一個(gè) nrf_storage_write
調(diào)用中)搞糕。否則勇吊,嘗試修改 nrf_storage_max_write_size
和增量 nrf_storage_max
重試。如果問(wèn)題仍然存在窍仰,請(qǐng)參考軟件規(guī)范文檔來(lái)確定更合適的掃描和廣播間隔汉规。
二、移植文件
注意:以下出現(xiàn)缺失common.h文件錯(cuò)誤驹吮,去除即可针史。uint8改為uint8_t或unsigned char或自己宏定義
鏈接:https://pan.baidu.com/s/1rWA9cKgN4XFTb9y6VEpkDQ 提取碼:e7lk
將 board_flash_fstorage.c 和 board_flash_fstorage.h 兩個(gè)文件加入工程的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讀寫(xiě)內(nèi)存初始化
@param 無(wú)
@return 無(wú)
*/
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處理的開(kāi)始地址和結(jié)束地址初始化
APP_ERROR_CHECK(errCode);
(void)getflashEndAddress(); // 獲取地址,判斷為可寫(xiě)地址大小
}
/**
@brief Fstorage讀寫(xiě)內(nèi)存操作
@param flashAddr -[in] 閃存地址
@param readWriteFlag -[in] 讀寫(xiě)操作標(biāo)志
@param pData -[in&out] 指向需要操作的數(shù)據(jù)
@param dataLen -[in] 數(shù)據(jù)長(zhǎng)度
@return 無(wú)
*/
void Fstorage_FlashContrl(uint32 flashAddr, uint8 readWriteFlag, uint32 *pData, uint8 dataLen)
{
ret_code_t errCode;
if(readWriteFlag == FSTORAGE_READ) // 讀取數(shù)據(jù)
{
errCode = nrf_fstorage_read(&s_fstorageHandle, flashAddr, pData, dataLen);
APP_ERROR_CHECK(errCode);
}
else // 寫(xiě)入數(shù)據(jù)
{
errCode = nrf_fstorage_erase(&s_fstorageHandle, flashAddr, 1, NULL); // 只能寫(xiě)入位值1碟狞,不能寫(xiě)入位值0啄枕,所以先擦后寫(xiě)
errCode = nrf_fstorage_write(&s_fstorageHandle, flashAddr, pData, dataLen, NULL);
APP_ERROR_CHECK(errCode);
waitForFlashReady(&s_fstorageHandle); // 等待寫(xiě)完
}
}
/*********************************************************************
* LOCAL FUNCTIONS
*/
/**
@brief Fstorage事件回調(diào)函數(shù)
@param pFstorageEvent -[in] Fstorage事件
@return 無(wú)
*/
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上可用于寫(xiě)入數(shù)據(jù)的地址
@param 無(wú)
@return 無(wú)
*/
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 等待寫(xiě)入完成
@param pFstorageHandle -[in] Fstorage句柄
@return 無(wú)
*/
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調(diào)用
需包含頭文件 board_flash_fstorage.h
Fstorage_FlashInit
功能 | 初始化Flash讀寫(xiě)模塊 |
---|---|
函數(shù)定義 | void Fstorage_FlashInit(void) |
參數(shù) | 無(wú) |
返回 | 無(wú) |
Fstorage_FlashContrl
功能 | Flash讀寫(xiě)操作 |
---|---|
函數(shù)定義 | void Fstorage_FlashContrl(uint32 flashAddr, uint8 readWriteFlag, uint32 *pData, uint8 dataLen) |
參數(shù) | flashAddr:進(jìn)行讀寫(xiě)的地址 readWriteFlag:讀寫(xiě)標(biāo)記族沃,0-讀频祝,1-寫(xiě) pData:讀寫(xiě)的數(shù)據(jù) dataLen:寫(xiě)入數(shù)據(jù)長(zhǎng)度 |
返回 | 無(wú) |
四、使用例子
1)添加頭文件
#include "board_flash_fstorage.h"
2)添加初始化代碼(SDK15.3 中 ble_peripheral 的 ble_app_template 工程 main() 函數(shù)中)
加入 Fstorage_FlashInit()
int main(void)
{
bool erase_bonds;
/*-------------------------- 外設(shè)驅(qū)動(dòng)初始化 ---------------------------*/
// Initialize.
log_init(); // 日志驅(qū)動(dòng)初始化
timers_init(); // 定時(shí)器驅(qū)動(dòng)初始化(在此加入自定義定時(shí)器)
Fstorage_FlashInit(); // 初始化Flash讀寫(xiě)模塊
/*-------------------------- 藍(lán)牙協(xié)議棧初始化 ---------------------------*/
power_management_init();
ble_stack_init(); // 協(xié)議棧初始化
gap_params_init();
gatt_init();
advertising_init(); // 廣播初始化
services_init(); // 服務(wù)初始化
conn_params_init(); // 連接參數(shù)初始化
peer_manager_init();
/*-------------------------- 開(kāi)啟應(yīng)用 ---------------------------*/
// Start execution.
NRF_LOG_INFO("Template example started.");
advertising_start(erase_bonds); // 開(kāi)啟廣播
application_timers_start(); // 定時(shí)器應(yīng)用開(kāi)啟(在此開(kāi)啟自定義定時(shí)器)
Fstorage_FlashContrl(CUSTOM_FSTORAGE_ADDR, FSTORAGE_READ, (uint32 *) &s_totalConfigData, sizeof(s_totalConfigData));
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
3)在開(kāi)啟應(yīng)用部分 讀取數(shù)據(jù)
/*-------------------------- 開(kāi)啟應(yīng)用 ---------------------------*/
// Start execution.
Fstorage_FlashContrl(CUSTOM_FSTORAGE_ADDR, FSTORAGE_READ, (uint32 *) &s_totalConfigData, sizeof(s_totalConfigData));
advertising_start(erase_bonds); // 開(kāi)啟廣播
application_timers_start(); // 定時(shí)器應(yīng)用開(kāi)啟(在此開(kāi)啟自定義定時(shí)器)
? 由 Leung 寫(xiě)于 2020 年 2 月 28 日
? 參考:青風(fēng)電子社區(qū)