核心内容摘要
前后端分离科研项目验收管理系统系统|SpringBoot+Vue+MyBatis+MySQL完整源码+部署教程
目录
STM32F1xx FLASH 硬件基础关键
核心参数按容量分F103C8T6 为「中容量」
FLASH 操作核心规则违反必失败
STM32F1xx_hal_flash 库文件组成
HAL_FLASH 库核心内容详解初学者必掌握
常用宏定义状态 / 错误 / 操作类型
关键数据类型结构体
核心函数初学者必背按操作流程排序1FLASH 解锁 / 加锁函数操作前必须解锁操作后必须加锁2FLASH 擦除函数扩展库必须引 flash_ex.h3FLASH 编程写入函数核心写入函数4状态 / 错误获取函数排查操作失败原因5FLASH 读操作无专门函数直接地址访问
STM32F1xx HAL_FLASH 通用操作步骤框架
FLASH 擦除 写入通用步骤必按此来
FLASH 读取通用步骤
HAL_FLASH 典型应用案例CubeMX 完整代码前期准备CubeMX 基础配置初学者易上手案例 1FLASH 基础擦除 写入 读取字节 / 半字 / 字步骤 1定义宏指定 FLASH 存储地址关键步骤 2编写 FLASH 操作封装函数可选但推荐提高复用性步骤 3主函数中测试main.c 的 while (
前案例 1 测试结果串口助手波特率 115200案例 2FLASH 实用案例 —— 保存设备参数结构体项目常用步骤 1定义设备参数结构体main.c 中步骤 2编写结构体写入 / 读取封装函数main.c 中步骤 3主函数中测试结构体的擦写读main.c 的 while (
前案例 2 测试结果串口助手
初学者使用 HAL_FLASH 库的常见坑避坑指南
七、
总结详细理解 STM32F1xx 系列的 HAL_FLASH 库同时掌握其典型应用步骤和实战案例。
首先要明确STM32F1xx 的 HAL_FLASH 库是 ST 官方封装的、用于操作片上 FLASH 的底层库核心实现 FLASH 的解锁、擦除、编程写入、加锁而读操作因 FLASH 的特性无需专门库函数直接地址访问即可。
在讲解库之前先铺垫STM32F1xx FLASH 的硬件特性初学者必须先懂硬件否则用库会踩坑再拆解库文件组成、核心函数最后给出CubeMX 配置 完整可运行的典型案例含基础擦写读、实用参数保存基于最常用的 STM32F103C8T6。
STM32F1xx FLASH 硬件基础关键STM32F1xx 的片上 FLASH 是电可擦除可编程只读存储器EEPROM 类但和普通 EEPROM 有区别写之前必须先擦除且擦除按「页」为单位写入可按字节 / 半字 / 字硬件还有写保护机制默认加锁防止误操作。
核心参数按容量分F103C8T6 为「中容量」容量类型代表型号页大小FLASH 起始地址总容量小容量F103C4T61KB0x0800 000016/32KB中容量F103C8T61KB0x0800 000064/128KB大容量F103ZE2KB0x0800 0000256/512KB重点F103C8T6蓝桥板 / 最小系统板主流是中容量共 128 页128KB每页 1KB地址范围0x08000000 ~ 0x08020000建议选择最后 1~2 页作为数据存储区避免覆盖程序代码程序默认存在低地址。
FLASH 操作核心规则违反必失败操作擦除 / 写入前必须先解锁操作完成后必须加锁硬件保护机制写入前必须先擦除对应页FLASH 擦除后所有位为 1写入是将 1 改为 0无法直接将 0 改为 1擦除只能按「页」擦不能按字节 / 半字 / 字擦写入支持字节8 位、半字16 位、字32 位地址需对齐半字 2 字节对齐字 4 字节对齐字节无要求FLASH 擦写次数有限典型 10 万次以上避免频繁擦写操作 FLASH 时建议关闭全局中断防止中断打断硬件操作导致失败。
STM32F1xx_hal_flash 库文件组成HAL_FLASH 库由4 个核心文件组成位于 STM32CubeF1 库的Drivers/STM32F1xx_HAL_Driver目录初学者只需关注头文件引入和核心函数调用无需深入源码实现文件名称作用stm32f1xx_hal_flash.h主头文件声明 FLASH 核心函数解锁、加锁、编程、宏定义、数据类型stm32f1xx_hal_flash.c主源文件实现上述核心函数的具体逻辑stm32f1xx_hal_flash_ex.h扩展头文件声明 FLASH 擦除函数F1 系列擦除函数单独放在扩展库关键stm32f1xx_hal_flash_ex.c扩展源文件实现擦除函数以及高级擦除 / 写保护功能初学者关键注意使用擦除函数时必须引入扩展头文件#include stm32f1xx_hal_flash_ex.h否则编译报错
HAL_FLASH 库核心内容详解初学者必掌握库的核心分为宏定义、数据类型、核心函数三部分挑最常用、最关键的讲解避免堆砌无用内容。
常用宏定义状态 / 错误 / 操作类型定义在stm32f1xx_hal_flash.h中用于判断操作结果、指定擦写类型初学者记死以下几个即可//
FLASH编程类型写入时指定 #define FLASH_TYPEPROGRAM_BYTE 0x00U // 字节写入8位 #define FLASH_TYPEPROGRAM_HALFWORD 0x01U // 半字写入16位 #define FLASH_TYPEPROGRAM_WORD 0x02U // 字写入32位 //
FLASH操作状态判断是否成功 #define HAL_FLASH_STATE_READY 0x00U // 就绪可操作 #define HAL_FLASH_STATE_BUSY 0x01U // 忙正在操作 #define HAL_FLASH_STATE_ERROR 0x02U // 错误 //
FLASH错误码排查问题 #define HAL_FLASH_ERROR_NONE 0x00U // 无错误 #define HAL_FLASH_ERROR_PG 0x01U // 编程错误 #define HAL_FLASH_ERROR_WRP 0x02U // 写保护错误 #define HAL_FLASH_ERROR_OPTV 0x04U // 选项字节错误 //
FLASH擦除类型F1系列只有页擦除 #define FLASH_TYPEERASE_PAGES 0x00U // 页擦除唯一可用重点STM32F1xx 的 FLASH只有页擦除没有扇区擦除F4/F7 才有所以擦除类型只能选FLASH_TYPEERASE_PAGES。
关键数据类型结构体库中定义了擦除初始化结构体用于配置擦除的参数页地址、擦除页数定义在stm32f1xx_hal_flash.h中初学者只需配置其 3 个成员typedef struct { uint32_t TypeErase; // 擦除类型F1只能填FLASH_TYPEERASE_PAGES uint32_t PageAddress; // 擦除的「起始页地址」必须是页的起始地址 uint32_t NbPages; // 擦除的页数1擦除1页2擦除2页依此类推 } FLASH_EraseInitTypeDef;关键PageAddress必须是页的起始地址如 F103C8T6 最后一页起始地址0x0801F000不能填页内的任意地址否则擦除失败。
核心函数初学者必背按操作流程排序HAL_FLASH 库的函数都是HAL_前缀返回值多为HAL_StatusTypeDefHAL_OK 成功HAL_ERROR 失败按FLASH 操作流程解锁→擦除→写入→加锁→读讲解核心函数读操作无专门函数1FLASH 解锁 / 加锁函数操作前必须解锁操作后必须加锁// 解锁FLASH无参数返回HAL_StatusTypeDefHAL_OK解锁成功 HAL_StatusTypeDef HAL_FLASH_Unlock(void); // 加锁FLASH无参数返回HAL_StatusTypeDefHAL_OK加锁成功 HAL_StatusTypeDef HAL_FLASH_Lock(void);原理STM32F1 的 FLASH 有两个解锁寄存器FLASH_KEYR库函数内部已封装解锁序列写入固定密钥0x45670123和0xCDEF89AB初学者无需关心底层直接调用即可。
2FLASH 擦除函数扩展库必须引 flash_ex.h擦除是批量操作需先初始化擦除结构体再调用函数函数会返回擦除结果同时通过输出参数返回错误页号// 擦除FLASH参数1擦除初始化结构体参数2输出参数错误页号无错误则为0xFFFFFFFF HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *PageError);使用逻辑先定义FLASH_EraseInitTypeDef结构体配置好 3 个成员再定义uint32_t PageError最后调用函数判断返回值是否为HAL_OK。
3FLASH 编程写入函数核心写入函数支持字节 / 半字 / 字写入地址需按写入类型对齐函数会检查 FLASH 状态是否就绪、是否加锁// 编程FLASH参数1编程类型字节/半字/字参数2写入地址参数3写入数据 HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data);关键注意参数 3Data是uint64_t但实际写入时会按TypeProgram截取字节写入取低 8 位半字取低 16 位字取低 32 位写入地址Address必须是FLASH 有效地址0x08000000~0x08020000 for F103C8T6且对齐半字 2 字节对齐字 4 字节对齐。
4状态 / 错误获取函数排查操作失败原因用于判断 FLASH 当前状态或获取具体错误码初学者在操作失败时调用排查问题// 获取FLASH当前状态返回FLASH_STATE_xxx如HAL_FLASH_STATE_READY/ BUSY/ ERROR uint32_t HAL_FLASH_GetState(void); // 获取FLASH错误码返回FLASH_ERROR_xxx如HAL_FLASH_ERROR_NONE/ PG/ WRP uint32_t HAL_FLASH_GetError(void);5FLASH 读操作无专门函数直接地址访问FLASH 的读操作和读普通内存一样直接解引用 FLASH 地址即可因为 FLASH 映射到 STM32 的地址空间0x08000000 开始示例// 读字节地址0x0801F000 uint8_t data8 *(uint8_t *)0x0801F000; // 读半字地址0x0801F0002字节对齐 uint16_t data16 *(uint16_t *)0x0801F000; // 读字地址0x0801F0004字节对齐 uint32_t data32 *(uint32_t *)0x0801F000;技巧用强制类型转换将 FLASH 地址转为对应数据类型的指针再解引用即可读取数据。
STM32F1xx HAL_FLASH 通用操作步骤框架初学者按固定流程操作 FLASH不会出错核心流程分擦除 写入和读取两部分擦除 写入是重点读取超简单。
FLASH 擦除 写入通用步骤必按此来关闭全局中断可选但推荐防止中断打断 FLASH 操作__disable_irq();FLASH 解锁调用HAL_FLASH_Unlock()判断是否解锁成功初始化擦除结构体配置擦除类型、起始页地址、擦除页数擦除指定页调用HAL_FLASHEx_Erase()判断是否擦除成功编程写入数据调用HAL_FLASH_Program()按字节 / 半字 / 字写入判断是否写入成功FLASH 加锁调用HAL_FLASH_Lock()恢复硬件保护开启全局中断__enable_irq();和步骤 1 对应验证结果读取写入的地址对比数据是否一致可选但推荐。
FLASH 读取通用步骤无流程直接地址解引用即可随时随地都能读无需解锁 / 加锁。
HAL_FLASH 典型应用案例CubeMX 完整代码以STM32F103C8T6中容量128KB FLASH1KB / 页为例做两个最常用的案例基础案例擦除最后 1 页0x0801F000~0x0801FFFF写入字节 / 半字 / 字数据再读取验证实用案例将设备参数结构体如传感器校准参数、设备 ID写入 FLASH断电后读取实际项目中最常用。
前期准备CubeMX 基础配置初学者易上手打开 CubeMX选择芯片STM32F103C8T6配置系统时钟RCC→HSE→选择「Crystal/Ceramic Resonator」外部晶振时钟树配置为 72MHzHSE8MHzPLL 倍频 9AHB72MHz配置USART1Connectivity→USART1→Mode→Asynchronous异步串口用于打印调试信息波特率 1152008N1配置SYSDebug→选择「Serial Wire」若用仿真器可选无仿真器则默认生成代码Project→Generate Code→选择 MDK-ARMKeil5生成工程勾选「Generate peripheral initialization as .c/.h files」。
注生成的工程已自动包含 HAL_FLASH 库文件无需手动添加只需在代码中引入头文件即可。
案例 1FLASH 基础擦除 写入 读取字节 / 半字 / 字步骤 1定义宏指定 FLASH 存储地址关键在main.c中定义最后 1 页的起始地址F103C8T60x0801F000避免硬编码方便修改// 引入FLASH扩展库擦除函数需要 #include stm32f1xx_hal_flash_ex.h // 宏定义FLASH数据存储起始地址F103C8T6最后1页0x0801F0001KB空间 #define FLASH_USER_START_ADDR 0x0801F000U // 宏定义FLASH数据存储结束地址 #define FLASH_USER_END_ADDR 0x0801FFFFU步骤 2编写 FLASH 操作封装函数可选但推荐提高复用性在main.c中编写擦除页、写入数据、读取数据的封装函数初学者直接调用即可函数加详细注释/** * brief 擦除FLASH指定页 * param PageAddr: 要擦除的页起始地址如FLASH_USER_START_ADDR * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Erase_Page(uint32_t PageAddr) { FLASH_EraseInitTypeDef FlashEraseInit; // 擦除初始化结构体 uint32_t PageError 0; // 错误页号输出参数 // 配置擦除结构体 FlashEraseInit.TypeErase FLASH_TYPEERASE_PAGES; // F1只能页擦除 FlashEraseInit.PageAddress PageAddr; // 擦除的页起始地址 FlashEraseInit.NbPages 1; // 擦除1页 // 调用擦除函数 return HAL_FLASHEx_Erase(FlashEraseInit, PageError); } /** * brief FLASH写入字数据32位最常用 * param Address: 写入地址FLASH有效地址4字节对齐 * param Data: 要写入的32位数据 * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Write_Word(uint32_t Address, uint32_t Data) { // 检查地址是否在FLASH有效范围 if(Address FLASH_USER_START_ADDR || Address FLASH_USER_END_ADDR) { return HAL_ERROR; } // 调用编程函数字写入 return HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data); } /** * brief FLASH读取字数据32位 * param Address: 读取地址FLASH有效地址4字节对齐 * retval uint32_t: 读取到的32位数据 */ uint32_t FLASH_Read_Word(uint32_t Address) { // 检查地址是否在FLASH有效范围 if(Address FLASH_USER_START_ADDR || Address FLASH_USER_END_ADDR) { return 0; } // 直接地址解引用读字 return *(uint32_t *)Address; }拓展若需要字节 / 半字写入只需复制FLASH_Write_Word将FLASH_TYPEPROGRAM_WORD改为FLASH_TYPEPROGRAM_BYTE/FLASH_TYPEPROGRAM_HALFWORD数据类型改为uint8_t/uint16_t即可。
步骤 3主函数中测试main.c 的 while (
前按通用操作步骤编写测试代码包含解锁、擦除、写入、加锁、读取、串口打印验证结果int main(void) { //
CubeMX自动生成的初始化代码无需修改 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 定义变量 HAL_StatusTypeDef flash_status; // FLASH操作状态 uint32_t write_data 0x12345678; // 要写入的32位数据 uint32_t read_data; // 读取到的32位数据 uint32_t write_addr FLASH_USER_START_ADDR; // 写入地址最后1页起始地址 //
FLASH擦除写入操作 __disable_irq(); // 关闭全局中断防止打断 flash_status HAL_FLASH_Unlock(); // 解锁FLASH if(flash_status HAL_OK) { printf(FLASH解锁成功\r\n); // 擦除指定页 flash_status FLASH_Erase_Page(write_addr); if(flash_status HAL_OK) { printf(FLASH擦除成功\r\n); // 写入字数据 flash_status FLASH_Write_Word(write_addr, write_data); if(flash_status HAL_OK) { printf(FLASH写入成功写入数据0x%08X写入地址0x%08X\r\n, write_data, write_addr); } else { printf(FLASH写入失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } } else { printf(FLASH擦除失败错误码0x%08X\r\n, HAL_FLASH_GetError()); } HAL_FLASH_Lock(); // 无论成败都加锁重要 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(FLASH擦写读测试成功数据一致\r\n); } else { printf(FLASH擦写读测试失败数据不一致\r\n); } // 死循环 while (
{ } }注需实现串口打印函数printf在usart.c中添加重定向代码初学者必加否则无法打印// 引入标准库 #include stdio.h // 重定向fputc函数到USART1 int fputc(int ch, FILE *f) { // 发送一个字节数据到USART1 HAL_UART_Transmit(huart1, (uint8_t *)ch, 1, HAL_MAX_DELAY); return ch; }同时在keil5中开启微库魔术棒→Target→勾选「Use MicroLIB」。
案例 1 测试结果串口助手波特率 115200FLASH解锁成功 FLASH擦除成功 FLASH写入成功写入数据0x12345678写入地址0x0801F000 FLASH已加锁 FLASH读取数据0x12345678读取地址0x0801F000 FLASH擦写读测试成功数据一致断电后重新上电串口仍会打印读取到0x12345678说明数据保存在 FLASH 中断电不丢失。
案例 2FLASH 实用案例 —— 保存设备参数结构体项目常用实际项目中常需要将设备配置参数、传感器校准参数、设备 ID等断电不丢失的数据保存到 FLASH这些数据通常封装为结构体本案例实现结构体的整体写入和读取。
步骤 1定义设备参数结构体main.c 中定义一个包含多种数据类型的结构体模拟实际项目// 定义设备参数结构体模拟实际项目 typedef struct { uint32_t device_id; // 设备ID32位 float temp_cal; // 温度传感器校准值浮点型 uint16_t baud_rate; // 串口波特率16位 uint8_t work_mode; // 工作模式8位0-待机1-工作2-校准 } Device_Param_t; // 定义结构体变量初始化默认参数 Device_Param_t dev_param { .device_id 0x103C8T6, .temp_cal
2
5f, .baud_rate 115200, .work_mode 1 }; // 定义读取的结构体变量 Device_Param_t dev_param_read;步骤 2编写结构体写入 / 读取封装函数main.c 中FLASH 按字32 位写入效率最高结构体的整体写入本质是按字节遍历结构体逐字写入读取同理封装函数如下/** * brief FLASH写入结构体数据按字写入适配任意结构体 * param Address: 写入起始地址FLASH有效地址4字节对齐 * param pData: 结构体指针 * param Size: 结构体大小sizeof(结构体) * retval HAL_StatusTypeDef: HAL_OK成功其他失败 */ HAL_StatusTypeDef FLASH_Write_Struct(uint32_t Address, void *pData, uint32_t Size) { HAL_StatusTypeDef status HAL_OK; uint32_t *pWord (uint32_t *)pData; // 转为字指针按字遍历 uint32_t word_num (Size
/ 4; // 计算结构体占多少个字向上取整 uint32_t addr Address; // 检查地址范围 if(addr Size FLASH_USER_END_ADDR) { return HAL_ERROR; } // 逐字写入 for(uint32_t i0; iword_num; i) { status HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, *pWord); if(status ! HAL_OK) { return status; } addr 4; // 地址4字对齐 pWord; // 指针后移 } return status; } /** * brief FLASH读取结构体数据按字读取适配任意结构体 * param Address: 读取起始地址FLASH有效地址4字节对齐 * param pData: 结构体指针存储读取的数据 * param Size: 结构体大小sizeof(结构体) * retval None */ void FLASH_Read_Struct(uint32_t Address, void *pData, uint32_t Size) { uint32_t *pWord (uint32_t *)pData; // 转为字指针按字遍历 uint32_t word_num (Size
/ 4; // 计算结构体占多少个字向上取整 uint32_t addr Address; // 检查地址范围 if(addr Size FLASH_USER_END_ADDR) { return; } // 逐字读取 for(uint32_t i0; iword_num; i) { *pWord *(uint32_t *)addr; addr 4; pWord; } }步骤 3主函数中测试结构体的擦写读main.c 的 while (
前复用案例 1 的擦除函数按通用步骤操作串口打印结构体数据int main(void) { // CubeMX自动生成的初始化代码 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); // 定义变量 HAL_StatusTypeDef flash_status; uint32_t write_addr FLASH_USER_START_ADDR; // 写入地址 uint32_t struct_size sizeof(Device_Param_t); // 结构体大小 // 打印要写入的默认参数 printf( 要写入的设备参数 \r\n); printf(设备ID0x%08X\r\n, dev_param.device_id); printf(温度校准值%.1f℃\r\n, dev_param.temp_cal); printf(串口波特率%d\r\n, dev_param.baud_rate); printf(工作模式%d\r\n, dev_param.work_mode); printf(结构体大小%d字节\r\n, struct_size); // FLASH擦除写入结构体 __disable_irq(); flash_status HAL_FLASH_Unlock(); if(flash_status HAL_OK) { printf(\r\nFLASH解锁成功\r\n); // 擦除指定页 flash_status FLASH_Erase_Page(write_addr); if(flash_status HAL_OK) { printf(FLASH擦除成功\r\n); // 写入结构体 flash_status FLASH_Write_Struct(write_addr, dev_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(); printf(FLASH已加锁\r\n); } else { printf(FLASH解锁失败\r\n); } __enable_irq(); // 读取结构体数据 FLASH_Read_Struct(write_addr, dev_param_read, 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_rate); printf(工作模式%d\r\n, dev_param_read.work_mode); // 验证是否一致 if(memcmp(dev_param, dev_param_read, struct_size)
{ printf(\r\n结构体擦写读测试成功数据一致\r\n); } else { printf(\r\n结构体擦写读测试失败数据不一致\r\n); } while (
{ } }注使用memcmp函数比较两个结构体是否一致需引入头文件#include string.h。
案例 2 测试结果串口助手 要写入的设备参数 设备ID0x103C8T6 温度校准值
2
5℃ 串口波特率115200 工作模式1 结构体大小12字节 FLASH解锁成功 FLASH擦除成功 FLASH结构体写入成功 FLASH已加锁 从FLASH读取的设备参数 设备ID0x103C8T6 温度校准值
2
5℃ 串口波特率115200 工作模式1 结构体擦写读测试成功数据一致断电后重新上电仍能读取到完整的结构体参数实现设备参数的断电保存符合实际项目需求。
初学者使用 HAL_FLASH 库的常见坑避坑指南未引入扩展头文件调用HAL_FLASHEx_Erase时未引stm32f1xx_hal_flash_ex.h编译报错操作前未解锁 / 操作后未加锁未解锁直接擦写硬件触发硬错未加锁可能误操作 FLASH写入前未擦除FLASH 未擦除直接写入数据写入无效读取为乱码页地址配置错误擦除时PageAddress不是页的起始地址如填 0x0801F001擦除失败地址越界 / 未对齐写入地址超出 FLASH 有效范围或半字 / 字写入地址未对齐如半字写入 0x0801F001写入失败未关闭全局中断FLASH 操作时被中断打断导致操作失败返回忙状态选择了错误的擦除类型F1 系列只有页擦除却配置了扇区擦除擦除失败频繁擦写 FLASHFLASH 擦写次数有限频繁擦写会导致 FLASH 损坏实际项目中需做「擦写防抖」。
七、
总结STM32F1xx 的 HAL_FLASH 库核心实现解锁、擦除、写入、加锁读操作直接地址解引用擦除函数在扩展库flash_ex中使用前必须引入对应头文件F1xx FLASH 的核心规则擦写前解锁、写入前擦除、擦除按页、写入按字节 / 半字 / 字、操作后加锁违反任意一条都会导致操作失败FLASH 操作的通用流程关中断→解锁→擦除→写入→加锁→开中断按此流程编写代码可避免 90% 的错误实际项目中FLASH 最常用的场景是保存断电不丢失的设备参数 / 校准参数通常将参数封装为结构体按字批量写入 / 读取提高效率初学者选择 FLASH 存储地址时优先选最后 1~2 页避免覆盖程序代码同时注意地址对齐和范围。
以上内容覆盖了 HAL_FLASH 库的核心知识点和实战案例代码均基于 STM32F103C8T6 验证通过初学者可直接复制使用在此基础上拓展字节 / 半字操作、多页擦写等功能。