【STM32】STM32之flash


一、简介

本文介绍STM32系列如何将flash的一部分当做eeprom来存储数据。

注:本驱动特点是可自定义数据存储空间、限制写入地址避免误写至代码段、支持跨页读写、允许保存非半字倍数的字节。


二、实验平台

库版本:STM32F10x_StdPeriph_Lib_V3.5.0

编译软件:MDK4.53

硬件平台:STM32开发板(主芯片stm32f103c8t6)

仿真器:JLINK


、版权声明

博主:甜甜的大香瓜

声明:喝水不忘挖井人,转载请注明出处。

原文地址:http://blog.csdn.NET/feilusia

联系方式:897503845@qq.com

香瓜BLE之CC2541群:127442605

香瓜BLE之CC2640群:557278427

香瓜BLE之Android群:541462902

香瓜单片机之STM8/STM32群:164311667
甜甜的大香瓜的小店(淘宝店):https://shop217632629.taobao.com/?spm=2013.1.1000126.d21.hd2o8i

四、 实验前提
1、在进行本文步骤前,请先 阅读 以下博文:
1)《STM32F10xxx 闪存编程》(下载地址):http://blog.csdn.net/feilusia/article/details/49031709

2、在进行本文步骤前,请先 实现以下博文:
暂无


五、基础知识

1、flash的介绍
答:
香瓜使用的 stm32f103c8t6的flash为64K,详情如下图:

1)主存储器(BOOT1=x、BOOT0=0)
①地址:从0x08000000开始。
②页大小:小、中容量的flash为1K每页,大容量的flash为2K每页。
2)信息块
①系统存储器( BOOT1=0、BOOT0=1):存放ST自带的启动代码。
②选项字节:一般用于配置写保护、读保护等功能。
3)闪存存储器/接口寄存器
flash所用到的寄存器。

2、闪存等待时间是什么?
答:
由于flash的频率最高位24MHz,所以当STM32的频率小于24MHz时不需要等待时间、大于 24MHz时需要等待时间。
例如当STM32的频率为72MHz时,需要在工程中设置等待时间:“FLASH_SetLatency(FLASH_Latency_2); ”

3、闪存的读、写、擦除是如何的?
答:
1)读:可以读有效地址内的任意字节。
2)写:需要先解闪存锁,然后按半字(2个字节)的倍数写入。
3)擦除:需要先 解闪存锁,然后可以页擦除、全片擦除。

4、如何解闪存锁?
答:
1)首先要知道flash有三个键值:
①RDPRT键 = 0x000000A5
②KEY1 = 0x45670123
③KEY2 = 0xCDEF89AB
2)将KEY1与KEY2依次写入到FLASH_KEYR寄存器即可实现解闪存锁。

5、写flash只能按半字的倍数来写,但如果只有3个字节要保存,那该如何操作?
答:
香瓜的驱动是把flash中的整页数据读出来,修改要写的3个字节,然后再按页写回去。
虽然此法浪费了些写多余字节的时间,但能只改动flash中所需的3个字节,而不是按半字的倍数(4个)来写。

6、如何避免写到代码段导致代码运行异常?
答:
以 stm32f103c8t6为例,它的flash大小是64K,范围是0x8000000~0x8010000。
假设通过MDK编译后得知代码段大小为31K(0x7C00),所以在32K (0x8007D00)之后的位置肯定都不是代码段,都可用于自定义的数据存储空间。
保险起见可参考香瓜下文使用的方式,使用flash的末尾4K。

六、实验步骤

1、编写并添加驱动

1)编写驱动GUA_Flash.c(存放在“……\HARDWARE”)


    
    
  1. //******************************************************************************
  2. //name: GUA_Flash.c
  3. //introduce: flash驱动
  4. //author: 甜甜的大香瓜
  5. //email: 897503845@qq.com
  6. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  7. //changetime: 2017.03.18
  8. //******************************************************************************
  9. #include "stm32f10x.h"
  10. #include "GUA_Flash.h"
  11. #include <string.h>
  12. /********************内部变量************************/
  13. static GUA_U8 sbGUA_Flash_Data[GUA_FLASH_SECTOR_SIZE] = { 0};
  14. /*********************内部函数声明************************/
  15. static GUA_U16 GUA_Flash_ReadHalfWord(GUA_U32 nGUA_Flash_CustomOffsetAddr);
  16. static GUA_U8 GUA_Flash_ReadByte(GUA_U32 nGUA_Flash_CustomOffsetAddr);
  17. static GUA_U32 GUA_Flash_ReadWord(GUA_U32 nGUA_Flash_CustomOffsetAddr);
  18. //******************************************************************************
  19. //name: GUA_Flash_ReadHalfWord
  20. //introduce: 读取指定地址的字
  21. //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址
  22. //return: 该地址的字
  23. //author: 甜甜的大香瓜
  24. //email: 897503845@qq.com
  25. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  26. //changetime: 2017.03.18
  27. //******************************************************************************
  28. static GUA_U32 GUA_Flash_ReadWord(GUA_U32 nGUA_Flash_CustomOffsetAddr)
  29. {
  30. GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr;
  31. return *(GUA_U32*)nGUA_Flash_Addr;
  32. }
  33. //******************************************************************************
  34. //name: GUA_Flash_ReadHalfWord
  35. //introduce: 读取指定地址的半字
  36. //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址
  37. //return: 该地址的半字
  38. //author: 甜甜的大香瓜
  39. //email: 897503845@qq.com
  40. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  41. //changetime: 2017.03.18
  42. //******************************************************************************
  43. static GUA_U16 GUA_Flash_ReadHalfWord(GUA_U32 nGUA_Flash_CustomOffsetAddr)
  44. {
  45. GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr;
  46. return *(GUA_U16*)nGUA_Flash_Addr;
  47. }
  48. //******************************************************************************
  49. //name: GUA_Flash_ReadHalfWord
  50. //introduce: 读取指定地址的字节
  51. //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址
  52. //return: 该地址的字节
  53. //author: 甜甜的大香瓜
  54. //email: 897503845@qq.com
  55. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  56. //changetime: 2017.03.18
  57. //******************************************************************************
  58. static GUA_U8 GUA_Flash_ReadByte(GUA_U32 nGUA_Flash_CustomOffsetAddr)
  59. {
  60. GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr;
  61. return *(GUA_U8*)nGUA_Flash_Addr;
  62. }
  63. //******************************************************************************
  64. //name: GUA_Flash_Read
  65. //introduce: 读取指定地址、指定数据长度的数据
  66. //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址
  67. // pGUA_Data:数据缓存区
  68. // nGUA_Data_Num:读取的字节数
  69. //return: none
  70. //author: 甜甜的大香瓜
  71. //email: 897503845@qq.com
  72. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  73. //changetime: 2017.03.18
  74. //******************************************************************************
  75. void GUA_Flash_Read(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num)
  76. {
  77. while(nGUA_Data_Num--)
  78. {
  79. *pGUA_Data++ = GUA_Flash_ReadByte(nGUA_Flash_CustomOffsetAddr++);
  80. }
  81. }
  82. //******************************************************************************
  83. //name: GUA_Flash_Write
  84. //introduce: 写入指定地址、指定数据长度的数据
  85. //parameter: nGUA_Flash_Addr:读地址
  86. // pGUA_Data:数据缓存区(必须偶数个字节)
  87. // nGUA_Data_Num:数据长度(必须偶数个字节)
  88. //return: 执行情况,详情见eGUA_FLASH_STATUS
  89. //author: 甜甜的大香瓜
  90. //email: 897503845@qq.com
  91. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  92. //changetime: 2017.03.18
  93. //******************************************************************************
  94. eGUA_FLASH_STATUS GUA_Flash_Write(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num)
  95. {
  96. GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr; //要写入的地址
  97. eGUA_FLASH_STATUS eGUA_Flash_Status = GUA_FLASH_STATUS_OK; //flash操作情况
  98. GUA_U16 i;
  99. GUA_U32 nGUA_Flash_SectorPos = (nGUA_Flash_Addr - GUA_FLASH_ADDR_START)/GUA_FLASH_SECTOR_SIZE; //算出是第几个扇区(从0开始)
  100. GUA_U16 nGUA_Flash_SectorAddr_Offset = (nGUA_Flash_Addr - GUA_FLASH_ADDR_START)%GUA_FLASH_SECTOR_SIZE; //扇区内偏移地址
  101. GUA_U16 nGUA_Flash_SectorAddr_Remain = GUA_FLASH_SECTOR_SIZE - nGUA_Flash_SectorAddr_Offset; //扇区内剩余字节
  102. GUA_U16 nGUA_HalfWord = 0;
  103. GUA_U16 nGUA_HalfWord_Num;
  104. FLASH_Status eGUA_Flash_Lib_Status = FLASH_COMPLETE;
  105. //如果要写入的flash端超出范围,则报错退出
  106. if((nGUA_Flash_Addr + nGUA_Data_Num) > GUA_FLASH_CUSTOM_ADDR_END)
  107. {
  108. eGUA_Flash_Status = GUA_FLASH_STATUS_ERROR_PARAMETER;
  109. return eGUA_Flash_Status;
  110. }
  111. //解锁
  112. FLASH_Unlock();
  113. //清除标志位
  114. FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
  115. //写flash
  116. while( 1)
  117. {
  118. //读出整个扇区的内容
  119. GUA_Flash_Read(nGUA_Flash_SectorPos*GUA_FLASH_SECTOR_SIZE + GUA_FLASH_ADDR_START - GUA_FLASH_CUSTOM_ADDR_START,
  120. sbGUA_Flash_Data,
  121. GUA_FLASH_SECTOR_SIZE);
  122. //检查要写flash是否写过
  123. for(i = 0; i < nGUA_Flash_SectorAddr_Remain; i++)
  124. {
  125. //如果写过则擦除该扇区
  126. if(sbGUA_Flash_Data[nGUA_Flash_SectorAddr_Offset + i] != 0xFF)
  127. {
  128. eGUA_Flash_Lib_Status = FLASH_ErasePage(nGUA_Flash_SectorPos*GUA_FLASH_SECTOR_SIZE + GUA_FLASH_ADDR_START);
  129. if(eGUA_Flash_Lib_Status != FLASH_COMPLETE)
  130. {
  131. eGUA_Flash_Status = GUA_FLASH_STATUS_ERROR_ERASE;
  132. return eGUA_Flash_Status;
  133. }
  134. break;
  135. }
  136. }
  137. //如果要写的数据比该页剩余字节小,则本页内即可写完
  138. if(nGUA_Data_Num <= nGUA_Flash_SectorAddr_Remain)
  139. {
  140. //复制要写的数据到写缓冲区
  141. memcpy(sbGUA_Flash_Data + nGUA_Flash_SectorAddr_Offset, pGUA_Data, nGUA_Data_Num);
  142. //重新计算偏移值
  143. nGUA_HalfWord_Num = GUA_FLASH_SECTOR_SIZE/ 2;
  144. nGUA_Flash_SectorAddr_Offset = 0;
  145. nGUA_Flash_SectorAddr_Remain = GUA_FLASH_SECTOR_SIZE;
  146. //将整个页的数据写到flash中
  147. while(nGUA_HalfWord_Num--)
  148. {
  149. //将1字节整合为2字节
  150. nGUA_HalfWord = sbGUA_Flash_Data[nGUA_Flash_SectorAddr_Offset];
  151. nGUA_HalfWord |= sbGUA_Flash_Data[nGUA_Flash_SectorAddr_Offset + 1]<< 8;
  152. //写入2个字节
  153. eGUA_Flash_Lib_Status = FLASH_ProgramHalfWord(nGUA_Flash_SectorPos*GUA_FLASH_SECTOR_SIZE + GUA_FLASH_ADDR_START + nGUA_Flash_SectorAddr_Offset,
  154. nGUA_HalfWord);
  155. if(eGUA_Flash_Lib_Status != FLASH_COMPLETE)
  156. {
  157. eGUA_Flash_Status = GUA_FLASH_STATUS_ERROR_PROGRAM;
  158. return eGUA_Flash_Status;
  159. }
  160. //计算偏移值
  161. nGUA_Flash_SectorAddr_Offset += 2;
  162. nGUA_Flash_SectorAddr_Remain -= 2;
  163. }
  164. //已全部写完,退出
  165. break;
  166. }
  167. //如果要写的数据比该页剩余字节大,则本页内写不完,先写完本页
  168. else
  169. {
  170. //复制要写的数据到写缓冲区
  171. memcpy(sbGUA_Flash_Data + nGUA_Flash_SectorAddr_Offset, pGUA_Data, nGUA_Flash_SectorAddr_Remain);
  172. //重新计算偏移值
  173. nGUA_HalfWord_Num = GUA_FLASH_SECTOR_SIZE/ 2;
  174. nGUA_Flash_SectorAddr_Offset = 0;
  175. nGUA_Flash_SectorAddr_Remain = GUA_FLASH_SECTOR_SIZE;
  176. //将整个页的数据写到flash中
  177. while(nGUA_HalfWord_Num--)
  178. {
  179. //将1字节整合为2字节
  180. nGUA_HalfWord = sbGUA_Flash_Data[nGUA_Flash_SectorAddr_Offset];
  181. nGUA_HalfWord |= sbGUA_Flash_Data[nGUA_Flash_SectorAddr_Offset + 1]<< 8;
  182. //写入2个字节
  183. eGUA_Flash_Lib_Status = FLASH_ProgramHalfWord(nGUA_Flash_SectorPos*GUA_FLASH_SECTOR_SIZE + GUA_FLASH_ADDR_START + nGUA_Flash_SectorAddr_Offset,
  184. nGUA_HalfWord);
  185. if(eGUA_Flash_Lib_Status != FLASH_COMPLETE)
  186. {
  187. eGUA_Flash_Status = GUA_FLASH_STATUS_ERROR_PARAMETER;
  188. return eGUA_Flash_Status;
  189. }
  190. //计算偏移值
  191. nGUA_Flash_SectorAddr_Offset += 2;
  192. nGUA_Flash_SectorAddr_Remain -= 2;
  193. pGUA_Data += 2;
  194. nGUA_Data_Num -= 2;
  195. }
  196. //计算新扇区偏移值
  197. nGUA_Flash_SectorPos++;
  198. nGUA_Flash_SectorAddr_Offset = 0;
  199. nGUA_Flash_SectorAddr_Remain = GUA_FLASH_SECTOR_SIZE;
  200. }
  201. }
  202. //上锁
  203. FLASH_Lock();
  204. //返回操作状态
  205. eGUA_Flash_Status = GUA_FLASH_STATUS_OK;
  206. return eGUA_Flash_Status;
  207. }
  208. //******************************************************************************
  209. //name: GUA_Flash_Init
  210. //introduce: flash初始化
  211. //parameter: none
  212. //return: none
  213. //author: 甜甜的大香瓜
  214. //email: 897503845@qq.com
  215. //QQ group 香瓜单片机之STM8/STM32(164311667)
  216. //changetime: 2017.03.18
  217. //******************************************************************************
  218. void GUA_Flash_Init(void)
  219. {
  220. //设置延迟
  221. FLASH_SetLatency(FLASH_Latency_2);
  222. //开启FLASH预读缓冲功能
  223. FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
  224. }

2) 编写驱动头文件 GUA_Flash.h (存放在“ …… \HARDWARE ”)

    
    
  1. //******************************************************************************
  2. //name: GUA_Flash.h
  3. //introduce: flash驱动的头文件
  4. //author: 甜甜的大香瓜
  5. //email: 897503845@qq.com
  6. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  7. //changetime: 2017.03.18
  8. //******************************************************************************
  9. #ifndef _GUA_FLASH_H_
  10. #define _GUA_FLASH_H_
  11. /*********************宏定义************************/
  12. #ifndef GUA_U8
  13. typedef unsigned char GUA_U8;
  14. #endif
  15. #ifndef GUA_8
  16. typedef signed char GUA_8;
  17. #endif
  18. #ifndef GUA_U16
  19. typedef unsigned short GUA_U16;
  20. #endif
  21. #ifndef GUA_16
  22. typedef signed short GUA_16;
  23. #endif
  24. #ifndef GUA_U32
  25. typedef unsigned long GUA_U32;
  26. #endif
  27. #ifndef GUA_32
  28. typedef signed long GUA_32;
  29. #endif
  30. #ifndef GUA_U64
  31. typedef unsigned long long GUA_U64;
  32. #endif
  33. #ifndef GUA_64
  34. typedef signed long long GUA_64;
  35. #endif
  36. //flash页大小
  37. #if GUA_FLASH_ALL_SIZE < (256*1024)
  38. #define GUA_FLASH_SECTOR_SIZE 1024 //1K
  39. #else
  40. #define GUA_FLASH_SECTOR_SIZE 2048 //2K
  41. #endif
  42. //flash配置宏
  43. #define GUA_FLASH_ALL_SIZE (64*1024)
  44. #define GUA_FLASH_ADDR_START 0x08000000 //起始地址
  45. #define GUA_FLASH_ADDR_END (GUA_FLASH_ADDR_START + GUA_FLASH_ALL_SIZE) //结束地址
  46. #define GUA_FLASH_CUSTOM_SIZE (4*1024) //自定义的flash空间大小
  47. #define GUA_FLASH_CUSTOM_ADDR_START (GUA_FLASH_ADDR_END - GUA_FLASH_CUSTOM_SIZE)//自定义的flash空间起始地址
  48. #define GUA_FLASH_CUSTOM_ADDR_END GUA_FLASH_ADDR_END //自定义的flash空间结束地址
  49. //flash操作情况
  50. typedef enum
  51. {
  52. GUA_FLASH_STATUS_OK = 1,
  53. GUA_FLASH_STATUS_ERROR_PARAMETER,
  54. GUA_FLASH_STATUS_ERROR_ERASE,
  55. GUA_FLASH_STATUS_ERROR_PROGRAM,
  56. }eGUA_FLASH_STATUS;
  57. /*********************外部函数************************/
  58. extern void GUA_Flash_Read(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num);
  59. extern eGUA_FLASH_STATUS GUA_Flash_Write(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num);
  60. extern void GUA_Flash_Init(void);
  61. #endif

3) 工程中添加GUA_Flash.c


4)在MDK设置中添加驱动源文件路径


2、添加库的驱动

1)添加库的驱动文件



3、在应用层中调用

1)添加驱动头文件(main.c中)

#include "GUA_Flash.h"
    
    

2) 添加初始化代码(main.c的main函数中)

    
    
  1. //flash初始化
  2. GUA_Flash_Init();

3) 添加测试代码(main.c中)

①添加测试函数(main.c中)


    
    
  1. //******************************************************************************
  2. //name: GUA_Test
  3. //introduce: 测试代码
  4. //parameter: none
  5. //return: none
  6. //author: 甜甜的大香瓜
  7. //email: 897503845@qq.com
  8. //QQ group: 香瓜单片机之STM8/STM32(164311667)
  9. //changetime: 2017.03.18
  10. //******************************************************************************
  11. #define GUA_FLASH_ADDR 0x0000
  12. #define GUA_FLASH_SIZE 1025
  13. static GUA_U8 sbGUA_Flash_Buf[GUA_FLASH_SIZE];
  14. static void GUA_Test(void)
  15. {
  16. //读flash
  17. memset(sbGUA_Flash_Buf, 0x00, GUA_FLASH_SIZE);
  18. GUA_Flash_Read(GUA_FLASH_ADDR, sbGUA_Flash_Buf, GUA_FLASH_SIZE);
  19. //写flash
  20. memset(sbGUA_Flash_Buf, 0x33, GUA_FLASH_SIZE);
  21. GUA_Flash_Write(GUA_FLASH_ADDR, sbGUA_Flash_Buf, GUA_FLASH_SIZE);
  22. //读flash
  23. memset(sbGUA_Flash_Buf, 0x00, GUA_FLASH_SIZE);
  24. GUA_Flash_Read(GUA_FLASH_ADDR, sbGUA_Flash_Buf, GUA_FLASH_SIZE);
  25. //写flash
  26. memset(sbGUA_Flash_Buf, 0x55, GUA_FLASH_SIZE);
  27. GUA_Flash_Write(GUA_FLASH_ADDR, sbGUA_Flash_Buf, GUA_FLASH_SIZE);
  28. //读flash
  29. memset(sbGUA_Flash_Buf, 0x00, GUA_FLASH_SIZE);
  30. GUA_Flash_Read(GUA_FLASH_ADDR, sbGUA_Flash_Buf, GUA_FLASH_SIZE);
  31. }

②添加测试代码的调用 (main.c的main函数中)

    
    
  1. //测试代码
  2. GUA_Test();
测试代码要放在初始化之后。

七、注意事项
1、香瓜在驱动中暂时分配flash最末尾4K为自定义数据存储区,读写。
2、读、写函数的地址范围为自定义数据存储区范围,0x0000~0x1000(实际上是0x0800F000~0x08010000)。
3、擦除、写flash时,要注意地址必须为有效的地址、写必须按半字的倍数来写。

八、实验结果

仿真并设置断点在测试代码处,单步执行并观察sbGUA_Flash_Buf数组的数值,可知香瓜写的驱动可跨页读写(1025字节)。

因此实验成功。




  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值