1. FLASH和EEPROM读写数据的对比
2. FLASH模拟EEPROM的原理
3. FLASH模拟EEPROM的优点
4. 实战项目工程代码
基本特性对比
特性 | FLASH | EEPROM |
---|---|---|
存储单元结构 | NAND(共享位线,高密度)/ NOR(独立位线,高速) | 双晶体管结构(控制栅 + 存储栅,支持字节操作) |
擦除单位 | Block(块擦除) | Byte(字节级擦除) |
写入速度 | 0.2-10ms/page | 1-10ms/byte |
擦除时间 | 1-300ms/block (NOR Flash) | 字节擦除与写入同时完成(无需单独擦除步骤) |
擦写次数 | 10K-100K次 | 100K-1M次 |
存储密度 | 高(适合大容量存储) | 低(适合小数据存储) |
功耗 | 较高(需要高压擦除) | 较低 |
典型应用场景 | 操作系统存储、文件系统、代码 / 数据批量存储 | 配置参数存储、少量数据频繁读写 |
核心优势对比
优势维度 | 传统EEPROM | FLASH模拟方案 |
---|---|---|
硬件成本 | 需要独立芯片 | 复用现有FLASH |
存储容量 | 通常<512KB | 可扩展至MB级别 |
系统集成度 | 需要额外接口电路 | 片上集成 |
可移植性 | 依赖特定硬件 | 纯软件实现 |
功耗表现 | 静态功耗较高 | 静态功耗趋近于零 |
可使用的flash 软件模拟EEPROM lib功能库 :https://download.csdn.net/download/weixin_43176196/90797265
flash 软件模拟EEPROM 项目工程程序 :https://download.csdn.net/download/weixin_43176196/90789413
需将功能模块 eepromSoft 添加到工程下,并实现 flash读写操作.具体应用如下
// eeprom 可分配使用的个数, 最大不超过 5
#define EPROM_SOFT_NUM (5 )
// 分配页缓冲区大小, 该数值必须为使用 pageSize 最大的一个 EEPROM_SOFT_INDEX 的 pageSize
#define EPROM_BUF_DATA_SIZE (128 * 2)
typedef int32_t EEPROM_SOFT_INDEX; // 索引类型
// eeprom 错误码
typedef enum{
EPROM_ERR_NORMAL = 0, // 正常
EPROM_ERR_UNDEFINE, // 未知错误,可能为传入的参数无效
EPROM_ERR_MEMBUF, // 数据缓冲区设置出错
EPROM_ERR_READ, // 读失败
EPROM_ERR_WRITE, // 写错误
EPROM_ERR_ERASE, // 擦除扇区错误
EPROM_ERR_OVER_DATA, // 数据过大,会超出页大小
EPROM_ERR_PAGE_SIZE, // 分配的页大小不正确
EPROM_ERR_ADDR, // 分配的 falsh 的 adrrStart 或 adrrEnd 不正确
EPROM_ERR_FUNC, // 设置的函数指针无效
EPROM_ERR_SOFT_NUM, // 分配的个数不足,需设大 EPROM_SOFT_NUM
EPROM_ERR_NUM_SET, // EPROM_SOFT_NUM 设置不符合要求
EPROM_ERR_LOCK, // 加锁失败
EPROM_ERR_INDEX, // 未知索引号
EPROM_ERR_NUM
}EPROM_ERR;
/* eeprom 基准信息数据结构 */
typedef struct{
uint32_t adrrStart; // 分配的 flash 扇区起始地址(此地址必须为4的倍数,且不能为0)
uint32_t adrrEnd; // 分配的 flash 扇区结束地址(此地址必须为4的倍数)
// 设置的页大小(该数值必须是 128 的倍数),进行 falsh 的页分配,单位:Byte
// 页大小并不代码实际存放数据的最大长度,每页里面会保存基准信息
uint32_t pageSize;
/* flash 读函数指针,
// flashAddr,要读取数据的flash地址;
// buf, 读取数据缓冲的指针
// readLen, 要读取数据的长度(单位:字节)
// 返回, 实际读取到的数据长度
*/
uint32_t (*fRead)(uint32_t flashAddr, uint8_t *buf, uint32_t readLen);
/* flash 写函数指针(不带擦除扇区写入数据),
// flashAddr,开始写入数据的flash地址;
// data, 要写入的数据指针
// len, 要写入的数据长度(单位:字节)
// 返回, 0, 写入成功
// 1, 写入失败
*/
uint8_t (*fWrite)(uint32_t flashAddr,const uint8_t *data, uint32_t len);
/* flash 擦除函数指针,
// flashAddr,要擦除flash数据的起始地址;
// len, 要擦除数据的数据长度(单位:字节)
// 返回, 0, 成功
// 1, 失败
*/
uint8_t (*fErase)(uint32_t flashAddr, uint32_t len);
}EPROM_SOFT_INFO;
// 声明外部其他文件定义的 crc32校验函数; 若只模拟一个eeprom 可进行简单求和校验 .
extern uint32_t AlgorithCrc32(const uint8_t* data, uint32_t len);
/*!
*
* @param[in] timeOutMs :毫秒超时时间
* @param[out] none
* @return 1 :加锁成功
* @return 0 :加锁失败
*
* @brief 声明外部其他文件定义的 加锁保护机制.
* @note . 主要用于 EPROM_BUF_DATA_SIZE 分配的数据缓冲区资源保护;
*
*/
extern uint8_t EepromSoftLock(uint32_t timeOutMs);
extern void EepromSoftUnLock(void); // 释放锁
/*!
*
* @param[in] num : eeprom 可模拟的个数,取值 1 - 10;
* @param[in] pageBuf : 要分配的页缓冲区指针;
* @param[in] pageSize : 页缓冲区的大小(必须为 128的倍数,单位:字节);
* @param[out] none
* @return EPROM_ERR 错误码
*
* @brief 模拟eeprom 公共资源分配设置
* @note . 必须在 EepromSoftInit() 函数使用前,调用该函数执行一次;
*
*/
EPROM_ERR EepromSoftSet(uint8_t num, uint8_t* pageBuf, uint32_t pageSize);
/*!
*
* @param[in] info : 要配置的基准信息结构指针
* @param[out] errCode :EPROM_ERR 返回指针,存放返回的错误码
* @return >=0的数值 : eeprom 索引号
* @return 其他值: 出错
*
* @brief 初始化基准信息,若成功,就返回对应 eeprom 的索引号.
* @note . 该操作会检测分配的地址是否正确,并获取上次写入有效数据的基准信息;
* . 若未获取到有效基准信息,就擦除扇区,从起始地址开始读/写数据;
*
*/
EEPROM_SOFT_INDEX EepromSoftInit(EPROM_SOFT_INFO* info, EPROM_ERR* errCode);
/*!
*
* @param[in] index :eeprom索引号
* @param[in] buf :数据缓冲区,存放读取到的数据
* @param[in] readLen :要读取的数据长度
* @param[out] errCode :EPROM_ERR 返回指针,存放返回的错误码
* @return 实际读取到的数据长度
*
* @brief 读数据
* @note . 从上次成功写入数据的地址读取数据;
*
*/
uint32_t EepromSoftRead(EEPROM_SOFT_INDEX index, uint8_t *buf, uint32_t readLen, EPROM_ERR* errCode);
/*!
*
* @param[in] index :eeprom索引号
* @param[in] data: 要写入的数据指针
* @param[in] len:要写入的数据长度
* @param[out] none
* @return PROM_ERR 错误码
*
* @brief 写入数据
* @note . 写数据之前会将上次成功写入的地址进行偏移,若数据成功写入数据,就记录该地址;
*
*/
EPROM_ERR EepromSoftWrite(EEPROM_SOFT_INDEX index, uint8_t *data, uint32_t len);
使用方法:
/* USER CODE BEGIN 4 */
// 定义 crc32校验
uint32_t AlgorithCrc32(const uint8_t* data, uint32_t len)
{
uint32_t val = 0;
for(int i = 0; i < len; i++){
val += data[i];
}
return val;
}
// 定义保护锁机制
uint8_t s_LockStatus = 0;
uint8_t EepromSoftLock(uint32_t timeOutMs)
{
s_LockStatus = 1;
return s_LockStatus;
}
// 释放锁
void EepromSoftUnLock(void)
{
s_LockStatus = 0;
}
// 定义模拟 eeprom 缓冲区
static uint8_t s_EepromBuf[EPROM_BUF_DATA_SIZE];
EEPROM_SOFT_INDEX s_EepronIndex1 = -1, s_EepronIndex2 = -1; // 定义索引变量
void TestFlashHandle(void)
{
unsigned char buf[20 + 8] = {0x11, 0x22, 0x33, 0x44, 0x55};
EPROM_ERR errCode = 0;
uint32_t len = 0;
EPROM_SOFT_INFO eepromInfo;
// 设置 eeprom 公共资源分配
errCode = EepromSoftSet(EPROM_SOFT_NUM, s_EepromBuf, EPROM_BUF_DATA_SIZE);
// 模拟 第一个 eeprom
eepromInfo.adrrStart = FLASH_ADDR_SECTOR_9; // 设置模拟eeprom 的扇区起始地址
eepromInfo.adrrEnd = FLASH_ADDR_SECTOR_10; // 设置模拟eeprom 的扇区结束地址
eepromInfo.pageSize = 128; // 每次写数据操作的flash大小(最小128)
eepromInfo.fErase = FlashHardErase;
eepromInfo.fRead = FlashHardRead;
eepromInfo.fWrite = FlashWriteNoErase;
s_EepronIndex1 = EepromSoftInit(&eepromInfo, &errCode); // 初始化基准信息
if(s_EepronIndex1 < 0) {
return ;
}
// 写数据
buf[16] = 0xAA;
buf[17] = 0xBB;
buf[18] = 0xCC;
buf[19] = 0xDD;
errCode = EepromSoftWrite(s_EepronIndex1, buf, 20);
// 读数据
memset(buf, 0, 20);
len = EepromSoftRead(s_EepronIndex1, buf, 20, &errCode);
// 模拟 第二个 eeprom
eepromInfo.adrrStart = FLASH_ADDR_SECTOR_10; // 设置模拟eeprom 的扇区起始地址
eepromInfo.adrrEnd = FLASH_ADDR_SECTOR_11; // 设置模拟eeprom 的扇区结束地址
eepromInfo.pageSize = EPROM_BUF_DATA_SIZE; // 每次写数据操作的flash大小(最小128)
eepromInfo.fErase = FlashHardErase;
eepromInfo.fRead = FlashHardRead;
eepromInfo.fWrite = FlashWriteNoErase;
s_EepronIndex2 = EepromSoftInit(&eepromInfo, &errCode); // 初始化基准信息
if(s_EepronIndex2 < 0) {
return ;
}
// 写数据
buf[16] = 0xA1;
buf[17] = 0xB1;
buf[18] = 0xC1;
buf[19] = 0xD1;
errCode = EepromSoftWrite(s_EepronIndex2, buf, 20);
// 读数据
memset(buf, 0, 20);
len = EepromSoftRead(s_EepronIndex2, buf, 20, &errCode);
// 再读取 s_EepronIndex1 ,观察是否有变化
memset(buf, 0, 20);
len = EepromSoftRead(s_EepronIndex1, buf, 20, &errCode);
}