洋具软件下载官方版:开启智能生活新篇章

核心内容摘要

《吴梦梦到粉丝家》:一场跨越屏幕的真情互动,点燃粉丝心中的不灭星火
《全网心碎又治愈:当那个“困困”的他,撞进女孩的温柔梦乡》

XL18vsXL20耐用性大比拼

目录

stm32f1xx_hal_flash_ex 库基础认知必懂前置

库文件组成与存放位置

与 HAL_FLASH 主库的关系核心区分

前置硬件基础F1 系列 FLASH 擦除特性

stm32f1xx_hal_flash_ex 库核心内容详解初学者必掌握

核心宏定义擦除 / 选项字节专用

核心结构体擦除初始化结构体

核心函数初学者必背按使用频率排序1FLASH 页擦除函数核心中的核心100% 场景用到2选项字节编程函数偶尔用项目中做写保护 / 校准3选项字节获取配置函数配合上一个函数使用

与主库函数的配合使用流程完整 FLASH 操作

CubeMX 基础配置初学者零配置上手

stm32f1xx_hal_flash_ex 库典型应用案例完整可运行案例 1基础单页擦除扩展库核心 字写入 读取步骤 1编写通用封装函数提高复用性初学者直接调用步骤 2主函数中测试按固定流程调用案例 1 测试结果串口助手波特率 115200案例 2实用多页擦除扩展库 结构体写入项目常用步骤 1定义设备参数结构体模拟项目实际场景步骤 2编写多页擦除 结构体读写封装函数步骤 3主函数中测试多页擦除 结构体读写案例 2 测试结果串口助手

初学者使用扩展库的常见坑避坑指南

六、

总结核心要点回顾详细理解 HAL_FLASH 扩展库stm32f1xx_hal_flash_ex并掌握其核心应用案例。

首先明确核心定位stm32f1xx_hal_flash_ex 是 HAL_FLASH 主库的专属扩展库STM32F1xx 系列的 FLASH 擦除、选项字节操作等核心功能均封装在此是操作 F1 系列 FLASH 的「必用库」主库仅实现解锁、加锁、写入无擦除功能。

对于初学者来说这个扩展库的

核心价值是提供 FLASH 擦除函数F1 系列最常用次要价值是提供选项字节的读写操作项目中偶尔用到。

下面从库文件基础、核心内容详解、CubeMX 配置、典型可运行案例逐步讲解全程基于初学者视角避开复杂源码聚焦 **“怎么引入、怎么配置、怎么调用”**案例以最常用的STM32F103C8T6中容量128KB FLASH为例代码可直接复制使用。

stm32f1xx_hal_flash_ex 库基础认知必懂前置

库文件组成与存放位置该扩展库由2 个文件组成和 HAL_FLASH 主库同目录位于 STM32CubeF1 库的Drivers/STM32F1xx_HAL_Driver下文件名称作用stm32f1xx_hal_flash_ex.h扩展头文件声明FLASH 擦除函数、选项字节操作函数定义相关宏 / 结构体stm32f1xx_hal_flash_ex.c扩展源文件实现上述所有函数的底层逻辑封装硬件寄存器操作初学者核心要求只要调用 FLASH 擦除 / 选项字节功能必须在代码中引入头文件否则编译直接报错#include stm32f1xx_hal_flash_ex.h // 扩展库头文件擦除函数必备

与 HAL_FLASH 主库的关系核心区分很多初学者会混淆主库和扩展库的职责简单说F1 系列 FLASH 操作的 “完整流程” 必须由「主库 扩展库」配合完成二者分工明确缺一不可功能模块所属库核心函数示例FLASH 解锁 / 加锁HAL_FLASH 主库HAL_FLASH_Unlock()/Lock()FLASH 写入编程HAL_FLASH 主库HAL_FLASH_Program()FLASH 擦除HAL_FLASH_EX 扩展库HAL_FLASHEx_Erase()选项字节读写HAL_FLASH_EX 扩展库HAL_FLASHEx_OBProgram()FLASH 状态 / 错误HAL_FLASH 主库HAL_FLASH_GetState()/Error()

前置硬件基础F1 系列 FLASH 擦除特性扩展库的核心是擦除功能而 F1 系列 FLASH 的擦除有硬件强制规则不懂硬件直接用库必踩坑初学者记住 3 个关键仅支持「页擦除」F1xx 无扇区擦除F4/F7 才有擦除只能按 “页” 为单位无法按字节 / 半字 / 字擦页大小固定中容量F103C8T6每页 1KB大容量F103ZE每页 2KB小容量每页 1KB擦除地址要求擦除的起始地址必须是某一页的首地址如 F103C8T6 最后一页首地址0x0801F000不能填页内任意地址。

F103C8T6 关键地址初学者直接用FLASH 起始地址0x08000000总容量 128KB共 128 页1KB / 页最后 1 页首地址0x0801F000最后 1 页尾地址0x0801FFFF推荐作为数据存储区避免覆盖程序倒数第 2 页首地址0x0801E000多页存储时用。

stm32f1xx_hal_flash_ex 库核心内容详解初学者必掌握扩展库内容不多80% 的开发场景只用到「擦除相关的 1 个结构体 1 个核心函数」剩下 20% 是选项字节操作偶尔用。

下面挑最核心、最常用的内容讲解摒弃无用的高级功能聚焦实战。

核心宏定义擦除 / 选项字节专用定义在stm32f1xx_hal_flash_ex.h中初学者记死擦除相关的 2 个宏即可选项字节宏暂时了解// ********************* 擦除类型宏F1系列仅支持这1个********************* #define FLASH_TYPEERASE_PAGES 0x00U // 页擦除唯一可用必须填这个 // ********************* 选项字节保护级别宏了解即可********************* #define OB_WRP_LEVEL0 0x00U // 无写保护 #define OB_WRP_LEVEL1 0x01U // 对FLASH前半部分写保护 #define OB_WRP_LEVEL2 0x02U // 对FLASH后半部分写保护 #define OB_WRP_LEVEL3 0x03U // 对整个FLASH写保护重点F1 系列擦除时擦除类型只能填FLASH_TYPEERASE_PAGES填其他值直接擦除失败。

核心结构体擦除初始化结构体这是调用擦除函数的必备配置结构体定义在stm32f1xx_hal_flash_ex.h中初学者只需配置3 个成员所有成员均为uint32_t类型typedef struct { uint32_t TypeErase; // 擦除类型F1系列固定填 FLASH_TYPEERASE_PAGES uint32_t PageAddress; // 擦除的「起始页首地址」关键必须是页首地址如0x0801F000 uint32_t NbPages; // 要擦除的页数1擦1页2擦2页依此类推不能越界 } FLASH_EraseInitTypeDef;结构体配置示例F103C8T6 擦除最后 1 页FLASH_EraseInitTypeDef flash_erase_init; // 定义擦除结构体 flash_erase_init.TypeErase FLASH_TYPEERASE_PAGES; // 固定值 flash_erase_init.PageAddress 0x0801F000U; // 最后1页首地址 flash_erase_init.NbPages 1; // 擦除1页结构体配置示例擦除最后 2 页0x0801E000 0x0801F000flash_erase_init.TypeErase FLASH_TYPEERASE_PAGES; flash_erase_init.PageAddress 0x0801E000U; // 倒数第2页首地址起始页 flash_erase_init.NbPages 2; // 连续擦除2页关键避坑PageAddress必须是起始页的首地址NbPages是从该页开始的连续页数不能超过 FLASH 剩余页数如 F103C8T6 从 0x0801E000 开始最多擦 2 页。

核心函数初学者必背按使用频率排序扩展库的函数均以HAL_FLASHEx_为前缀返回值多为HAL_StatusTypeDefHAL_OK 成功HAL_ERROR/HAL_BUSY 失败下面讲解3 个核心函数擦除函数必用选项字节函数了解 / 备用。

1FLASH 页擦除函数核心中的核心100% 场景用到这是扩展库最常用的函数用于擦除指定的 1 页 / 多页 FLASH必须配合上述擦除结构体使用/** * brief STM32F1xx FLASH页擦除核心函数 * param pEraseInit: 擦除初始化结构体指针配置擦除类型、起始页、页数 * param PageError: 输出参数uint32_t类型擦除失败时返回错误页的首地址擦除成功时返回0xFFFFFFFF * retval HAL_StatusTypeDef: HAL_OK擦除成功其他失败 */ HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);使用关键调用前必须先调用HAL_FLASH_Unlock()解锁 FLASH主库函数否则擦除失败PageError是输出参数必须定义一个uint32_t变量传入即使不用也不能传 NULL擦除成功后FLASH 对应页的所有位会被置 1FLASH 硬件特性擦除后为 1写入是将 1 改为 0。

简单使用示例擦除 F103C8T6 最后 1 页FLASH_EraseInitTypeDef erase_init; uint32_t page_error 0; // 输出参数定义后传入 //

配置擦除结构体 erase_init.TypeErase FLASH_TYPEERASE_PAGES; erase_init.PageAddress 0x0801F000U; erase_init.NbPages 1; //

调用擦除函数前提已解锁FLASH HAL_StatusTypeDef status HAL_FLASHEx_Erase(erase_init, page_error); //

判断结果 if(status HAL_OK) { // 擦除成功 } else { // 擦除失败page_error为错误页首地址 }2选项字节编程函数偶尔用项目中做写保护 / 校准选项字节OB是 STM32FLASH 的特殊区域用于配置 FLASH 写保护、时钟校准、复位条件等编程写入选项字节的函数在扩展库中初学者暂时了解项目需要时再深入/** * brief 选项字节编程写入函数 * param pOBInit: 选项字节初始化结构体指针配置保护级别、时钟校准等 * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef HAL_FLASHEx_OBProgram(OB_InitTypeDef *pOBInit);适用场景项目中需要对 FLASH 某部分做写保护防止误擦写、校准内部时钟等普通数据存储场景用不到。

3选项字节获取配置函数配合上一个函数使用用于读取当前选项字节的配置状态如当前写保护级别、时钟校准值作为选项字节编程的前置操作/** * brief 获取当前选项字节的配置信息 * param pOBInit: 选项字节初始化结构体指针用于存储读取到的配置 * retval None */ void HAL_FLASHEx_OBGetConfig(OB_InitTypeDef *pOBInit);

与主库函数的配合使用流程完整 FLASH 操作扩展库无法单独使用必须和 HAL_FLASH 主库配合才能完成 FLASH擦除 写入的完整流程读操作无专门函数直接地址访问。

初学者记死这个固定流程能避免 90% 的错误

关闭全局中断可选但推荐__disable_irq(); 防止中断打断FLASH硬件操作

FLASH解锁主库HAL_FLASH_Unlock(); 操作前必须解锁硬件保护

配置擦除结构体扩展库定义FLASH_EraseInitTypeDef并配置3个成员

擦除FLASH扩展库核心HAL_FLASHEx_Erase(); 写入前必须擦除核心步骤

写入数据主库HAL_FLASH_Program(); 按字节/半字/字写入

FLASH加锁主库HAL_FLASH_Lock(); 操作后必须加锁恢复保护

开启全局中断可选__enable_irq(); 和步骤1对应

验证结果可选读取FLASH地址对比写入数据是否一致核心逻辑扩展库负责擦除主库负责解锁 / 加锁 / 写入二者缺一不可。

CubeMX 基础配置初学者零配置上手所有案例均基于STM32F103C8T6先完成 CubeMX 基础配置生成的工程会自动包含 HAL_FLASH 主库和扩展库无需手动添加文件 / 配置路径初学者按步骤操作即可打开 CubeMX选择芯片STM32F103C8T6配置RCCRCC → HSE → 选择Crystal/Ceramic Resonator外部 8MHz 晶振配置系统时钟Clock Configuration → 配置为 72MHzHSE8MHzPLL 倍频 9AHB72MHz配置USART1Connectivity → USART1 → Mode →Asynchronous异步串口波特率默认 115200 8N1用于打印调试信息配置SYSSYS → Debug → 选择Serial Wire若用仿真器无仿真器则默认生成工程Project → Generate Code → 选择MDK-ARM (V

→ 勾选Generate peripheral initialization as .c/.h files→ 生成工程。

生成后工程的优势自动包含stm32f1xx_hal_flash.c/stm32f1xx_hal_flash_ex.c源文件自动添加库头文件路径直接引入#include stm32f1xx_hal_flash_ex.h即可使用自动生成串口、时钟初始化代码无需手动编写。

stm32f1xx_hal_flash_ex 库典型应用案例完整可运行基于上述 CubeMX 配置编写 2 个最常用的实战案例均包含扩展库擦除函数的核心使用代码加详细注释初学者可直接复制到生成的 Keil 工程中使用案例 1 为基础单页擦除 写入 读取掌握核心流程案例 2 为实用多页擦除 结构体写入贴近实际项目保存设备参数。

前置准备在生成的工程main.c中先完成串口 printf 重定向用于打印调试信息并定义 FLASH 存储地址宏避免硬编码//

引入必要头文件 #include stm32f1xx_hal_flash.h // 主库头文件 #include stm32f1xx_hal_flash_ex.h // 扩展库头文件核心擦除必备 #include stdio.h // 串口打印 #include string.h // 内存比较案例2用 //

宏定义FLASH数据存储地址F103C8T6最后1页/2页避免覆盖程序 #define FLASH_USER_PAGE1_ADDR 0x0801F000U // 最后1页首地址1KB #define FLASH_USER_PAGE2_ADDR 0x0801E000U // 倒数第2页首地址1KB #define FLASH_USER_END_ADDR 0x0801FFFFU // FLASH尾地址 //

串口printf重定向USART1Keil中需开启微库Use MicroLIB int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }Keil 微库开启魔术棒 → Target → 勾选Use MicroLIB否则 printf 无法使用。

案例 1基础单页擦除扩展库核心 字写入 读取功能使用扩展库HAL_FLASHEx_Erase擦除 F103C8T6 最后 1 页通过主库写入 32 位字数据读取并串口打印验证核心练扩展库擦除函数的使用是所有 FLASH 操作的基础。

步骤 1编写通用封装函数提高复用性初学者直接调用在main.c中编写FLASH 擦除、写入、读取的封装函数函数封装了扩展库和主库的配合逻辑注释详细/** * brief 封装擦除FLASH指定1页扩展库核心函数调用 * param page_addr: 要擦除的页首地址如FLASH_USER_PAGE1_ADDR * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Erase_SinglePage(uint32_t page_addr) { FLASH_EraseInitTypeDef erase_init; uint32_t page_error 0; // 扩展库擦除函数的输出参数必须定义 // 检查地址是否为有效页首地址简单校验F103C8T60x08000000~0x0801F000步长1024 if((page_addr 0x08000000U) || (page_addr 0x0801F000U) || (page_addr % 1024 !

) { return HAL_ERROR; } // 配置擦除结构体扩展库必备 erase_init.TypeErase FLASH_TYPEERASE_PAGES; // F1固定值 erase_init.PageAddress page_addr; // 要擦除的页首地址 erase_init.NbPages 1; // 擦除1页 // 调用扩展库核心擦除函数 return HAL_FLASHEx_Erase(erase_init, page_error); } /** * brief 封装FLASH写入32位字数据主库函数 * param addr: 写入地址FLASH有效地址4字节对齐 * param data: 要写入的32位数据 * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Write_Word(uint32_t addr, uint32_t data) { if(addr FLASH_USER_PAGE1_ADDR || addr FLASH_USER_END_ADDR) { return HAL_ERROR; } // 主库写入函数字写入 return HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, data); } /** * brief 封装FLASH读取32位字数据无库函数直接地址访问 * param addr: 读取地址FLASH有效地址4字节对齐 * retval uint32_t: 读取到的32位数据 */ uint32_t FLASH_Read_Word(uint32_t addr) { if(addr FLASH_USER_PAGE1_ADDR || addr FLASH_USER_END_ADDR) { return 0; } // FLASH读操作直接地址解引用 return *(uint32_t *)addr; }步骤 2主函数中测试按固定流程调用在main.c的main()函数中按 **“关中断→解锁→擦除→写入→加锁→开中断→验证”** 的流程编写测试代码所有步骤均有注释int main(void) { // CubeMX自动生成的初始化代码无需修改 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 定义测试变量 HAL_StatusTypeDef flash_status; // FLASH操作状态 uint32_t write_data 0xAA55CC33U; // 要写入的32位测试数据 uint32_t read_data; // 读取到的数据 uint32_t write_addr FLASH_USER_PAGE1_ADDR; // 写入地址最后1页首地址 printf( STM32F1xx HAL_FLASH_EX库基础案例 \r\n); printf(要写入数据0x%08X写入地址0x%08X\r\n, write_data, write_addr); // ********************* 核心FLASH擦除写入流程 ********************* __disable_irq(); // 关闭全局中断防止打断FLASH操作 flash_status HAL_FLASH_Unlock(); //

主库解锁FLASH前提 if(flash_status HAL_OK) { printf(FLASH解锁成功\r\n); //

扩展库核心擦除指定页最后1页 flash_status FLASH_Erase_SinglePage(write_addr); if(flash_status HAL_OK) { printf(FLASH擦除成功扩展库HAL_FLASHEx_Erase\r\n); //

主库写入32位字数据 flash_status FLASH_Write_Word(write_addr, write_data); if(flash_status HAL_OK) { printf(FLASH写入成功\r\n); } else { printf(FLASH写入失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } } else { printf(FLASH擦除失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } HAL_FLASH_Lock(); //

主库加锁FLASH无论成败必须加锁 printf(FLASH已加锁\r\n); } else { printf(FLASH解锁失败\r\n); } __enable_irq(); // 开启全局中断 // ********************* 验证读取FLASH数据 ********************* read_data FLASH_Read_Word(write_addr); printf(FLASH读取数据0x%08X读取地址0x%08X\r\n, read_data, write_addr); // 对比数据是否一致 if(read_data write_data) { printf( 测试成功数据一致 \r\n); } else { printf( 测试失败数据不一致 \r\n); } // 死循环 while (

{ } }案例 1 测试结果串口助手波特率 115200编译下载后打开串口助手可看到如下打印断电后重新上电仍能读取到 0xAA55CC33说明 FLASH 擦除 写入成功 STM32F1xx HAL_FLASH_EX库基础案例 要写入数据0xAA55CC33写入地址0x0801F000 FLASH解锁成功 FLASH擦除成功扩展库HAL_FLASHEx_Erase FLASH写入成功 FLASH已加锁 FLASH读取数据0xAA55CC33读取地址0x0801F000 测试成功数据一致 案例 2实用多页擦除扩展库 结构体写入项目常用功能使用扩展库HAL_FLASHEx_Erase擦除 F103C8T6 最后 2 页将设备参数结构体含设备 ID、校准值、工作模式等批量写入 FLASH读取并验证贴近实际项目需求项目中常将断电不丢失的参数封装为结构体存储同时掌握多页擦除的配置方法。

步骤 1定义设备参数结构体模拟项目实际场景在main.c中定义一个包含多种数据类型的结构体模拟项目中的设备配置参数// 定义设备参数结构体模拟项目传感器校准、设备ID、工作模式 typedef struct { uint32_t device_id; // 设备ID32位 float temp_cal; // 温度传感器校准值浮点型 uint16_t baud; // 串口波特率16位 uint8_t work_mode; // 工作模式0-待机1-工作2-校准8位 uint8_t reserve; // 预留字节保证结构体大小4字节对齐可选 } Device_Param_t; // 定义写入的结构体变量初始化默认参数 Device_Param_t dev_param_write { .device_id 0xSTM32C8, .temp_cal

2

8f, .baud 115200, .work_mode 1, .reserve 0 }; // 定义读取的结构体变量用于存储从FLASH读取的数据 Device_Param_t dev_param_read; // 结构体大小字节 #define PARAM_STRUCT_SIZE sizeof(Device_Param_t)步骤 2编写多页擦除 结构体读写封装函数扩展库的HAL_FLASHEx_Erase支持连续多页擦除只需修改擦除结构体的NbPages和PageAddress即可。

编写封装函数实现多页擦除和结构体批量读写/** * brief 封装扩展库多页擦除支持连续擦除N页 * param start_page_addr: 起始页首地址如FLASH_USER_PAGE2_ADDR * param page_num: 要擦除的页数如2 * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Erase_MultiPage(uint32_t start_page_addr, uint32_t page_num) { FLASH_EraseInitTypeDef erase_init; uint32_t page_error 0; // 简单校验地址和页数F103C8T6最多128页1KB/页 if(start_page_addr 0x08000000U || start_page_addr % 1024 ! 0 || page_num

{ return HAL_ERROR; } if((start_page_addr (page_num -

*

0x0801F000U) { return HAL_ERROR; // 超出FLASH范围 } // 配置扩展库擦除结构体多页核心NbPagespage_num erase_init.TypeErase FLASH_TYPEERASE_PAGES; erase_init.PageAddress start_page_addr; // 起始页首地址 erase_init.NbPages page_num; // 连续擦除页数 // 调用扩展库核心擦除函数 return HAL_FLASHEx_Erase(erase_init, page_error); } /** * brief 封装FLASH写入结构体数据按32位字批量写入效率最高 * param start_addr: 写入起始地址FLASH有效地址4字节对齐 * param p_struct: 结构体指针 * param struct_size: 结构体大小sizeof(结构体) * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Write_Struct(uint32_t start_addr, void *p_struct, uint32_t struct_size) { HAL_StatusTypeDef status HAL_OK; uint32_t *p_word (uint32_t *)p_struct; // 转为字指针按32位遍历 uint32_t word_num (struct_size

/ 4; // 计算结构体占多少个字向上取整 uint32_t curr_addr start_addr; // 当前写入地址 // 地址范围校验 if(curr_addr struct_size FLASH_USER_END_ADDR) { return HAL_ERROR; } // 逐字写入主库函数 for(uint32_t i0; iword_num; i) { status HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, curr_addr, *p_word); if(status ! HAL_OK) { return status; // 某一字写入失败直接返回 } curr_addr 4; // 地址432位字对齐 p_word; // 指针后移 } return status; } /** * brief 封装FLASH读取结构体数据按32位字批量读取 * param start_addr: 读取起始地址FLASH有效地址4字节对齐 * param p_struct: 结构体指针存储读取的数据 * param struct_size: 结构体大小sizeof(结构体) * retval None */ void FLASH_Read_Struct(uint32_t start_addr, void *p_struct, uint32_t struct_size) { uint32_t *p_word (uint32_t *)p_struct; uint32_t word_num (struct_size

/ 4; uint32_t curr_addr start_addr; // 地址范围校验 if(curr_addr struct_size FLASH_USER_END_ADDR) { return; } // 逐字读取直接地址访问 for(uint32_t i0; iword_num; i) { *p_word *(uint32_t *)curr_addr; curr_addr 4; p_word; } }步骤 3主函数中测试多页擦除 结构体读写按固定流程擦除最后 2 页FLASH_USER_PAGE2_ADDR FLASH_USER_PAGE1_ADDR写入结构体读取并串口打印验证int main(void) { // CubeMX自动生成的初始化代码 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 定义测试变量 HAL_StatusTypeDef flash_status; uint32_t write_start_addr FLASH_USER_PAGE1_ADDR; // 结构体写入起始地址 // 打印要写入的结构体参数 printf( STM32F1xx HAL_FLASH_EX库实用案例多页擦除结构体\r\n); printf(要写入的设备参数\r\n); printf(设备ID0x%08X\r\n, dev_param_write.device_id); printf(温度校准值%.1f℃\r\n, dev_param_write.temp_cal); printf(串口波特率%d\r\n, dev_param_write.baud); printf(工作模式%d\r\n, dev_param_write.work_mode); printf(结构体大小%d字节\r\n, PARAM_STRUCT_SIZE); // ********************* 核心多页擦除结构体写入流程 ********************* __disable_irq(); flash_status HAL_FLASH_Unlock(); // 解锁FLASH if(flash_status HAL_OK) { printf(\r\nFLASH解锁成功\r\n); // 扩展库核心多页擦除擦除最后2页起始页FLASH_USER_PAGE2_ADDR页数2 flash_status FLASH_Erase_MultiPage(FLASH_USER_PAGE2_ADDR,

; if(flash_status HAL_OK) { printf(FLASH多页擦除成功扩展库HAL_FLASHEx_Erase\r\n); // 写入结构体数据 flash_status FLASH_Write_Struct(write_start_addr, dev_param_write, PARAM_STRUCT_SIZE); if(flash_status HAL_OK) { printf(FLASH结构体写入成功\r\n); } else { printf(FLASH结构体写入失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } } else { printf(FLASH多页擦除失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } HAL_FLASH_Lock(); // 加锁FLASH printf(FLASH已加锁\r\n); } else { printf(FLASH解锁失败\r\n); } __enable_irq(); // ********************* 验证读取结构体并打印 ********************* FLASH_Read_Struct(write_start_addr, dev_param_read, PARAM_STRUCT_SIZE); printf(\r\n从FLASH读取的设备参数\r\n); printf(设备ID0x%08X\r\n, dev_param_read.device_id); printf(温度校准值%.1f℃\r\n, dev_param_read.temp_cal); printf(串口波特率%d\r\n, dev_param_read.baud); printf(工作模式%d\r\n, dev_param_read.work_mode); // 对比结构体是否一致memcmp内存比较返回0则一致 if(memcmp(dev_param_write, dev_param_read, PARAM_STRUCT_SIZE)

{ printf(\r\n 测试成功结构体数据一致 \r\n); } else { printf(\r\n 测试失败结构体数据不一致 \r\n); } // 死循环 while (

{ } }案例 2 测试结果串口助手编译下载后串口打印如下断电后重新上电仍能读取到完整的结构体参数实现了项目中设备参数的断电保存同时验证了扩展库多页擦除的功能 STM32F1xx HAL_FLASH_EX库实用案例多页擦除结构体 要写入的设备参数 设备ID0x00STM32C8 温度校准值

2

8℃ 串口波特率115200 工作模式1 结构体大小16字节 FLASH解锁成功 FLASH多页擦除成功扩展库HAL_FLASHEx_Erase FLASH结构体写入成功 FLASH已加锁 从FLASH读取的设备参数 设备ID0x00STM32C8 温度校准值

2

8℃ 串口波特率115200 工作模式1 测试成功结构体数据一致

初学者使用扩展库的常见坑避坑指南这是初学者使用stm32f1xx_hal_flash_ex库时最容易犯的错误避开这些坑能解决 99% 的 FLASH 操作失败问题未引入扩展库头文件调用HAL_FLASHEx_Erase时未写#include stm32f1xx_hal_flash_ex.h编译直接报错最常见擦除前未解锁 FLASH直接调用扩展库擦除函数未先调用HAL_FLASH_Unlock()硬件保护导致擦除失败返回HAL_ERROR擦除结构体配置错误TypeErase填了非FLASH_TYPEERASE_PAGES的值F1 仅支持页擦除PageAddress不是页首地址如填 0x0801F001NbPages配置的页数超出 FLASH 剩余页数如从 0x0801F000 开始擦 2 页未传入 PageError 参数调用HAL_FLASHEx_Erase时第二个参数传 NULL或未定义uint32_t变量导致程序硬错多页擦除起始页选择错误多页擦除时PageAddress不是连续页的起始页如擦除最后 2 页起始页应填 0x0801E000而非 0x0801F000擦除后未加锁 FLASH操作完成后未调用HAL_FLASH_Lock()导致 FLASH 处于解锁状态易被误操作擦除 / 写入地址未对齐 / 越界擦除的页地址超出 FLASH 有效范围或写入地址未按字 / 半字对齐字写入需 4 字节对齐。

六、

总结核心要点回顾stm32f1xx_hal_flash_ex 是 HAL_FLASH 的专属扩展库F1 系列 FLASH擦除功能仅在此库中实现主库仅负责解锁 / 加锁 / 写入二者必须配合使用扩展库最核心的内容1 个FLASH_EraseInitTypeDef擦除初始化结构体 1 个HAL_FLASHEx_Erase页擦除核心函数80% 的开发场景只用到这两个F1 系列擦除的硬件强制规则仅支持页擦除擦除地址必须是页首地址中容量F103C8T6每页 1KBFLASH 完整操作流程关中断→主库解锁→扩展库擦除→主库写入→主库加锁→开中断这是固定流程初学者必须记死扩展库的HAL_FLASHEx_Erase支持单页 / 多页擦除仅需修改擦除结构体的NbPages页数和PageAddress起始页实际项目中扩展库的核心用途是擦除 FLASH 数据区配合主库实现断电不丢失的参数存储如设备配置、传感器校准值。

以上内容覆盖了 stm32f1xx_hal_flash_ex 扩展库的核心知识点和实战案例所有代码均基于 STM32F103C8T6 验证通过初学者可直接复制使用在此基础上拓展字节 / 半字写入、单参数存储等功能即可。

下载香蕉视频-下载香蕉视频应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123