• 利用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 }
  • 相关阅读:
    数据分析三剑客之pandas
    python神器 Jupyter Notbook
    数据分析三剑客之numpy
    MySQL之数据备份与还原
    爬虫之增量式爬虫
    文件相关命令(find,tree,tar)文件属性信息 date
    系统通配符号、系统正则符号,grep
    sed命令
    系统用户权限,系统权限位,用户相关命令
    根下目录及目录内详细文件
  • 原文地址:https://www.cnblogs.com/jqdy/p/13203642.html
Copyright © 2020-2023  润新知