stm32之FLASH

目录

1.简介

STM32F1系列的FLASH内存是一个非常重要的存储区域,它主要由三个部分组成:程序存储器系统存储器选项字节

1. 程序存储器(Program Memory)

程序存储器用于存放固化在微控制器中的程序代码。它是一个非易失性存储器,也就是说,当电源断开时,存储在其中的数据不会丢失。

  • 程序存储器通常是STM32系列微控制器中最大的存储区域,存储着应用程序的代码。
  • 在开发过程中,程序存储器的主要作用是存储用户编写的程序代码,程序代码的加载和执行都依赖于这个区域。

2. 系统存储器(System Memory)

系统存储器是由微控制器厂商预先配置的存储区域,通常用于存储引导程序(Bootloader)和启动代码。

  • 引导程序是用来初始化硬件、加载应用程序或者通过特定方式进行程序下载的代码。
  • 它也可以在没有外部存储设备的情况下启动系统。

3. 选项字节(Option Bytes)

选项字节存储了一些控制和配置微控制器的参数,比如读写保护启动模式上电复位配置等。

  • 选项字节的设置通常在开发初期完成,并且修改后,通常不需要再更改。它们为系统提供了基本的启动和保护功能。

4. FLASH存储的功能

STM32F1系列的FLASH不仅可以用于存储程序代码,还具有一些额外的功能,主要用于支持用户数据的存储和系统自我更新。

用户数据存储

  • 利用程序存储器的剩余空间保存用户数据

    在程序中,你可以利用程序存储器中未使用的部分来保存一些掉电不丢失的用户数据。例如,存储设备的配置、历史记录或者其他需要持久保存的数据。

    • 这样,程序在启动时可以读取这些数据,并根据存储的内容执行相应的操作。

程序自我更新

  • 通过IAP实现程序的自我更新

    STM32F1系列支持In-Application Programming(IAP),这是一种可以在应用程序运行时更新程序存储器内容的方式。

    • IAP允许在不依赖外部工具或设备的情况下,通过通信接口将新的程序代码写入FLASH。
    • 例如,当微控制器运行时,可以通过串口、SPI或I2C等通信接口接收新的固件,并将其写入FLASH,从而实现固件更新。

5. 在线编程与在应用中编程

在线编程(ICP)

在线编程(In-Circuit Programming, ICP)是一种更新程序存储器的方式,它通常通过JTAGSWD协议 或者Bootloader(系统引导加载程序)进行。使用这些工具,可以:

  • 更新程序存储器的全部内容(包括程序代码、配置等)。
  • JTAG和SWD协议通常需要外部编程工具来与微控制器通信,而Bootloader则可以通过串口或其他接口直接进行固件更新。

在应用中编程(IAP)

在应用中编程(In-Application Programming, IAP)则是在程序代码已经加载并运行时,通过程序内嵌的通信接口(例如串口、CAN总线、USB等)来更新固件。

  • IAP的优势是:设备可以在运行时实现固件的更新,无需外部编程工具,用户通过串口或者其他通信接口即可直接上传新的程序,系统会将新的程序写入FLASH。

2.闪存模块组织

这里只展示中容量产品的:

闪存主要分为三个部分:主存储器(程序存储器)启动程序代码(系统存储器)用户选项字节

对于闪存存储器接口寄存器实际上并不属于闪存,地址是40开头的,实际上就是一个普通的外设。可以看作是上面闪存的管理员,控制编程和擦除的过程。(为什么没有读?? -- 直接通过指针就可以对闪存进行读操作)

对于闪存的操作都是以页为单位的,和之前讲解的W25Q64模块原理是差不多的(SPI那一章节),只不过会更简单一点,但是针对特性是相同的:写入前必须擦除、擦除必须以最小单位为基础、擦除后数据后全变为1、数据只能1写0而不能0写成1、擦除后写入后得需要等待忙

3.基本结构

3.1 FPEC

FPEC(Flash Memory Program/Erase Controller)负责控制FLASH存储器的读写、擦除操作。为了防止未经授权的访问或修改,FPEC和与 相关的寄存器(如FLASH_CR)默认处于加锁状态,需要通过特定的解锁步骤才能对其进行操作 。

解锁FPEC的核心在于向寄存器FLASH_KEYR写入一组预定义的键值。这些键值为:

  • RDPRT键 (0x000000A5)
    用于读取保护相关操作。
  • KEY1 (0x45670123)
    解锁FPEC的第一步键值。
  • KEY2 (0xCDEF89AB)
    解锁FPEC的第二步键值。

解锁FPEC:

默认状态:

  • 在系统复位后,FPEC默认处于锁定状态,此时任何对FLASH_CR寄存器的写操作都会被禁止。
  • 必须执行解锁操作,才能使FPEC进入解锁状态,以允许对FLASH存储器进行编程或擦除。

读取FLASH_CR寄存器的LOCK位。如果LOCK位为1,说明FPEC处于锁定状态,需要解锁。

解锁过程需要向寄存器FLASH_KEYR按顺序写入两个键值:

  • 先写入KEY1(0x45670123)
  • 再写入KEY2(0xCDEF89AB)
  • 如果序列正确,FPEC将被解锁;如果序列错误,FPEC会锁死,直到下次复位才能再次尝试解锁。

解锁成功后,FLASH_CR寄存器的LOCK位会自动清零,表示FPEC已解锁。

如果解锁过程中,写入的键值序列不正确,FPEC将被锁死。在锁死状态下,任何操作都会被拒绝,直到系统复位重新初始化FPEC。因此,必须确保写入的键值顺序和内容完全正确。

提醒 :FLASH_CR位于闪存存储器接口寄存器当中,其实也就是位于FPEC当中。

加锁:

设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

解锁后,应尽快完成所需的FLASH操作,并及时加锁以确保存储器安全。如果在解锁后发生异常(如掉电),可能会导致FLASH操作失败,需要处理错误或重启系统。解锁和加锁操作应在对数据完整性和系统安全性有明确考虑的前提下进行。


使用指针访问存储器:

3.2 程序存储器

3.2.1 标准编程

在STM32F1系列的FLASH编程过程中,标准编程模式是一种通过逐个半字(16位)写入数据到FLASH存储器的方式。

  • FLASH存储器的最小可编程单位是半字(16位)

  • 编程时,CPU会将一个半字写入到指定的FLASH地址。此操作需要在FLASH_CR寄存器中启用PG(Programming)位。

  • 在写入过程中,FPEC会执行以下检查:

    1. 目标地址是否已被擦除 :如果目标地址未被擦除(值不是0xFFFF),编程不会执行,并在FLASH_SR寄存器的PGERR位标记错误。
    2. 地址是否写保护 :如果地址被写保护,编程不会执行,并在FLASH_SR寄存器的WRPRTERR位标记错误。
    3. 如果要写入的数值是0x0000,即使目标地址未被擦除,也可以正确写入,不会触发PGERR错误。

示例:

c 复制代码
#include "stm32f10x.h"  // 根据使用的库选择合适的头文件

void FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {
    // 检查FLASH是否空闲
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待FLASH空闲
    }
    
    // 启用编程模式
    FLASH->CR |= FLASH_CR_PG;
    
    // 写入目标地址和数据
    *(volatile uint16_t*)Address = Data;
    
    // 等待操作完成
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待编程完成
    }
    
    // 检查写入结果
    if (*(volatile uint16_t*)Address != Data) {
        // 编程失败,可以处理错误
    }
    
    // 清除编程模式
    FLASH->CR &= ~FLASH_CR_PG;
}

int main(void) {
    uint32_t TargetAddress = 0x08004000;  // 目标地址
    uint16_t DataToWrite = 0x1234;        // 要写入的数据

    // 解锁FLASH(前置操作,需确保解锁代码已正确实现)
    Unlock_FPEC();

    // 编程FLASH
    FLASH_ProgramHalfWord(TargetAddress, DataToWrite);

    // 加锁FLASH
    Lock_FPEC();

    while (1) {
        // 主循环
    }
}

注意事项

  1. 擦除前的检查
    编程前目标地址必须被擦除(值为0xFFFF)。如果未擦除,编程将失败并设置PGERR错误标志。
  2. 写保护检查
    如果目标地址被写保护,编程操作会被阻止,并设置WRPRTERR错误标志。
  3. 不能操作寄存器时机
    BSY位为1时,不能对任何FLASH相关寄存器(如FLASH_CRFLASH_KEYR)执行写操作,否则可能导致操作失败或FLASH状态不一致。
  4. EOP标志
    在编程成功后,FLASH_SR寄存器中的EOP(End of Programming)位会被设置为1,表示编程操作完成。
  5. 关闭编程模式
    在完成编程后,建议清除PG位,以关闭编程模式,避免误操作。
plain 复制代码
FLASH->CR &= ~FLASH_CR_PG;  // 清除PG位

3.2.2 页擦除

检查FLASH的状态 :读取FLASH_SR寄存器的BSY位。如果BSY位为1,说明当前有其他操作正在进行(如编程或擦除)。在BSY位清零(0)之前,不能执行擦除操作。

启用页擦除模式 :设置FLASH_CR寄存器的PER位为1,启用页擦除功能。

选择目标页 :在FLASH_AR寄存器中写入目标页的起始地址。目标页的起始地址必须对齐到页边界。

启动擦除 :设置FLASH_CR寄存器的STRT位为1,触发擦除操作。

等待擦除完成 :监视BSY位,当其由1变为0时,表示擦除操作完成。

验证擦除结果 :读取目标页的所有地址,确保每个字的值均为0xFFFF

关闭页擦除模式 :清除FLASH_CR寄存器的PER位。

示例:

plain 复制代码
void FLASH_ErasePage(uint32_t PageAddress) {
    // 检查FLASH是否空闲
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待FLASH空闲
    }

    // 启用页擦除模式
    FLASH->CR |= FLASH_CR_PER;

    // 设置目标页地址
    FLASH->AR = PageAddress;

    // 启动擦除
    FLASH->CR |= FLASH_CR_STRT;

    // 等待擦除完成
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待擦除完成
    }

    // 验证擦除结果
    for (int i = 0; i < PageSize / 2; i++) {
        if (*(volatile uint16_t*)(PageAddress + i * 2) != 0xFFFF) {
            // 擦除失败
        }
    }

    // 关闭页擦除模式
    FLASH->CR &= ~FLASH_CR_PER;
}

页擦除适用于清空指定页的数据,操作范围小,效率较高。

3.2.3 全擦除

检查FLASH的状态 :与页擦除相同,先检查BSY位,确保当前没有其他操作正在进行。

启用整片擦除模式 :设置FLASH_CR寄存器的MER位为1,启用整片擦除功能。

启动擦除 :设置FLASH_CR寄存器的STRT位为1,触发擦除操作。

等待擦除完成 :监视BSY位,当其由1变为0时,表示擦除操作完成。

验证擦除结果 :遍历所有用户区的FLASH地址,确保每个字的值均为0xFFFF

关闭整片擦除模式 :清除FLASH_CR寄存器的MER位。

示例:

plain 复制代码
void FLASH_EraseAll(void) {
    // 检查FLASH是否空闲
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待FLASH空闲
    }

    // 启用整片擦除模式
    FLASH->CR |= FLASH_CR_MER;

    // 启动擦除
    FLASH->CR |= FLASH_CR_STRT;

    // 等待擦除完成
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待擦除完成
    }

    // 验证擦除结果
    for (uint32_t Address = FLASH_BASE; Address < FLASH_END; Address += 2) {
        if (*(volatile uint16_t*)Address != 0xFFFF) {
            // 擦除失败
        }
    }

    // 关闭整片擦除模式
    FLASH->CR &= ~FLASH_CR_MER;
}

整片擦除适用于清空整个用户区,操作范围大,但需要更长时间。

3.3 选项字节

选择字节共有8个字节,由用户根据应用的需要配置;例如:可以选择使用硬件模式的看门狗或软件的看门狗。

  • 前4个字节:写保护配置(Write Protection)
  • 1个字节:读保护配置(Read Protection)
  • 1个字节:通用配置选项
  • 2个字节:用户数据(可用于存储自定义信息)

注意: 每个选项字节对应一个反码(Complement Byte),确保数据的一致性和可靠性。

在选择字节中每个32位的字被划分为下述格式:

RDP:写入RDPRT键(0x000000A5)后解除读保护

USER:配置硬件看门狗和进入停机/待机模式是否产生复位

Data0/1:用户可自定义使用

WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)

具体的去看闪存编程参考手册

3.3.1 编程

前置要求:

解锁FPEC控制器:

  • 向FLASH_OPTKEYR寄存器依次写入两个解锁密钥:

    • KEY1 = 0x45670123
    • KEY2 = 0xCDEF89AB
  • 成功解锁后,FLASH_CR寄存器中的OPTWRE位会自动置1,允许对选项字节进行操作。

检查FLASH状态:

  • 在进行编程操作前,必须检查FLASH_SR寄存器的BSY位,确保没有正在进行的编程或擦除操作

编程流程:

设置FLASH_CR寄存器的OPTPG位为1,进入选项字节编程模式。

将要写入的半字(16位数据)写入指定的选项字节地址。FPEC会自动计算反码,并将数据写入对应的反码地址。

监视FLASH_SR寄存器的BSY位,当其变为0时,表示编程操作完成。检查FLASH_SR寄存器的EOP位是否为1,以确认操作结束。

读取目标地址,验证写入的数据是否正确。验证时注意同时检查反码是否正确。

清除FLASH_CR寄存器的OPTPG位,退出选项字节编程模式。

特殊情况:

当将读保护选项从"未保护"更改为"保护"时,编程完成后目标设备会立即进入读保护模式。

如果将读保护选项从"保护"改为"未保护",FPEC会自动执行整片擦除操作(用户区的所有数据将被清空)。这是为了防止非法访问FLASH存储器中的数据。

如果只修改其他选项字节(如写保护或用户数据),则不会触发整片擦除操作。

示例:

c 复制代码
#include "stm32f10x.h"

void FLASH_ProgramOptionByte(uint32_t OptionByteAddress, uint16_t DataToProgram) {
    // 解锁选项字节控制
    FLASH->OPTKEYR = 0x45670123;  // 写入KEY1
    FLASH->OPTKEYR = 0xCDEF89AB;  // 写入KEY2

    // 检查FLASH是否空闲
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待FLASH空闲
    }

    // 启用选项字节编程模式
    FLASH->CR |= FLASH_CR_OPTPG;

    // 写入选项字节数据
    *(volatile uint16_t*)OptionByteAddress = DataToProgram;

    // 等待编程完成
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待FLASH空闲
    }

    // 检查编程是否成功
    if (*(volatile uint16_t*)OptionByteAddress != DataToProgram) {
        // 验证失败,执行错误处理
    }

    // 关闭选项字节编程模式
    FLASH->CR &= ~FLASH_CR_OPTPG;
}

3.3.2 擦除

在任何擦除或编程操作前,必须检查FLASH_SR寄存器的BSY位,确认没有其他正在进行的闪存操作。

擦除操作需要先解锁选项字节操作功能:

  1. FLASH_OPTKEYR寄存器依次写入两个解锁密钥:
    • KEY1 = 0x45670123
    • KEY2 = 0xCDEF89AB
  1. 解锁成功后,FLASH_CR寄存器中的OPTWRE位会被置为1,允许对选项字节区域进行操作。

设置FLASH_CR寄存器的OPTER位为1,进入选项字节擦除模式。

设置FLASH_CR寄存器的STRT位为1,启动擦除操作。

监视FLASH_SR寄存器的BSY位,等待其变为0,表示擦除操作完成。

读取目标地址,验证擦除操作是否成功(应读取到默认擦除值0xFFFF)。

示例:

c 复制代码
#include "stm32f10x.h"

void FLASH_EraseOptionBytes(void) {
    // 检查闪存是否空闲
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待闪存空闲
    }

    // 解锁选项字节控制
    FLASH->OPTKEYR = 0x45670123;  // 写入KEY1
    FLASH->OPTKEYR = 0xCDEF89AB;  // 写入KEY2

    // 设置选项字节擦除模式
    FLASH->CR |= FLASH_CR_OPTER;

    // 启动擦除操作
    FLASH->CR |= FLASH_CR_STRT;

    // 等待擦除完成
    while (FLASH->SR & FLASH_SR_BSY) {
        // 等待操作完成
    }

    // 验证擦除结果
    if (*(volatile uint16_t*)OptionByteAddress != 0xFFFF) {
        // 验证失败,处理错误
    }

    // 关闭选项字节擦除模式
    FLASH->CR &= ~FLASH_CR_OPTER;
}

4.器件电子签名

电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名

闪存容量寄存器:

  • 基地址:0x1FFF F7E0
  • 大小:16位

产品唯一身份标识寄存器:

  • 基地址: 0x1FFF F7E8
  • 大小:96位

5.实验-读取内部FLASH

📎15-1 读写内部FLASH.zip

System:

User:

相关推荐
智商偏低4 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen5 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森7 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白7 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D8 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术11 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt11 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘12 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang12 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n14 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件