zoukankan      html  css  js  c++  java
  • STM32的Flash

    STM32中存储区分为:随机存取存储器RAM只读存储器ROM。 
    其中:

    • RAM为常说的内存,比如手机的2G内存4G内存等,就是程序跑起来的时候所占用的存储空间,特点是掉电数据丢失
    • ROM为常说的硬盘,比如手机的64G和128G等,可以简单的理解为硬盘的存储空间,特点是掉电数据不丢失,所以又叫“非易失性存储器件”。 
      • ROM又包含:EEPROM和flash。

    画个嵌入式产品存储器件的思维导图如下(如有什么地方不对,恳请大神们进行指正): 
    嵌入式设备存储器件思维导图

    作为ROM的一份子,flash的特点自然是掉电数据不丢失。但是,flash在STM32中比较重要,程序也是保存在这个地方,所以轻易不让用户进行随意的读写,以避免不必要的问题。

    而这篇博客就先简单记录一下flash的访问流程和方法(读和写),具体原理以后理解深刻了再做补充。

    1、STM32 FLASH操作流程

    Flash操作已经属于嵌入式设备中很底层的操作了,直接对地址进行存取,简单描述,Flash操作大致需要以下流程:

    • 1、确定要写入Flash的首地址(稍后介绍确定地址的方法)
    • 2、解锁Flash
    • 3、对Flash进行操作(写入数据)
    • 4、对Flash重新上锁

    1.1 如何查找并选定要写入Flash十六进制地址值的方法

    要想选定安全的Flash地址进行读写,可以根据自己的STM32 MCU型号,查找数据手册,确定FLASH的地址区段,因为起始段会存储代码,所以一定要避开起始段,以避免数据错误。(我一般是根据Flash大小计算Flash的最末尾地址,往前推一段地址空间,在这里一般不会对代码中的数据产生覆盖等影响)

    我此次操作Flash使用的MCU是STM32103C8T6,所以以该型号MCU为例进行描述:

    • 在数据手册中,可以看到STM32103C8T6的flash起始地址是0x0800 0000(如下图所示),而STM32103C8T6的Flash大小为64K,可以计算出STM32103C8T6的Flash地址范围是:0x0800 0000——0x0800 FFFF(计算方法参考另一篇博客:STM32内存大小与地址的对应关系以及计算方法)。这里选取0x0800 F000作为读写操作的起始地址,对于C8T6这款MCU,操作这个起始地址应该算是很安全的范围了。 
      STM32103C8T6 Flash地址

    2、Flash基本知识点

    2.1 Flash容量

    Flash根据容量大小可以分为以下三种:

    • 1、小容量产品:Flash大小为1-32KB(STM32F10X_LD)
    • 2、中容量产品:Flash大小为64-128KB(STM32F10X_MD)
    • 3、大容量产品:Flash大小为256KB以上(STM32F10X_HD)

    2.2 ST库对Flash操作的支持

    ST库中对Flash操作主要提供了以下几类操作API函数:

    • 1、Flash解锁、锁定函数 
      • void FLASH_Unlock(void);//解锁函数:在对Flash操作之前必须解锁
      • void FLASH_Lock(void);//锁定函数:同理,操作完Flash之后必须重新上锁
    • 2、Flash写操作函数 
      • FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);//32位字写入函数
      • FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);//16位半字写入函数
      • FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);//用户选择字节写入函数 
        注:这里需要说明,32 位字节写入实际上是写入的两次 16 位数据,写完第一次后地址+2,这与我们前面讲解的 STM32 闪存的编程每次必须写入 16 位并不矛盾。写入 8位实际也是占用的两个地址了,跟写入 16 位基本上没啥区别。
    • 3、Flash擦除函数 
      • FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
      • FLASH_Status FLASH_EraseAllPages(void);
      • FLASH_Status FLASH_EraseOptionBytes(void);
    • 4、获取Flash状态 
      • FLASH_Status FLASH_GetStatus(void); 
        获取Flash状态函数,主要是为了获取Flash的状态,以便于根据状态对Flash进行操作。该函数返回值是通过枚举类型定义的,在代码中可以看到FLASH_Status类型定义如下(具体含义看注释即可):
      •                                                  typedef enum 
                                                         { 
                                                            FLASH_BUSY = 1,        //忙 
                                                            FLASH_ERROR_PG,      //编程错误 
                                                            FLASH_ERROR_WRP,   //写保护错误 
                                                            FLASH_COMPLETE,      //操作完成 
                                                            FLASH_TIMEOUT         //操作超时 
                                                          }FLASH_Status;
    • 5、等待操作完成函数 
      • FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); 
        注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。

    3、OK,上干货,上代码

    根据ST库提供的上述函数,我们可以自己编写Flash的读写操作代码如下:

    3.1 先定义一个Flash操作的起始地址宏定义Flash状态指示标志位

    #define STARTADDR 0x0800F000 //STM32F103C8T6适用
    
    volatile FLASH_Status FLASHStatus = FLASH_BUSY; //Flash操作状态变量
    
    

    3.2 编写各个读写函数

    //////////////////////////////////////////////////////////////////////////////////
    // Name:        WriteFlashOneWord
    //
    // Function:    向内部Flash写入32位数据
    //
    // Input:       WriteAddress:数据要写入的目标地址(偏移地址)
    //              WriteData:   写入的数据
    //////////////////////////////////////////////////////////////////////////////////
    void WriteFlashOneWord(uint32_t WriteAddress, uint32_t WriteData)
    {   
        FLASH_UnlockBank1();
        FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
    
        FLASHStatus = 1;    //清空状态指示标志位
        FLASHStatus = FLASH_ErasePage(STARTADDR);  
        if(FLASHStatus == FLASH_COMPLETE)   
        {  
            FLASHStatus = 1;    //清空状态指示标志位
            FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函数
        }
    
        FLASHStatus = 1;    //清空状态指示标志位
        FLASH_LockBank1();    
    }
    
    //////////////////////////////////////////////////////////////////////////////////
    // Name:        WriteFlashData
    //
    // Function:    向内部Flash写入数据
    //
    // Input:       WriteAddress:数据要写入的目标地址(偏移地址)
    //              data[]:      写入的数据首地址
    //              num:         写入数据的个数
    //////////////////////////////////////////////////////////////////////////////////
    void WriteFlashData(uint32_t WriteAddress, uint8_t data[], uint32_t num)
    {
        uint32_t i = 0;
        uint16_t temp = 0;
    
        FLASH_UnlockBank1();    //解锁flash
        FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); 
    
        FLASHStatus = 1;        //清空状态指示标志位
        FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整页
        if(FLASHStatus == FLASH_COMPLETE)//flash操作完成
        {
            FLASHStatus = 1;    //清空状态指示标志位
            for(i=0; i<num; i++)
            {
                temp = (uint16_t)data[i];
                FLASHStatus = FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, temp);//写入数据
            }
        }
    
        FLASHStatus = 1;    //清空状态指示标志位
    
        FLASH_LockBank1();  //锁定flash
    } 

    //////////////////////////////////////////////////////////////////////////////////
    // Name:        ReadFlashNBtye
    //
    // Function:    从内部Flash读取N字节数据
    //
    // Input:       ReadAddress:数据地址(偏移地址)
    //              ReadBuf:读取到的数据存放位置指针
    //              ReadNum:读取字节个数
    //
    // Output:      读取的字节数
    //////////////////////////////////////////////////////////////////////////////////
    int ReadFlashNBtye(uint32_t ReadAddress, uint8_t *ReadBuf, int32_t ReadNum)
    {   
        int DataNum = 0;
    
        ReadAddress = (uint32_t)STARTADDR + ReadAddress;  
        while(DataNum < ReadNum)   
        {        
            *(ReadBuf + DataNum) = *(__IO uint8_t*) ReadAddress++;  
            DataNum++;     
        }
    
        return DataNum;    
    }
    
    //////////////////////////////////////////////////////////////////////////////////
    // Name:        ReadFlashData
    //
    // Function:    从内部Flash读取num字节数据
    //
    // Input:       ReadAddress:数据地址(偏移地址)
    //              dest_Data:  读取到的数据存放位置指针
    //              num:        读取字节个数
    //////////////////////////////////////////////////////////////////////////////////
    void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
    {
        int i = 0;
        ReadAddress = (uint32_t)STARTADDR + ReadAddress; 
        while(i < num) 
        {
            *(dest_Data+i) = *(__IO uint16_t*) ReadAddress;
            ReadAddress += 2;
    
            i++;
        }
    }

    来源:https://blog.csdn.net/Ace_Shiyuan/article/details/78196648
  • 相关阅读:
    【贪心】【堆】Gym
    【并查集】Gym
    【拓扑排序】【bitset】Gym
    【递归】【线段树】【堆】AtCoder Regular Contest 080 E
    【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
    【动态规划】【滚动数组】【bitset】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal
    【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix
    【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game
    【推导】【构造】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem E. Space Tourists
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem D. Clones and Treasures
  • 原文地址:https://www.cnblogs.com/zzdbullet/p/9483116.html
Copyright © 2011-2022 走看看