历史上的今天
返回首页

历史上的今天

今天是:2025年07月26日(星期六)

正在发生

2019年07月26日 | STM32操作访问flash,包括写入数据到flash和从flash读取数据

2019-07-26 来源:eefocus

序言:flash相关知识背景

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

其中:


RAM为常说的内存,比如手机的2G内存4G内存等,就是程序跑起来的时候所占用的存储空间,特点是掉电数据丢失。

ROM为常说的硬盘,比如手机的64G和128G等,可以简单的理解为硬盘的存储空间,特点是掉电数据不丢失,所以又叫“非易失性存储器件”。

ROM又包含:EEPROM和flash。

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


嵌入式设备存储器件思维导图

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


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


一、FLASH操作流程与操作选址

1.1 FLASH操作流程

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


1、确定要写入Flash的首地址(稍后介绍确定地址的方法)

2、解锁Flash

3、对Flash进行操作(写入数据)

4、对Flash重新上锁


1.2 如何查找并选定要写入Flash十六进制地址

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


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


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

STM32F103C8T6 Flash地址

二、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);

1

      获取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);

1

注:在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。所以在每次操作之前,我们都要等待上一次操作完成这次操作才能开始。


三、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        {

            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++;

    }

}


推荐阅读

史海拾趣

AINFO Inc公司的发展小趣事

AINFO Inc公司在追求经济效益的同时,也积极履行社会责任,关注可持续发展。公司注重环境保护和资源节约,通过采用环保材料和节能技术,降低了生产过程中的能耗和排放。同时,公司也积极参与公益事业,为社会做出了积极贡献。

请注意,以上故事仅为框架性的描述,并非AINFO Inc公司的实际发展历程。如果您需要了解该公司的具体发展情况,建议查阅相关资料或访问其官方网站。

Hei Inc Optoelectronic Division公司的发展小趣事
通过编程MCU来实现定时功能,可以灵活地设置加热时间和加热温度等参数,并实时监测和控制加热过程。MCU还可以与其他传感器和执行器连接,实现更复杂的控制逻辑。
E-Switch公司的发展小趣事

E-Switch公司始终坚持以客户为中心的经营理念,致力于为客户提供优质的服务和产品。公司建立了完善的客户服务体系,及时解决客户在使用过程中遇到的问题。同时,E-Switch还积极与客户保持沟通交流,了解市场需求变化,以便及时调整产品策略和生产计划。正是这种以客户至上的经营理念和持续发展的战略眼光,让E-Switch在电子行业中不断发展壮大。

ADMOS公司的发展小趣事

在电子行业的早期,ADMOS公司以其前瞻性的技术视野和不懈的研发努力,成功开发出一款高效能、低能耗的功率管理芯片。这款芯片在市场上迅速获得了认可,为ADMOS公司赢得了良好的口碑。这一技术突破不仅奠定了ADMOS在功率管理领域的领先地位,也为公司的后续发展奠定了坚实的基础。

Horizon Electronics Enterprises Group公司的发展小趣事

在快速发展的同时,Horizon始终不忘履行社会责任和推动可持续发展。公司积极倡导绿色生产理念,采用环保材料和工艺,减少生产过程中的能源消耗和废弃物排放。同时,Horizon还积极参与社会公益事业和环保项目,为社会的可持续发展贡献自己的力量。这些举措不仅赢得了社会各界的广泛赞誉和认可,也为公司的长远发展奠定了良好的社会基础。

请注意,以上故事均为虚构内容,旨在展示一个假设的电子行业公司可能的发展路径和成就。实际情况可能因公司具体情况和市场环境而有所不同。

ABL Heatsink公司的发展小趣事

ABL Heatsink公司在电子散热领域一直默默耕耘,直到某天,公司的研发团队成功开发了一种新型的高效散热材料。这种材料不仅导热性能卓越,而且成本相对较低,立即引起了业界的关注。随着这种新型散热材料的广泛应用,ABL Heatsink公司的订单量激增,公司规模迅速扩大。

问答坊 | AI 解惑

步进系统的运行精度

  从步进电机的角度来说,需要满足一些公差标准,包括机械公差和电气公差。相绕组电感的不均衡是重要因素,其他一些原因包括极靴、转子的不对准,定转子间气隙的不均匀,定转子齿槽关系,以及转矩脉动等。达到并持续控制这些参数并不是非常困难的 ...…

查看全部问答>

keil学习(二)

二、工程的详细设置 图 4 重复加入文件的错误 工程建立好以后,还要对工程进行进一步的设置,以满足要求。 首先点击左边 Project 窗口的 Target 1,然后使用菜单“Project->Option for target ‘target1’” 即出现对工程设置的对话框,这个对 ...…

查看全部问答>

向gooogleman求救!

    我有一个多普达828的主板,系统是WM6.1 CPU是PXA272 ROM是128M RAM是64M,手机\\PDA功能都正常.这个主板有三个UART,其中一个STUART应该接红外端口用来进行红外数据传输.我把红外的驱动卸载了(也就是把驱动用的DLL改了名)然后,对这个RXD ...…

查看全部问答>

步进电机的嵌入式驱动程序

跪求eeworld的朋友们给我一个步进电机的嵌入式驱动程序,或者给我一个可以学习和参考的内容.我的一个设计是关于步进电机的嵌入式驱动程序的.请赐教. 电子邮件:blueink_200451@hotmail.com qq:279697361 请写:步进电机 或 嵌入式驱动程序 和任何和 ...…

查看全部问答>

怎么学好DSP和单片机

各位达人,你们好!我是一名大三的学生,我的专业是电子信息工程,读大二的时候因为没有用心好好学数电,模电,特别是高频,现在我参加了单片机培训,对单片机比较感兴趣,想扎实学好单片机这门技术,学校也开了数字信号处理,老师说非常重要,因为 ...…

查看全部问答>

winCE下进行FTP传输文件编程

如题:小弟想在winCE实现FTP给远程主机(是PC机)传输文件的功能,怎样编程呀,需要哪些函数呢?…

查看全部问答>

救急!哪位有关于单片机的英文资料?

各位大虾:     现在小弟急用关于单片机的英文资料,在网上找了半天没找到! 希望大家帮帮忙啊!能有对应的中文翻译更好!英文大概5000字符的,多了当然更好! 谢谢了!! 我的邮箱:s1029384756@163.com      QQ:3052847 ...…

查看全部问答>

大家给推荐几本关于H.264的视频编解码的书

现在在做视频相关的工作,可对视频编解码一窍不通,大家给推荐几本这方面的书,谢谢了…

查看全部问答>

1138驱动1602的问题,各位大神进来看看

请问5V的1602能直接接到1138上吗?要不要上拉?我的没加上拉,一直显示不了,如果是程序问题,哪位高手能给个能用的程序不,谢谢!…

查看全部问答>

PADS制库,如此方便

原来一直手工制的,今天突然想到它的Wizard,随即打开看看,哇,原来这么好用: 不用算,不用量,直接将datasheet中的数据填上即可了, 真方便!…

查看全部问答>