嵌入式笔记6.1 Flash

针对 stm32L431

一、主要特点#

Flash存储器特点:

  • 失电后不丢失信息

  • 电可擦除

  • 可在线编程

  • 存储密度高

  • 功耗低和成本较低

Flash存储器用途:

  • 存储程序与常数
  • 在线编程,存储工作过程参数

stm32L431 Flash存储器:

闪存接口管理CPU AHB ICode和DCode对闪存的访问。它实现了擦除和编程闪存操作以及读写保护机制。 闪存接口通过指令预取和缓存系统加速代码执行。

  • 72位宽数据读(64位加8位ECC)
  • 72位宽数据写(64位加8位ECC)
  • 页面擦除(2 K字节)和整体擦除

闪存接口特性:

  • 闪存读操作
  • 闪存编程/擦除操作
  • 通过选项激活的读保护(RDP)
  • ICODE上的预取
  • 指令缓存:ICODE上的32条4 x 64位缓存线(1 KB RAM)
  • 数据缓存:DCODE上的8条4 x 64位缓存线(256B RAM)
  • 错误代码校正(ECC):64位双字的8位
  • 选项字节加载器
  • 低功耗模式

二、空间结构#

Flash模块 - 单银行组织#

stm32L431RCT6 Flash 地址范围是:0x0800_0000-0x0803_FFFF,扇区大小为2KB,共128扇区,256KB。


地址空间#

Flash 位于代码区中,地址范围(0x0800 0000 ~ 0x0804 0000)共 256 KB。

对于 STM32 微控制器,内部 Flash 存储器的地址空间通常是从 0x0800 0000 开始的。这是 STM32 大多数型号的常规设置,用于存放程序代码和常量数据。具体的起始地址和 Flash 大小可能会根据不同的 STM32 系列和型号有所不同,但起始地址 0x0800 0000 是一个常见的默认值。

stm32L431 最大 Flash 大小为 256 KB。

三、寄存器信息(stm32L431)#

寄存器简表#

偏移量 寄存器名 R/W 功能简述
0x00 Flash 访问控制寄存器(ACR) R/W 设置对 Flash 的访问控制
0x04 Flash 掉电密钥寄存器(PDKEYR) W 设定运行时掉电锁定的 Flash 解锁方法
0x08 Flash 密钥寄存器(KEYR) W 解锁 Flash 控制寄存器
0x0C Flash 选项密钥寄存器(OPTKEYR) W 解锁 Flash 字节访问控制寄存器
0x10 Flash 状态寄存器(SR) R/W 各种操作的标志位
0x14 Flash 控制寄存器(CR) R/W Flash 各种操作的控制位
0x18 Flash ECC 寄存器(ECCR) R 使用 ECC 检测与纠正数据
0x20 Flash 选项寄存器(OPTR) R/W 设置 Flash 的工作模式
0x24 Flash PCROP 起始地址寄存器(PCROP1SR) R/W 专有代码读出保护(Proprietary Code Read-Out Protection, PCROP),设置仅允许被执行的代码首地址,以防非法读出
0x28 Flash PCROP 结束地址寄存器(PCROP1ER) R/W 专有代码读出保护(Proprietary Code Read-Out Protection, PCROP),设置仅允许被执行的代码末地址,以防非法读出
0x2C Flash WRP 区域 A 地址寄存器(WRP1AR) R/W 设置 Flash 中用户扇区写保护功能,可防止因程序计数器(PC)跑飞而发生意外的写操作。设置区域 A 的区域信息
0x30 Flash WRP 区域 B 地址寄存器(WRP1BR) RW 设置 Flash 中用户扇区写保护功能,可防止因程序计数器(PC)跑飞而发生意外的写操作。设置区域 B 的区域信息

四、金葫芦 Flash 驱动构件信息#

五、Flash存储器的擦写模式#

在电路编程(ICP)方法用于更新闪存的整个内容,使用 JTAG、SWD 协议或引导加载程序将用户应用程序加载到微控制器中。ICP提供了快速高效的设计迭代,消除了不必要的封装处理或设备插座。

与 ICP 方法相比,在应用编程(IAP)可以使用微控制器支持的任何通信接口(I/O、USB、CAN、UART、I2C、SPI等)将编程数据下载到存储器中。IAP允许用户在应用程序运行时重新编程闪存。然而,部分应用程序必须事先通过ICP编程到闪存中。 如果在闪存操作期间设备重置,闪存内容将无法保证。 在对闪存进行编程/擦除操作期间,任何试图读取闪存的操作都会使总线暂停。一旦编程/擦除操作完成,读操作将正常进行。

在系统编程 (ISP)、在应用编程 (IAP)、在电路编程 (ICP) 这三种闪存编程方式的主要区别在于使用的工具、应用场景和实现方式。下面将详细讲解每种方式,并提供示例代码以帮助理解。

1、在电路编程 (ICP)#

ICP (In-Circuit Programming) 直接通过编程器连接到微控制器的调试接口(如 SWD 或 JTAG),进行编程和调试。

  • 使用场景:开发阶段、生产阶段。

  • 工具:需要使用专用的编程器,例如 ST-LINK、J-Link 等。

  • 优点

    • 直接访问微控制器的调试和编程接口(如 SWD 或 JTAG),速度快且可靠。
    • 支持完整的芯片调试功能,可以方便地进行调试和故障排查。
  • 缺点

    • 需要专用硬件编程器。
    • 通常需要将微控制器连接到开发板或测试夹具上

示例

  1. 使用 ST-LINK 编程器和 STM32CubeProgrammer 工具,将固件写入 STM32 微控制器。
  2. 连接 ST-LINK 到 STM32 的 SWD 接口。
  3. 使用 STM32CubeProgrammer 选择固件文件并执行编程。

没有具体的代码示例,因为这是使用外部工具直接进行操作。

2、在应用编程 (IAP)#

IAP (In-Application Programming) 是在微控制器运行的应用程序中实现的编程。设备可以在正常运行时通过特定的命令或事件触发固件更新。

  • 使用场景:远程更新、自更新、现场升级。

  • 实现方式

    • 应用程序代码中包含 IAP 功能模块,通过外部命令或触发条件启动IAP流程。
    • 使用串口、CAN、以太网等通信接口接收新固件数据。
  • 主要特点

    • 适用于现场升级和远程更新,系统不需要进入调试模式或暂停运行。
    • 可以部分更新Flash存储器的内容,避免影响其他功能模块的正常运行。
    • 通常包含启动加载器(Bootloader),负责在系统启动时检测并执行IAP功能。
  • 典型应用

    • 物联网设备、嵌入式系统等需要在现场或远程更新固件的场景。
    • 不便于拆卸或无法中断运行的系统。
  • 工具:无需额外工具,通过代码实现。

  • 优点

    • 允许在设备运行时更新固件,适用于需要远程更新或自我更新的设备。

    • 不需要额外的硬件工具。

  • 缺点

    • 需要在应用程序中实现可靠的编程算法和错误处理机制。

    • 由于是在应用程序运行时进行的更新,可能会有性能和稳定性的影响。

示例代码

  1. Bootloader:在系统启动时检测是否需要进入 IAP 模式。
  2. IAP程序:从通信接口接收新固件并写入 Flash 存储器。

Bootloader示例(C代码)

#include "stm32f4xx_hal.h"

#define APPLICATION_ADDRESS 0x08008000

void JumpToApplication(void) {
    uint32_t jump_address = *(__IO uint32_t*)(APPLICATION_ADDRESS + 4);
    void (*reset_handler)(void) = (void (*)(void))jump_address;
    __set_MSP(*(__IO uint32_t*)APPLICATION_ADDRESS);
    reset_handler();
}

int main(void) {
    HAL_Init();

    // 检查是否有更新命令(例如通过按键或通信接口接收命令)
    if (/* 更新命令检测逻辑 */) {
        // 执行IAP程序
        IAP_UpdateFirmware();
    } else {
        // 跳转到应用程序
        JumpToApplication();
    }

    while (1) {
    }
}

IAP更新固件示例(C代码)

#include "stm32f4xx_hal.h"

void IAP_UpdateFirmware(void) {
    // 解锁Flash
    HAL_FLASH_Unlock();

    // 擦除目标扇区
    FLASH_Erase_Sector(FLASH_SECTOR_2, VOLTAGE_RANGE_3);

    // 从通信接口接收新固件数据并写入Flash
    uint32_t address = APPLICATION_ADDRESS;
    uint32_t data = 0;
    while (/* 接收数据逻辑 */) {
        // 读取数据
        data = /* 接收的数据 */;
        // 写入Flash
        HAL_FLASH_Program(TYPEPROGRAM_WORD, address, data);
        address += 4;
    }

    // 锁定Flash
    HAL_FLASH_Lock();
}

int main(void) {
    HAL_Init();

    // 检查是否有更新命令
    if (/* 更新命令检测逻辑 */) {
        // 执行IAP更新
        IAP_UpdateFirmware();
    } else {
        // 跳转到应用程序
        JumpToApplication();
    }

    while (1) {
    }
}

在实际应用中,IAP实现可能会更加复杂,涉及数据校验、错误处理、通信协议等。具体实现需要根据项目需求和硬件环境进行设计。

使用 UART 接收新固件并编程

#include "stm32f4xx_hal.h"
#include <string.h>

#define APP_ADDRESS 0x08008000  // 应用程序起始地址
#define UART_BUFFER_SIZE 1024

UART_HandleTypeDef huart1;
uint8_t uart_buffer[UART_BUFFER_SIZE];

void SystemClock_Config(void);
void Error_Handler(void);
void UART_Init(void);
void Flash_Write(uint32_t address, uint8_t* data, uint32_t length);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    UART_Init();

    while (1)
    {
        // 假设通过 UART 接收新固件数据
        HAL_UART_Receive(&huart1, uart_buffer, UART_BUFFER_SIZE, HAL_MAX_DELAY);
        
        // 将接收到的数据写入闪存
        Flash_Write(APP_ADDRESS, uart_buffer, UART_BUFFER_SIZE);
    }
}

void Flash_Write(uint32_t address, uint8_t* data, uint32_t length)
{
    HAL_FLASH_Unlock();
    for (uint32_t i = 0; i < length; i += 4)
    {
        uint32_t word = *(uint32_t*)(data + i);
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + i, word) != HAL_OK)
        {
            Error_Handler();
        }
    }
    HAL_FLASH_Lock();
}

void UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler();
    }
}

void SystemClock_Config(void)
{
    // 系统时钟配置代码
}

void Error_Handler(void)
{
    while (1)
    {
        // 错误处理
    }
}

3、在系统编程 (ISP)#

ISP (In-System Programming) 通过标准通信接口(如 UART、USB、I²C、SPI)进行编程。这种方式适用于设备已经装配完成后进行固件更新。

  • 使用场景:现场升级、批量更新。

  • 主要特点

    • 适用于初始编程和固件更新。
    • 编程过程中微控制器通常处于复位或暂停状态,不能执行正常的应用程序。
    • 支持完全擦除和重新编程整个 Flash 存储器。
  • 典型应用

    • 在生产线上对新芯片进行编程。
    • 开发过程中进行调试和代码下载。
  • 工具:可以使用标准通信接口和相关软件工具,例如 STM32CubeProgrammer。

  • 优点

    • 适用于已经安装在设备中的微控制器,可以通过标准通信接口进行编程。
    • 方便现场更新或批量更新。
  • 缺点

    • 编程速度通常较慢,取决于通信接口的速度。
    • 需要可靠的通信连接和协议实现。

示例(通过 UART 和 STM32CubeProgrammer 工具):

  1. 将设备通过 UART 接口连接到计算机。
  2. 使用 STM32CubeProgrammer 选择固件文件和通信端口,执行编程。

示例代码(同样使用 UART 接收新固件):
与 IAP 示例类似,只是通过不同的方式触发固件更新。下面代码展示了如何通过 UART 接收并写入新固件:

#include "stm32f4xx_hal.h"
#include <string.h>

#define APP_ADDRESS 0x08008000  // 应用程序起始地址
#define UART_BUFFER_SIZE 1024

UART_HandleTypeDef huart1;
uint8_t uart_buffer[UART_BUFFER_SIZE];

void SystemClock_Config(void);
void Error_Handler(void);
void UART_Init(void);
void Flash_Write(uint32_t address, uint8_t* data, uint32_t length);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    UART_Init();

    while (1)
    {
        // 假设通过 UART 接收新固件数据
        HAL_UART_Receive(&huart1, uart_buffer, UART_BUFFER_SIZE, HAL_MAX_DELAY);
        
        // 将接收到的数据写入闪存
        Flash_Write(APP_ADDRESS, uart_buffer, UART_BUFFER_SIZE);
    }
}

void Flash_Write(uint32_t address, uint8_t* data, uint32_t length)
{
    HAL_FLASH_Unlock();
    for (uint32_t i = 0; i < length; i += 4)
    {
        uint32_t word = *(uint32_t*)(data + i);
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + i, word) != HAL_OK)
        {
            Error_Handler();
        }
    }
    HAL_FLASH_Lock();
}

void UART_Init(void)
{
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler();
    }
}

void SystemClock_Config(void)
{
    // 系统时钟配置代码
}

void Error_Handler(void)
{
    while (1)
    {
        // 错误处理
    }
}

注意:实际应用中需要加入更多的错误处理和安全机制,确保固件更新过程的安全性和可靠性。

六、系统存储器(System Memory)#

地址:0x1FFF 0000 - 0x1FFF FFFF (系统内存/一次性可编程存储器(OTP)/选项字节)

闪存组织为 72 位宽的存储单元(64 位加 8 位 ECC),可用于存储代码和数据常量。

包含以下内容的信息块:

  • 系统内存

系统存储区是用户不能访问的区域,它在芯片出厂时已经固化了启动代码,它负责实现串口、USB以及CAN等ISP烧录功能。

设备在系统内存启动模式下从该内存启动。该区域由STMicroelectronics保留使用,包含用于通过以下接口之一重新编程闪存的引导加载程序:USART1, USART2, USART3, USB (DFU), I2C1, I2C2, I2C3, SPI1, SPI2, SPI3。该程序在设备制造时由STMicroelectronics编程,并受到防止伪写/擦除操作的保护。有关详细信息,请参考www.st.com上的AN2606。

  • OTP区域

OTP(One Time Program),指的是只能写入一次的存储区域,容量为 1K 字节(128个双字)用户数据的 OTP(一次性可编程)字节。OTP 数据不能被擦除且只能写入一次。如果只有一个位为0,则整个双字不能再被写入,即使是0x0000 0000 0000 0000的值也不行。

  • 选项字节

选项字节用于配置FLASH的读写保护、电源管理中的BOR级别、软件/硬件看门狗等功能,这部分共32字节。可以通过修改FLASH的选项控制寄存器修改。

在 STM32 微控制器中,地址范围 0x1FFF 0000 到 0x1FFF FFFF 通常是系统存储器(System Memory)的区域。系统存储器包含了STM32的内置引导加载程序(bootloader),该程序用于 ISP(In-System Programming)和 IAP(In-Application Programming)功能,允许通过外部接口(如 UART、USB 等)进行固件更新和其他系统级操作。

具体来说:

  • 起始地址:0x1FFF 0000
  • 结束地址:0x1FFF FFFF
  • 用途:系统存储器,包含内置引导加载程序

系统存储器的内容和功能由 STMicroelectronics 预先编程,并且在正常使用中是只读的。这部分存储器的具体大小和功能可以根据不同的 STM32 系列和型号有所不同。以下是几个常见的 STM32 系列在这个地址范围内的系统存储器的大小和用途:

STM32F1系列

  • 系统存储器大小: 8KB
  • 功能: 包含内置引导加载程序,支持通过USART和CAN接口进行引导加载。

STM32F4系列

  • 系统存储器大小: 30KB
  • 功能: 包含内置引导加载程序,支持通过USART、I2C、SPI、USB DFU等接口进行引导加载。

STM32F7系列

  • 系统存储器大小: 112KB
  • 功能: 包含更高级的引导加载程序,支持多种接口(如USART、I2C、SPI、USB DFU、SDIO等)。

引导加载程序功能

系统存储器中的引导加载程序提供了一些常见功能:

  • 固件更新:通过串行接口(如 UART、USB 等)进行固件更新。
  • 引导模式选择:根据 BOOT 引脚配置或选项字节来选择引导模式。
  • 诊断和调试:提供基本的诊断和调试功能。

引导配置

STM32 微控制器的引导模式可以通过引脚(BOOT0 和 BOOT1)或选项字节来配置。例如,对于 STM32F4 系列:

  • BOOT0 = 1, BOOT1 = 0:启动时,从系统存储器(0x1FFF 0000)开始执行,引导加载程序会运行。
  • BOOT0 = 0, BOOT1 = x:启动时,从用户Flash存储器(0x0800 0000)开始执行,这是默认配置。

综上所述,地址范围 0x1FFF 0000 到 0x1FFF FFFF 通常包含 STM32 微控制器的系统存储器区域,内置了引导加载程序,用于固件更新和系统级操作。具体的功能和大小因 STM32 系列和型号不同而异。可以参考具体型号的参考手册和数据手册以获取详细信息。


选项字节(Option Bytes)#

在 STM32 微控制器中,选项字节(Option Bytes)是一组专门的配置字节,用于配置微控制器的一些关键特性和行为,包括引导配置、安全保护、时钟设置、读写保护等。选项字节存储在一个特殊的内存(FLASH)区域,并且通常只能通过特定的编程接口(如ST-Link、JTAG 等)进行修改。

以下是STM32选项字节的一些常见功能和用途:

引导配置(Boot Configuration)

  • nBOOT0、nBOOT1:配置引导模式(从 Flash、系统存储器或 SRAM 启动)。
  • nBOOT_SEL:选择引导配置的来源(引脚或选项字节)。

读写保护(Read/Write Protection)

  • WRP(Write Protection):配置 Flash 存储器的写保护区域,防止意外写操作。
  • RDP(Read Protection Level):设置读保护等级,防止未经授权的访问。常见的保护等级有:
    • Level 0:无保护。
    • Level 1:读保护激活,禁止通过调试接口读取内部存储器。
    • Level 2:最高级别保护,彻底禁止调试接口和外部编程。

安全保护(Security Options)

  • PCROP(Proprietary Code Read-Out Protection):设置部分 Flash 区域为专有代码保护,防止被读取或修改。
  • SEC:安全功能选项,启用一些高级的安全功能。

时钟和复位配置(Clock and Reset Configuration)

  • BOR_LEV(Brown-Out Reset Level):配置欠压复位阈值。
  • nRST_STOP:控制停止模式下的复位行为。
  • nRST_STDBY:控制待机模式下的复位行为。

用户配置(User Configuration)

  • USER(User Option Bytes):用户自定义的选项字节,用于特定应用的配置需求。

具体实例

以下是STM32F4系列微控制器的一些具体选项字节配置:(不同系列的 Option Bytes 字段地址和内容可能不同)

字段名称 地址 位位置 作用说明 详细说明
RDP 0x1FFF C000 [7:0] 读保护等级(Read Protection Level) 0xAA:无保护(Level 0),0x00:读保护(Level 1),其他值:芯片级保护(Level 2)
BOR_LEV 0x1FFF C000 [3:2] 欠压复位阈值(Brown-Out Reset Level) 选择欠压复位的阈值,4个级别:0b00:低,0b01:中低,0b10:中高,0b11:高
nRST_STOP 0x1FFF C000 6 停止模式复位(Reset when entering Stop mode) 0:启用复位,1:禁用复位
nRST_STDBY 0x1FFF C000 7 待机模式复位(Reset when entering Standby mode) 0:启用复位,1:禁用复位
WDG_SW 0x1FFF C008 0 独立看门狗软件使能(Independent Watchdog Software Enable) 0:硬件看门狗,1:软件看门狗
nBOOT1 0x1FFF C008 4 引导配置1(Boot Configuration 1) 0:根据引导引脚配置,1:从用户Flash启动
SRAM2_PE 0x1FFF C008 9 SRAM2奇偶校验使能(SRAM2 Parity Check Enable) 0:禁用,1:使能
SRAM2_RST 0x1FFF C008 10 SRAM2复位行为(SRAM2 Erase when System Reset) 0:复位保持,1:复位清除
WRP_SECTOR0 0x1FFF C014 [7:0] 扇区0写保护(Write Protection for Sector 0) 0:写保护,1:无保护
WRP_SECTOR1 0x1FFF C014 [15:8] 扇区1写保护(Write Protection for Sector 1) 0:写保护,1:无保护
WRP_SECTOR2 0x1FFF C014 [23:16] 扇区2写保护(Write Protection for Sector 2) 0:写保护,1:无保护
WRP_SECTOR3 0x1FFF C014 [31:24] 扇区3写保护(Write Protection for Sector 3) 0:写保护,1:无保护
nBOOT0 0x1FFF C018 0 引导配置0(Boot Configuration 0) 0:根据引导引脚配置,1:从系统存储器启动
nBOOT_SEL 0x1FFF C018 1 引导选择(Boot Selection) 0:引导引脚有效,1:选项字节配置有效
PCROP_RDP 0x1FFF C01C 31 PCROP读保护(Proprietary Code Read-Out Protection) 0:禁用,1:启用
SEC_SIZE 0x1FFF C020 [7:0] 安全区域大小(Security Size) 设置安全区域的大小,以扇区为单位
OPTION_BYTE_KEY 0x1FFF C024 [31:0] 选项字节解锁密钥(Option Byte Unlock Key) 通常为0x08192A3B,用于解锁选项字节的编程功能

选项字节编程

选项字节通常通过以下方式进行编程:

  • ST-Link Utility:使用 ST-Link 调试器和 ST 提供的工具软件。
  • STM32CubeProgrammer:ST 官方提供的多功能编程工具。
  • OpenOCD:开源的调试工具,支持通过 JTAG/SWD 接口进行编程。

示例代码

使用 STM32Cube HAL 库修改选项字节的示例代码:

FLASH_OBProgramInitTypeDef OBInit;
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
HAL_FLASHEx_OBGetConfig(&OBInit);

OBInit.OptionType = OPTIONBYTE_WRP;
OBInit.WRPState = OB_WRPSTATE_ENABLE;
OBInit.WRPSector = FLASH_WRP_SECTORS; // 需要保护的扇区
HAL_FLASHEx_OBProgram(&OBInit);

HAL_FLASH_OB_Launch();
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();

综上所述,选项字节用于配置STM32微控制器的关键特性和行为,包括引导模式、读写保护、安全设置、时钟和复位配置等。每个系列的具体选项字节配置和用途可能有所不同,详细信息可以参考具体型号的参考手册。

七、Flash 擦除与编程操作#

1、Flash读写结构#

Flash 存储元:

左:“0”状态

右:“1”状态

控制栅加足够正电压时,浮空栅储存大量负电荷,为“0”态;

控制栅不加正电压时,浮空栅少带或不带负电荷,为“1”态。

左:编程,写”0“

右:擦除,写“1”

左:读“0”

右:读“1”

有三种操作:擦除、编程、读取

  • 写入:快擦(所有单元为1)-- 编程(需要之处写0)
  • 读出:控制栅加正电压,若状态为0,则读出电路检测不到电流;若状态为1,则能检测到电流。

Falsh擦除与写入的含义:

对 Flash 存储器的读写不同于对一般的 RAM 读写,需要专门的编程过程。

Flash 编程的基本操作有两种:擦除(Erase)和写入(Program)。

  • 擦除操作的含义是将存储单元的内容由二进制的 0 变成 1
  • 写入操作的含义是将存储单元的某些位由二进制的 1 变成 0
  • Flash 在线编程的写入操作是以字为单位进行的。

Flash擦除与写入的基本要求:

在执行写入操作之前,要确保写入区在上一次擦除之后没有被写入过,即写入区是空白的。所以,在写入之前一般都要先执行擦除操作。Flash 在线编程的擦除操作包括整体擦除和以 m 个字为单位的擦除。这 m 个字在不同厂商或不同系列的 MCU 中,其称呼不同,有的称为“块”,有的称为“页”,有的称为“扇区”等等。它表示在线擦除的最小度量单位,我们可以统一使用扇区这个术语。

2、解锁闪存#

复位后,闪存控制寄存器(FLASH_CR)中的写操作是不允许的,以保护闪存免受可能由于电气干扰等原因导致的不必要操作。以下顺序用于解锁此寄存器:

  1. 在闪存密钥寄存器(FLASH_KEYR)中写入 KEY1 = 0x45670123
  2. 在 FLASH_KEYR 寄存器中写入 KEY2 = 0xCDEF89AB

任何错误的顺序将锁定 FLASH_CR 寄存器直到下次系统复位。如果密钥序列错误,将检测到总线错误并产生硬故障中断。

通过在FLASH_CR 寄存器中设置 LOCK 位,可以再次通过软件锁定 FLASH_CR 寄存器。

注意:当闪存状态寄存器(FLASH_SR)中的 BSY 位被设置时,不能写入 FLASH_CR 寄存器。任何在 BSY 位被设置时写入的尝试都会导致AHB总线暂停,直到 BSY 位被清除。

3、Flash主存储器擦除顺序#

Flash 存储器擦除操作可以在页面级别或整个 Flash 存储器(批量擦除)上执行。批量擦除不影响信息块(系统Flash、OTP和选项字节)。

页面擦除#

要擦除一个页面(2 KB),请按照以下步骤进行:

  1. 通过检查Flash状态寄存器(FLASH_SR)中的BSY位,确认没有Flash存储器操作正在进行。
  2. 检查并清除由于先前编程导致的所有错误编程标志。如果没有清除,将设置PGSERR。
  3. 在Flash控制寄存器(FLASH_CR)中设置PER位并选择要擦除的页面(PNB)。
  4. 在FLASH_CR寄存器中设置STRT位。
  5. 等待FLASH_SR寄存器中的BSY位清除。

注意:当设置STRT位时,内部振荡器HSI16(16 MHz)会自动启用,当STRT位清除时会自动禁用,除非之前在RCC_CR寄存器中使用HSION启用了HSI16。
如果页面擦除是写保护区域(通过WRP或PCROP)的一部分,将设置WRPERR并中止页面擦除请求。

批量擦除#

要执行批量擦除,请按照以下步骤进行:

  1. 通过检查FLASH_SR寄存器中的BSY位,确认没有Flash存储器操作正在进行。
  2. 检查并清除由于先前编程导致的所有错误编程标志。如果没有清除,将设置PGSERR。
  3. 在Flash控制寄存器(FLASH_CR)中设置MER1位。
  4. 在FLASH_CR寄存器中设置STRT位。
  5. 等待Flash控制寄存器(FLASH_CR)中的BSY位清除。

注意:当设置STRT位时,内部振荡器HSI16(16 MHz)会自动启用,当STRT位清除时会自动禁用,除非之前在RCC_CR寄存器中使用HSION启用了HSI16。
如果Flash存储器包含写保护区域(通过WRP或PCROP),将设置WRPERR并中止批量擦除请求。

4、Flash主存储器编程顺序#

Flash 存储器每次编程 72 位(64 位+8 位 ECC)。除非写入的数据全为零,否则不允许在先前编程的地址中进行编程,任何尝试将设置Flash 状态寄存器(FLASH_SR)中的 PROGERR 标志。

只能编程双字(2×32位数据)。

  • 任何尝试写入字节或半字的操作将设置 FLASH_SR 寄存器中的 SIZERR 标志。
  • 任何尝试写入未与双字地址对齐的双字将设置 FLASH_SR 寄存器中的 PGAERR 标志。

标准编程#

标准模式下的Flash存储器编程顺序如下:

  1. 通过检查Flash状态寄存器(FLASH_SR)中的BSY位,确认没有Flash主存储器操作正在进行。

  2. 检查并清除由于先前编程导致的所有错误编程标志。如果没有清除,将设置PGSERR。

  3. 在Flash控制寄存器(FLASH_CR)中设置PG位。

  4. 在所需的内存地址(主存储块或OTP区域内)执行数据写入操作。只能编程双字。

    • 在与双字对齐的地址中写入第一个字。
    • 写入第二个字。
  5. 等待FLASH_SR寄存器中的BSY位清除。

  6. 检查FLASH_SR寄存器中的EOP标志(表示编程操作成功),并通过软件清除它。

  7. 如果没有更多编程请求,请清除FLASH_CR寄存器中的PG位。

注意:当Flash接口收到正确的序列(双字)时,编程会自动启动,并设置BSY位。当PG位被设置时,内部振荡器HSI16(16 MHz)会自动启用,当PG位被清除时会自动禁用,除非之前在RCC_CR寄存器中使用HSION启用了HSI16。
如果用户只需要编程一个字,则必须使用擦除值0xFFFF FFFF来完成双字,以自动启动编程。
ECC从要编程的双字计算得出。

快速编程#

此模式允许编程一行(32个双字),通过消除在编程前验证Flash位置的需要以及避免每个双字的高电压上升和下降时间来减少页面编程时间。在快速编程期间,CPU时钟频率(HCLK)必须至少为8 MHz。
只有主存储器可以在快速编程模式下编程。
标准模式下的Flash主存储器编程顺序如下:

  1. 执行要编程的银行的批量擦除。如果不执行,将设置PGSERR。

  2. 通过检查Flash状态寄存器(FLASH_SR)中的BSY位,确认没有Flash主存储器操作正在进行。

  3. 检查并清除由于先前编程导致的所有错误编程标志。

  4. 在Flash控制寄存器(FLASH_CR)中设置FSTPG位。

  5. 写入32个双字以编程一行。只能编程双字:

    • 在与双字对齐的地址中写入第一个字。
    • 写入第二个字。
  6. 等待FLASH_SR寄存器中的BSY位清除。

  7. 检查FLASH_SR寄存器中的EOP标志(表示编程操作成功),并通过软件清除它。

  8. 如果没有更多编程请求,请清除FLASH_CR寄存器中的FSTPG位。

注意:如果在同一银行进行读取操作时尝试在快速编程模式下写入Flash,编程将中止,没有任何系统通知(不设置错误标志)。
当Flash接口收到第一个双字时,编程会自动启动。当第一次应用高电压时,设置BSY位,当最后一个双字被编程或发生错误时,清除BSY位。当设置FSTPG位时,内部振荡器HSI16(16 MHz)会自动启用,当FSTPG位被清除时会自动禁用,除非之前在RCC_CR寄存器中使用HSION启用了HSI16。
32个双字必须连续写入。高电压在编程期间保持在Flash上。两次双字写入请求之间的最大时间为编程时间(约20微秒)。如果第二个双字在此编程时间后到达,快速编程将中断并设置MISSERR。
在两次擦除之间,整个行的高电压时间不得超过8毫秒。这通过32个双字连续写入并且时钟系统频率大于或等于8MHz来保证。当设置快速编程时,内部超时计数器计数7毫秒,并在超时结束时停止编程。在这种情况下,设置FASTERR位。
如果发生错误,高电压会停止,下一个要编程的双字不会被编程。无论如何,所有先前的双字都已正确编程。

器件电子签名#

  • 唯一设备 ID 寄存器(96 位):可用于在对内部 Flash 进行编程前将唯一 ID 与软件加密原语和协议结合使用时用作安全密钥以提高 Flash 中代码的安全性

  • Flash 大小寄存器:只读 = 0xXXXX,其中 X 是出厂前编程的;

    位 15:0 F_ID(15:0): Flash 大小。此处的位字段指示以 KB 表示的设备 Flash 大小。例如, 0x0400 对应于 1024 KB。

作者:YZXE

出处:https://www.cnblogs.com/YZXE/p/18244259

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   一只心耳  阅读(218)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示