FLASH存储之FS和FDS
1.fstorage
1.1勾选配置
1.2相关代码
写flash比较耗时间,此时cpu挂起,可能导致ble连接断开。
1页为4KB
注意大量数据的话要分批写入。擦除和写入分开进行
//FS事件处理函数
static void fstorage_evt_handler(nrf_fstorage_evt_t *p_evt)
{
//FS操作错误
if (p_evt->result != NRF_SUCCESS)
{
printf("--> Event received: ERROR while executing an fstorage operation.");
return;
}
//判断事件类型,执行相应处理,这里只是打印出事件信息
switch (p_evt->id)
{
//FS写完成事件
case NRF_FSTORAGE_EVT_WRITE_RESULT:
{
printf("--> Event received: wrote %d bytes at address 0x%x.",
p_evt->len, p_evt->addr);
}
break;
//擦除完成事件
case NRF_FSTORAGE_EVT_ERASE_RESULT:
{
printf("--> Event received: erased %d page from address 0x%x.",
p_evt->len, p_evt->addr);
}
break;
default:
break;
}
}
//定义实例
NRF_FSTORAGE_DEF(nrf_fstorage_t fstorage) =
{
/* Set a handler for fstorage events. */
.evt_handler = fstorage_evt_handler, //回调
/* 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, //结束地址
};
//读写,擦除测试函数
void flash_test(void)
{
ret_code_t rc;
printf("fstorage example started.\r\n"); //开始演示
nrf_fstorage_api_t *p_fs_api;
p_fs_api = &nrf_fstorage_sd; //处理操作类型设置
rc = nrf_fstorage_init(&fstorage, p_fs_api, NULL); //flash处理的开始地址和结束地址初始化
APP_ERROR_CHECK(rc);
(void)nrf5_flash_end_addr_get(); //获取地址,判断为可写地址大小。加void是应对语法检查
rc = nrf_fstorage_erase(&fstorage, FLASH_START_ADDR, 1, NULL); //擦除1页
APP_ERROR_CHECK(rc);
rc = nrf_fstorage_write(&fstorage, 0x3e000, &m_data, sizeof(m_data), NULL); //向0x3e000写入数据m_data数据
APP_ERROR_CHECK(rc);
wait_for_flash_ready(&fstorage); //等待写完
printf("Writing \"%x\" to flash.\r\n", m_data); //打印第一次写入的值
printf("Done.\r\n");
m_data = 0xDEADBEEF; //重新向m_data赋值
rc = nrf_fstorage_write(&fstorage, 0x3e100, &m_data, sizeof(m_data), NULL); //再把数据写到0x3e100地址去
APP_ERROR_CHECK(rc);
wait_for_flash_ready(&fstorage); //等待完成
printf("Writing \"%x\" to flash.\r\n", m_data); //打印第二次写入的值
printf("Done.\r\n");
nrf_fstorage_read(&fstorage, 0x3e000,
&m_data2,
sizeof(m_data2)); //从0x3e000地址读取数据
printf("Read \"%x\" to flash.\r\n", m_data2); //打印读取的值
nrf_fstorage_read(&fstorage, 0x3e100,
&m_data2,
sizeof(m_data2)); //从0x3e100地址读取数据
printf("Read \"%x\" to flash.\r\n", m_data2); //打印读取的值
}
2.FDS
只存储记录,相当于把数据和记录绑定到一起了,文件ID和KEY可以不唯一,取值范围有限制,最后会生成唯一的记录ID
2.1配置
位于nrf_libraries下,同时使能FS,crc16
1.1FDS使用的虚拟页的数量。FDS使用的flash的大小取决于这两个值
1.2默认情况4K,可以增加以便能够存储大于物理页面的数据
2.
3.如果FDS用户较多或者APP希望一次排队更多的操作而不等待先前的操作完成,可以增加大小。一般情况下,如果出现FDS_ERR_NO_SPACE_IN_QUEUES则增加队列大小
4.CRC可选,如果使能,FDS打开记录读数据的时候会对记录校验
读操作校验,还有写操作校验
5.表示最大用户数量,一个用户对应一个回调函数。不同的程序模块要使用FDS需要注册各自的回调
2.2相关代码
static bool volatile m_fds_initialized;
static void fds_evt_handler(fds_evt_t const *p_evt) //fds处理事件回调
{
switch (p_evt->id)
{
case FDS_EVT_INIT:
if (p_evt->result == FDS_SUCCESS)
{
m_fds_initialized = true;
}
break;
case FDS_EVT_WRITE:
{
if (p_evt->result == FDS_SUCCESS)
{
NRF_LOG_INFO("Record ID:\t0x%04x", p_evt->write.record_id);
NRF_LOG_INFO("File ID:\t0x%04x", p_evt->write.file_id);
NRF_LOG_INFO("Record key:\t0x%04x", p_evt->write.record_key);
}
}
break;
case FDS_EVT_DEL_RECORD:
{
if (p_evt->result == FDS_SUCCESS)
{
NRF_LOG_INFO("Record ID:\t0x%04x", p_evt->del.record_id);
NRF_LOG_INFO("File ID:\t0x%04x", p_evt->del.file_id);
NRF_LOG_INFO("Record key:\t0x%04x", p_evt->del.record_key);
}
m_delete_all.pending = false;
}
break;
default:
break;
}
}
bool record_delete_next(void)
{
ret_code_t rc;
fds_find_token_t tok = {0};
fds_record_desc_t desc = {0};
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok);
if (rc == FDS_SUCCESS)
{
ret_code_t rc = fds_record_delete(&desc);
if (rc != FDS_SUCCESS)
{
return false;
}
return true;
}
else
{
/* No records left to delete. */
return false;
}
}
#define CONFIG_FILE (0xF010)
#define CONFIG_REC_KEY (0x7010)
typedef struct //可以为任意类型,但需要4字节对齐
{
uint32_t device_name;
} __attribute__((aligned(4))) configuration_t;
/* Dummy configuration data. */
static configuration_t m_dummy_cfg =
{
.device_name = 0x123456,
};
/* A record containing dummy configuration data.包含仿真配置数据的记录 */
static fds_record_t const m_dummy_record =
{
.file_id = CONFIG_FILE, //文件ID
.key = CONFIG_REC_KEY, //记录钥匙
.data.p_data = &m_dummy_cfg, //仿真数据配置
/* The length of a record is always expressed in 4-byte units (words). 数据长度一般超过4个字节*/
.data.length_words = (sizeof(m_dummy_cfg) + 3) / sizeof(uint32_t),
};
void fds_test(void)//要在协议栈初始化之前调用
{
//fds_gc()垃圾回收
ret_code_t rc;
uint32_t *data;
(void)fds_register(fds_evt_handler); //注册FDS事件回调函数接收FS事件
rc = fds_init(); //fds初始化
APP_ERROR_CHECK(rc);
while (!m_fds_initialized) //等待初始化完成
{
sd_app_evt_wait(); //等待过程中待机
}
fds_stat_t stat = {0};
rc = fds_stat(&stat); //设置统计数据
APP_ERROR_CHECK(rc);
record_delete_next(); //把所有记录清零,
fds_record_desc_t desc = {0}; //用来操作记录的描述符结构清零
fds_find_token_t tok = {0}; //保存秘钥的令牌清零
rc = fds_record_write(&desc, &m_dummy_record); //写记录和数据
APP_ERROR_CHECK(rc);
rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); //对应KEY记录查找数据
if (rc == FDS_SUCCESS) //如果查找成功
{
/* A config file is in flash. Let's update it. */
fds_flash_record_t config = {0}; //把配置清零
/* Open the record and read its contents. */
rc = fds_record_open(&desc, &config); //打开记录读取数据
APP_ERROR_CHECK(rc);
/* Copy the configuration from flash into m_dummy_cfg.复制数据到cfg */
memcpy(&m_dummy_cfg, config.p_data, sizeof(configuration_t));
NRF_LOG_INFO("Found Record ID = %d", desc.record_id);
NRF_LOG_INFO("Data = ");
data = (uint32_t *)config.p_data;
for (uint16_t i = 0; i < config.p_header->length_words; i++)
{
NRF_LOG_INFO("0x%8x ", data[i]); //打印输出数据
}
NRF_LOG_INFO("\r\n");
/* Close the record when done reading. */
rc = fds_record_close(&desc); //关闭记录
APP_ERROR_CHECK(rc);
/* Write the updated record to flash. 更新flash上的记录*/
rc = fds_record_update(&desc, &m_dummy_record);
APP_ERROR_CHECK(rc);
}
else
{
/* System config not found; write a new one. */
NRF_LOG_INFO("Writing config file...");
rc = fds_record_write(&desc, &m_dummy_record); //重新写记录
APP_ERROR_CHECK(rc);
}
}