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.score 配置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.hstm32f10x_it.c,stm32f10x_it.h,system_stm32f10x.c】源码头文件内容#ifndef__FLASH_SAFE_STORAGE_H#define__FLASH_SAFE_STORAGE_H#includestm32f10x.h#includestm32f10x_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_OK0,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函数实现内容#includeflash_safe_storage.h#includestm32f10x_flash.h#includestm32f10x_crc.h#includestring.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*)data4);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_crcCalculate_CRC32(out_data);if(calc_crc!out_data-crc32){return0;}return1;}/** * brief 加载默认参数 */voidFlash_LoadDefault(StorageData_t*data){memset(data,0,sizeof(StorageData_t));data-status0xFFFFFFFF;// 空白状态data-magicSTORAGE_MAGIC;data-version1;data-pid_kp2.0f;data-pid_ki0.5f;data-pid_kd0.1f;data-device_addr0x01;data-run_mode0;data-crc32Calculate_CRC32(data);}/** * brief 安全读取存储数据 */StorageStatus_tFlash_SafeRead(StorageData_t*out_data){StorageData_t page0_data,page1_data;uint8_tpage0_validIsPageValid(STORAGE_PAGE0_ADDR,page0_data);uint8_tpage1_validIsPageValid(STORAGE_PAGE1_ADDR,page1_data);if(!page0_valid!page1_valid){Flash_LoadDefault(out_data);returnSTORAGE_ERROR_NO_DATA;}if(page0_valid!page1_valid){*out_datapage0_data;returnSTORAGE_OK;}if(!page0_validpage1_valid){*out_datapage1_data;returnSTORAGE_OK;}*out_data(page0_data.versionpage1_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_ti1;isizeof(StorageData_t)/4;i){if(FLASH_ProgramWord(addri*4,p_data[i])!FLASH_COMPLETE){returnSTORAGE_ERROR_WRITE;}}returnSTORAGE_OK;}/** * brief 擦除指定Flash页只擦一次增加回读校验 */staticStorageStatus_tFlash_ErasePage(uint32_tpage_addr){FLASH_Status statusFLASH_ErasePage(page_addr);if(status!FLASH_COMPLETE){returnSTORAGE_ERROR_ERASE;}// 回读确认整页已全部擦除为0xFFFFFFFFfor(uint32_ti0;isizeof(StorageData_t);i4){if(*(volatileuint32_t*)(page_addri)!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_addrSTORAGE_PAGE0_ADDR;new_page_addrSTORAGE_PAGE1_ADDR;}else{old_page_addrSTORAGE_PAGE1_ADDR;new_page_addrSTORAGE_PAGE0_ADDR;}in_data-magicSTORAGE_MAGIC;in_data-versionold_data.version1;in_data-crc32Calculate_CRC32(in_data);__disable_irq();FLASH_Unlock();FLASH_ClearFlag(FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR);statusFlash_ErasePage(new_page_addr);if(status!STORAGE_OK)gotoERROR_EXIT;statusFlash_WriteData(new_page_addr,in_data);if(status!STORAGE_OK)gotoERROR_EXIT;// 原子提交正确写入有效状态标记不再把地址当数据写if(FLASH_ProgramWord(new_page_addr,STATUS_VALID)!FLASH_COMPLETE){statusSTORAGE_ERROR_WRITE;gotoERROR_EXIT;}StorageData_t verify_data;if(IsPageValid(new_page_addr,verify_data)0||memcmp((uint8_t*)in_data4,(uint8_t*)verify_data4,sizeof(StorageData_t)-4)!0){statusSTORAGE_ERROR_VERIFY;gotoERROR_EXIT;}// 正确标记旧页为无效写入0x00000000FLASH_ProgramWord(old_page_addr,0x00000000);ERROR_EXIT:FLASH_Lock();__enable_irq();returnstatus;}主工程入口部分#includestm32f10x.h#includestm32f10x_spi.h#includebsp_usart.h#includestdio.h#includestring.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_kd0.2F;StorageStatus_t resFlash_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){}}