以下将为你详细介绍 STM32 的 Flash 操作相关知识,包含基本原理、操作步骤,并给出具体的代码示例。

1. STM32 Flash 基本原理

1.1 Flash 存储器结构

STM32 的 Flash 存储器用于存储程序代码和一些需要掉电保存的数据。它通常被划分为多个扇区,每个扇区有固定的大小。不同系列的 STM32 芯片,其 Flash 扇区大小和数量可能不同。例如,STM32F103 系列的 Flash 扇区大小有 1KB、2KB、4KB、16KB、64KB 和 128KB 等。

1.2 Flash 操作限制
  • 写操作:在进行写操作之前,必须先擦除相应的扇区。因为 Flash 的写操作只能将 “1” 变为 “0”,而擦除操作会将整个扇区的数据都变为 “1”。
  • 擦除操作:擦除操作是以扇区为单位进行的,不能只擦除一个字节或一个字。
  • 保护机制:为了防止误操作或恶意修改,STM32 的 Flash 提供了写保护和读保护机制。
  • 2. STM32 Flash 操作步骤

    2.1 解锁 Flash

    在进行写或擦除操作之前,需要先解锁 Flash。因为 Flash 默认是处于锁定状态的,以防止误操作。

    2.2 擦除扇区

    如果要写入新的数据,需要先擦除相应的扇区。可以选择擦除单个扇区或多个扇区。

    2.3 写入数据

    在扇区擦除完成后,就可以向 Flash 中写入数据了。写入数据时需要注意数据的对齐方式,通常是以半字(16 位)或字(32 位)为单位进行写入。

    2.4 锁定 Flash

    在完成写或擦除操作后,需要锁定 Flash,以保护数据安全。

    3. 代码示例(以 STM32F103 为例)

    #include "stm32f10x.h"
    
    // 解锁Flash
    void FLASH_Unlock(void)
    {
        if ((FLASH->CR & FLASH_CR_LOCK) != 0)
        {
            FLASH->KEYR = FLASH_KEY1;
            FLASH->KEYR = FLASH_KEY2;
        }
    }
    
    // 锁定Flash
    void FLASH_Lock(void)
    {
        FLASH->CR |= FLASH_CR_LOCK;
    }
    
    // 擦除指定扇区
    uint8_t FLASH_EraseSector(uint32_t Sector)
    {
        uint32_t timeout = 0xFFFF;
        FLASH_Unlock();
        FLASH->CR |= FLASH_CR_PER;
        FLASH->AR = Sector;
        FLASH->CR |= FLASH_CR_STRT;
        while ((FLASH->SR & FLASH_SR_BSY) != 0)
        {
            if (timeout-- == 0)
            {
                FLASH->CR &= ~FLASH_CR_PER;
                FLASH_Lock();
                return 1; // 擦除超时
            }
        }
        if ((FLASH->SR & FLASH_SR_EOP) != 0)
        {
            FLASH->SR |= FLASH_SR_EOP;
        }
        FLASH->CR &= ~FLASH_CR_PER;
        FLASH_Lock();
        return 0; // 擦除成功
    }
    
    // 写入半字数据到指定地址
    uint8_t FLASH_WriteHalfWord(uint32_t Address, uint16_t Data)
    {
        uint32_t timeout = 0xFFFF;
        FLASH_Unlock();
        FLASH->CR |= FLASH_CR_PG;
        *(__IO uint16_t*)Address = Data;
        while ((FLASH->SR & FLASH_SR_BSY) != 0)
        {
            if (timeout-- == 0)
            {
                FLASH->CR &= ~FLASH_CR_PG;
                FLASH_Lock();
                return 1; // 写入超时
            }
        }
        if ((FLASH->SR & FLASH_SR_EOP) != 0)
        {
            FLASH->SR |= FLASH_SR_EOP;
        }
        FLASH->CR &= ~FLASH_CR_PG;
        FLASH_Lock();
        return 0; // 写入成功
    }
    
    // 从指定地址读取半字数据
    uint16_t FLASH_ReadHalfWord(uint32_t Address)
    {
        return *(__IO uint16_t*)Address;
    }
    
    int main(void)
    {
        uint32_t SectorAddress = 0x08004000; // 假设要操作的扇区地址
        uint16_t WriteData = 0x1234;
        uint16_t ReadData;
    
        // 擦除扇区
        if (FLASH_EraseSector(SectorAddress) == 0)
        {
            // 写入数据
            if (FLASH_WriteHalfWord(SectorAddress, WriteData) == 0)
            {
                // 读取数据
                ReadData = FLASH_ReadHalfWord(SectorAddress);
                // 这里可以根据读取的数据进行相应的处理
            }
        }
    
        while (1)
        {
            // 主循环
        }
    }
    

    4. 代码解释

  • FLASH_Unlock():用于解锁 Flash,通过向FLASH->KEYR寄存器写入特定的解锁密钥来实现。
  • FLASH_Lock():用于锁定 Flash,将FLASH->CR寄存器的LOCK位置 1。
  • FLASH_EraseSector():擦除指定扇区。首先解锁 Flash,然后设置擦除标志位,启动擦除操作,等待擦除完成,最后清除擦除标志位并锁定 Flash。
  • FLASH_WriteHalfWord():向指定地址写入半字数据。首先解锁 Flash,然后设置写入标志位,写入数据,等待写入完成,最后清除写入标志位并锁定 Flash。
  • FLASH_ReadHalfWord():从指定地址读取半字数据。
  • 5. 注意事项

  • 地址选择:在选择 Flash 地址时,要确保该地址不会影响到程序代码的正常运行。
  • 擦除操作:擦除操作会将整个扇区的数据都清除,所以在擦除之前要确保该扇区的数据已经备份或不再需要。
  • 数据对齐:写入数据时要注意数据的对齐方式,通常是以半字或字为单位进行写入。
  • 当不使用寄存器直接操作时,我们可以利用 STM32 HAL 库来进行 Flash 操作。HAL(Hardware Abstraction Layer)库是 ST 公司提供的硬件抽象层,它对底层寄存器操作进行了封装,使得代码的编写更加简洁和易于理解。以下是一个使用 STM32 HAL 库进行 Flash 操作的示例,以 STM32F4 系列为例:

    1. 代码示例

    #include "stm32f4xx_hal.h"
    
    // 定义要操作的扇区和地址
    #define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_6   // 假设使用扇区6
    #define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_7 - 1
    
    // 解锁Flash
    void Flash_Unlock(void) {
        HAL_FLASH_Unlock();
    }
    
    // 锁定Flash
    void Flash_Lock(void) {
        HAL_FLASH_Lock();
    }
    
    // 擦除指定扇区
    uint8_t Flash_EraseSector(uint32_t Sector) {
        FLASH_EraseInitTypeDef EraseInitStruct;
        uint32_t SectorError;
    
        EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
        EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
        EraseInitStruct.Sector = Sector;
        EraseInitStruct.NbSectors = 1;
    
        if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
            return 1; // 擦除失败
        }
        return 0; // 擦除成功
    }
    
    // 写入32位数据到指定地址
    uint8_t Flash_WriteWord(uint32_t Address, uint32_t Data) {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) != HAL_OK) {
            return 1; // 写入失败
        }
        return 0; // 写入成功
    }
    
    // 从指定地址读取32位数据
    uint32_t Flash_ReadWord(uint32_t Address) {
        return *(__IO uint32_t*)Address;
    }
    
    int main(void) {
        HAL_Init();
    
        uint32_t WriteData = 0xABCD1234;
        uint32_t ReadData;
    
        // 解锁Flash
        Flash_Unlock();
    
        // 擦除扇区
        if (Flash_EraseSector(FLASH_SECTOR_6) == 0) {
            // 写入数据
            if (Flash_WriteWord(FLASH_USER_START_ADDR, WriteData) == 0) {
                // 读取数据
                ReadData = Flash_ReadWord(FLASH_USER_START_ADDR);
                // 这里可以根据读取的数据进行相应的处理
            }
        }
    
        // 锁定Flash
        Flash_Lock();
    
        while (1) {
            // 主循环
        }
    }
    

    2. 代码解释

    2.1 头文件和宏定义
    #include "stm32f4xx_hal.h"
    #define FLASH_USER_START_ADDR   ADDR_FLASH_SECTOR_6
    #define FLASH_USER_END_ADDR     ADDR_FLASH_SECTOR_7 - 1
    
  • #include "stm32f4xx_hal.h":包含 STM32F4 系列的 HAL 库头文件。
  • FLASH_USER_START_ADDRFLASH_USER_END_ADDR:定义了要操作的 Flash 扇区的起始和结束地址。
  • 2.2 解锁和锁定 Flash
    void Flash_Unlock(void) {
        HAL_FLASH_Unlock();
    }
    
    void Flash_Lock(void) {
        HAL_FLASH_Lock();
    }
    
  • HAL_FLASH_Unlock():调用 HAL 库函数解锁 Flash。
  • HAL_FLASH_Lock():调用 HAL 库函数锁定 Flash。
  • 2.3 擦除指定扇区
    uint8_t Flash_EraseSector(uint32_t Sector) {
        FLASH_EraseInitTypeDef EraseInitStruct;
        uint32_t SectorError;
    
        EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
        EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
        EraseInitStruct.Sector = Sector;
        EraseInitStruct.NbSectors = 1;
    
        if (HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError) != HAL_OK) {
            return 1; // 擦除失败
        }
        return 0; // 擦除成功
    }
    
  • FLASH_EraseInitTypeDef:定义一个 Flash 擦除初始化结构体,用于配置擦除操作的参数。
  • HAL_FLASHEx_Erase():调用 HAL 库函数进行扇区擦除操作。
  • 2.4 写入 32 位数据到指定地址
    uint8_t Flash_WriteWord(uint32_t Address, uint32_t Data) {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, Address, Data) != HAL_OK) {
            return 1; // 写入失败
        }
        return 0; // 写入成功
    }
    
  • HAL_FLASH_Program():调用 HAL 库函数向指定地址写入 32 位数据。
  • 2.5 从指定地址读取 32 位数据
    uint32_t Flash_ReadWord(uint32_t Address) {
        return *(__IO uint32_t*)Address;
    }
    
  • 直接从指定地址读取 32 位数据。
  • 3. 注意事项

  • 扇区选择:要根据实际需求选择合适的 Flash 扇区,避免影响程序代码的正常运行。
  • 初始化:在使用 HAL 库之前,需要调用HAL_Init()函数进行初始化。
  • 错误处理:在进行 Flash 操作时,要注意检查函数的返回值,以便及时处理可能出现的错误。
  • 作者:weixin_58038206

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 Flash操作指南

    发表回复