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

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

所属标签

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