大家好,我是『芯知识学堂』的SingleYork,前一篇文章给大家介绍了“【STC15系列】SYK-0806-A2S1 工业自动化控制之【16-自定义协议通信】”,
这一篇中,笔者将会给大家介绍“EEPROM的基本操作”。
在实际应用中,很多时候我们需要对一些设置参数实现掉电保持,以免每次上电都需要重新设置参数,
或者产品在运行过程中,需要实时保存一些数据,下次开机后还能查询到。
像这种应用场合,就需要用到单片机的EEPROM功能了,当然,也可以用到外部FLASH、EEPROM等芯片,
只是,单片机本身自带EEPROM,又何必去另外花钱呢,对吧?
那么,本例笔者就来跟大家介绍一下这款IAP15W413AS控制板的掉电存储功能吧。
首先,什么是EEPROM相信大家都应该知道了,不知道的也可以自行百度,
毕竟这不是笔者要讲的重点,还是直接切入正题吧,
从芯片手册上我们可以知道,IAP15W413AS这款单片机是没有专门的EEPROM的,纳尼?
刚才不是说有EEPROM的么?怎么现在又说没有了呢,你是在逗我玩么?
哈哈,别急,IAP15W413AS虽然没有专门的EEPROM,但是我们可以将用户程序区的FLASH当EEPROM来使用,
也就是说,芯片的存储空间有多大,EEPROM就可以有多大,
当然咯,这是比较理想的状况,实际上,我们的程序大小不可能为零!
从上面表格中,我们可以知道,IAP15W413AS单片机的EEPROM最多可以分为26个扇区,每个扇区的大小为512个字节,
如果我们需要同时操作的数据不大的话,一般只用一个扇区应该就够了,
关于这部分详细的知识点,大家可以看芯片手册,笔者在此就不再赘述。
本例中,笔者以一个电子钟的程序为例来进行讲解,需要保存的数据就是时、分、秒,很显然只有三个数,一个扇区足矣!
那么我们就用使用最后一个扇区吧。
首先,我们定义用于EEPROM功能的扇区的起始地址:
- #define IAP_ADDRESS 0x3200 //0x3200-0x33FF 512BYTE
复制代码
然后,我们定义几个变量,用于电子钟的时、分、秒、毫秒:
- u16 time_hour = 0;//时间-时
-
- u16 time_min = 0;//时间-分
-
- u16 time_sec = 0;//时间-秒
-
- u16 time_msec = 0;//时间-毫秒
复制代码
另外定义两个数组,用于EEPROM数据读写的缓存:
- u8 EEPROM_ReadTable[7] = {0};
-
- u8 EEPROM_WriteTable[7]= {0};
复制代码
电子钟的计时,还是用定时器0来实现,我们将定时器0的中断频率设置在1k Hz:
- #define Timer0_Fre 500UL //timer0中断频率 500*2Hz
复制代码
- Timer0_config(Timer0_Fre); //定时器0配置
复制代码
- /************************ 定时器配置 ****************************/
-
- void Timer0_config(u32 TIM0_Fre)
-
- {
-
- TIM_InitTypeDef TIM_InitStructure; //结构定义
-
-
-
- TIM_InitStructure.TIM_Mode = TIM_16BitAutoReload; //指定工作模式, TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMask
-
- TIM_InitStructure.TIM_Polity = PolityHigh; //指定中断优先级(低到高) Polity_0,Polity_1,Polity_2,Polity_3
-
- TIM_InitStructure.TIM_Interrupt = ENABLE; //中断是否允许, ENABLE或DISABLE
-
- TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T; //指定时钟源, TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
-
- TIM_InitStructure.TIM_ClkOut = DISABLE; //是否输出高速脉冲, ENABLE或DISABLE
-
- TIM_InitStructure.TIM_Value = 65536UL - (MAIN_Fosc / TIM0_Fre/2); //初值,
-
- TIM_InitStructure.TIM_Run = ENABLE; //是否初始化后启动定时器, ENABLE或DISABLE
-
- Timer_Inilize(Timer0,&TIM_InitStructure); //初始化Timer0 Timer0,Timer1,Timer2,Timer3,Timer4
-
- }
复制代码
app初始化程序中,在芯片上电后,除了对定时器、串口、GPIO做一些初始化之后,我们从EEPROM中读取7个数据,分别为:电子钟-时(2byte)、电子钟-分(2byte)、电子钟-秒(2byte)、时间更新标志,具体代码如下:
- /********************* APP初始化 ***********************/
-
- void app_init(void)
-
- {
-
- Timer0_config(Timer0_Fre); //定时器0配置
-
-
-
- UART_config(); //UART初始化
-
- GPIO_Config(); //GPIO初始化
-
-
-
- TI = 1;
-
- EA = 1;//开启总中断
-
-
-
- EEPROM_read_n(IAP_ADDRESS,EEPROM_ReadTable,7);//从EEPROM中一次性读取7个数据
-
-
-
- time_hour = EEPROM_ReadTable[0]*256 + EEPROM_ReadTable[1];
-
- time_min = EEPROM_ReadTable[2]*256 + EEPROM_ReadTable[3];
-
- time_sec = EEPROM_ReadTable[4]*256 + EEPROM_ReadTable[5];
-
-
-
- if(EEPROM_ReadTable[6] != 0x01)//如果是第一次上电,初始化变量
-
- {
-
- time_hour = 0;//时间-时
-
- time_min = 0;//时间-分
-
- time_sec = 0;//时间-秒
-
- time_msec = 0;//时间-毫秒
-
-
-
- printf("This is First Power On!\r\n");//串口打印结果
-
- }
-
- else
-
- {
-
- printf("EEPROM Test Start!\r\n");//串口打印
-
- }
-
- }
复制代码
电子钟的基础定时,是由timer0来实现:
- /********************* Timer0中断函数************************/
-
- void timer0_int (void) interrupt TIMER0_VECTOR //频率可变
-
- {
-
- time_msec ++;
-
- }
复制代码
电子钟功能的主要代码在app_run()中实现,每秒会通过printf()函数打印一次当前时间:
- /********************* APP运行 ***********************/
-
- void app_run(void)
-
- {
-
- //计时:时、分、秒、毫秒
-
- if(time_msec>=1000)
-
- {
-
- time_msec = 0;
-
-
-
- printf("Time = %02d:%02d:%02d\r\n",time_hour,time_min,time_sec);//串口打印输出时间
-
-
-
- EEPROM_SectorErase(IAP_ADDRESS);//扇区擦除,为写EEPROM做准备
-
-
-
- EEPROM_WriteTable[0] = time_hour/256;
-
- EEPROM_WriteTable[1] = time_hour%256;
-
- EEPROM_WriteTable[2] = time_min /256;
-
- EEPROM_WriteTable[3] = time_min %256;
-
- EEPROM_WriteTable[4] = time_sec /256;
-
- EEPROM_WriteTable[5] = time_sec %256;
-
- EEPROM_WriteTable[6] = 0x01; //参数更新标志
-
-
-
- EEPROM_write_n(IAP_ADDRESS,EEPROM_WriteTable,7);//将数据写到EEPROM
-
-
-
- if(++time_sec>=60)
-
- {
-
- time_sec = 0;
-
- if(++time_min>=60)
-
- {
-
- time_min = 0;
-
- time_hour ++ ;
-
- }
-
- }
-
- }
-
- }
-
复制代码
关于printf()函数的用法,网上一查一大把,笔者就不在这赘述了,这里需要提醒大家的是,在使用printf()函数时一定要注意一下两点:
必须包含stdio.h文件,即:
复制代码
在调用printf()函数之前需要先将TI置“1”,即:
复制代码
程序下载到板子中,第一次上电时回打印如下信息:
This is First Power On!
Time = 00:00:00
Time = 00:00:01
Time = 00:00:02
Time = 00:00:03
断电后再重新上电,会打印如下信息:
EEPROM Test!
Time = 00:00:03
Time = 00:00:04
Time = 00:00:05
Time = 00:00:06
说明我们的时间是按照我们设定的“每秒”自动写到的EEPROM中了,EEPROM读写都是OK的。
另外有一点笔者需要提醒大家,我们都知道,芯片的擦写次数是有上限的,一般都是在10万次或者100万次,
不管是多少次,都意味着他是有寿命的,而我们进行EEPROM写操作的时候,每次都会需要先进行擦除操作,才能成功写入数据,
既然这样,如果我们每秒来操作一次EEPROM的话,一天就需要擦写86400次,即使EEPROM的寿命有100万次,也就不到12天可能就要挂逼了。
因此,在实际应用中,我们对EEPROM的擦写操作,不能这么频繁。
因此,如果我们要实时存储运行过程中的一些数据,比较理想的方式是,我们搭建专门的掉件检测电路来检测外部电源是否掉电,
如果掉电这个时候才保存参数即可。
关于这个掉电检测电路,实际上STC芯片手册也给出了一种比较容易实现的方案,如下图所示:
当然咯,由于笔者这个板子上没有做这块的电路,所以我们暂时就没办法演示了,
但是笔者也有在别的板子上用到这个电路,效果还不错,感兴趣的小伙伴们可以自己搭建电路去试下。
另外,在一些不太方便搭建外部掉电检测电路的情况下,我们也可以通过操作不同的扇区来延长实现,
比如,当某个扇区擦写次数达到某一数值后,我们可以切换另外的扇区来存储数据。
好了,关于本讲的内容就简单介绍到这里了,有疑问的小伙伴们可以给笔者留言或者直接参与评论,
下一节笔者将给大家介绍“单片机ADC的应用”,详见“SYK-0806-A2S1 工业自动化控制之【18-内部AD采集外部电压】”,感谢大家的支持!
本章附件:
【STC15系列】SYK-0806-A2S1- 17-EEPROM实现数据掉电保存.rar
(81.56 KB, 下载次数: 6)
|