重庆旅游网站建设公司,视觉设计师前景,搜索引擎营销的内容,大学生实训网站建设心得1. NOR Flash与NAND Flash
先deepseek看结论#xff1a;
特性Nor FlashNAND Flash读取速度快#xff08;支持随机访问#xff0c;直接执行代码#xff09;较慢#xff08;需按页顺序读取#xff09;写入/擦除速度慢#xff08;擦除需5秒#xff0c;写入需逐字节操作
特性Nor FlashNAND Flash读取速度快支持随机访问直接执行代码较慢需按页顺序读取写入/擦除速度慢擦除需5秒写入需逐字节操作快擦除4ms按块操作存储密度低1MB-1GB适合小容量高8GB-1TB适合大容量擦写寿命约10万次约100万次成本高低坏块管理无需坏块管理可靠性高需ECC纠错和坏块管理代码执行直接运行代码需将代码加载到RAM运行
本次只针对STM32内部Flash即Nor Flash进行总结不考虑坏块问题。本次设计结合实际项目存储固定个数的文件进行说明文件存储时包含文件索引文件内容。
2. 掉电保护原理
2.1 Flash擦除过程掉电
参考链接nor flash之擦除与写入擦除的步骤有三步 先设置全0此过程掉电FLASH前面部分数据会变成0后面部分数据不变擦除此过程掉电FLASH全部数据可能是错乱的数值维持全1此过程掉电FLASH中大部分数据读出是0xFF,某些部分读出的数据可能为其他数值 通过设置magic num且存放在扇区头部可以解决前两种问题但第三种无法解决这种问题只能通过扫描是否全是0xFF来检查并重新擦除
2.2 Flash写入过程掉电
掉电后前面写入的数据为正常数据后面未写入仍未0xFF。 比如写一个结构体可能会出现写一半的情况可通过一个状态字段来进行识别后面进行详细说明。
2.3 磨损平衡
考虑到FLASH的寿命写入数据时是增量写入的比如修改一个变量或者结构会重新写入一个新的同时将旧的标记为已删除这个标记可以和状态字段整合到一起。 还需要考虑扇区写满后数据转移的问题本次项目中采用主扇区和备份扇区交替写入的机制来控制磨损平衡需添加扇区头结构对扇区进行标识要做到既可以在启动时选择到正确的扇区又可以识别出破坏的扇区。
2.4 断电数据恢复
当写扇区头或者写文件时如果掉电那么本次的数据需在启动时识别出来并可以恢复到增量写入时上个文件完整的数据。本次项目未考虑数据恢复的问题只识别出了是否发生了损坏损坏会全部擦除上位机重新下发文件。
3. 扇区头部结构
考虑到自己项目中文件较小且写入频次较低项目设计并没采用类似easyFlash库那样对所有扇区进行平衡写入。为了加快搜索项目中对文件索引单独存储一个扇区同时结合另一个相同大小的扇区进行写满后换扇区写入。同样文件数据也采用两个扇区进行交替写入。 不管哪个扇区考虑到擦除中断电导致扇区被破坏每个扇区的头部添加如下结构
typedef struct tagSectorHeader{uint32_t u32Magic; //魔术字0x12345678uint32_t u32SectorStatus; //扇区的状态
}TSectorHeader;
//扇区的状态
#define SECTOR_STATUS_INIT 0xFFFFFFFF //未使用
#define SECTOR_STATUS_USED 0x00FFFFFF //已使用写完数据后设置该装填
#define SECTOR_STATUS_ERASE 0x0000FFFF //擦除中先搬运数据搬运完毕后擦除扇区变为未使用这里使用了Flash中的一个小技巧对同一个地址写4字节数据时可以多次写入但必须按照先写0x00FFFFFF再写0x0000FFFF这种方式进行写入只要之前是FF位置的数据就可以写入。
判定扇区是否损坏
uint32_t isBadSector(TSectorHeader *pHdr)
{// 魔术字无效且不是初始值 或 魔术字是初始值但扇区状态不是初始值if (pHdr-u32Magic ! SECTOR_MAGIC_NUM pHdr-u32Magic ! 0xFFFFFFFF || pHdr-u32Magic 0xFFFFFFFF pHdr-u32SectorStatus ! SECTOR_STATUS_INIT){return 1; // 扇区损坏}return 0;
}第一次写扇区头
int32_t writeNewSectorHdr(uint32_t u32SectorAddr)
{// 标记新扇区 - 使用状态if (writeSectorStatus(u32SectorAddr offsetof(TSectorHeader, u32SectorStatus), SECTOR_STATUS_USED) ! 0){return -1;}// 标记新扇区 - 魔术字uint32_t u32Magic SECTOR_MAGIC_NUM;if (AflFlashWriteWords(u32SectorAddr offsetof(TSectorHeader, u32Magic), u32Magic, 1) ! 0){return -1;}return 0;
}
更改扇区状态
int32_t writeSectorStatus(uint32_t u32StatusStartAddr, SECTOR_STATUS_DEFINE statusNow)
{int32_t iRet 0;FILEINDEX_STATUS_DEFINE statusLast; // 注意状态写入必须按状态定义的顺序进行写入AflFlashReadWords(u32StatusStartAddr, statusLast, 1);if (statusNow SECTOR_STATUS_USED){if (statusLast ! SECTOR_STATUS_INIT){ASSERT(1, last sector status err!); return -1;}}else if (statusNow SECTOR_STATUS_ERASE){if (statusLast ! SECTOR_STATUS_USED){ASSERT(1, last sector status err!);return -1;}}else{}iRet AflFlashWriteWords(u32StatusStartAddr, statusNow, 1);return iRet;
}4. 文件索引结构
//文件索引的操作状态
#define FILEINDEX_STATUS_INIT 0xFFFFFFFF //未使用
#define FILEINDEX_STATUS_PRE_WRITE 0x00FFFFFF //准备写入
#define FILEINDEX_STATUS_FIN_WRITE 0x0000FFFF //已写入 写入索引结构数据即更新文件数据靠CRC校验
#define FILEINDEX_STATUS_FIN_DEL 0x000000FF //已删除
//#define FILEINDEX_STATUS_PRE_DEL 0x000000FF //准备删除 该操作可以用来恢复上次的数据本次设计无需恢复数据
//文件索引信息
typedef struct tagFileIndex
{uint32_t u32DataStatus; // 操作状态uint32_t u32IndexAddr; // 该索引在扇区中的起始地址uint32_t u32DataAddr; // 该文件在Flash中的起始地址uint32_t u32DataLen; // 数据块的长度uint16_t u16FileCRC; // 数据校验值uint16_t u16AlignReserved; // 对齐保留// 其他数据
}TFileIndex;写索引时先写状态为准备写入然后写结构体字段写完后再修改状态为写入完成。 比如写一个文件会先写索引结构再写文件内容索引结构中添加状态字段来进行控制文件内容没写完掉电可通过检查索引结构中的文件长度、CRC来对文件内容进行校验。
写索引数据
// 写索引数据状态无需传入内部控制
int32_t writeFileIndexToFlash(uint32_t u32CopyToAddr, TFileIndex *ptfileIndex)
{int32_t iRet 0;do{// 先写状态为【准备写入】iRet writeFileIndexStatus(u32CopyToAddr offsetof(TFileIndex, u32DataStatus), FILEINDEX_STATUS_PRE_WRITE);if (iRet ! 0)break;//写索引地址iRet AflFlashWriteWords(u32CopyToAddr offsetof(TFileIndex, u32IndexAddr), u32CopyToAddr, 1);if (iRet ! 0)break;// 写剩余的其他所有数据iRet AflFlashWriteWords(u32CopyToAddr offsetof(TFileIndex, u32DataAddr), (uint32_t *)ptfileIndex-u32DataAddr, (sizeof(TFileIndex) - offsetof(TFileIndex, u32DataAddr))/4);if (iRet ! 0)break;// 最后写状态为【已写入】iRet writeFileIndexStatus(u32CopyToAddr offsetof(TFileIndex, u32DataStatus), FILEINDEX_STATUS_FIN_WRITE);if (iRet ! 0)break;ptfileIndex-u32DataStatus FILEINDEX_STATUS_FIN_WRITE;ptfileIndex-u32IndexAddr u32CopyToAddr;} while (0);if (iRet ! 0){DEBUG(DEBUG_ERROR, (writeFileIndexToFlash Error!!));}return iRet;
}写文件数据略过程就是先写索引再写数据最后检查写入的文件CRC是否正确。
5. 总结
以上是状态控制的核心代码其他和写文件数据内容相关、扇区切换相关略去了以后有机会可以写个通用的、小型的模块。 数据恢复的思路如下本次检查到非法数据直接擦了 当写入新的结构时注意这个结构必须具备唯一标识符需将旧的结构标记为准备删除然后写新的结构当新结构数据写完后会将旧的结构数据再标记为已删除。这样初始化时扫描所有数据如果同一个标识符的结构一个状态为准备删除一个为准备写入此时认为数据未拷贝完可以将准备写入改为已删除废掉这个数据重新拷贝一个新的此时即使再次掉电也没问题。如果同一个标识符的结构一个状态为准备删除一个为写入完成此时认为数据已经拷贝完将准备删除改为已删除即可。 6. 调试中问题
使用ST-Link单步调试读写flash代码可能会出现写失败甚至数据被篡改改为J-Link解决该问题。