【STC库函数】使用芯片自带的EEPROM来保存掉电不丢失的数据
之前开源过集成俩STC芯片的核心板,可以往回翻翻。
用的芯片是STC32G12K128和STC8G1K08。
那我来考你一考,它们为什么叫这个名字。
其实很简单,我们直接看它的手册。
STC是它的公司名,32表示这个32位单片机,G是系列。
12K是因为内部的SRAM和扩展RAM一共12K字节。
128则是Flash存储器是128K字节的。
我们烧录的程序就在这个Flash里,但是我们烧录的程序大小是多少呢?
以官方库函数里的综合示例程序为例,它可是把能包含的都包含进去了,编译出来才31K。
就算你程序更复杂我再给你翻个倍,那也才60+K的大小,而我板子用的这个芯片可是有128K的Flash,这不是很浪费嘛?
这时候我们对这块空间可以有别的用法🤓👆。
比如说我们对单片机有一些配置不是一开始写在程序里的,而是后续用户自己配置的,那么这些配置参数是需要单独保存起来的,是要掉电不丢失的,那么我们可以把这些数据放到Flash里,反正程序用不了这么大的空间,和尚摸得我摸不得?
问题在于我们应该如何使用,这时候又要翻一翻手册了。
EEPROM是使用是需要先擦除再写入的,因为我们写操作只能把1变成0,但是没法把0变成1(0和1表示的是二进制的bit值,请不要有端联想)。
如果要让0变成1,那么就需要一次性把512个Byte的每个bit都变成1,也就是擦除一整个扇区。
这个是EEPROM的物理结构决定的,反正我们在写数据之前先擦除就对了。
接着是EEPROM的操作时间,读取一个字节是4个系统时钟,我们用的时钟频率是24MHz,那么4个系统时间就是差不多167ns。
写入一个字节是30~40us。
擦除一个扇区比较花时间,因为是对512个字节进行操作,要4~6ms。
所以我们操作EEPROM的时候要注意一下时间。
那么说了一大堆,我们到底要怎么操作EEPROM呢?
使用库函数的话是非常简单的,因为都给我们封装好了。
把.c文件包含进编译里。
把.h文件包含进文件里。
一共是三个函数,我们直接一起看,因为非常简单。
首先是它们都需要的参数是EEPROM的地址,这一点后面再说。
读写函数的参数要多两个,是要写入(读出)的数据缓冲区地址,还有要写入(读出)的数据长度。
虽然文档里写的是一次性只能写入或读出一个字节的数据,但是库函数都封装好了,我们可以“一次性”写入或读出一堆字节。
我们使用EEPROM的流程就是如果要写入数据,那么写之前先擦除。
如果要读的话可以直接读。
最后剩下一个问题就是EEPROM的地址了。
参考手册里的说法,使用IAP方式的时候地址从0000开始,使用MOV指令则是FE0000。
而我们库函数里妥妥是IAP方式,所以我们操作EEPROM的地址从0000开始。
可以参考一下下表。
虽然上面说从0000开始,但结束可不是在FFFF结束,是从下表可知,128K大小最极端的结束地址是1FFFF。
关于我们使用多大的空间用于EEPROM,我们可以在烧录程序的时候设置,在官方烧录软件里。
根据自己的需求来改哈,一般存储几个数据的话有个几K就很够用了。
接下来我们实操一下。
先是从000000这个地址开始擦除一个扇区的数据(512Byte),接着在000000这个地址写入一个字节0,我用它来记录芯片启动的次数。
先将上述步骤的程序烧录进去初始化一下。
接着就可以进入正题了,首先读出000000这个地址的一个byte的数据,取出来后加一再写进去,写之前先擦除。
#include "STC32G_GPIO.h"
#include "STC32G_Delay.h"
#include "STC32G_NVIC.h"
#include "STC32G_UART.h"
#include "STC32G_EEPROM.h"
#include "STC32G_Switch.h"void GPIO_Init(void){P0_MODE_IN_HIZ(GPIO_Pin_0);P0_MODE_OUT_PP(GPIO_Pin_1);
}void UART_Init(void){COMx_InitDefine initer;initer.BaudRateDouble = DISABLE;initer.Morecommunicate = DISABLE;initer.UART_BaudRate = 115200;initer.UART_BRT_Use = BRT_Timer3;initer.UART_Mode = UART_8bit_BRTx;initer.UART_RxEnable = DISABLE;UART_Configuration(UART3, &initer);NVIC_UART3_Init(ENABLE, Priority_3);UART3_SW(UART3_SW_P00_P01);
}void main(void){uint8 count = 0;WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快EAXSFR(); //扩展SFR(XFR)访问使能 CKCON = 0; //提高访问XRAM速度GPIO_Init(); UART_Init();EA = 1;// 第一次先把地址为0的字节修改为0// EEPROM_SectorErase(0x000000);// EEPROM_write_n(0x000000, &count, 1);// 后续就先读出数据,加一后写回去EEPROM_read_n(0x000000, &count, 1);printf("read count is %d, ", count);EEPROM_SectorErase(0x000000);count++;printf("write count is %d\r\n", count);EEPROM_write_n(0x000000, &count, 1);while(1){delay_ms(1000);}
}
没问题,可以正常读写数据。
最后需要提醒的还是写数据之前要先擦除扇区数据。
如果你有十个字节的数据放在了一起,但是你需要修改其中一个字节的数据,那么你要做的是把十个数据都取出来,把要修改的数据修改完之后,擦除这个扇区,最后把十个数据再次写进去。
因此建议把同一批次修改的数据放在同一个扇区。