文章目录
- 一.概要
- 二.FLASH模拟EEPROM的优势
- 三.FLASH模拟EEPROM的原理
- 四.数据读写步骤
- 五.数据转移流程图
- 六.FLASH模拟EEPROM读写例程
- 七.CubeMX工程源代码下载
- 八.小结
一.概要
STM32F103C8T6是一款强大而灵活的微控制器,它的片内Flash存储器可以用来存储有关数据,但没有专门的EEPROM。EEPROM具有较高的可擦写次数,一般FLASH只有1万次擦写次数,EEPROM可以有100万次擦写次数,对于有些写入频繁的数据如果放在FLASH中存储,容易造成FLASH损坏,就有一种FLASH存储模拟EEPROM的方法,可以增加FLASH的擦写次数,不需要外挂一颗EEPROM,可以减少额外硬件成本。
二.FLASH模拟EEPROM的优势
由于FLASH在写入数据前,需要将FLASH数据先擦除为0xFF,而FLASH擦除时通常为扇区擦除,例如STM32F103C8T6的FLASH页大小为1K字节,这个特性决定了不能简单的将旧数据擦除然后写新数据,因为这样会导致存储在这个页内的其他数据也被擦除,并且也会导致FLASH频繁擦除而降低其使用寿命。
所以FLASH模拟EEPROM的思路是:
1.新数据存储不影响旧数据。
2.尽量减少FLASH擦除次数,延长FLASH使用寿命。
FLASH模拟EEPROM的优点:
1.低成本:可节约一颗EEPROM芯片;
2.存储、读取速度快:通讯速度快于使用I2C或者SPI通讯的EEPROM元件;
3.抗干扰能力强:由于FLASH在单片机内部,不会存在通讯总线被外部干扰的问题;
4.容量可调:可根据实际使用,灵活调整存储空间大小。
三.FLASH模拟EEPROM的原理
首先使用2页FLASH空间,如果0页空间写满数据,那么把0页空间里面的【有效数据】复制到1页,如果1页数据满那么把1页空间里面的【有效数据】复制到0页,这样循环使用,当然如果你想增加使用寿命可以增加多页循环,官方例子只是按2页实现的例子。每页前面4字节保留,其中前2字节是该页状态标志。
存储结构:
存储的数据格式为数据 + 数据地址,地址和数据都是16位方式存储,每一次存储占用32位也就是4 个字节。图中data列为数据,data address列为数据地址,flash address列为数据存储的实际flash 地址偏移量。例如上图中页0的flash address=12处,数据为0x3003,数据地址为0x0002。
页状态标志:
在第一个数据存储区,存储页状态标志status,页状态标志有3种:
有效状态:VALID_PAGE,status = 0x0000,读取和写数据在此页进行。
数据转移状态:RECEIVE_DATA,status = 0xEEEE,另外一页满了,正在传输有效数据到本页。
擦除状态:ERASED,status = 0xFFFF。
四.数据读写步骤
数据写入:
每一次写入数据前,都会从页起始地址开始寻找第一个未存储数据的区域(值为0xFFFFFFFF),然后将待写入的数据和数据地址写到未存储数据的区域。例如上图中页0的flash address = 20处,值为0xFFFFFFFF,就是第一个未存储数据的区域。
当知道了页的大小后,就可以算出最大的变量存储个数:页容量/4-1。例如当页大小为1K时,最大可存储的变量数量为1024/4-1=255。需要注意的是,在实际使用中,应该尽量留出较多的空闲容量,这样可以减小FLASH擦除次数,提高FLASH寿命。
另外数据地址不可以超过最大能存储的变量数量,例如当页大小为1K时,最大可存储的变量数量为1024/4-1=255,那么数据地址data address不可以大于255。
数据读取:
每一次读取数据都会从页结束地址开始向前寻找最后一个存储的有效数据,例如现在要读取地址为0x0000 的数据。从上图中看到flash address = 4和flash address = 16都是地址为0x0000的数据,因为最后一次存储的数据为flash address = 16处的数据,所以此时读取地址0x0000的数据为0x1234。
五.数据转移流程图
当一页数据存满了之后,会将数据传输到空闲页,将会执行以下操作(以页0满,页1空为例):
将页1状态标记为数据传输状态(RECEIVE_DATA);
将页0所有有效数据复制到页1(注意是有效数据,不是所有数据,一个地址有效数据就是最新存入的一个数据);
擦除页0;
将页1状态标记为有效状态(VALID_PAGE)。
当两次保存同一虚拟地址的数据时如右图所示:从上到下,最新一个虚拟地址是0X7777对应的数据5454才是有效的,所以等Pag0满了,在转移的过程中只是把7777地址的数据只是把5454转移过去了,5555地址最新的数据是BDBD,6666地址最新的数据是6464。
六.FLASH模拟EEPROM读写例程
硬件准备:
STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口。
打开STM32CubeMX软件,新建工程
Part Number处输入STM32F103C8,再双击就创建新的工程
配置下载口引脚
配置外部晶振引脚
配置系统主频
配置工程文件名,保存路径,KEIL5工程输出方式
生成工程
用Keil5打开工程
添加代码
添加EEPROM.C文件
主要代码
uint16_t VirtAddVarTab[NB_OF_VAR] = {0x5555, 0x6666, 0x7777};
uint16_t VarDataTab[NB_OF_VAR] = {0, 0, 0};
uint16_t VarValue = 0;
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();//初始化1毫秒 Tick
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();//外部8M晶振,系统72M主频
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* Unlock the Flash Program Erase controller */
HAL_FLASH_Unlock();
/* EEPROM Init */
EE_Init();
/* --- Store successively many values of the three variables in the EEPROM ---*/
/* Store 0x1000 values of Variable1 in EEPROM */
for (VarValue = 1; VarValue <= 0x1000; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[0], VarValue);//0x5555地址存入数据,最后一个数据是0x1000
}
/* read the last stored variables data*/
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据
/* Store 0x2000 values of Variable2 in EEPROM */
for (VarValue = 1; VarValue <= 0x2000; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[1], VarValue);//0x6666地址存入数据,最后一个数据是0x2000
}
/* read the last stored variables data*/
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据
EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);//从0x6666读取数据
/* Store 0x3000 values of Variable3 in EEPROM */
for (VarValue = 1; VarValue <= 0x3000; VarValue++)
{
EE_WriteVariable(VirtAddVarTab[2], VarValue);//0x7777地址存入数据,最后一个数据是0x3000
}
/* read the last stored variables data*/
EE_ReadVariable(VirtAddVarTab[0], &VarDataTab[0]);//从0x5555读取数据
EE_ReadVariable(VirtAddVarTab[1], &VarDataTab[1]);//从0x6666读取数据
EE_ReadVariable(VirtAddVarTab[2], &VarDataTab[2]);//从0x7777读取数据
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
#define ADDR_FLASH_PAGE_32 ((uint32_t)0x08008000) /* Base @ of Page 32, 1 Kbytes */
/* Define the size of the sectors to be used */
#define PAGE_SIZE (uint32_t)FLASH_PAGE_SIZE /* Page size */
/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS ((uint32_t)ADDR_FLASH_PAGE_32) /* EEPROM emulation start address */
/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE0_ID ADDR_FLASH_PAGE_32
#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x10000))
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x10000 + PAGE_SIZE - 1))
#define PAGE1_ID ADDR_FLASH_PAGE_96
/* Used Flash pages for EEPROM emulation */
#define PAGE0 ((uint16_t)0x0000)
#define PAGE1 ((uint16_t)0x0040)
/* No valid page define */
#define NO_VALID_PAGE ((uint16_t)0x00AB)
/* Page status definitions */
#define ERASED ((uint16_t)0xFFFF) /* Page is empty */
#define RECEIVE_DATA ((uint16_t)0xEEEE) /* Page is marked to receive data */
#define VALID_PAGE ((uint16_t)0x0000) /* Page containing valid data */
/* Valid pages in read and write defines */
#define READ_FROM_VALID_PAGE ((uint8_t)0x00)
#define WRITE_IN_VALID_PAGE ((uint8_t)0x01)
/* Page full define */
#define PAGE_FULL ((uint8_t)0x80)
调试结果如下图所示,地址0x5555,0x6666,0x7777地址里存的数据都是每个地址空间最后一个存入的数据。
七.CubeMX工程源代码下载
通过百度网盘分享的文件:25.片上FLASH模拟EEPROM实验.rar
链接:https://pan.baidu.com/s/16gcHMxPJIImZp2323F1wrA
提取码:pkb8
如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行
八.小结
因为片内Flash的擦写次数有限,所以要加上特定法来增加使用寿命,此算法ST提供了例程,可以移植到程序里直接使用,比较方便。