当前位置:   article > 正文

【FLASH】STM32内部Flash模拟EEPROM磨损均衡算法--存储设备擦写均衡自带掉电保护接口-如何在同等存储空间下增加FLASH寿命呢?往下看-STM32F334实现FLASH擦写均衡_flash的均衡磨损和掉电保护算法

flash的均衡磨损和掉电保护算法

        STM32内部Flash的写寿命大约是1万次,假如我们在其Flash中存储数据,每天100次写操作,100天后Flash就无法继续可靠使用了;外部FLASH,比如说W25Q32,擦写次数也只有十万次,在高频率读写下也支撑不了多久, 本文采取了一种非常简单的方法,将Flash的使用寿命无限延长,取决于你为它分配的存储区大小。

主要思想就是将FLASH 分配一块区域给我们的管理机,然后用索引的方式累积写FLASH,中途不进行擦写,在存满整个分区时进行统一擦写,读取根据ID进行读取,并且加上了数据校验,异常回调。主要用于存储系统配置,运行记录等。支持多个存储管理机管理不同的区域。

FLASH存储模型如下:

 管理者内存结构如下:

  1. typedef struct{
  2. unsigned int PageSize; //页大小
  3. unsigned int Pages; //写的总页数
  4. unsigned int StartAddr; //起始地址
  5. unsigned int DriftAddr; //偏移地址需要大于四字节
  6. unsigned int Number; //总共有效条数
  7. unsigned char * Buffer; //数据缓冲区地址
  8. unsigned int ReadAddr; //读地址,用于节省查找时间提高效率
  9. unsigned int WriteAddr; //写地址,用于节省时间提高查找空区效率
  10. void (* Error_Handle)(FERROR err, unsigned char * src, unsigned int len);
  11. }FlashConfig;


 

PageSize:用户为该管理机提供的存储分区业大小。同时也最小擦除单位。

Pages:存储分区页大小。

StartAddr:存储分区起始地址。

DriftAddr:需要存储数据的数据宽度
Number:需要存储的数据条数,也就是最大ID,这里如果你相同类型的数据有多条有效数据,就通过ID来进行区分。打个比方:周一到周五的菜单,每天固定三个菜,要进行存储的话,就需要提供五组数据,那么日期就是ID,用来区分周几。

Buffer:这个是你数据的内存首地址,这个在后面不同的接口会有介绍

ReadAddr:读地址,由于查找最后一块有效数据地址随着分区的大小,查找时间会相应的变长,所以在系统上电初始化的时候将最后一块有效数据块首地址记录,之后就不用再从子分区0查找了,提高系统执行效率,这个保留在这,暂未实现,后面的版本会加入。

WriteAddr:写地址,与上面一样。

Error_Handle:错误回调,如未实现,请赋值为NULL

存储器接口:

  1. void open_service()
  2. {
  3. //这里可提供线程安全防止中断flash操作,由close_service释放安全锁
  4. eeplog("%s", "open service\n");
  5. }
  6. void close_service()
  7. {
  8. eeplog("%s", "close service\n");
  9. }
  10. /**
  11. * @brief 写储存器接口。
  12. * @param addr:写首地址
  13. sd:写入的数据首地址
  14. len:写入的长度
  15. * @retval None
  16. */
  17. void write_flash(unsigned int addr, const unsigned char * sd, unsigned int len){
  18. unsigned short * pt = (unsigned short *)sd;
  19. HAL_FLASH_Unlock();
  20. for(int i = 0; i < len/2; i++)
  21. {
  22. HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, addr+i*2, *(pt+i));
  23. }
  24. HAL_FLASH_Lock();
  25. }
  26. /**
  27. * @brief 擦除储存器接口
  28. * @param addr:擦除地址
  29. * @param 擦写长度,暂时未用到,保留,接口默认擦除一页,
  30. * @retval None
  31. */
  32. void erase_flash(unsigned int addr, unsigned int len){
  33. uint32_t PageError = 0;
  34. FLASH_EraseInitTypeDef pEraseInit;
  35. pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
  36. pEraseInit.PageAddress = addr;
  37. pEraseInit.NbPages = 1;
  38. HAL_FLASH_Unlock();
  39. HAL_StatusTypeDef s = HAL_FLASHEx_Erase(&pEraseInit, &PageError);
  40. HAL_FLASH_Lock();
  41. }
  42. /**
  43. * @brief 读储存器接口.
  44. * @param addr:读起始地址
  45. sd:读数据缓存区首地址
  46. len:读的长度
  47. * @retval None
  48. */
  49. void read_flash(unsigned int addr, unsigned char * sd, unsigned int len){
  50. unsigned short * pt = (unsigned short *)sd;
  51. for(int i = 0; i < len / 2; i++){
  52. pt[i] = *((__IO uint16_t*)(addr+i*2));
  53. }
  54. }

erase_flash:这里的长度其实无效,这里需要提供的是擦除一页的接口,这里也许会有人说NAND Flash,最小擦除单位为1块,也就是64页,怎么办呢?这里你就需要在配置管理类的时候,将页数量配置为1,然后页大小不超过一块的大小就可以。

下面是全部代码:
eeprom.c

  1. /*********************************************************************************
  2. *Copyright(C) -
  3. *FileName: eeprom.c
  4. *Author: 我不是阿沸
  5. *Version: 6.1.0
  6. *Date: 2023.08.20
  7. *Description: 用于嵌入式系统保存数据至存储器,兼容大多数存储器,经内部算法优化,擦写寿命可达:正常寿命*100倍,这个取决于你分配的空间大小,空间越大寿命越长。
  8. *Others: 互斥需自己实现
  9. *History:
  10. 1.Date:2023/04/03
  11. Author:我不是阿沸
  12. Modification:增加自定义互斥接口,由用户实现
  13. 2.Date:2023/05/16
  14. Author:我不是阿沸
  15. Modification:修改相关接口实现方式
  16. 3.Date:2023/07/18
  17. Author:我不是阿沸
  18. Modification:修改读写数据实现方式
  19. 4.Date:2023/08/20
  20. Author:我不是阿沸
  21. Modification:删除冗余成分
  22. 4.Date:2023/010/24
  23. Author:我不是阿沸
  24. Modification:增加查找分区地址记录,只需上电查找一次,提高效率
  25. 5.其他修改
  26. **********************************************************************************/
  27. #include "eeprom.h"
  28. void open_service()
  29. {
  30. //HAL_FLASH_Unlock();
  31. //这里可提供线程安全防止中断flash操作,由close_service释放安全锁
  32. eeplog("%s", "open service\n");
  33. }
  34. void close_service()
  35. {
  36. eeplog("%s", "close service\n");
  37. //HAL_FLASH_Lock();
  38. }
  39. //ProgramGroup programGroups[10];
  40. /**
  41. * @brief 写储存器接口。
  42. * @param addr:写首地址
  43. sd:写入的数据首地址
  44. len:写入的长度
  45. * @retval None
  46. */
  47. void write_flash(epmu32 addr, const epmu8 * sd, epmu32 len){
  48. //注意,这里传入的sd必须是偶数地址,不然在转换为16位时会出现字节对齐的情况
  49. uint64_t * pt = (uint64_t *)sd;
  50. HAL_FLASH_Unlock();
  51. //rt_kprintf("\nlen:%d\n", len);
  52. Flash_If_Write((uint8_t*)sd, addr, len);
  53. // for(int i = 0; i < len/8; i++)
  54. // {
  55. // HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr+i*8, *(pt+i));
  56. // }
  57. HAL_FLASH_Lock();
  58. }
  59. /**
  60. * @brief 擦除储存器接口
  61. * @param addr:擦除地址
  62. * @retval None
  63. */
  64. void erase_flash(epmu32 addr, epmu32 len){
  65. epmu32 PageError = 0;
  66. //FLASH_EraseInitTypeDef pEraseInit;
  67. HAL_FLASH_Unlock();
  68. Flash_If_Erase(addr);
  69. HAL_FLASH_Lock();
  70. }
  71. /**
  72. * @brief 读储存器接口.
  73. * @param addr:读起始地址
  74. @param sd:读数据缓存区首地址
  75. @param len:读的长度
  76. * @retval None
  77. */
  78. void read_flash(epmu32 addr, epmu8 * sd, epmu32 len){
  79. uint64_t * pt = (uint64_t *)sd;
  80. // for(int i = 0; i < len / 8; i++){
  81. // pt[i] = *((__IO uint64_t*)(addr+i*8));
  82. // }
  83. Flash_If_Read(sd, addr, len);
  84. }
  85. /**
  86. * @brief 校验数据接口
  87. * @param src:需要校验的数据首地址
  88. len:要校验的长度
  89. * @retval epmbool:返回校验成功或者失败
  90. */
  91. epmbool check_flash(epmu8 *src, epmu32 len){
  92. eeplog("start check data\n");
  93. epmu32 src_ckeck = epm_crc32(src, len -4);
  94. epmu32 dec_ckeck = epmu32_buffer_max((src+ len - 4));
  95. if(src_ckeck != dec_ckeck) {
  96. eeplog("data check error!\n");
  97. return EPMFALSE;
  98. }
  99. eeplog("data check success!\n");
  100. return EPMTRUE;
  101. }
  102. /**
  103. * @brief 判断第一块存储区是否进行初始化
  104. * @param flg:分区信息
  105. * @retval epmbool:true:存储区已初始化 false:未初始化
  106. */
  107. epmbool isinit_first_block(FlashConfig flg){
  108. epmu8 ch[10];
  109. read_flash(flg.start_addr, ch, 8);
  110. int i = 0;
  111. for(; i < 8; i++){
  112. if(ch[i] != 0xA5) break;
  113. }
  114. if(i != 8) return EPMFALSE;
  115. return EPMTRUE;
  116. }
  117. /**
  118. * @brief 第一块存储区初始化,写入初始化信息
  119. * @param flg:分区信息
  120. * @retval epmbool:true:存储区初始化成功 false:分区损坏
  121. */
  122. epmbool first_block_init(FlashConfig flg){
  123. eeplog("Is check epm init?\n");
  124. epmu8 ch[10];
  125. memset(ch, 0xA5, 8);
  126. eeplog("Sector not initialized, about to initialize...\n");
  127. for(int m = 0; m < flg.pages; m++){
  128. erase_flash(flg.start_addr+m*flg.page_size, flg.page_size);
  129. }
  130. write_flash(flg.start_addr, ch, 8);
  131. read_flash(flg.start_addr, ch, 8);
  132. for(int i = 0; i < 8; i++){
  133. if(ch[i] != 0xA5) {
  134. eeplog("Sector initialization default!!!!!!!!!!!!!!!\n");
  135. return EPMFALSE;
  136. }
  137. }
  138. eeplog("Sector initialization successful\n");
  139. return EPMTRUE;
  140. }
  141. /**
  142. * @brief 校验flash读出的数据是否为空
  143. * @param data:需要校验的数据首地址
  144. len:数据的长度
  145. * @retval epmbool:未被写入过数据返回true
  146. */
  147. epmbool is_eeprom_null(FlashConfig flg, epmu8 * data, int len){
  148. int n = 0;
  149. eeplog("Is flash empty?\n");
  150. for(n = 0; n < len; n++){
  151. if(data[n] != 0xff){
  152. eeplog("flash isn`t empty\n");
  153. return EPMFALSE;
  154. }
  155. }
  156. eeplog("flash is empty\n");
  157. return EPMTRUE;
  158. }
  159. /**
  160. * @brief CRC32校验
  161. * @param data:需要校验的数据首地址
  162. len:数据的长度
  163. * @retval CRC32校验值
  164. */
  165. #define POLY 0xEDB88320UL
  166. epmu32 epm_crc32(epmu8 * data, epmu32 datalen)
  167. {
  168. const uint8_t *bytes = data;
  169. uint32_t crc = 0xFFFFFFFFUL;
  170. // 循环处理每个字节
  171. for (size_t i = 0; i < datalen; i++) {
  172. crc ^= bytes[i]; // 把当前字节与 crc 的低 8 位进行异或操作
  173. // 处理当前字节的 8 位,每次处理一位
  174. for (int j = 0; j < 8; j++) {
  175. if (crc & 1) { // 如果 crc 的最低位为 1,则右移并与多项式除数进行异或操作
  176. crc = (crc >> 1) ^ POLY;
  177. } else { // 否则,只右移一个比特位
  178. crc >>= 1;
  179. }
  180. }
  181. }
  182. return ~crc; // 取反操作得到最终结果
  183. }
  184. /**
  185. * @brief 初始化分区信息
  186. * @param 分区信息指针
  187. * @param 起始地址
  188. * @param 偏移地址,子存储区大小
  189. * @param 有效数据条数
  190. * @param 分区占用存储器页数
  191. * @param 存储器叶大小
  192. * @param 数据缓存区指针
  193. * @param 错误回调接口
  194. * @retval none
  195. */
  196. void eeprom_init(FlashConfig * flg, epmu32 start_addr, epmu8 drift_addr, epmu16 number, epmu16 pages, epmu32 page_size, epmu8 * buffer, error_handle error)
  197. {
  198. flg->find_flag = EPMFALSE;
  199. flg->find_addr = start_addr;
  200. flg->start_addr = start_addr;
  201. flg->drift_addr = drift_addr;
  202. flg->number = number;
  203. flg->pages = pages;
  204. flg->page_size = page_size;
  205. flg->buffer = buffer;
  206. flg->error = error;
  207. }
  208. /**
  209. * @brief 顺序查找flash空区
  210. * @param flg:分区信息
  211. * @retval int:返回空区首地址
  212. */
  213. epmu32 epm_seq_search_addr(FlashConfig * flg){
  214. int findCount = flg->page_size * flg->pages / (flg->drift_addr+4);
  215. epmu32 start_addr = flg->start_addr;
  216. epmu32 find_addr = start_addr;
  217. epmu8 data[200];
  218. int i;
  219. open_service();
  220. eeplog("epy find start!\n");
  221. //这里注意,首地址为初始化校验区,无有效数据
  222. if(flg->find_flag == EPMFALSE)
  223. {
  224. epmbool bl = isinit_first_block(*flg);
  225. if(bl == EPMFALSE) {
  226. close_service();
  227. flg->find_flag = EPMTRUE;
  228. flg->find_addr = start_addr;
  229. return start_addr;
  230. }
  231. for(i = 1; i < findCount; i++){
  232. read_flash(start_addr + i*(flg->drift_addr+4), data, flg->drift_addr+4);
  233. if(is_eeprom_null(*flg, data, flg->drift_addr+4) == EPMTRUE){
  234. find_addr = start_addr + i*(flg->drift_addr+4);
  235. eeplog("epy find end, return emp addr!\n");
  236. break;
  237. }
  238. }
  239. if(i == findCount)
  240. {
  241. find_addr = start_addr + findCount*(flg->drift_addr+4);
  242. }
  243. else
  244. {
  245. }
  246. flg->find_addr = find_addr;
  247. flg->find_flag = EPMTRUE;
  248. }
  249. close_service();
  250. return flg->find_addr;
  251. }
  252. /**
  253. * @brief 写入数据
  254. * @param flg:分区信息
  255. data:需要写入的数据
  256. * @retval int:返回对应的空区首地址
  257. */
  258. FERROR epm_write_data(FlashConfig * flg, const epmu8 * data){
  259. epmu8 ch[200] = {0};
  260. epmu8 chb[200] = {0};
  261. epmu32 addr = flg->start_addr;
  262. int findCount = flg->page_size * flg->pages / (flg->drift_addr+4);
  263. addr = epm_seq_search_addr(flg);
  264. if(flg->find_addr >= flg->start_addr + findCount* (flg->drift_addr+4))
  265. {
  266. flg->find_addr = flg->start_addr;
  267. addr = flg->start_addr;
  268. }
  269. open_service();
  270. if(addr == flg->start_addr){
  271. epmbool b = first_block_init(*flg);
  272. if(b == EPMFALSE){
  273. flg->error(InitError, NULL, 0);
  274. close_service();
  275. return InitError;
  276. }
  277. for(int i = 0; i < flg->number; i++){
  278. memcpy(ch, flg->buffer+i*flg->drift_addr, flg->drift_addr);
  279. epmu32 src_ckeck = epm_crc32(ch, flg->drift_addr);
  280. buffer_epmu32_max(src_ckeck, (ch+flg->drift_addr));
  281. write_flash(addr +(i+1)*(flg->drift_addr+4), ch, flg->drift_addr+4);
  282. //这里可适当加入回读错误处理
  283. }
  284. flg->find_addr = addr + (flg->drift_addr+4)*(flg->number+1);
  285. eeplog("addr:%08X\n", addr);
  286. close_service();
  287. return Success;
  288. }
  289. memcpy(ch, data, flg->drift_addr);
  290. epmu32 src_ckeck = epm_crc32(ch, flg->drift_addr);
  291. buffer_epmu32_max(src_ckeck, (ch+flg->drift_addr));
  292. write_flash(addr, ch, flg->drift_addr+4);
  293. read_flash(addr, chb, flg->drift_addr+4);
  294. flg->find_addr += (flg->drift_addr+4);
  295. if(flg->find_addr >= flg->start_addr + findCount* (flg->drift_addr+4))
  296. {
  297. flg->find_addr = flg->start_addr;
  298. }
  299. int m = 0;
  300. for( ; m < flg->drift_addr+4; m++){
  301. if(chb[m] != ch[m]){
  302. break;
  303. }
  304. }
  305. if(flg->drift_addr+4 == m)
  306. {
  307. close_service();
  308. eeplog("%s", "data write succse");
  309. return Success;
  310. }
  311. else
  312. {
  313. eeplog("%s", "data write error, sd have bad block");
  314. }
  315. close_service();
  316. eeplog("%s", "data write error, sd have bad block");
  317. return WriteError;
  318. }
  319. /**
  320. * @brief 读出数据
  321. * @param flg:分区信息
  322. data:需要写入的数据
  323. * @retval NONE
  324. */
  325. FERROR epm_read_data(FlashConfig *flg, epmu8 * data){
  326. epmu32 addr = epm_seq_search_addr(flg);
  327. epmu8 ch[200];
  328. if(flg->drift_addr+4 >= 200) return IndexError;
  329. if(addr == flg->start_addr){
  330. flg->error(Empty, NULL, 0);
  331. return Empty;
  332. }
  333. else{
  334. addr -= (flg->drift_addr+4);
  335. open_service();
  336. read_flash(addr, ch, flg->drift_addr+4);
  337. close_service();
  338. if(check_flash(ch, flg->drift_addr+4) == EPMFALSE){
  339. flg->error(ReadError, NULL, 0);
  340. return ReadError;
  341. }
  342. memcpy(data, ch, flg->drift_addr);
  343. }
  344. return Success;
  345. }
  346. /**
  347. * @brief 查询全部数据 ,并且通过ID写入对应的缓冲区
  348. * @param flg:分区信息
  349. * @retval void
  350. */
  351. void epm_select_all_data(FlashConfig * flg)
  352. {
  353. int findCount = (flg->page_size * flg->pages) / (flg->drift_addr+4);
  354. epmu32 start_addr = flg->start_addr;
  355. int i, n;
  356. epmu8 ch[200] = {0};
  357. if(flg->drift_addr+4 >= 200) return;
  358. open_service();
  359. epmbool b = isinit_first_block(*flg);
  360. if(b == EPMFALSE){
  361. flg->error(InitError, NULL, 0);
  362. close_service();
  363. return;
  364. }
  365. for(i = 1; i < findCount; i++){
  366. read_flash(start_addr + i*(flg->drift_addr+4), ch, flg->drift_addr+4);
  367. if(is_eeprom_null(*flg, ch, flg->drift_addr+4) == EPMTRUE)
  368. {
  369. break;
  370. }
  371. if(check_flash(ch, flg->drift_addr+4) == EPMFALSE){
  372. if(flg->error != NULL)
  373. {
  374. flg->error(CheckError, ch, flg->drift_addr);
  375. }
  376. continue;
  377. }
  378. int id = *((epmu16*)ch);
  379. if(id >= flg->number) id = flg->number - 1;
  380. for(n = 0; n < flg->drift_addr; n++){
  381. flg->buffer[id*flg->drift_addr+n] = ch[n];
  382. }
  383. }
  384. close_service();
  385. return;
  386. }
  387. /**
  388. * @brief 通过索引查询多条数据数据
  389. * @param flg:分区信息
  390. start:开始条数,这里换算成地址为:flg->drift_addr* start
  391. num:查寻条数
  392. * @retval int:返回对应的空区首地址
  393. */
  394. void epm_select_data(FlashConfig * flg, epmu32 start, epmu32 num)
  395. {
  396. epmu32 start_addr = flg->start_addr;
  397. epmu8 ch[200] = {0};
  398. if(flg->drift_addr+ 4 >= 200) return;
  399. open_service();
  400. for(int i = 1; i < num && i < flg->number; i++){
  401. read_flash(start_addr + (i+start)*(flg->drift_addr+4), ch, flg->drift_addr+4);
  402. if(check_flash(ch, flg->drift_addr+4) == EPMFALSE){
  403. flg->error(CheckError, ch, flg->drift_addr);
  404. continue;
  405. }
  406. for(int n = 0; n < flg->drift_addr; n++){
  407. flg->buffer[(i-1)*flg->drift_addr+n] = ch[n];
  408. }
  409. }
  410. close_service();
  411. return;
  412. }

eeprom.h:

  1. /*********************************************************************************
  2. *Copyright(C) -
  3. *FileName: eeprom.h
  4. *Author: 我不是阿沸
  5. *Version: 6.1.0
  6. *Date: 2023.08.20
  7. *Description: 用于嵌入式系统保存数据至存储器,兼容大多数存储器,经内部算法优化,擦写寿命可达:正常寿命*100倍,这个取决于你分配的空间大小,空间越大寿命越长。
  8. *Others: 互斥需自己实现
  9. *History:
  10. 1.Date:2023/04/03
  11. Author:我不是阿沸
  12. Modification:增加自定义互斥接口,由用户实现
  13. 2.Date:2023/05/16
  14. Author:我不是阿沸
  15. Modification:修改相关接口实现方式
  16. 3.Date:2023/07/18
  17. Author:我不是阿沸
  18. Modification:修改读写数据实现方式
  19. 4.Date:2023/08/20
  20. Author:我不是阿沸
  21. Modification:删除冗余成分
  22. 4.Date:2023/010/24
  23. Author:我不是阿沸
  24. Modification:增加查找分区地址记录,只需上电查找一次,提高效率
  25. 5.其他修改
  26. **********************************************************************************/
  27. #ifndef __EEPROMS_H
  28. #define __EEPROMS_H
  29. #include <stdio.h>
  30. #include "if_flash.h"
  31. #include <stdio.h>
  32. #include <string.h>
  33. //#define __EEPDEBUG
  34. #ifdef __EEPDEBUG
  35. #define eeplog(format, ...) \
  36. rt_kprintf("[%s:%d->%s]:"format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
  37. #else
  38. #define eeplog(format, ...)
  39. #endif
  40. #define FLASH_PAGE_STEP FLASH_PAGE_SIZE
  41. #define START_ADDRESS (uint32_t)0x08000000
  42. #define epmu32_buffer_min(x) (epmu32)(*((x+3))<<24 | *((x)+2)<<16 | *((x)+1)<<8 | *(x))
  43. #define epmu32_buffer_max(x) (epmu32)(*(x)<<24 | *((x)+1)<<16 | *((x)+2)<<8 | *((x)+3))
  44. #define epmu16_buffer_min(x) (epmu16)(*((x)+1)<<8 | *(x))
  45. #define epmu16_buffer_max(x) (epmu16)(*(x)<<8 | *(x+1))
  46. #define buffer_epmu32_min(x, buf) {(buf)[0] = (x); (buf)[1] = (x)>>8; (buf)[2] = (x)>>16; (buf)[3] = (x)>>24;}
  47. #define buffer_epmu32_max(x, buf) {(buf)[3] = (x); (buf)[2] = (x)>>8; (buf)[1] = (x)>>16; (buf)[0] = (x)>>24;}
  48. #define buffer_epmu16_min(x, buf) {(buf)[0] = (x); (buf)[1] = (x)>>8;}
  49. #define buffer_epmu16_max(x, buf) {(buf)[1] = (x); (buf)[0] = (x)>>8;}
  50. typedef unsigned int epmu32;
  51. typedef unsigned short epmu16;
  52. typedef unsigned char epmu8;
  53. typedef enum flash_error{
  54. Success,
  55. WriteError,
  56. ReadError,
  57. IndexError,
  58. CheckError,
  59. InitError,
  60. Empty
  61. }FERROR;
  62. typedef enum{
  63. EPMFALSE,
  64. EPMTRUE
  65. }epmbool;
  66. typedef void (* error_handle)(FERROR err, epmu8 * src, epmu32 len);
  67. typedef struct{
  68. epmu32 page_size; //页大小
  69. epmu16 pages; //写的总页数
  70. epmu32 start_addr; //起始地址
  71. epmu8 drift_addr; //偏移地址需要大于四字节
  72. epmu32 number; //总共有效条数
  73. epmu8 * buffer; //数据缓冲区地址
  74. epmu32 find_addr;
  75. epmbool find_flag;
  76. error_handle error;
  77. }FlashConfig;
  78. void eeprom_init(FlashConfig * flg, epmu32 start_addr, epmu8 drift_addr, epmu16 number, epmu16 pages, epmu32 page_size, epmu8 * buffer, error_handle error);
  79. epmu32 epm_seq_search_addr(FlashConfig * flg);
  80. void epm_select_data(FlashConfig * flg, epmu32 start, epmu32 num);
  81. epmu32 epm_crc32(epmu8 * data, epmu32 datalen);
  82. epmbool check_flash(epmu8 *src, epmu32 len);
  83. void erase_flash(epmu32 addr, epmu32 len);
  84. void write_flash(epmu32 addr, const epmu8 * sd, epmu32 len);
  85. void read_flash(epmu32 addr, epmu8 * sd, epmu32 len);
  86. FERROR epm_read_data(FlashConfig *flg, epmu8 * data);
  87. FERROR epm_write_data(FlashConfig * flg, const epmu8 * data);
  88. void epm_select_all_data(FlashConfig * flg);
  89. #endif

main.c

  1. #include "stdio.h"
  2. #include "eeprom.h"
  3. typedef struct
  4. {
  5. char id;
  6. char depth; //宽度
  7. short cmp;
  8. short angle;
  9. short temp; //温度
  10. short count; //执行次数
  11. short width; //间隔时间
  12. short lc ; //lc开关值
  13. unsigned int freq;
  14. int expand;
  15. int outi;
  16. int kp1;
  17. int ki1;
  18. int kd1;
  19. int power;
  20. int kp2;
  21. int ki2;
  22. int kd2;
  23. int outv;
  24. int kp3;
  25. int ki3;
  26. int kd3;
  27. int y1;
  28. int y2;
  29. int y3;
  30. int ts;
  31. int xx;
  32. }Preset;
  33. Preset preset;
  34. FlashConfig PresetCfg;
  35. void preset_config_ehandle(FERROR err, unsigned char * src, unsigned int len)
  36. {
  37. eeplog("preset_config_ehandle error:%d\n", err);
  38. }
  39. void printf_preset(Preset p)
  40. {
  41. eeplog("freq:%d,cmp:%d,angle:%d,expand:%d,temp:%d,count:%d,width:%d,depth:%d", p.freq, p.cmp, p.angle, p.expand, p.temp, p.count, p.width, p.depth);
  42. }
  43. epmbool write_preset(void)
  44. {
  45. //printf_preset(preset);
  46. epm_write_data(&PresetCfg, (unsigned char *)&preset);
  47. return EPMTRUE;
  48. }
  49. /**
  50. * @brief read_preset 读取预设组
  51. * @param
  52. * @retval bool:读取是否成功
  53. */
  54. epmbool read_preset(void)
  55. {
  56. epm_read_data(&PresetCfg, (unsigned char * )&preset);
  57. return EPMTRUE;
  58. }
  59. epmbool preset_config_init(void){
  60. eeplog("size:%d\n", sizeof(Preset));
  61. eeprom_init(&PresetCfg, 59*FLASH_PAGE_STEP + START_ADDRESS, sizeof(Preset), 1, 4,
  62. FLASH_PAGE_STEP, (unsigned char * )&preset, preset_config_ehandle);
  63. read_preset();
  64. printf_preset(preset);
  65. return EPMTRUE;
  66. }

如有疑问可以评论区留言,或者联系QQ:2227273007,另外附上eeprom的源文件:

(7条消息) STM32存储设备磨损均衡算法与数据结构资源-CSDN文库

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/676288
推荐阅读
相关标签
  

闽ICP备14008679号