完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32 没有自带 EEPROM,但是 STM32 具有 IAP(在应用编程)功能,所以我们可以把它的 FLASH 当成 EEPROM 来使用。内部FLASH可以省存储芯片和IO引脚,但是需要有额外的FLASH资源。
数据保存,简单来说就是在特定的内存地址上插入一段想保存的数据, 先保存扇区(1~2K)的数据,然后擦除该片区,然后拼接插入改扇区,再把最终数据写入到扇区,因为写操作费时间也易出错,可以再写入前后分别进行和校验 ● 写数据 /******************************************************************* * @brief 写数据到flash中,在STM32F302中,一页为1k(2K) * @param [buf] 写入的数据 [flash_addr] 写入的数据的地址 [len] 写入的数据的长度 * @retval None * @note 暂不支持跨扇区写数据, 充分验证通过,所以写入的地址需要严格控制 * @runtime 78.86ms 其中主要是写数据的时间,0.86ms为两次和校验的计算 2048byte~78ms、1024byte~50ms、2byte~22ms ********************************************************************/ u8 WriteDataToFlash(void *buf, u32 flash_addr, u16 len) { /*0.关闭所有串口中断,因为串口中断没有关闭,而执行写flash操作时候,会优先执行flash操作,串口接收中断不被执行,会卡死整个系统*/ DisableUsartInterrupt(); //1.校验地址和合理性 if(flash_addr return 1; } flashstm.addr = flash_addr - STM32_FLASH_BASE_ADDR ; flashstm.secPos = flashstm.addr / FLASH_PAGE_SIZE; //第几个扇区 flashstm.secAddr = flashstm.secPos * FLASH_PAGE_SIZE + STM32_FLASH_BASE_ADDR; //扇区的地址 flashstm.secOff = flash_addr - flashstm.secAddr; //相对于N号扇区首地址的偏移值 //2.读出扇区的数据 ReadDataFromFlash(&flashstm.buf[0], flashstm.secAddr, FLASH_PAGE_SIZE); //3.擦除扇区的数据 HAL_FLASH_Unlock(); // printf("addr is: %XrnsecPos is %drnsecAddr is %Xrnflashstm.secOff is %Xrnrn", flashstm.addr,flashstm.secPos,flashstm.secAddr,flashstm.secOff); FlashErase(flashstm.secAddr, 1); //一擦就是1页 1页为1024bytes //4.插入数据到1K的buf中 for(u8 i=0; i } //5.计算写入前的校验和 for(u16 j=0; j } //6.写入数据到扇区 ,利用了HAL库写的,函数里,需要解锁写数据再上锁的。 FlashWrite(flashstm.secAddr, (void *)flashstm.buf, FLASH_PAGE_SIZE); //48byteS HAL_FLASH_Lock(); //7.读出写入的数据到buf中 ReadDataFromFlash(&flashstm.buf[0], flashstm.secAddr, FLASH_PAGE_SIZE); //8.计算读出的数据 for(u16 j=0; j } //9.校验 if(flashstm.chucksumold != flashstm.chucksumnew) { while (1) { /* Make LED2 blink (100ms on, 2s off) to indicate error in Erase operation */ LED_ON(); HAL_Delay(100); LED_OFF(); HAL_Delay(2000); } return 2; } // printf("flashstm.chucksumold is: %drnflashstm.chucksumnew is %drnrn",flashstm.chucksumold,flashstm.chucksumnew); /*10.打开所有串口中断*/ EnableUsartInterrupt(); return 0; } ● 读数据: /******************************************************************* * @brief 必须为4的倍数,这bug,后续再改进 * @param PRO_INFOm: 校准参数结构体 * @retval None ********************************************************************/ void ReadDataFromFlash(void *buf, u32 flash_addr, u16 len) { u8 val = 0; u32 tmp = 0; for(int i = 0; i < len; i += 4) //48 --49 { val = len-i; switch(val) { // case 1: // *((char *)buf + i) = *((unsigned int *)(flash_addr + i)) & 0xff; // break; // case 2: // *((u16 *)(char *)buf + i) = (*((unsigned int *)(flash_addr + i)) & 0xffff); // break; // case 3: // tmp = (*((unsigned int *)(flash_addr + i)) & 0xffffff); // break; default : *((unsigned int *)((char *)buf + i)) = *((unsigned int *)(flash_addr + i)); break; } } } ● 测试代码 //test.h: //**FLASH, 注意内存大小为4的倍数 typedef struct _stUserSave_t_ { u32 irPowerCode; u32 irFuncCode; u8 isPowerOpen; //1:开机 0:关机 } stUserSave_t; #define STM32_FLASH_SIZE 32 //FLASH 容量为32K #define STM32_FLASH_BASE_ADDR 0x08000000 //STM32 FLASH起始地址 #define STM32_FLASH_END_ADDR (STM32_FLASH_BASE_ADDR + 1024*STM32_FLASH_SIZE) #define LAST_SEC_FLASH_ADDR STM32_FLASH_BASE_ADDR + 31*1024 //test.c: stUserSave_t userSave; void testWrite(void) { WriteDataToFlash(&userSave, LAST_SEC_FLASH_ADDR, sizeof(stUserSave_t)); } void testRead(void) { ReadDataFromFlash(&userSave, LAST_SEC_FLASH_ADDR, sizeof(userSave)); } 经验证,重启单片机,数据和写入的一致,并且掉电不丢失: |
|
|
|
完整的代码如下:
● flash.c #include "flash.h" #include "led.h" FLASH_T flashstm; /******************************************************************* * @brief 关闭所有串口中断 * @param None * @retval None ********************************************************************/ static void DisableUsartInterrupt(void) { // USART_Cmd(USART1, DISABLE);//失能串口1 // USART_Cmd(USART2, DISABLE);//失能串口2 __set_PRIMASK(1); } /******************************************************************* * @brief 打开所有串口中断 * @param None * @retval None ********************************************************************/ static void EnableUsartInterrupt(void) { // USART_Cmd(USART1, ENABLE);//使能串口1 // USART_Cmd(USART2, ENABLE);//使能串口2 __set_PRIMASK(0); } /******************************************************************* * @brief 写数据到flash中,在STM32F302中,一页为2K,本芯片K8U6共64K有32页,擦除以页为单位 * @param [buf] 写入的数据 [flash_addr] 写入的数据的地址 [len] 写入的数据的长度 * @retval None * @note 暂不支持跨扇区写数据,充分验证通过 * @runtime 78.86ms 其中主要是写数据的时间,0.86ms为两次和校验的计算 2048byte~78ms、1024byte~50ms、2byte~22ms ********************************************************************/ u8 WriteDataToFlash(void *buf, u32 flash_addr, u16 len) { /*0.关闭所有串口中断,因为串口中断没有关闭,而执行写flash操作时候,会优先执行flash操作,串口接收中断不被执行,会卡死整个系统*/ DisableUsartInterrupt(); //1.校验地址和合理性 if(flash_addr return 1; } flashstm.addr = flash_addr - STM32_FLASH_BASE_ADDR ; flashstm.secPos = flashstm.addr / FLASH_PAGE_SIZE; //第几个扇区 flashstm.secAddr = flashstm.secPos * FLASH_PAGE_SIZE + STM32_FLASH_BASE_ADDR; //扇区的地址 flashstm.secOff = flash_addr - flashstm.secAddr; //相对于N号扇区首地址的偏移值 //2.读出扇区的数据 ReadDataFromFlash(&flashstm.buf[0], flashstm.secAddr, FLASH_PAGE_SIZE); //3.擦除扇区的数据 HAL_FLASH_Unlock(); // printf("addr is: %XrnsecPos is %drnsecAddr is %Xrnflashstm.secOff is %Xrnrn", flashstm.addr,flashstm.secPos,flashstm.secAddr,flashstm.secOff); FlashErase(flashstm.secAddr, 1); //一擦就是1页 1页为1024bytes //4.插入数据到1K的buf中 for(u8 i=0; i } //5.计算写入前的 for(u16 j=0; j } //6.写入数据到扇区 FlashWrite(flashstm.secAddr, (void *)flashstm.buf, FLASH_PAGE_SIZE); //48byteS HAL_FLASH_Lock(); //7.读出写入的数据到buf中 ReadDataFromFlash(&flashstm.buf[0], flashstm.secAddr, FLASH_PAGE_SIZE); //8.计算读出的数据 for(u16 j=0; j } //9.校验 if(flashstm.chucksumold != flashstm.chucksumnew) { while (1) { /* Make LED2 blink (100ms on, 2s off) to indicate error in Erase operation */ LED_ON(); HAL_Delay(100); LED_OFF(); HAL_Delay(2000); } return 2; } // printf("flashstm.chucksumold is: %drnflashstm.chucksumnew is %drnrn",flashstm.chucksumold,flashstm.chucksumnew); /*10.打开所有串口中断*/ EnableUsartInterrupt(); return 0; } /******************************************************************* * @brief 必须为4的倍数 * @param PRO_INFOm: 校准参数结构体 * @retval None ********************************************************************/ void ReadDataFromFlash(void *buf, u32 flash_addr, u16 len) { u8 val = 0; u32 tmp = 0; for(int i = 0; i < len; i += 4) { val = len-i; switch(val) { // case 1: // *((char *)buf + i) = *((unsigned int *)(flash_addr + i)) & 0xff; // break; // case 2: // *((u16 *)(char *)buf + i) = (*((unsigned int *)(flash_addr + i)) & 0xffff); // break; // case 3: // tmp = (*((unsigned int *)(flash_addr + i)) & 0xffffff); // break; default : *((unsigned int *)((char *)buf + i)) = *((unsigned int *)(flash_addr + i)); break; } } } /******************************************************************* * @brief 从addr地址开始,连续擦除page_num页 * @param addr:要擦除页的首地址 @param page_num:页数 * @retval 1 ********************************************************************/ int FlashErase(unsigned int addr, unsigned int page_num) { int i; uint32_t PAGEError = 0; FLASH_EraseInitTypeDef EraseInitStruct; EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; //页擦除 EraseInitStruct.PageAddress = addr; EraseInitStruct.NbPages = page_num; //默认就是1页 if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) { while (1) { /* Make LED2 blink (100ms on, 2s off) to indicate error in Erase operation */ LED_ON(); HAL_Delay(100); LED_OFF(); HAL_Delay(2000); } } return 1; } /******************************************************************* * @brief 从addr地址开始,连续写入len个字节,len应当是4的倍数,利用HAL库 * @param addr:要写入的首地址 @param buf: 数据源的首地址 @param len: 写入的长度 * @retval 1 ********************************************************************/ //flashstm.secAddr, (void *)flashstm.buf, FLASH_PAGE_SIZE int FlashWrite(unsigned int addr, void *buf, unsigned int len) { for(int i = 0; i < len; i+=4) { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, *((unsigned int *)((char *)buf + i))) != HAL_OK) { while (1) { /* Make LED2 blink (100ms on, 2s off) to indicate error in Write operation */ LED_ON(); HAL_Delay(100); LED_OFF(); HAL_Delay(2000); } } } return 1; } ● flash.h #ifndef __FLASH_H #define __FLASH_H #include "system.h" #define STM32_FLASH_SIZE 32 //FLASH 容量为32K #define STM32_FLASH_BASE_ADDR 0x08000000 //STM32 FLASH起始地址 #define STM32_FLASH_END_ADDR (STM32_FLASH_BASE_ADDR + 1024*STM32_FLASH_SIZE) #define LAST_SEC_FLASH_ADDR STM32_FLASH_BASE_ADDR + 31*1024 #define USRE_FLASH_ADDR LAST_SEC_FLASH_ADDR typedef struct { u8 buf[1024]; //1024 2048 因SRAM不够只用512K u32 addr; //实际的偏移地址 u8 secPos; //第几个扇区 u32 secAddr; //扇区起始地址 u32 secOff; //偏移值 u8 chucksumold; u8 chucksumnew; }FLASH_T; u8 WriteDataToFlash(void *buf, u32 flash_addr, u16 len); void ReadDataFromFlash(void *buf, u32 flash_addr, u16 len); int FlashErase(unsigned int addr, unsigned int page_num); int FlashWrite(unsigned int addr, void *buf, unsigned int len); #endif //_FLASH_H_ 需要复杂点的可以直接移植正点原子的,它考虑了各种跨扇区的操作,但是阅读性没那么舒服~ |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
883 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
690 浏览 1 评论
420 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
332 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
745 浏览 2 评论
1476浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
148浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
190浏览 3评论
166浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
169浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-7-1 16:46 , Processed in 0.785143 second(s), Total 46, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191