历史上的今天
今天是:2025年08月10日(星期日)
2019年08月10日 | 【STM32】STM32之flash
2019-08-10 来源:eefocus
一、简介
本文介绍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”)
//******************************************************************************
//name: GUA_Flash.c
//introduce: flash驱动
//author: 甜甜的大香瓜
//email: 897503845@qq.com
//QQ group: 香瓜单片机之STM8/STM32(164311667)
//changetime: 2017.03.18
//******************************************************************************
#include "stm32f10x.h"
#include "GUA_Flash.h"
#include /********************内部变量************************/ static GUA_U8 sbGUA_Flash_Data[GUA_FLASH_SECTOR_SIZE] = {0}; /*********************内部函数声明************************/ static GUA_U16 GUA_Flash_ReadHalfWord(GUA_U32 nGUA_Flash_CustomOffsetAddr); static GUA_U8 GUA_Flash_ReadByte(GUA_U32 nGUA_Flash_CustomOffsetAddr); static GUA_U32 GUA_Flash_ReadWord(GUA_U32 nGUA_Flash_CustomOffsetAddr); //****************************************************************************** //name: GUA_Flash_ReadHalfWord //introduce: 读取指定地址的字 //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址 //return: 该地址的字 //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜单片机之STM8/STM32(164311667) //changetime: 2017.03.18 //****************************************************************************** static GUA_U32 GUA_Flash_ReadWord(GUA_U32 nGUA_Flash_CustomOffsetAddr) { GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr; return *(GUA_U32*)nGUA_Flash_Addr; } //****************************************************************************** //name: GUA_Flash_ReadHalfWord //introduce: 读取指定地址的半字 //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址 //return: 该地址的半字 //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜单片机之STM8/STM32(164311667) //changetime: 2017.03.18 //****************************************************************************** static GUA_U16 GUA_Flash_ReadHalfWord(GUA_U32 nGUA_Flash_CustomOffsetAddr) { GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr; return *(GUA_U16*)nGUA_Flash_Addr; } //****************************************************************************** //name: GUA_Flash_ReadHalfWord //introduce: 读取指定地址的字节 //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址 //return: 该地址的字节 //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜单片机之STM8/STM32(164311667) //changetime: 2017.03.18 //****************************************************************************** static GUA_U8 GUA_Flash_ReadByte(GUA_U32 nGUA_Flash_CustomOffsetAddr) { GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr; return *(GUA_U8*)nGUA_Flash_Addr; } //****************************************************************************** //name: GUA_Flash_Read //introduce: 读取指定地址、指定数据长度的数据 //parameter: nGUA_Flash_CustomOffsetAddr:偏移地址 // pGUA_Data:数据缓存区 // nGUA_Data_Num:读取的字节数 //return: none //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜单片机之STM8/STM32(164311667) //changetime: 2017.03.18 //****************************************************************************** void GUA_Flash_Read(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num) { while(nGUA_Data_Num--) { *pGUA_Data++ = GUA_Flash_ReadByte(nGUA_Flash_CustomOffsetAddr++); } } //****************************************************************************** //name: GUA_Flash_Write //introduce: 写入指定地址、指定数据长度的数据 //parameter: nGUA_Flash_Addr:读地址 // pGUA_Data:数据缓存区(必须偶数个字节) // nGUA_Data_Num:数据长度(必须偶数个字节) //return: 执行情况,详情见eGUA_FLASH_STATUS //author: 甜甜的大香瓜 //email: 897503845@qq.com //QQ group: 香瓜单片机之STM8/STM32(164311667) //changetime: 2017.03.18 //****************************************************************************** eGUA_FLASH_STATUS GUA_Flash_Write(GUA_U32 nGUA_Flash_CustomOffsetAddr, GUA_U8 *pGUA_Data, GUA_U32 nGUA_Data_Num) { GUA_U32 nGUA_Flash_Addr = GUA_FLASH_CUSTOM_ADDR_START + nGUA_Flash_CustomOffsetAddr; //要写入的地址 eGUA_FLASH_STATUS eGUA_Flash_Status = GUA_FLASH_STATUS_OK; //flash操作情况 GUA_U16 i; GUA_U32 nGUA_Flash_SectorPos = (nGUA_Flash_Addr - GUA_FLASH_ADDR_START)/GUA_FLASH_SECTOR_SIZE; //算出是第几个扇区(从0开始) GUA_U16 nGUA_Flash_SectorAddr_Offset = (nGUA_Flash_Addr - GUA_FLASH_ADDR_START)%GUA_FLASH_SECTOR_SIZE; //扇区内偏移地址 GUA_U16 nGUA_Flash_SectorAddr_Remain = GUA_FLASH_SECTOR_SIZE - nGUA_Flash_SectorAddr_Offset; //扇区内剩余字节 GUA_U16 nGUA_HalfWord = 0; GUA_U16 nGUA_HalfWord_Num; FLASH_Status eGUA_Flash_Lib_Status = FLASH_COMPLETE;
史海拾趣
|
在一个很嘈杂的环境中比如闹市中,用什么传感器只能捕捉到半径很小的一个范围内的声音(也就是自己说话的声音或者是距离自己很近的人所说话的声音),查了很多相关资料能不能从说话人的声波波段出发?或者是从近声场和远声场出发? 请 ...… 查看全部问答> |
|
有酬项目:S3C2440 WINCE 5.0上SDIO接口marvel 8686 WIFI模块驱动移植,已有WIFI驱动,需要做SDIO的驱动,有BSP,但是 这个WIFI模块为GM9601,厂家提供的驱动是marvel 8686驱动。 据厂家说,只要修改SDIO驱动即可,工作量1天。 由于本人能力有限,对WINCE系统不熟,现将此项目外包。 要求: 1. 一定是在S3C2440、WINCE 5.0上移植过SDIO WIFI模块的人,由于进度紧 ...… 查看全部问答> |
|
请问哪个函数可以得到内核镜像 的基址? 我在DDK文档中找不到这样的函数. 还有,有什么函数能取到内核镜像的大小呢??/ 本人菜鸟,初学驱动编译,问题都比较弱智. 望大大们,不要见怪,请赐教,谢谢!!!… 查看全部问答> |
|
MOV A,#30H MOV R1,#0E0H MOVX @R1,A MOVX A,@R1复制代码 意思是一个对片外RAM操作的,用keil下自带的模拟下,调用内存,输入I:0e0h,现实的是00,为什么啊?不是应该是30h的嘛?? 最终的a的值是30h,但是片外里面显示的是00,怎么回事啊??… 查看全部问答> |
|
我们最近拿到的LM3S811板子只是个核心板,像普通的RS232或RS485、SPI(即ssi)、IIC等外设都没有 更别说其他的一些高级的外设了 谁有这方面的板子或者图纸 坛子有没有兴趣组织个这样的活动 将这个板子画出来 如果相应的 ...… 查看全部问答> |
|
刚刚看到论坛上的 wuqingyou 坛友突然发现launchpad的IO数量不够用,正为这个事烦恼的帖子,也看到了坛子上的“老人” ——wangfucong,juring 的热心回复。launchpad作为G系列这个低成本的430的开发板,确实没有提供非常多的IO口;其 ...… 查看全部问答> |
|
这个系统用来实现数据采集的功能,AD采用的是tlc5510,控制器采用EP2C8Q208,与上位总线通讯机通过ISA通讯,但是仿真可以实现采集的功能,但是实际采集的数据却是全零… 查看全部问答> |




