当前位置: 首页 > news >正文

【工具使用】STM32CubeMX-片内Flash读写操作

一、概述

    无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。
    本文主要讲述STM32芯片片内Flash功能的应用及其相关知识。

二、软件说明

    STM32CubeMX是ST官方出的一款针对ST的MCU/MPU跨平台的图形化工具,支持在Linux、MacOS、Window系统下开发,其对接的底层接口是HAL库,另外习惯于寄存器开发的同学们,也可以使用LL库。STM32CubeMX除了集成MCU/MPU的硬件抽象层,另外还集成了像RTOS,文件系统,USB,网络,显示,嵌入式AI等中间件,这样开发者就能够很轻松的完成MCU/MPU的底层驱动的配置,留出更多精力开发上层功能逻辑,能够更进一步提高了嵌入式开发效率。
    演示版本 6.1.0

三、片内Flash功能简介

    首先看下Flash的概念,Flash(闪存)是一种非易失性存储技术,广泛应用于电子设备中。它结合了 ROM(只读存储器)的非易失性和 RAM(随机存取存储器)的可读写特性,成为现代数据存储的核心组件。最早的Flash是EEPROM(电可擦可编程只读存储器),EEPROM的特点是可单个字节擦写,但缺点就是效率低、成本高,于是就有人提出块擦除的概念,并在几年后推出首款NOR-Flash芯片,命名为Flash Memory,跟传统EEPROM对比,降低了成本,并提高了擦除效率。下面简单看下两者的对比:

特性EEPROMFlash
擦除单位字节(Byte)级擦除,可随机修改单个字节块(Block)或页(Page)级擦除(如 4KB/8KB/64KB)
擦写寿命高(10 万 - 100 万次 P/E 循环)中低(SLC: 10 万次,MLC: 1 万次,TLC/QLC: 1000-3000 次)
读写速度写入:中等(约 100μs / 字节)
读取:快(约 50-100ns)
写入:慢(需先擦除整块,约 1-10ms / 页)
读取:快(随机读取快)
存储密度低(成本高),容量通常为 KB 级别高(成本低),容量从 MB 到 TB 级别
随机访问能力支持按字节随机读写,无需预先擦除仅支持块级擦除,写入前需擦除整个块
典型应用场景配置参数存储(如 I2C EEPROM)
频繁更新的小数据(如计数器)
程序代码存储(如 MCU 片内 Flash)
SSD、U 盘、存储卡等大容量存储
数据保存时间长(>10 年)中(5-10 年,依赖存储技术和使用环境)
成本 / 位低(Flash 是主流低成本方案)
技术实现复杂度简单(电路结构直接)复杂(需 FTL 磨损均衡、ECC 纠错等技术)

注:因为Flash有擦写寿命,所以不可以频繁擦写,要控制擦写次数。

    简单介绍完通用Flash后,我们来看一下STM32内部的Flash是怎么样的,这里以STM32F103C8T6为例,内部Flash总共有128k(官方文档会写只有64k,但实际是有128k的,只是出厂的时候他们只保证64k是测试过没问题的),分成128页,每页1k的大小。STM32内部的Flash一般是用来存储代码,所以在单片机启动地址的选择中,有一个就是从0x08000000开始。除了存储代码以外,内部Flash还可以用来存储一些需要掉电后仍需要保持的数据。

Flash
    操作Flash时一般是需要关闭中断的,因为把Flash当成一个资源来看时,内核是需要使用一些通信指令去操作Flash的。当操作Flash时,内核需要发送指令去操作Flash,如果此时几个指令发送到一半时,突然产生中断,中断的代码是在Flash中的,相当于此时内核又重新发送指令去读取Flash中的代码指令,从时序上来看,是中断和操作Flash两个操作共用了Flash这一个资源,从而造成重入问题。一般出现这种问题会报硬件错误。然而STM32自身做了保护,即在擦除或写入时,会禁止读取,即擦除或写入Flash期间,中断无法执行。下面是官方STM32F10xxx 闪存编程手册(PM0075)的描述。
Flash编程手册

四、片内Flash配置

    有了以上一些基础知识,接下来我们来实现一个实际点的功能,存储一个数据,用来识别该软件是否第一次使用,如果是,那就打印一条第一次使用的信息,否则则打印欢迎回来的信息。首先先做个Flash的配置,这个功能配置很简单,简单到根本不需要单独配置。CubeMX直接配个带时钟的最简易的工程能跑就行。
梗图
    为了方便新手学习,这里还是一张图演示搞定。
在这里插入图片描述

    配置完时钟后,就直接来看实现吧,对于内部Flash,其实我们最关心的就是读跟写的功能,对于片内Flash来说,读取可以直接使用指针索引即可,但为了方便学习,这里还是写下具体实现。

  • HAL库代码实现
#define FLASH_USER_START_ADDR    0x08004000U    // 用户Flash起始地址
#define FLASH_USER_END_ADDR      0x08010000U    // 用户Flash结束地址/*** @brief  写入数据到STM32F103内部Flash,支持跨页擦写* @param  startAddress: 写入的起始地址,必须是2字节对齐* @param  data: 待写入数据的指针* @param  size: 待写入数据的字节数* @retval HAL_StatusTypeDef: 操作状态*/
HAL_StatusTypeDef FLASH_WriteData(uint32_t startAddress, uint8_t* data, uint32_t size)
{HAL_StatusTypeDef status = HAL_OK;uint32_t pageError = 0;uint32_t currentAddress = startAddress;uint32_t dataIndex = 0;uint32_t firstPage = startAddress / FLASH_PAGE_SIZE;uint32_t lastPage = (startAddress + size - 1) / FLASH_PAGE_SIZE;/* 检查地址和数据合法性 */if ((startAddress < FLASH_USER_START_ADDR) || (startAddress + size > FLASH_USER_END_ADDR) ||(startAddress % 2 != 0) || (size % 2 != 0)){return HAL_ERROR;}/* 解锁Flash控制器 */status = HAL_FLASH_Unlock();if (status != HAL_OK) return status;/* 擦除涉及的页 */FLASH_EraseInitTypeDef eraseInit;eraseInit.TypeErase = FLASH_TYPEERASE_PAGES;eraseInit.PageAddress = firstPage * FLASH_PAGE_SIZE;eraseInit.NbPages = lastPage - firstPage + 1;status = HAL_FLASHEx_Erase(&eraseInit, &pageError);if (status != HAL_OK) goto FLASH_OPERATION_END;/* 写入数据(按半字,即16位操作) */while (dataIndex < size){uint16_t halfWordData = (uint16_t)(data[dataIndex + 1] << 8) | data[dataIndex];status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, currentAddress, halfWordData);if (status != HAL_OK) break;currentAddress += 2;dataIndex += 2;}FLASH_OPERATION_END:/* 锁定Flash控制器 */HAL_FLASH_Lock();return status;
}/*** @brief  读取STM32F103内部Flash数据,支持跨页读取* @param  startAddress: 读取的起始地址,必须是2字节对齐* @param  data: 待读取数据的指针* @param  size: 待读取数据的字节数* @retval HAL_StatusTypeDef: 操作状态*/
HAL_StatusTypeDef FLASH_ReadData(uint32_t startAddress, uint8_t* data, uint32_t size)
{for (uint32_t i = 0; i < size; i++){data[i] = *((uint8_t *)startAddress + i);}return HAL_OK;
}uint8_t write_en = 0;                   // 写入使能
uint32_t write_addr = 0;                // 写入数据的Flash首地址(基于0x08004000)
uint8_t write_data[FLASH_PAGE_SIZE];    // 待写入的数据uint8_t read_en = 0;                    // 读取使能
uint32_t read_addr = 0;                 // 读取数据的Flash首地址(基于0x08004000)
uint8_t read_data[FLASH_PAGE_SIZE];     // 待读取的数据/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */if (write_en){FLASH_WriteData(FLASH_USER_START_ADDR + write_addr, write_data, FLASH_PAGE_SIZE);write_en = 0;}if (read_en){FLASH_ReadData(FLASH_USER_START_ADDR + read_addr, read_data, FLASH_PAGE_SIZE);read_en = 0;}/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
  • 效果演示
    Flash读写

五、注意事项

  1. 正常来讲,Flash要写入数据前,必须先擦除原本的整页的数据,但STM32内部Flash是支持不擦除写入的,但必须确保数据是从1变成0,因为Flash本身的特性,数据从0变1只能是通过擦除的动作来实现。
  2. 因为内部Flash最小的擦除单位是页,所以当需要修改同一页里的某个数据时,必须先把整页数据读至RAM,然后整页擦除,修改RAM中要改变的值,再将整页RAM写回Flash中,所以操作Flash时,RAM至少要留与Flash页大小一致的一片空间。
  3. 擦除或写入Flash前需要关闭中断,不然会出现重入问题,导致出现硬件错误。但STM32除外,因为STM32擦写Flash时内部会禁止读取Flash。
  4. 写Flash前,需要确认写入的数据是否跟原本的数据一样,如果是一样的可以不写入。因为Flash存在擦写寿命,如果过于频繁地操作擦写Flash,当达到擦写的寿命时,Flash会损坏。一般标称可擦写10w次。

六、相关链接

对于刚入门的小伙伴可以先看下STM32CubeMX的基础使用及Keil的基础使用。
【工具使用】STM32CubeMX-基础使用篇
【工具使用】Keil5软件使用-基础使用篇

相关文章:

  • 深入解析 Oracle session_cached_cursors 参数及性能对比实验
  • Spring 代理与 Redis 分布式锁冲突:一次锁释放异常的分析与解决
  • Linux-进程间通信
  • (C语言篇)处理字符串的四个基础函数
  • 一发入魂:极简解决 SwiftUI 复杂视图未能正确刷新的问题(下)
  • Navicat连接开启sm3认证的瀚高数据库
  • 关于摄像头模块的红外截止滤光片
  • C++-演讲比赛项目
  • 【机器人】复现 3D-Mem 具身探索和推理 | 3D场景记忆 CVPR 2025
  • 深度解析3D模型生成器:基于StyleGAN3与PyTorch3D的多风格生成工具开发实战
  • 【Web渗透】DVWA搭建详细教程
  • 用Python构建学生成绩管理系统的基本方案
  • leetcode hot100:解题思路大全
  • Web安全基础
  • 网络I/O学习-poll(三)
  • 异步记录用户操作日志
  • LLM笔记(九)KV缓存(2)
  • 智谱清言微服务架构转型实践——基于 CloudWeGo 的技术演进
  • 软件设计师“UML”真题考点分析——求三连
  • Triton介绍和各平台支持情况分析
  • 中青报聚焦上海社区心理服务:社工介入让居民“心畅”
  • 浙江广厦:诚挚道歉,涉事责任人交公安机关
  • 调查丨永久基本农田沦为垃圾堆场,整改为何成“纸面工程”?
  • 三件珍贵标本开箱!中国恐龙大展5月26日在沪开幕,明星标本汇聚一堂
  • 2人恶意传播刘国梁谣言被处罚,媒体:以法律利剑劈谣斩邪,加快推进依法治体
  • 广药集团原董事长李楚源被“双开”:去年8月被查,曾多次发表争议言论