美文网首页物联网loT从业者物联网相关技术研究
STM32F103学习笔记(3)——读写内部Flash

STM32F103学习笔记(3)——读写内部Flash

作者: Leung_ManWah | 来源:发表于2020-07-13 11:28 被阅读0次

    一、简介

    在STM32芯片内部有一个 FLASH 存储器,它主要用于存储代码,我们在电脑上编写好应用程序后,使用下载器把编译后的代码文件烧录到该内部 FLASH 中,由于 FLASH 存储器的内容在掉电后不会丢失,芯片重新上电复位后,内核可从内部 FLASH 中加载代码并运行。


    STM32 的内部 FLASH 包含主存储器、系统存储器以及选项字节区域,它们的地址分布及大小见下表


    • 主存储器

    一般我们说 STM32 内部 FLASH 的时候,都是指这个主存储器区域,它是存储用户应用程序的空间,芯片型号说明中的 256K FLASH、512K FLASH 都是指这个区域的大小。

    主存储器分为 256 页,每页大小为 2KB,共 512KB。这个分页的概念,实质就是 FLASH 存储器的扇区,与其它 FLASH 一样,在写入数据前,要先按页(扇区)擦除。

    注意上表中的主存储器是本实验板使用的 STM32VET6 型号芯片的参数,即 STM32F1 大容量产品。若使用超大容量、中容量或小容量产品,它们主存储器的页数量、页大小均有不同,使用的时候要注意区分。
    主存储器是以页为单位划分的。stm32根据FLASH主存储块容量、页面的不同,系统存储器的不同,分为小容量、中容量、大容量、互联型,共四类产品。

    • 小容量产品:主存储块1-32KB, 每页1KB。系统存储器2KB
    • 中容量产品:主存储块64-128KB, 每页1KB。系统存储器2KB
    • 大容量产品:主存储块256KB以上, 每页2KB。系统存储器2KB
    • 互联型产品:主存储块256KB以上, 每页2KB。系统存储器18KB
    • 系统存储区

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

    • 选项字节

    选项字节用于配置 FLASH 的读写保护、待机/停机复位、软件/硬件看门狗等功能,这部分共 16 字节。可以通过修改 FLASH 的选项控制寄存器修改。

    二、查看工程的空间分布

    由于内部 FLASH 本身存储有程序数据,若不是有意删除某段程序代码,一般不应修改程序空间的内容,所以在使用内部 FLASH 存储其它数据前需要了解哪一些空间已经写入了程序代码,存储了程序代码的扇区都不应作任何修改。通过查询应用程序编译时产生
    的“*.map”后缀文件,可以了解程序存储到了哪些区域。


    打开 map 文件后,查看文件最后部分的区域,可以看到一段以 “Memory Map of the image” 开头的记录(若找不到可用查找功能定位)

    观察表中的最后一项,它的基地址是 0x0800175c,大小为 0x00000020,可知它占用的
    最高的地址空间为 0x0800177c,跟执行区域的最高地址 0x0000177c 一样,但它们比加载
    区域说明中的最高地址 0x80017a8 要小,所以我们以加载区域的大小为准。对比表 45-1 的
    内部 FLASH 页地址分布表,可知仅使用页 0 至页 2 就可以完全存储本应用程序,所以从页
    3(地址 0x08001800)后的存储空间都可以作其它用途,使用这些存储空间时不会篡改应用程
    序空间的数据。

    三、写入Flash

    3.1 写入过程

    3.1.1 解锁

    由于内部 FLASH 空间主要存储的是应用程序,是非常关键的数据,为了防止误操作修改了这些内容,芯片复位后默认会给控制寄存器 FLASH_CR 上锁,这个时候不允许设置 FLASH 的控制寄存器,从而不能修改 FLASH 中的内容。

    所以对 FLASH 写入数据前,需要先给它解锁。解锁的操作步骤如下:

    1. 往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY1 = 0x45670123
    2. 再往 FPEC 键寄存器 FLASH_KEYR 中写入 KEY2 = 0xCDEF89AB

    3.1.2 页擦除

    在写入新的数据前,需要先擦除存储区域,STM32 提供了页(扇区)擦除指令和整个 FLASH 擦除(批量擦除)的指令,批量擦除指令仅针对主存储区。
    页擦除的过程如下:

    1. 检查 FLASH_SR 寄存器中的“忙碌寄存器位 BSY”,以确认当前未执行任何 Flash 操作;
    2. 在 FLASH_CR 寄存器中,将“激活页擦除寄存器位 PER ”置 1;
    3. 用 FLASH_AR 寄存器选择要擦除的页;
    4. 将 FLASH_CR 寄存器中的“开始擦除寄存器位 STRT ”置 1,开始擦除;
    5. 等待 BSY 位被清零时,表示擦除完成。

    3.1.3 写入数据

    擦除完毕后即可写入数据,写入数据的过程并不是仅仅使用指针向地址赋值,赋值前还需要配置一系列的寄存器,步骤如下:

    1. 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何其它的内部 Flash 操作;
    2. 将 FLASH_CR 寄存器中的 “激活编程寄存器位 PG” 置 1;
    3. 向指定的 FLASH 存储器地址执行数据写入操作,每次只能以 16 位的方式写入;
    4. 等待 BSY 位被清零时,表示写入完成。

    3.2 写入函数

    /* STM32大容量产品每页大小2KByte,中、小容量产品每页大小1KByte */
    #if defined (STM32F10X_HD) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) || defined (STM32F10X_XL)
      #define FLASH_PAGE_SIZE       ((uint16_t)0x800)   // 2048
    #else
      #define FLASH_PAGE_SIZE       ((uint16_t)0x400)   // 1024
    #endif
    
    #define WRITE_START_ADDR        ((uint32_t)0x08008000)
    #define WRITE_END_ADDR          ((uint32_t)0x0800C000)
    
    /**
     @brief 内部Flash写入
     @param address -[in] 写入的地址
     @param pData -[in&out] 指向需要操作的数据
     @param dataLen -[in] 数据长度
     @return true - 成功;false - 失败
    */
    bool Internal_WriteFlash(uint32_t addrStart, uint32_t *pData, uint32_t dataLen)
    {   
        uint32_t i = 0;
        uint32_t eraseCounter = 0x00;                                                               // 记录要擦除多少页
        uint32_t address = 0x00;                                                                    // 记录写入的地址
        uint32_t numberOfPage = 0x00;                                                               // 记录写入多少页
        FLASH_Status flashStatus = FLASH_COMPLETE;                                                  // 记录每次擦除的结果
        
        address = addrStart;
        
        FLASH_Unlock();                                                                             // 解锁
        numberOfPage = (WRITE_END_ADDR - address) / FLASH_PAGE_SIZE;                                // 计算要擦除多少页
        FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);  // 清除所有标志
        
        // 按页擦除
        for(eraseCounter = 0; (eraseCounter < numberOfPage) && (flashStatus == FLASH_COMPLETE); eraseCounter++)
        {
            flashStatus = FLASH_ErasePage(address + (FLASH_PAGE_SIZE * eraseCounter));
        }
        
        for(i = 0; (i < dataLen)&&(flashStatus == FLASH_COMPLETE); i++)
        {
            flashStatus = FLASH_ProgramWord(address, pData[i]);                                     // 写入一个字(32位)的数据入指定地址
            address = address + 4;                                                                  // 地址偏移4个字节 
        }
        
        FLASH_Lock();                                                                               // 重新上锁
        
        if(flashStatus == FLASH_COMPLETE)
        {
            return true;
        }
        return false;
    }
    

    四、读取Flash

    4.1 读取函数

    /**
     @brief 内部Flash读取
     @param address -[in] 读取的地址
     @param pData -[in&out] 指向需要操作的数据
     @param dataLen -[in] 数据长度
     @return true - 成功;false - 失败
    */
    bool Internal_ReadFlash(uint32_t addrStart, uint32_t *pData, uint32_t dataLen)
    {
        uint32_t i = 0;
        uint32_t address = 0x00;
        
        address = addrStart;
        
        for(i = 0; i < dataLen; i++)
        {
            pData[i] = (*(__IO uint32_t*) address);                                                 // 读指定地址的一个字的数据
            address += 4;                                                                           // 地址偏移4个字节        
        }
        
        return true;
    }
    

    五、举例

    int main(void)
    {    
        u32 in_data[5]={11,22,33,44,55};//要写入的数据
        u32 out_data[5];//读存放
        int i;
        u8 STATUS=0;
        USART1_Config();//串口1配置
        GPIO_Configuration();//GPIO配置,用于点亮led
        STATUS=Internal_WriteFlash(0x08001800,in_data,5);
        Delay(0x02FFFF);
        if(STATUS)
        {
                GPIO_SetBits(GPIOD, GPIO_Pin_13);//点亮led1
                Internal_ReadFlash(0x08001800,out_data,5);
                printf("\r\n The Five Data Is : \r\n");
                for(i=0;i<5;i++)
                {
                        printf("\r %d \r",out_data[i]);
                }
        }
        while(1);
    }
    

    • 由 Leung 写于 2020 年 7 月 13 日

    • 参考:[零死角玩转STM32——基于野火F103【指南者】开发板]
        stm32——Flash读写

    相关文章

      网友评论

        本文标题:STM32F103学习笔记(3)——读写内部Flash

        本文链接:https://www.haomeiwen.com/subject/bcxacktx.html