[应用相关] 初学stm32 --- flash模仿eeprom

[复制链接]
2395|6
 楼主| Haizangwang 发表于 2025-2-11 18:41 | 显示全部楼层 |阅读模式
STM32内部FLASH简介
        在STM32芯片内部有一个FLASH存储器,主要用于存储代码。

7184967aab0eb2a7e0.png

7073867aab0e1dc493.png

内部FLASH构成(F1)
        内部FLASH主要由三部分组成:主存储器、信息块、闪存存储器接口寄存器。

1036467aab0db585ec.png

主存储器:

        地址范围为0x0800 0000 ~ 0x0807 FFFF,分为256页,每页2KB(小/中容量为1K)。

        当BOOT0接地,系统将从0x0800 0000地址处开始读取代码(从主存储器启动)

信息块:

        系统存储大小为2KB,用来存储ST自带的启动程序,用来串口下载代码。

        选项字节大小为16B,一般用于设置内存的写保护、读保护

        当BOOT0接VCC,BOOT1接GND (串口下载程序) ,系统运行的就是这部分代码

8729567aab0cde5d04.png

FLASH读写过程(F1)
  对FLASH的核心操作就是读和写。

          FLASH的物理特性:只能写0,不能写1,写1靠擦除。

闪存的读取
        直接在通用地址空间直接寻址,任何32位数据的读操作都能访问闪存模块的内容并得到相对应的数据。

4096567aab0c6e706c.png

        CPU运行速度比FLASH快得多,STM32F103的FLASH最快访问速度≤24MHz,CPU频率超过这个速度,得加入等待时间,否则读写FLASH可能出错,导致死机等情况。

8954267aab0bf90471.png

        正确设置好等待周期后,利用指针读取数据。

        从地址addr,读取数据(字节为8位,半字为16位,字为32位)

                data = *(volatile uint8_t *)addr;  /* 读取一个字节数据 */

                data = *(volatile uint16_t *)addr;  /* 读取一个半字数据 */

                data = *(volatile uint32_t *)addr;  /* 读取一个字数据 */

        将addr强制转换为uintx_t指针,然后取该指针所指向地址的值,即可获得addr地址的数据。

        注意: 在进行写或擦除操作时,不能进行代码或数据的读取操作。



闪存的写入
        闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的。

        写操作有四步:解锁 à 擦除 à 写数据 à 上锁

解锁:将两个特定的解锁序列号(KEY1:0x45670123 KEY2:0xCDEF89AB)依次写入FLASH_KEYR

擦除:FLASH物理特性(只能写0,不能写1),所以写FLASH之前需要擦除,将要写入的区域变为0xFFFF。擦除操作分为:页擦除和批量擦除

写数据:擦除完成,可以向FLASH写数据,每次只能以16位方式写入

上锁:写入数据完成,需要设置FLASH_CR[LOCK]位1,重新上锁,以防数据不小心被修改。

6433667aab0ae3d854.png

内部FLASH构成(F4 / F7 / H7)
        内部FLASH主要由四部分组成:主存储器、系统存储器、OPT区域和选项字节。
3366167aab0a5a4f66.png


        闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。

         注意:对于H7来说,在Bank1内部有2KB大小的用户选项字节,不过没有映射到内存地址上,可通过FLASH相关寄存器访问。

4529867aab0980e536.png

        STM32F7的FLASH访问路径有两条:AXIM和ITCM,对应不同的地址映射。

F767 单Bank的1M闪存组织结构(默认)双Bank详看《STM32F7xx参考手册》

6642467aab089e1256.png

3979267aab07c21318.png

FLASH读写过程(F4 / F7 / H7)
        对FLASH的核心操作就是读和写。

        FLASH的物理特性:只能写0,不能写1,写1靠擦除。

111667aab06d00e7e.png

4730067aab05eb4df6.png


177067aab04fd0c8b.png

闪存的读取
        正确设置好等待周期后,利用指针读取数据。

        从地址addr,读取数据(字节为8位,半字为16位,字为32位)

                data = *(volatile uint8_t *)addr;  /* 读取一个字节数据 */

                data = *(volatile uint16_t *)addr;  /* 读取一个半字数据 */

                data = *(volatile uint32_t *)addr;  /* 读取一个字数据 */

        将addr强制转换为uintx_t指针,然后取该指针所指向地址的值,即可获得addr地址的数据。

        注意: 在进行写或擦除操作时,不能进行代码或数据的读取操作。



闪存的写入
        对STM32F4/F7/H7 FLASH进行写操作或擦除,电源电压会影响数据的最大操作位数。

6384067aab0468d97c.png

        最大操作位数会影响擦除和写入的速度。

        开发板使用的电压是3.3V,PSIZE设置10,即32位并行位数。擦除或者写入都以32位为单位。

        写操作有四步:解锁 à 擦除 à 写数据 à 上锁

解锁:将两个特定的解锁序列号(KEY1:0x45670123 KEY2:0xCDEF89AB)依次写入FLASH_KEYR

擦除:FLASH物理特性(只能写0,不能写1),所以写FLASH之前需要擦除,将要写入的区域变为0xFFFF。擦除操作分为:页扇区擦除和整片擦除

写数据:擦除完成,可以向FLASH写数据,根据PSIZE设置写入数据的单位

PSZIE:00 字节、PSIZE:01 半字、PSIZE:10 字、PSIZE:11 双字

上锁:写入数据完成,需要设置FLASH_CR[LOCK]位1,重新上锁,以防数据不小心被修改。

      注意:对于STM32H7 FLASH写入的规则:写入首地址必须是32的倍数,写入数据长度必须是32字节的倍数。  

7057467aab03bd7d01.png

FLASH接口寄存器(写入 & 擦除相关)

3824367aab02cefbd1.png

FLASH_CR

LOCK:指示FLASH_CR寄存器是否被锁住(1锁 0未锁)

STRT:用于开始一次擦除操作(1开始)

PSIZE:用于设置编程宽度(3.3V PSIZE为2)

SNB:用于选择要擦除的扇区编号

SER:用于选择扇区擦除操作(页擦除置1)

PG:用于选择编程操作,往FLASH写数据需置1

         对于H7还有FLASH_CCR 清除与控制寄存器用于清除相关错误。

FLASH相关HAL库函数简介(F1 / F4 / F7 / H7)

8707767aab02408b0c.png

FLASH相关结构体:FLASH_EraseInitTypeDef
F1:

uint32_t        TypeErase                 /* 擦除类型 */
uint32_t        Banks                /* 擦除的bank编号(整片擦除) */
uint32_t         PageAddress        /* 擦除页面地址 */
uint32_t         NbPages                /* 擦除的页面数 */
F4/F7/H7

uint32_t        TypeErase                
uint32_t        Banks               
uint32_t         Sector       
uint32_t         NbSectors
uint32_t        VoltageRange       
F103ZET6 stmflash_write思路
        每个扇区(页)是2KB,也就是2048个地址,写任何一个地址前,如果该地址的值并不是0xFF,需先擦除再写入

1305167aab01a125c1.png

①根据w_addr,确定No.sector号以及w_addr在该sector的偏移

②根据w_addr和length,确定写入的内容是否跨sector

③确定好要操作的sector以及sector的地址范围

④遍历要写的地址区域数据是否都是0xFF,如果都是不用擦除,否则需要先读出保存在buf后擦除

⑤把该sector要操作的数据,也写到buf,最后一次性把buf写入到这个对应的sector即可

        跨扇区:需要注意偏移(扇区地址 / 扇区中的偏移 / 写入数据的偏移 / 写地址偏移 / 写入长度)

可操作的地址怎么确定?
        不能影响原有已存放有用数据的FLASH区域,通过MDK编译完成提示或者map文件可得占用flash大小。

2936167aab010594a2.png

        占用FLASH大小 40588(27360 + 12868 + 360)

        占用SRAM大小 4496(360 + 4136)

Code段:表示程序代码占用FLASH的大小(FLASH)

RO-data段,即Read Only-Data,程序定义的所有常量(FLASH)

RW-data段,即Read Write-Data,已经初始化的所有静态变量,占用FLASH空间(存储初值)和RAM(读写操作)

ZI-data(Zero initialized)段:未初始化的静态变量

RO(Read Only)段:指代码Code以及只读数据RO-data的统称,占用FLASH空间
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/gdragen_/article/details/145215823

tpgf 发表于 2025-4-9 16:35 | 显示全部楼层
Flash存储器一旦被编程后,可以通过普通的读操作来访问存储在其中的数据
xiaoqizi 发表于 2025-4-13 21:57 | 显示全部楼层
在某些应用场合下,需要非易失性存储器来保存少量经常更改的数据,这时可以使用STM32的Flash存储器来模拟EEPROM
木木guainv 发表于 2025-4-14 09:11 | 显示全部楼层
通过按字节或字对Flash存储器进行擦除和编程操作,可以实现对指定地址的数据修改
Jiangxiaopi 发表于 2025-4-14 12:21 | 显示全部楼层
使用HAL库提供的函数,可以擦除整个Flash扇区或根据需要选择单个Flash页的擦除
荣陶陶 发表于 2025-4-14 15:12 | 显示全部楼层
编程数据时需要注意编程顺序,以避免数据冲突和损坏
Zhiniaocun 发表于 2025-4-14 18:28 | 显示全部楼层
#include "stm32f4xx_hal.h"

void EEPROM_WriteByte(uint32_t addr, uint8_t data)
{
    HAL_FLASH_Unlock();
    if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, addr, data) != HAL_OK)
    {
        // 编程失败处理
    }
    HAL_FLASH_Lock();
}

uint8_t EEPROM_ReadByte(uint32_t addr)
{
    return *(uint8_t *)(addr);
}

int main(void)
{
    HAL_Init();
    HAL_FLASH_SetLatency(FLASH_LATENCY_5);
    HAL_FLASH_Unlock();
    FLASH_Erase_Sector(FLASH_SECTOR_4, FLASH_VOLTAGE_RANGE_3);
    EEPROM_WriteByte(0x080C0000, 0xAA);
    EEPROM_WriteByte(0x080C0001, 0x55);
    uint8_t data1 = EEPROM_ReadByte(0x080C0000);
    uint8_t data2 = EEPROM_ReadByte(0x080C0001);
    HAL_FLASH_Lock();
    while (1)
    {
        // 主循环
    }
}


您需要登录后才可以回帖 登录 | 注册

本版积分规则

75

主题

240

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部