您现在的位置是:首页 >学无止境 >基于flash的数据存储算法实现网站首页学无止境
基于flash的数据存储算法实现
背景
项目中总是会遇到存储数据的需求,当成本没有限制时,可以用考虑在项目中使用铁电来实现存储需求,但如果资源有限,成本要求更低,存储数据很少也比较单一的情况下,可以使用flash来实现,MCU内部的flash,或者外部的nor flash都可以。下面介绍一个我在项目中实现的案例。用低功耗单片机实现采集脉冲信号,通过LoRa将数据传输出去,采集的信号需要保存下来,使用了小华的HC32L110芯片,TSSOP20的封装,16个可用管脚,还有一路RS485,具体可以看看《无线传输终端(LoRa DTU)》中的介绍,这块只说说关于数据存储部分。
技术介绍
铁电存储器(Ferroelectric RAM,FRAM),具有低功耗,读写次数没限制,数据保存十年的特性,非常适合嵌入式场景下,需要存储数据的要求,缺点是,容量小,价格贵。
Nor Flash,也具有低功耗,读写次数可到十万次,数据也可以长久保存,支持按位写入,但只能将数据1变为0,如果要将0变为需要整个扇区进行擦除操作。
nand flash,在嵌入式系统基于MCU的开发很少使用,需要MCU管理坏块,需要占用一些资源,存储量也比较大,MCU场景一般没有太多的数据要进行存储。
当前环境下,MCU内部集成的程序存储器基本都换成了Nor Flash,所以项目中可以使用MCU内部的Nor flash来实现数据的存储。
算法介绍
基于笑话的HC32L110单片机,内部集成32K的Flash,将最后两个扇区用来存储脉冲总数信息。
- 累计脉冲数为32位的无符号整数,占用4个字节空间;
- HC32L110的单个扇区位512个字节;
- 使用扇区结尾的16个字节,16*8=128个bit位来表示后面128个位置的存储信息,0表示已存储信息,1表示未使用;
- 每个存储位置为4个字节的无符号整数,4*128=512个字节,超出了单个扇区的大小,所以有效存储个数为124个,124*4+16=512个字节。
- 使用最后两个扇区来实现数据的交替存储。
序号 | 字节序 | 含义 |
1 | 0~495 | 每四个字节表示一个数据,总共124个整数,496个字节 |
2 | 496~511 | bit位表示位图,0表示数据已写入,1表示数据尚未写入,124个bit位,15.5个字节,取整是16个字节 |
#define BIT_MAP_ADD 496
#define BIT_MAP_SIZE 16
#define DATA_SIZE 124
typedef union {
uint32_t u32_byte[BIT_MAP_SIZE/4];
}bit_map_u;
typedef struct {
uint32_t port;
bit_map_u bit;
uint32_t cur;
}bit_map_s;
int32_t bit_map_init(bit_map_s *bit_map);
int32_t bit_map_read(bit_map_s *bit_map,uint32_t addres,uint32_t *p_data);
int32_t bit_map_read_cur(bit_map_s *bit_map,uint32_t *p_data);
int32_t bit_map_write(bit_map_s *bit_map,uint32_t data);
int32_t bit_map_erase(bit_map_s *bit_map);
代码实现:
#include "flash_bit_data.h"
#include <stdio.h>
#include "hal_flash.h"
#define FLASH_ADD_1 0x7A00
#define FLASH_ADD_2 0x7C00
int32_t bit_map_init(bit_map_s *bit_map)
{
int32_t ret = -1;
uint32_t i = 0;
if(bit_map == NULL){
return ret;
}
switch(bit_map->port){
case 0:
flash_multi_read(FLASH_ADD_1 + BIT_MAP_ADD,bit_map->bit.u32_byte,BIT_MAP_SIZE);
break;
case 1:
flash_multi_read(FLASH_ADD_2 + BIT_MAP_ADD,bit_map->bit.u32_byte,BIT_MAP_SIZE);
break;
default:
break;
}
for(i = 0;i < BIT_MAP_SIZE;i++){
switch(bit_map->bit.byte[i]){
case 0xFF:
bit_map->cur = i*8;
break;
case 0xFE:
bit_map->cur = i*8 + 1;
break;
case 0xFC:
bit_map->cur = i*8 + 2;
break;
case 0xF8:
bit_map->cur = i*8 + 3;
break;
case 0xF0:
bit_map->cur = i*8 + 4;
break;
case 0xE0:
bit_map->cur = i*8 + 5;
break;
case 0xC0:
bit_map->cur = i*8 + 6;
break;
case 0x80:
bit_map->cur = i*8 + 7;
break;
case 0x00:
bit_map->cur = i*8 + 8;
break;
default:
return ret;
}
if(bit_map->bit.byte[i] != 0){
break;
}
}
ret = 0;
return ret;
}
int32_t bit_map_read(bit_map_s *bit_map,uint32_t addres,uint32_t *p_data)
{
int32_t ret = -1;
uint32_t addr_temp = 0;
if((bit_map == NULL) || (addres >= DATA_SIZE)){
return ret;
}
switch(bit_map->port){
case 0:
addr_temp = FLASH_ADD_1;
break;
case 1:
addr_temp = FLASH_ADD_2;
break;
default:
break;
}
flash_multi_read(addr_temp + addres*4,p_data,4);
return 0;
}
int32_t bit_map_read_cur(bit_map_s *bit_map,uint32_t *p_data)
{
int32_t ret = -1;
uint32_t addr_temp = 0;
if((bit_map == NULL) || (bit_map->cur >= DATA_SIZE)){
return ret;
}
switch(bit_map->port){
case 0:
addr_temp = FLASH_ADD_1;
break;
case 1:
addr_temp = FLASH_ADD_2;
break;
default:
break;
}
flash_multi_read(addr_temp + (bit_map->cur - 1)*4,p_data,4);
return 0;
}
int32_t bit_map_write(bit_map_s *bit_map,uint32_t data)
{
int32_t ret = -1;
int32_t i = 0;
uint32_t addr_temp = 0;
if((bit_map == NULL) || (bit_map->cur >= DATA_SIZE)){
return ret;
}
if(bit_map->cur >= DATA_SIZE){
ret = -2;
return ret;
}
switch(bit_map->port){
case 0:
addr_temp = FLASH_ADD_1;
break;
case 1:
addr_temp = FLASH_ADD_2;
break;
default:
break;
}
i = bit_map->cur%8;
bit_map->bit.byte[bit_map->cur/8] = (~(1<<i)) & bit_map->bit.byte[bit_map->cur/8];
flash_multi_bprog(addr_temp + BIT_MAP_ADD + bit_map->cur/8,&bit_map->bit.byte[bit_map->cur/8],1);
flash_multi_wprog(addr_temp + bit_map->cur*4,&data,4);
bit_map->cur++;
ret = 0;
return ret;
}
int32_t bit_map_erase(bit_map_s *bit_map)
{
int32_t ret = -1;
uint32_t addr_temp = 0;
if(bit_map == NULL){
return ret;
}
switch(bit_map->port){
case 0:
addr_temp = FLASH_ADD_1;
break;
case 1:
addr_temp = FLASH_ADD_2;
break;
default:
break;
}
flash_ROM_ioctrl(IOCTRL_ROM_ERASE_SECTOR,(uint8_t *)&addr_temp);
bit_map->cur = 0;
ret = 0;
return ret;
}