☰
学习STM32掉电存储
2026/10/1 8:29:00 网站建设 项目流程

1.添加基础库

stm 官网下载地址:https://doc.embedfire.com/products/link/zh/latest/mcu/stm32/stm32f103c8t6_core_new1.html

项目参数配置:
Preprocessor Symbols Define 设置参数:

USE_STDPERIPH_DRIVER,USE_STM3210C_EVAL

#需要添加的文件:
mcu 配置:startup_stm32f10x_hd.s
core 配置:Libraries\CMSIS\CM3\CoreSupport 所有文件
inc配置:Libraries\STM32F10x_StdPeriph_Driver\inc 文件夹所有文件
src配置:Libraries\STM32F10x_StdPeriph_Driver\src 文件夹所有文件
eval 配置: Utilities\STM32_EVAL\STM3210C_EVAL 文件夹所有文件
fonts 配置:Utilities\STM32_EVAL\Common\fonts.h 文件
项目配置:Project\STM32F10x_StdPeriph_Examples\SysTick\TimeBase 文件夹下 【stm32f10x_conf.h,stm32f10x_it.c,stm32f10x_it.h,system_stm32f10x.c】
源码:
头文件内容

#ifndef__FLASH_SAFE_STORAGE_H#define__FLASH_SAFE_STORAGE_H#include"stm32f10x.h"#include"stm32f10x_flash.h"// 存储区域定义:使用STM32F103C8T6最后2个1KB页(程序大小需<62KB,避免覆盖)#defineFLASH_PAGE_SIZE1024U#defineSTORAGE_PAGE0_ADDR0x0800F800UL// 备份页0 62k 1k 0x08000400UL#defineSTORAGE_PAGE1_ADDR0x0800FC00UL// 备份页1 63k// 数据标识与状态定义#defineSTORAGE_MAGIC0x55AA55AAUL// 魔数:标识有效数据#defineSTATUS_EMPTY0xFFU// 页状态:空/未使用#defineSTATUS_VALID0xAAAAAAAAUL// 页状态:数据有效 32位读取逻辑完全匹配#defineSTATUS_INVALID0x00U// 页状态:数据已废弃// 错误码定义typedefenum{STORAGE_OK=0,STORAGE_ERROR_ERASE,// 擦除失败STORAGE_ERROR_WRITE,// 写入失败STORAGE_ERROR_VERIFY,// 校验失败STORAGE_ERROR_CRC,// CRC不匹配STORAGE_ERROR_NO_DATA// 无有效数据}StorageStatus_t;// 用户存储数据结构(可按需添加自己的参数)//__attribute__((packed)):告诉编译器不要进行任何填充(padding),使结构体占用最小的内存空间,但可能导致成员未对齐,从而引发访问异常或性能问题。typedef__attribute__((aligned(4)))struct{//告诉编译器至少按 4 字节边界对齐结构体的起始地址,可能会在结构体末尾添加填充字节以满足对齐要求,但能保证访问的安全性和效率。uint32_tstatus;// 状态标记,必须放在最开头,对应页首地址uint32_tmagic;// 魔数:校验数据合法性uint32_tversion;// 版本号:每次写入自增,用于双页选新/* ---------- 以下为用户自定义参数区 ---------- */floatpid_kp;// PID参数示例floatpid_ki;floatpid_kd;uint16_tdevice_addr;// 设备地址示例uint8_trun_mode;// 运行模式示例/* ---------- 用户参数区结束 ---------- */uint32_tcrc32;// CRC32校验值(必须放在最后)}StorageData_t;// 安全读取数据:自动校验、回滚,无有效数据时加载默认值StorageStatus_tFlash_SafeRead(StorageData_t*out_data);// 安全写入数据:双备份、原子提交、掉电不丢StorageStatus_tFlash_SafeWrite(StorageData_t*in_data);// 恢复默认参数voidFlash_LoadDefault(StorageData_t*data);#endif

函数实现内容:

#include"flash_safe_storage.h"#include"stm32f10x_flash.h"#include"stm32f10x_crc.h"#include<string.h>// 内部函数声明staticStorageStatus_tFlash_WriteData(uint32_taddr,StorageData_t*data);staticvoidCRC_Reset(void);/** * @brief 复位硬件CRC外设,修正STM32全局累加器不会自动清零的问题 */staticvoidCRC_Reset(void){// 先开CRC时钟(兼容不同版本标准库)RCC->AHBENR|=0x40;CRC_ResetDR();}/** * @brief 计算CRC32(排除末尾crc32字段,修复之前长度计算包含状态位的错误) */staticuint32_tCalculate_CRC32(StorageData_t*data){// 跳过开头4字节状态位,排除末尾4字节crc32字段,计算中间有效数据的长度uint32_tcrc_len=(sizeof(StorageData_t)-4-4)/4;uint32_t*p_buf=(uint32_t*)((uint8_t*)data+4);CRC_Reset();returnCRC_CalcBlockCRC(p_buf,crc_len);}/** * @brief 检查页是否有效:状态标记+魔数+CRC三重校验 */staticuint8_tIsPageValid(uint32_tpage_addr,StorageData_t*out_data){// 状态位现在是4字节,不再是单字节,避免对齐错位uint32_tstatus=*(volatileuint32_t*)page_addr;if(status!=STATUS_VALID){return0;}memcpy(out_data,(void*)page_addr,sizeof(StorageData_t));if(out_data->magic!=STORAGE_MAGIC){return0;}uint32_tcalc_crc=Calculate_CRC32(out_data);if(calc_crc!=out_data->crc32){return0;}return1;}/** * @brief 加载默认参数 */voidFlash_LoadDefault(StorageData_t*data){memset(data,0,sizeof(StorageData_t));data->status=0xFFFFFFFF;// 空白状态data->magic=STORAGE_MAGIC;data->version=1;data->pid_kp=2.0f;data->pid_ki=0.5f;data->pid_kd=0.1f;data->device_addr=0x01;data->run_mode=0;data->crc32=Calculate_CRC32(data);}/** * @brief 安全读取存储数据 */StorageStatus_tFlash_SafeRead(StorageData_t*out_data){StorageData_t page0_data,page1_data;uint8_tpage0_valid=IsPageValid(STORAGE_PAGE0_ADDR,&page0_data);uint8_tpage1_valid=IsPageValid(STORAGE_PAGE1_ADDR,&page1_data);if(!page0_valid&&!page1_valid){Flash_LoadDefault(out_data);returnSTORAGE_ERROR_NO_DATA;}if(page0_valid&&!page1_valid){*out_data=page0_data;returnSTORAGE_OK;}if(!page0_valid&&page1_valid){*out_data=page1_data;returnSTORAGE_OK;}*out_data=(page0_data.version>=page1_data.version)?page0_data:page1_data;returnSTORAGE_OK;}/** * @brief 按4字节对齐写入数据:现在从偏移4开始,状态位留到最后单独写 */staticStorageStatus_tFlash_WriteData(uint32_taddr,StorageData_t*data){uint32_t*p_data=(uint32_t*)data;// 直接从第2个字(偏移4)开始写,跳过第1个字的状态位,完全避免对齐错位for(uint32_ti=1;i<sizeof(StorageData_t)/4;i++){if(FLASH_ProgramWord(addr+i*4,p_data[i])!=FLASH_COMPLETE){returnSTORAGE_ERROR_WRITE;}}returnSTORAGE_OK;}/** * @brief 擦除指定Flash页:只擦一次,增加回读校验 */staticStorageStatus_tFlash_ErasePage(uint32_tpage_addr){FLASH_Status status=FLASH_ErasePage(page_addr);if(status!=FLASH_COMPLETE){returnSTORAGE_ERROR_ERASE;}// 回读确认整页已全部擦除为0xFFFFFFFFfor(uint32_ti=0;i<sizeof(StorageData_t);i+=4){if(*(volatileuint32_t*)(page_addr+i)!=0xFFFFFFFFUL){returnSTORAGE_ERROR_ERASE;}}returnSTORAGE_OK;}/** * @brief 安全写入存储数据 */StorageStatus_tFlash_SafeWrite(StorageData_t*in_data){StorageData_t old_data;uint32_told_page_addr,new_page_addr;StorageStatus_t status;Flash_SafeRead(&old_data);if(IsPageValid(STORAGE_PAGE0_ADDR,&old_data)){old_page_addr=STORAGE_PAGE0_ADDR;new_page_addr=STORAGE_PAGE1_ADDR;}else{old_page_addr=STORAGE_PAGE1_ADDR;new_page_addr=STORAGE_PAGE0_ADDR;}in_data->magic=STORAGE_MAGIC;in_data->version=old_data.version+1;in_data->crc32=Calculate_CRC32(in_data);__disable_irq();FLASH_Unlock();FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);status=Flash_ErasePage(new_page_addr);if(status!=STORAGE_OK)gotoERROR_EXIT;status=Flash_WriteData(new_page_addr,in_data);if(status!=STORAGE_OK)gotoERROR_EXIT;// 原子提交:正确写入有效状态标记,不再把地址当数据写if(FLASH_ProgramWord(new_page_addr,STATUS_VALID)!=FLASH_COMPLETE){status=STORAGE_ERROR_WRITE;gotoERROR_EXIT;}StorageData_t verify_data;if(IsPageValid(new_page_addr,&verify_data)==0||memcmp((uint8_t*)in_data+4,(uint8_t*)&verify_data+4,sizeof(StorageData_t)-4)!=0){status=STORAGE_ERROR_VERIFY;gotoERROR_EXIT;}// 正确标记旧页为无效,写入0x00000000FLASH_ProgramWord(old_page_addr,0x00000000);ERROR_EXIT:FLASH_Lock();__enable_irq();returnstatus;}

主工程入口部分:

#include"stm32f10x.h"#include"stm32f10x_spi.h"#include"bsp_usart.h"#include<stdio.h>#include<string.h>#include"../flash/flash_safe_storage.h"/* Private typedef -----------------------------------------------------------*//* spi 接口定义开头 -----------------------------------------------------------*/#defineSPI_FLASH_CS_LOW()GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN)#defineSPI_FLASH_CS_HIGH()GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN)#defineSOFT_DELAYDelay(0x0FFFFF)/* spi 接口定义结尾 -----------------------------------------------------------*//* Private function prototypes -----------------------------------------------*/voidDelay(__IOuint32_tnCount);// 延时函数声明/** * @brief 简单延时函数 * @param nCount: 延时计数值 * @retval None */voidDelay(__IOuint32_tnCount){for(;nCount!=0;nCount--);}/*======================= 主函数 =======================*/intmain(void){USART_Config();StorageData_t data;Flash_SafeRead(&data);printf("\r\n Before Write kd: %f, version: %d\r\n",data.pid_kd,data.version);data.pid_kd+=0.2F;StorageStatus_t res=Flash_SafeWrite(&data);printf("\r\n Write Status: %d\r\n",res);// 打印返回值,0就是成功,其他值对应不同错误Flash_SafeRead(&data);printf("\r\n After Read kd: %f, version: %d\r\n",data.pid_kd,data.version);while(1){}}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询