利用STC8A内部EEPROM存放系统配置参数

  STC8A芯片内部都有一定容量的Flash可以当作EEPROM。虽然擦除次数为10万次+,低于真正EEPROM芯片的100万次+,但是存储一些不经常修改的数据还是没有问题的,例如单片机的一些工作状态参数,在最初调整正常后很少再做调整。这样就可以省去一个EEPROM芯片的成本,大概1元左右。

1.       EEPROM操作相关寄存器和基本函数

  STC8的EEPROM操作涉及的寄存器一共有6个,分别是:

  • 数据寄存器: IAP_DATA
  • 高、低地址寄存器:IAP_ADDRH和IAP_ADDRL
  • 命令寄存器:IAP_CMD
  • 触发寄存器:IAP_TRIG
  • 控制寄存器:IAP_CONTR

  手册例程中给出了擦除、写(编程)、读(IAP模式和MOVC模式两种)、关闭IAP模式的例程,直接拷贝到自己的项目中,根据需要稍作修改即可。

  • 关闭IAP程序:void IapIdle()
  • IAP模式及MOVC模式读取程序:char IapRead(int addr)
  • 写(编程)程序:void IapProgram(int addr, char dat)
  • 擦除程序:void IapErase(int addr)

2.       注意事项

  以手册中给出几个底层例程为基础,很容易就可编写出项目中操作EEPROM的功能模块。

  有几点需要注意的问题,记录一下:

2.1     擦除操作是按照扇区进行的

  与读、写操作不同,擦除操作是按照扇区进行的,也就是说要擦除的话,会把IapErase(int addr)函数中addr地址参数所在的512字节的扇区的内容统统删除,给出的addr只要在同一个扇区,无论指向哪个具体的地址,都会擦除同一个扇区。原因如下:

  手册中记载“由于擦除是以 512 字节为单位进行操作的,所以执行擦除操作时所设置的目标地址的低 9位是无意义的。例如:执行擦除命令时,设置地址 1234H/1200H/1300H/13FFH,最终执行擦除的动作都是相同的,都是擦除 1200H~13FFH 这 512 字节”。

2.2     可自定义EEPROM大小的芯片,如何自定义

  STC8系列芯片中的大部分型号EEPROM空间都是固定的,只有少部分型号可以自定义EEPROM空间大小,例如我使用的STC8A8K64S4A12。

  该芯片有64K大小的Flash空间,在STC-ISP中按照扇区为单位(512字节,相当于0.5k)可定义0.5~64k大小的EEPROM空间。需要注意的是,自定义的EEPROM是被安排在64k Flash的末尾的,比如自定义了1k大小的EEPROM,那么前63k是Flash,最后1k是EEPROM。

  在我的项目中,只有几个系统参数需要调整,留下1个扇区0.5k大小的EEPROM空间就足够了。

 

2.3  如何确定MOVC模式读取的基地址

  与IAP模式的读取相比,MOVC模式的读取快捷便利,但是需要提供扇区的基地址,也就是下面程序片段中的宏IAP_OFFSET。

  像上面定义了0.5k大小的EEROM时,该如何确定这个基地址呢?具体方法如下:

  • 64k Flash的最后一个字节的地址是0xFFFF
  • 定义0.5k的EEPROM就往前查512个字节(0x200)作为基地址,也就是0xFE00
  • 同样也可计算出1k、2k、3k的基地址
 1 //使用的时候,将对应宏前面的注释符号去掉即可。
 2 //#define IAP_OFFSET 0x2000 //STC8A8K08S4A12
 3 //#define IAP_OFFSET 0x4000 //STC8A8K16S4A12
 4 //#define IAP_OFFSET 0x6000 //STC8A8K24S4A12
 5 //#define IAP_OFFSET 0x8000 //STC8A8K32S4A12
 6 //#define IAP_OFFSET 0xA000 //STC8A8K40S4A12
 7 //#define IAP_OFFSET 0xC000 //STC8A8K48S4A12
 8 //#define IAP_OFFSET 0xE000 //STC8A8K56S4A12
 9 //#define IAP_OFFSET 0xF000 //STC8A8K60S4A12
10 //#define IAP_OFFSET 0xF000 //STC8A8K60S4A12
11 /*自己计算给出*/
12 //#define IAP_OFFSET 0xF300 //STC8A8K64S4A12-3k
13 //#define IAP_OFFSET 0xF800 //STC8A8K64S4A12-2k
14 //#define IAP_OFFSET 0xFC00 //STC8A8K64S4A12-1k
15 //#define IAP_OFFSET 0xFE00 //STC8A8K64S4A12-0.5k
16 unsigned char IapRead(int addr)
17 {
18     addr += IAP_OFFSET; //使用 MOVC 读取 EEPROM 需要加上相应的偏移
19     return *(unsigned char code *)(addr); //使用 MOVC 读取数据
20 }

2.4     如何确定擦除操作的时间

  擦除操作需要相对准确的时间,时间短了还没擦除干净,时间长了又会影响Flash寿命。手册给出了要求的时间范围:

 

 

 

  手册指出,在擦除操作期间“此时 MCU 系统不给 CPU 供应时钟, CPU 没有时钟,所以无法工作, 也就是说,针对 EEPROM 操作所需要的等待时间是硬件自动完成的,用户不需要加额外的软件延时。”

  可见不能用常用的软件延时方式来控制擦除时间。那么该如何确定具体的时间呢?

  控制寄存器的B2~B0三个位的组合给出了对应的等待时间(以时钟周期为单位),比如选择011时,擦除操作就会等待60000个时钟周期,如果单片机系统时钟为12MHz,那么60000个时钟周期耗时:1秒÷12MHz×60000个周期 = 5ms。5ms的时间正好位于要求的4~6ms之内。因此,当系统时钟为12MHz时选择“011”是恰当的。

  如果选择的系统时钟没有在手册给出的举例频率之中(下表最后一列),按照上述计算方法计算一下,看是否在4~6ms之间即可。比如系统时钟工作在11.0592MHz,1秒÷11.0592MHz×60000个周期 = 4.608ms,也在4~6微秒范围之内,这时选择“011”也是恰当的。(已经实际测试)

  再试算一下18MHz的情况(没有实际测试):

  • 1秒÷18MHz×60000个周期 = 7.5ms,超过4~6ms范围,选“011”长了!
  • 1秒÷18MHz×100000个周期 = 5.5ms,符合4~6ms要求,选“010”组合正好!

 

 1 //系统工作参数结构
 2 typedef struct {
 3     int systemSleepingTimeSpan;
 4     float temperature;
 5     float coolingTime;
 6     float stopTime;
 7 } SysParamTypedef;
 8 
 9 /***************************************************
10 *说明:从eeprom中读取系统工作参数
11 *参数:db为系统工作参数结构类型的指针
12 ***************************************************/
13 void GetDatabase(SysParamTypedef *db)
14 {
15     int i;
16     for(i = 0; i < sizeof(SysParamTypedef); i++)
17     {
18         *((char*)db + i) = IapRead(i);
19     }
20 }
21 
22 /***************************************************
23 *说明:向eeprom中写入系统工作参数
24 *参数:db为系统工作参数结构类型的指针
25 ***************************************************/
26 void SaveDatabase(SysParamTypedef *db)
27 {
28     int i;
29     if(IapErase(0) == FAILURE)
30         ErrorHandle(Error_IapFailure);
31     for(i = 0; i < sizeof(SysParamTypedef); i++)
32     {
33         if(IapWrite(i, *((char *)db + i)) == FAILURE)
34             ErrorHandle(Error_IapFailure);
35     }    
36 }

 

posted @ 2020-06-28 16:35  jqdy  阅读(3486)  评论(0编辑  收藏  举报