你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32操作访问flash,包括写入数据到flash和从flash读取数据

[复制链接]
aimejia 发布时间:2018-6-1 13:17
本帖最后由 aimejia 于 2018-6-1 13:19 编辑

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

其中:

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

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

ROM又包含:EEPROM和flash。

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

1.png

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

作为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,操作这个起始地址应该算是很安全的范围了。

2.png


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状态指示标志位
  1. #define STARTADDR 0x0800F000 //STM32F103C8T6适用

  2. volatile FLASH_Status FLASHStatus = FLASH_BUSY; //Flash操作状态变量
复制代码
3.2 编写各个读写函数
  1. //////////////////////////////////////////////////////////////////////////////////
  2. // Name:        WriteFlashOneWord
  3. //
  4. // Function:    向内部Flash写入32位数据
  5. //
  6. // Input:       WriteAddress:数据要写入的目标地址(偏移地址)
  7. //              WriteData:   写入的数据
  8. //////////////////////////////////////////////////////////////////////////////////
  9. void WriteFlashOneWord(uint32_t WriteAddress, uint32_t WriteData)
  10. {   
  11.     FLASH_UnlockBank1();
  12.     FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);

  13.     FLASHStatus = 1;    //清空状态指示标志位
  14.     FLASHStatus = FLASH_ErasePage(STARTADDR);  
  15.     if(FLASHStatus == FLASH_COMPLETE)   
  16.     {  
  17.         FLASHStatus = 1;    //清空状态指示标志位
  18.         FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函数
  19.     }

  20.     FLASHStatus = 1;    //清空状态指示标志位
  21.     FLASH_LockBank1();   
  22. }

  23. //////////////////////////////////////////////////////////////////////////////////
  24. // Name:        WriteFlashData
  25. //
  26. // Function:    向内部Flash写入数据
  27. //
  28. // Input:       WriteAddress:数据要写入的目标地址(偏移地址)
  29. //              data[]:      写入的数据首地址
  30. //              num:         写入数据的个数
  31. //////////////////////////////////////////////////////////////////////////////////
  32. void WriteFlashData(uint32_t WriteAddress, uint8_t data[], uint32_t num)
  33. {
  34.     uint32_t i = 0;
  35.     uint16_t temp = 0;

  36.     FLASH_UnlockBank1();    //解锁flash
  37.     FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);

  38.     FLASHStatus = 1;        //清空状态指示标志位
  39.     FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整页
  40.     if(FLASHStatus == FLASH_COMPLETE)//flash操作完成
  41.     {
  42.         FLASHStatus = 1;    //清空状态指示标志位
  43.         for(i=0; i<num; i++)
  44.         {
  45.             temp = (uint16_t)data[i];
  46.             FLASHStatus = FLASH_ProgramHalfWord(STARTADDR+WriteAddress+i*2, temp);//写入数据
  47.         }
  48.     }

  49.     FLASHStatus = 1;    //清空状态指示标志位

  50.     FLASH_LockBank1();  //锁定flash
  51. }
复制代码
  1. //////////////////////////////////////////////////////////////////////////////////
  2. // Name:        ReadFlashNBtye
  3. //
  4. // Function:    从内部Flash读取N字节数据
  5. //
  6. // Input:       ReadAddress:数据地址(偏移地址)
  7. //              ReadBuf:读取到的数据存放位置指针
  8. //              ReadNum:读取字节个数
  9. //
  10. // Output:      读取的字节数
  11. //////////////////////////////////////////////////////////////////////////////////
  12. int ReadFlashNBtye(uint32_t ReadAddress, uint8_t *ReadBuf, int32_t ReadNum)
  13. {   
  14.     int DataNum = 0;

  15.     ReadAddress = (uint32_t)STARTADDR + ReadAddress;  
  16.     while(DataNum < ReadNum)   
  17.     {        
  18.         *(ReadBuf + DataNum) = *(__IO uint8_t*) ReadAddress++;  
  19.         DataNum++;     
  20.     }

  21.     return DataNum;   
  22. }

  23. //////////////////////////////////////////////////////////////////////////////////
  24. // Name:        ReadFlashData
  25. //
  26. // Function:    从内部Flash读取num字节数据
  27. //
  28. // Input:       ReadAddress:数据地址(偏移地址)
  29. //              dest_Data:  读取到的数据存放位置指针
  30. //              num:        读取字节个数
  31. //////////////////////////////////////////////////////////////////////////////////
  32. void ReadFlashData(uint32_t ReadAddress, uint8_t *dest_Data, uint32_t num)
  33. {
  34.     int i = 0;
  35.     ReadAddress = (uint32_t)STARTADDR + ReadAddress;
  36.     while(i < num)
  37.     {
  38.         *(dest_Data+i) = *(__IO uint16_t*) ReadAddress;
  39.         ReadAddress += 2;

  40.         i++;
  41.     }
  42. }
复制代码




转载自执念

评分

参与人数 1 活跃值 +9 收起 理由
STMCU + 9

查看全部评分

收藏 评论3 发布时间:2018-6-1 13:17

举报

3个回答
jgmqt 回答时间:2018-6-4 11:11:09
我这几天一直在调试,为了想省个EEPORM芯片,但就是没调通。执行FLASHStatus = FLASH_ErasePage(STARTADDR);//擦除整页这句
和FLASHStatus = FLASH_ProgramWord(STARTADDR+WriteAddress, WriteData); //flash.c 中API函数
这句时就会死机,一直找不到哪里的原因。求助!
jgmqt 回答时间:2018-6-4 11:12:23
我现在把你的代码搬进去了,也是不行啊,一样会死机,不知道哪里的原因了,快折腾疯了
beyond_on 回答时间:2019-6-4 10:47:08
对于写入或擦擦数据出现死机的情况,原因可能是操作地址上有程序代码。一般要先初始化一页或几页数据(写入、擦擦操作地址)用于防止程序代码占用该预留空间。注意防止编译器将该段数据初始化代码优化掉。

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版