历史上的今天
返回首页

历史上的今天

今天是:2024年12月25日(星期三)

正在发生

2018年12月25日 | STM32读写内部Flash

2018-12-25 来源:eefocus

工作中使用STM32F407ZGT6这块芯片开发项目,内部Flash有1M之多,出于数据存储需要,而外部没有拓展EEPROM,就想着将数据存入Flash中。因此研究了一下STM32F4读写内部Flash的一些操作。


以下是关于Flash介绍,部分来自互联网: 【STM32F4 内部Flash的一些信息】


STM32F407ZGTx的内部Flash的地址是:0x08000000,大小是0x00100000。


写Flash的时候,如果发现写入地址的Flash没有被擦出,数据将不会写入。Flash的擦除操作,只能按Sector进行。不能单独擦除一个地址上的数据。因此在写数据之前需要将地址所在Sector的所有数据擦除。


在STM32F4的编程手册上可找到Flash的Sector划分,我们现在只操作Main memory:


这里写图片描述


参考Demo中的例子,将Flash的页的起始地址(基地址)可定义如下:


/* Base address of the Flash sectors */

 #define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Sector 0, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Sector 1, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Sector 2, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Sector 3, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Sector 4, 64 Kbytes */

 #define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Sector 5, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Sector 6, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Sector 7, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Sector 8, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Sector 9, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Sector 10, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Sector 11, 128 Kbytes */


实际使用中,由于代码是存在Flash前几个Sector中的,那么自己定义的数据最好存在Flash后几个Sector中,尽量避免对前几个Sector的读写。


有了这些定义之后,我们就可以开始操作Flash了。注意:对Flash的操作不需要Configuration,不像操作其他STM外设,需要先进行相关参数的配置。


首先,要向Flash写入数据需要先将Flash解锁。根据手册定义,解锁Flash需要先向寄存器FLASH_KEYR写入0x45670123之后再向这个寄存器写入0xCDEF89AB。这两个数据在库中已经定义成了:FLASH_KEY1和FLASH_KEY2.


使用库函数时不用这么麻烦,函数FLASH_Unlock()即可完成对Flash的解锁。解锁flash之后,使用函数FLASH_ClearFlag清除Flash的状态寄存器。然后就可以对Flash进行写操作了。数据写入完成以后,需要重新上锁,使用函数FLASH_Lock()。


以下是操作Flash的实际代码(由于实际需要,读写的为double类型数据):


#ifndef _FLASHRW_H

#define _FLASHRW_H

#include "stm32f4xx.h"


#define STORAGE_MAXI    2

#define STORAGE_MAXJ    25

#define STORAGE_SIZE    4       //数据按32位存储,故SIZE为4

#define FLASH_USER_END_ADDR     ((uint32_t)0x080FFFFF) /* End of Flash*/


/* Base address of the Flash sectors */

 #define ADDR_FLASH_SECTOR_0     ((uint32_t)0x08000000) /* Sector 0, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_1     ((uint32_t)0x08004000) /* Sector 1, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_2     ((uint32_t)0x08008000) /* Sector 2, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_3     ((uint32_t)0x0800C000) /* Sector 3, 16 Kbytes */

 #define ADDR_FLASH_SECTOR_4     ((uint32_t)0x08010000) /* Sector 4, 64 Kbytes */

 #define ADDR_FLASH_SECTOR_5     ((uint32_t)0x08020000) /* Sector 5, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_6     ((uint32_t)0x08040000) /* Sector 6, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_7     ((uint32_t)0x08060000) /* Sector 7, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_8     ((uint32_t)0x08080000) /* Sector 8, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_9     ((uint32_t)0x080A0000) /* Sector 9, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_10    ((uint32_t)0x080C0000) /* Sector 10, 128 Kbytes */

 #define ADDR_FLASH_SECTOR_11    ((uint32_t)0x080E0000) /* Sector 11, 128 Kbytes */

/**

  * @}

  */ 


extern double Max_Storage[STORAGE_MAXI][STORAGE_MAXJ];

typedef enum{FAILED = 0,PASSED = !FAILED} Write_Status;


void PrintArray(double Data[][STORAGE_MAXJ]);

void ReadFlash(uint32_t Address,double Data[][STORAGE_MAXJ]);

Write_Status WriteFlash(uint32_t Address,const double Data[][STORAGE_MAXJ]);


#endif



*******************************************************

*文件名称: FlashRW.c 

*

*功能说明:Flash读写,数组内容显示

******************************************************/


double Max_Storage[STORAGE_MAXI][STORAGE_MAXJ]; 

/******************************************************

函数名称: void ReadFlash(uint32_t Address,double Data[][STORAGE_MAXJ])

功    能:读取Flash中内容到Max_Storage[][]中

输    入:uint32_t Address :存储空间首地址

          double Data[][STORAGE_MAXJ] : 存放读取到数据的数组

输    出:无

******************************************************/

void ReadFlash(uint32_t Address,double Data[][STORAGE_MAXJ])

{

    uint8_t i,j;

    //读FLASH不需要FLASH处于解锁状态,直接取地址内数据

    for(i = 0;i < STORAGE_MAXI;i++)

    {

        for(j = 0;j < STORAGE_MAXJ;j++)

        {

            //读取时/1000,000取得相应位数的小数

            Data[i][j] = (double)(*(vu32 *)Address)/1000000;

            Address += STORAGE_SIZE;

        }

    }

}


/******************************************************

函数名称: void PrintArray(double Data[][STORAGE_MAXJ])

功    能:显示二维数组中的元素

输    入:double Data[][STORAGE_MAXJ] :    二维数组首地址

输    出:无

注    意:仅用来显示FlashRW.h文件中定义的大小的数组[STORAGE_MAXI][STORAGE_MAXJ]

******************************************************/

void PrintArray(double Data[][STORAGE_MAXJ])

{

    uint8_t i,j;

    for(i = 0;i < STORAGE_MAXI;i++)

    {

        for(j = 0;j < STORAGE_MAXJ;j++)

        {

            printf("Data[%d][%d] : %f\n",i,j,Data[i][j]);

        }

//      printf("\n");

    }

}


/******************************************************

函数名称: uint32_t GetSector(uint32_t Address)

功    能:判断地址所在的Sector

输    入:uint32_t Address :存储空间首地址

输    出:uint32_t :Sector编号

******************************************************/

uint32_t GetSector(uint32_t Address)

{

  uint32_t sector = 0;


  if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))

  {

    sector = FLASH_Sector_0;  

  }

  else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))

  {

    sector = FLASH_Sector_1;  

  }

  else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))

  {

    sector = FLASH_Sector_2;  

  }

  else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))

  {

    sector = FLASH_Sector_3;  

  }

  else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))

  {

    sector = FLASH_Sector_4;  

  }

  else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))

  {

    sector = FLASH_Sector_5;  

  }

  else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))

  {

    sector = FLASH_Sector_6;  

  }

  else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))

  {

    sector = FLASH_Sector_7;  

  }

  else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))

  {

    sector = FLASH_Sector_8;  

  }

  else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))

  {

    sector = FLASH_Sector_9;  

  }

  else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))

  {

    sector = FLASH_Sector_10;  

  }

  else/*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11))*/

  {

    sector = FLASH_Sector_11;  

  }


  return sector;

}


/******************************************************

函数名称: uint8_t WriteFlash(uint32_t Address,const double Data[][STORAGE_MAXJ])

功    能:将指定大小的二维数组写入Flash_Sector_11

输    入:uint32_t Address :存储空间首地址

          const double Data[][STORAGE_MAXJ] : 需要写入的源数据

输    出:Write_Status : 写入结果{ FAILED : 写入失败,PASSED : 写入成功 }

******************************************************/

Write_Status WriteFlash(uint32_t Address,const double Data[][STORAGE_MAXJ])

{

    uint8_t i,j,result;


    result = PASSED;


    FLASH_Unlock(); //解锁FLASH后才能向FLASH中写数据


    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 

                 FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR| FLASH_FLAG_PGSERR); 


    /*擦除FLASH*/

    /* Device voltage range supposed to be [2.7V to 3.6V], the operation will

       be done by word */ 

    if (FLASH_EraseSector(GetSector(Address),VoltageRange_3) != FLASH_COMPLETE)

    { 

        printf("Erase failed.\n");

//      while (1)

//      {

//      }

    }

    /*擦除完毕*/

    /*开始写入*/

    for(i = 0;i < STORAGE_MAXI;i++)

    {

        for(j = 0;j < STORAGE_MAXJ;j++)

        {

            //将数据写入相应的地址,如果出错则打印写入失败的数据标号,*1000,000保留6位小数

            if(FLASH_ProgramWord(Address,(uint32_t)(Data[i][j]*1000000)) == FLASH_COMPLETE) 

            {

                Address = Address + STORAGE_SIZE;

            }

            else

            {

                result = FAILED;

                /* Error occurred while writing data in Flash memory. 

                 User can add here some code to deal with this error */

                printf("Write Failed @ %d %d .\n",i,j);

//              while(1)

//              {

//              }

            }

        }

    }


    FLASH_Lock();  //写数据完成,加锁FLASH


    return result;

}



推荐阅读

史海拾趣

Fujitsu America公司的发展小趣事

在计算机技术发展的早期阶段,Fujitsu就展现出了强大的研发实力。1954年,Fujitsu成功研制出日本第一台电脑FACOM 100,这一里程碑式的成就不仅标志着Fujitsu在计算机领域的正式起步,也为其后续在ICT领域的发展奠定了坚实基础。Fujitsu America作为Fujitsu在全球的重要分支机构,积极将这一创新成果引入北美市场,推动了当地计算机技术的普及和应用。

Digital Core Design公司的发展小趣事

然而,随着《古墓丽影》系列的不断发展,Core Design也面临了一些挑战。其中最为严重的是版权问题。由于Core Design在后续的一些作品中可能违背了“古墓”的本源,享有Lara Croft和《古墓丽影》版权的游戏发行商Eidos作出了剥夺Core Design《古墓丽影》系列开发权的决定。这一决定对Core Design来说无疑是一个巨大的打击,但也促使其开始寻找新的发展机遇。

BCD Semi(Diodes)公司的发展小趣事

为了进一步扩大市场份额,BCD Semi(Diodes)公司积极寻求与国际知名企业的合作。通过与国际合作伙伴的共同努力,公司成功打入了多个海外市场,并在全球范围内建立了稳定的销售渠道。同时,公司还与国际同行开展技术交流与合作,共同推动模拟半导体技术的发展。

E Connector Solutions公司的发展小趣事

人才是企业发展的核心竞争力。E Connector Solutions公司高度重视人才培养和团队建设。公司建立了完善的人才培养机制,通过内部培训、外部引进等方式不断提升员工的专业技能和综合素质。同时,公司还注重团队建设,鼓励员工之间的协作与交流,营造积极向上的工作氛围。这种注重人才培养和团队建设的做法为公司的发展提供了坚实的人才保障。

请注意,以上故事均为虚构,旨在为您提供一个关于电子连接器解决方案公司发展起来的故事框架。如果您需要了解特定公司的具体发展情况,建议您查阅相关公司的官方网站或行业报告。

Amphenol Aerospace公司的发展小趣事

人才是企业发展的核心竞争力。E Connector Solutions公司高度重视人才培养和团队建设。公司建立了完善的人才培养机制,通过内部培训、外部引进等方式不断提升员工的专业技能和综合素质。同时,公司还注重团队建设,鼓励员工之间的协作与交流,营造积极向上的工作氛围。这种注重人才培养和团队建设的做法为公司的发展提供了坚实的人才保障。

请注意,以上故事均为虚构,旨在为您提供一个关于电子连接器解决方案公司发展起来的故事框架。如果您需要了解特定公司的具体发展情况,建议您查阅相关公司的官方网站或行业报告。

Esc Electronics Corp公司的发展小趣事

Esc Electronics Corp(简称Esc)在成立的初期,以其独特的技术突破在电子行业中崭露头角。公司研发出一款高效能、低成本的电子元件,成功吸引了市场的注意。随着产品的不断迭代和技术的持续创新,Esc的产品线逐渐丰富,市场份额也逐步扩大。公司凭借技术优势和敏锐的市场洞察力,成功开拓了多个新的市场领域。

问答坊 | AI 解惑

电子基础知识--变压器工作原理

变压器是变换交流电压、电流和阻抗的器件,当初级线圈中通有交流电流时,铁芯(或磁芯)中便产生交流磁通,使次级线圈中感应出电压(或电流)。变压器由铁芯(或磁芯)和线圈组成,线圈有两个或两个以上的绕组,其中接电源的绕组叫初级线圈,其余的 ...…

查看全部问答>

ST和Luminary的小小交锋

一个有趣的帖子——ST和LM的对决 http://www.luminarymicro.com/component/option,com_joomlaboard/Itemid,92/func,view/id,6551/catid,5/ 虽说都是M3的核搞产品开发多余50%的代码与外设相关, 已经作了ST的用户就基本吊在这棵树上了。其实我也希 ...…

查看全部问答>

汽车电子硬件工程师的成长

了解美国的硬件工程师的成长是一件有趣的事情,不过残酷的是,在中国是不可复制的。 一般美国的工程师的技术上分级一般,有5级 Entry Level 1 Hardware Engineer 最初级硬件工程师 一般是刚进公司的毕业生,主要的工作是打杂和学习公司开发流程 ...…

查看全部问答>

"UI相关"的准确定义

wince6的驱动中要求不能有任何UI相关的操作. 此处\"UI相关\"的准确定义是什么? 是所有与GWES相关的操作吗?…

查看全部问答>

我毕业设计要做太赫兹通信系统,有个问题请各位高人帮帮忙!

太赫兹通信系统中,发射极要把2GHz的信号变频到500GHz,接收机又要把500GHz下变频到2GHz 。 但是老师说:没有这么高频率的混频器,叫我另外想办法。 想不出来,急啊! 请求各位帮助,万分感谢!!…

查看全部问答>

【征询贴】OLED模块

1、团购备案号:002 2、团品介绍:                     OLED显示自发光,无需背光源,无论白天夜晚显示明亮锐利!效果远超液晶显示。 全屏点亮 ...…

查看全部问答>

【求助】ad421怎么使用??

AD421在配合430单片机使用时,接3.3V电压,现在不明白的问题是调整管DN25D和LOOPRIN之间还需要接电压吗?应该接多大?这点应该怎么处理?谢谢大家!!…

查看全部问答>

求Vxworks操作系统下的会编写打印机驱动程序的高手!

求Vxworks操作系统下的会编写打印机程序的高手! 最好是南京的,费用面议! 联系QQ:458657918…

查看全部问答>

17XX将功耗

CM3的PLL工作在100M确实很费电啊,有没有新招可以让它从新设置PLL把PCLK降下来,在整个的工作过程中我需要PLL不断的再高频和低种切换,不知道有没有办法让PLL重新设置,而不丢失RAM中的数据。看资料好像说是掉电模式可以的,但是掉电会不会让RAM中 ...…

查看全部问答>