STC8A8K64D4系列单片机考虑到了这样的运用处景,他利用 ISP/IAP 技能可将内部 Data Flash当 EEPROM,从而实现数据的掉电不丢失存储,EEPROM按照扇区组织的,每个扇区的大小是512字节,支持的擦写次数在 10 万次以上。
片内EEPROM分布STC8A8K64D4 系列单片机片内集成了64K 字节的Flash(EEPROM),Flash和EEPROM的大小取决于详细的单片机型号,如下表所示。
不同型号的单片机的Flash和EEPROM大小不同,但是他们的和都是64K字节,该系列单片机中,STC8A8K64D4比较分外,他的EEPROM大小是可以由用户设定的。

表1:STC8A8K64D4 系列单片机片内Flash和EEPROM分布
单片机型号
EEPROM大小
EEPROM扇区数量
STC8A8K16D4
48K
96
STC8A8K32D4
32K
64
STC8A8K48D4
16K
32
STC8A8K60D4
4K
8
STC8A8K64D4
用户自定义,大小必须是512的倍数。
片内EEPROM特点STC8A8K64D4 系列单片机片内EEPROM 的擦除因此扇区为单位进行的。EEPROM是逐字节读写的,写之前,写入地址所在的扇区须要先实行扇区擦除操作。这是由于EEPROM 的写操作只能将字节中的各个位由1 写为 0,而不能将0写为1,因此,写之前须要通过扇区擦除操作将全体扇区的位都写为1。EEPROM操作韶光读取 1 字节:4 个别系时钟(利用 MOVC 指令读取更方便快捷)。编程 1 字节:约 30~40us(实际的编程韶光为 6~7.5us,但还须要加上状态转换韶光和各种掌握旗子暗记的 SETUP 和 HOLD 韶光)擦除 1 扇区( 512 字节): 约 4~6ms。EEPROM 操作所需韶光是硬件自动掌握的,用户只须要精确设置 IAP_TPS 寄存器即可。IAP_TPS=系统事情频率/1000000(小数部分四舍五入进行取整)。
例如: 系统事情频率为 12MHz,则 IAP_TPS 设置为 12。又例如: 系统事情频率为 22.1184MHz,则 IAP_TPS 设置为 22。EEPROM的访问EEPROM 的访问办法有两种: IAP 办法和 MOVC 办法。IAP 办法可对 EEPROM 实行读、写、擦除操作,但 MOVC 只能对 EEPROM 进行读操作,而不能进行写和擦除操作。无论是利用 IAP 办法还是利用MOVC办法访问 EEPROM,首先都须要设置精确的目标地址。IAP办法时,目标地址与 EEPROM 实际的物理地址是同等的,均是从地址 0000H 开始访问,但若要利用 MOVC 指令进行读取 EEPROM 数据时,目标地址必须是在 EEPROM 实际的物理地址的根本上还有加上程序大小的偏移。
运用建议:利用EEPROM存储数据时,可以考虑将关联性不强的数据分别存放在不同的扇区。这样,当某类数据须要更新时,就不须要对其他不干系的数据存放扇区进行操作。存储数据时,应对数据更新的频率进行评估,以担保产品的生命周期内擦写次数不超过芯片支持的最大值(10万次)。如果数据更新频率高,可以考虑将数据在不同的扇区轮换存放,或者在同一个扇区的不同地址轮换存放,以降落利用的EEPROM扇区的擦写次数。软件设计EEPROM读写步骤EEPROM操作须要按照下面几个步骤进行,包含方案写入数据的EEPROM空间、擦除EEPROM、写EEPROM以及读EEPROM。
图1:EEPROM读写操作步骤
方案写入数据的EEPROM空间操作EEPROM之前,我们须要从片内Flash中划分出EEPROM空间,STC8A8K64D4系列单片机中,STC8A8K64D4型号单片机的EEPROM是须要用户自己设定的,其他型号都是已经分配好的,固定大小的。
STC8A8K64D4型号单片机的EEPROM空间划分下载程序时在STC-ISP 软件中设置的,如下图所示。
图2:EEPROM划分方法
EEPROM 分配总是从顶端向下分配的,下图是STC8A8K64D4型号单片机分配4K字节EEPROM的示例。
图3:分配4K字节EEPROM
我们在分配EEPROM空间时须要把稳以下几点:
分配的EEPROM空间必须是512的倍数,即分配的单位是扇区。分配的EEPROM空间不能占用程序存储的空间。擦除EEPROM扇区EEPROM擦除扇区时,只能利用IAP办法。擦除流程中须要把稳的是,实行扇区擦除时,输入该扇区的任意地址即可,如擦除EEPROM的第一个扇区,地址输入“0x0000~0x01FF”中的任意地址都可以。常日,我们编程时,为了方便指定擦除的扇区,习气于利用扇区的首地址作为擦除该扇区的地址,如擦除EEPROM的第一个扇区,地址输入“0x0000”。
图4:EEPROM擦除流程
使能和关闭IAPEEPROM擦除扇区前须要使能IAP,擦除完成后须要关闭IAP。使能和关闭IAP由“EEPROM 掌握寄存器( IAP_CONTR)”的位7(IAPEN)掌握,如下图所示。
EEPROM 掌握寄存器( IAP_CONTR):
设置等待韶光
等待韶光由“EEPROM 等待韶光掌握寄存器(IAP_TPS)”的位5~位0(IAPTPS[5:0])设置,如下图所示。
EEPROM 等待韶光掌握寄存器( IAP_TPS):
IAP_TPS=系统事情频率/1000000(小数部分四舍五入进行取整)。如本例中利用的系统事情频率为 24MHz,因此 IAP_TPS 设置为 24。
设置IAP命令(擦除扇区)IAP命令由“EEPROM 命令寄存器(IAP_CMD)”的位1~位0(CMD[1:0])设置,如下图所示。
EEPROM 命令寄存器(IAP_CMD):
CMD[1:0]:发送EEPROM操作命令00: 空操作。01: 读 EEPROM 命令。读取目标地址所在的 1 字节。10: 写 EEPROM 命令。写目标地址所在的 1 字节。 把稳:写操作只能将目标字节中的 1 写为 0,而不能将 0 写为 1。一样平常当目标字节不为 FFH 时,必须先擦除该目标地址所在的扇区。11:擦除 EEPROM。擦除目标地址所在的扇区。把稳:擦除操作会一次擦除 1个扇区(512 字节),全体扇区的内容全部变成 FFH。设置扇区地址
扇区地址由“EEPROM 地址寄存器(IAP_ADDR)”设置,地址是16位的,IAP_ADDRH 保存扇区地址的高字节, IAP_ADDRL保存扇区地址的低字节,如下图所示。
EEPROM 地址寄存器(IAP_ADDR):
扇区地址设置示例:擦除EEPROM的第一个扇区(利用该扇区的任意地址都可以,习气上利用起始地址)
IAP_ADDRH = 0xF0; //地址的高8位写入IAP_ADDRH寄存器
IAP_ADDRL = 0x00; //地址的高8位写入IAP_ADDRL寄存器
触发EEPROM操作完成前面的4步配置之后,向EEPROM 触发寄存器(IAP_TRIG)先写入 5AH,再写入 A5H即可触发扇区擦除。EEPROM 触发寄存器(IAP_TRIG)如下图所示。
EEPROM 触发寄存器(IAP_TRIG):
写完触发命令后, CPU 会处于 IDLE 等待状态,直到相应的 IAP 操作实行完成后 CPU 才会从 IDLE 状态返回正常状态连续实行 CPU 指令。
写EEPROMEEPROM编程只能利用IAP办法。EEPROM编程步骤如下图所示,他和擦除步骤类似,图中赤色字体标注的步骤是和擦除操作不一样的。这里,我们只描述不一样的部分,其他步骤读者参照擦除操作即可。
图5:EEPROM写流程
设置IAP命令(编程EEPROM)编程EEPROM 时,“EEPROM 命令寄存器(IAP_CMD)”的位1~位0(CMD[1:0])写入数值“10”。
编程时须要把稳,待编程的地址所在的EEPROM扇区必须是擦除过的,否则,可能会导致缺点。
设置EEPROM编程地址和擦除操作地址一样,EEPROM编程地址也是由“EEPROM 地址寄存器(IAP_ADDR)”设置的。每次编程完成后,IAP_ADDRL 和 EEPROM 命令寄存器IAP_CMD 的内容不变,即地址不会自动递增,因此,在连续的地址上批量写入数据时,需手动更新地址寄存器IAP_ADDRH 和寄存器 IAP_ADDRL 的值。
待编程数据写入IAP_DATA寄存器待编程数据在触发 EEPROM 的写操作前,必须写入到“EEPROM 数据寄存器(IAP_DATA)”,每次只能写入一个字节数据。
读EEPROMEEPROM读取数据可以利用IAP办法或 MOVC 办法。IAP办法读步骤如下图所示,他和擦除、写步骤类似,图中赤色字体标注的步骤是和擦除、写操作不一样的。这里,我们只描述不一样的部分,其他步骤读者参照擦除、写操作即可。
图6:EEPROM读流程
设置IAP命令(读EEPROM)读EEPROM 时,“EEPROM 命令寄存器(IAP_CMD)”的位1~位0(CMD[1:0])写入数值“01”。
设置读地址EEPROM读地址同样是由“EEPROM 地址寄存器(IAP_ADDR)”设置的。每次读完成后,IAP_ADDRL 和 EEPROM 命令寄存器IAP_CMD 的内容不变,即地址不会自动递增,因此,在连续的地址上批量读出数据时,需手动更新地址寄存器IAP_ADDRH 和寄存器 IAP_ADDRL 的值。
从IAP_DATA寄存器读出数据读 EEPROM时,读命令实行完成后读出的 EEPROM 数据保存在 IAP_DATA 寄存器中。程序中,可以从IAP_DATA 寄存器获取读取的数据。
从EEPROM读取数据,还可以利用MOVC办法,MOVC办法读取速率要快于IAP办法。利用MOVC办法读取时,须要把稳地址和IAP模式是不一样的,MOVC办法须要加上偏移地址。如下图所示,当从Flash划分4K字节的EEPROM后,IAP办法操作EEPROM是从地址0开始的,MOVC办法操作EEPROM的地址是从0xF000开始的。
图7:IAP办法和MOVC办法操作EEPROM时的地址
EEPROM读写实验注:本节的实验是在“实验2-6-1:串口1数据收发实验”的根本上修正,本节对应的实验源码是:“实验2-8-1:片内EEPROM读写”。实验内容本例中,从Flash中划分4K字节的EEPROM(共8个扇区)。程序中检测KEY1按键的状态,当KEY1按键按下后,擦除EEPROM的第1个扇区,然后从该扇区的起始地址开始连续写入256字节数据,之后分别通过IAP办法和MOVC办法读出数据并通过串口输出。
代码编写新建一个名称为“eeprom.c”的文件及其头文件“eeprom.h”并保存到工程的“Source”文件夹,并将“eeprom.c”加入到Keil工程中的“SOURCE”组。引用头文件由于在“main.c”文件中利用了“eeprom.c”文件中的函数,以是须要引用下面的头文件“eeprom.h”。
代码清单:引用头文件
//引用EEPROM的头文件 #include "eeprom.h" 编写禁止访问EEPROM的函数利用IAP办法操作EEPROM时,每次操作完成后都须要关闭对EEPROM的访问,并清零干系的寄存器,为了方便程序操作,我们编写一个禁止访问EEPROM的函数“EEPROM_Disable ()”供EEPROM操作调用,代码清单如下。
代码清单:禁止访问EEPROM
/ 描 述 : 禁止访问EEPROM 参 数 : 无 返回值 : 无 / void EEPROM_Disable(void) { IAP_CONTR = 0x00; //禁止EEPROM操作 IAP_CMD = 0x00; //清零EEPROM命令寄存器 IAP_TRIG = 0x00; //清零EEPROM触发寄存器,防止ISP/IAP命令误触发 IAP_ADDRH = 0xFF; //将地址设置到非IAP区域,防止误操作 IAP_ADDRL = 0xFF; } 擦除EEPROM扇区由于EEPROM的编程事理是只能将各个bit由1写为 0,而不能将0写为1。因此在EEPROM编程之前,为了担保写入的精确性,如果扇区的内容不是0xFF,则必须将对应的扇区擦除,使得扇区的内容全部规复为0xFF,扇区擦除代码清单如下。
代码清单:擦除指定的EEPROM扇区
/ 描 述 : 擦除指定的EEPROM扇区 参 数 : EE_address: 待擦除扇区的首地址(利用扇区内任意地址都可以,习气上利用首地址) 返回值 : 无 / void EEPROM_SectorErase(u16 EE_address) { EA = 0; //禁止总中断 IAP_CONTR |= 0x80; //许可EEPROM操作 IAP_TPS = 24; //系统时钟利用的是:24MHz,IAP_TPS=24000000/1000000 = 24 IAP_CMD = 0x03; //写入命令:扇区擦除命令 IAP_ADDRH = EE_address / 256; //地址寄存器高8位赋值 IAP_ADDRL = EE_address % 256; //地址寄存器低8位赋值 IAP_TRIG = 0x5A; //触发EEPROM操作,IAP_TRIG先写入0x5A,再写入0xA5 IAP_TRIG = 0xA5; _nop_(); //空操作,延时 EEPROM_Disable(); //禁止访问EEPROM EA = 1; //开启总中断 } 批量写入数据批量写入数据实现了以给定的地址作为起始地址连续写入指定长度数据的功能,代码清单如下。把稳写入数据时不要超过划分的EEPROM的边界。
代码清单:批量写入数据
/ 描 述 : 以指定的EEPROM地址为起始地址连续写入指定长度的数据 参 数 : EE_address: EEPROM地址 p_buf: 指向存放待写入数据的缓存 w_size: 写入的字节数 返回值 : 无 / void EEPROM_write_bytes(u16 EE_address,u8 p_buf,u16 w_size) { EA = 0; //禁止总中断 IAP_CONTR |= 0x80; //许可EEPROM操作 IAP_TPS = 24; //系统时钟利用的是:24MHz,IAP_TPS=24000000/1000000 = 24 IAP_CMD = 0x02; //写入命令:写EEPROM命令 do //循环写入数据 { IAP_ADDRH = EE_address / 256; //地址寄存器高8位赋值 IAP_ADDRL = EE_address % 256; //地址寄存器低8位赋值 IAP_DATA = p_buf; //待编程数据写入EEPROM数据寄存器 IAP_TRIG = 0x5A; //触发EEPROM操作,IAP_TRIG先写入0x5A,再写入0xA5 IAP_TRIG = 0xA5; _nop_(); //空操作,延时 EE_address++; p_buf++; }while(--w_size); EEPROM_Disable(); //禁止访问EEPROM EA = 1; //开启总中断 }批量读取数据批量读取数据实现了从以给定的EEPROM地址为起始地址连续读出指定长度的数据的功能,读出的数据保存到p_buf指向的缓存。
读取数据可以利用IAP办法或MOVC办法,下面是两种办法的代码清单,读者可以比拟一下,不难创造,MOVC办法比IAP办法大略,并且读取速率快于IAP办法。
代码清单:批量读取数据:IAP办法
/ 描 述 : IAP办法读取数据。以给定的EEPROM地址为起始地址连续读出指定长度的数据, : 读出的数据保存到p_buf指向的缓存 参 数 : EE_address: EEPROM地址 : p_buf: 指向保存读出数据的缓存 : r_size: 读出的字节数 返回值 : 无 / void EEPROM_read_bytes_iap(u16 EE_address,u8 p_buf,u16 r_size) { EA = 0; //禁止总中断 IAP_CONTR |= 0x80; //许可EEPROM操作 IAP_TPS = 24; //系统时钟利用的是:24MHz,IAP_TPS=24000000/1000000 = 24 IAP_CMD = 0x01; //写入IAP命令:读EEPROM do { IAP_ADDRH = EE_address / 256; //地址寄存器高8位赋值 IAP_ADDRL = EE_address % 256; //地址寄存器低8位赋值 IAP_TRIG = 0x5A; //触发EEPROM操作,IAP_TRIG先写入0x5A,再写入0xA5 IAP_TRIG = 0xA5; _nop_(); //空操作,延时 p_buf = IAP_DATA; //读出EEPROM数据寄存器的值保存到指定缓存 EE_address++; //地址加1 p_buf++; //数据缓存地址加1 }while(--r_size); EEPROM_Disable(); //禁止访问ISP/IAP EA = 1; //开启总中断 }MOVC办法读取EEPROM的代码清单如下:
代码清单:批量读取数据:MOVC办法
/ 描 述 : MOVC办法读取数据。以给定的EEPROM地址为起始地址连续读出指定长度的数据, : 读出的数据保存到p_buf指向的缓存 参 数 : EE_address: 给定的读取数据的起始地址 : p_buf: 指向保存读出数据的缓存 : r_size: 读出的字节数 返回值 : 无 / void EEPROM_read_bytes_movc(u16 EE_address,u8 p_buf,u16 r_size) { u8 code p_dat; p_dat = (u8 )EE_address; while(r_size--) //循环读出数据 { p_buf = p_dat; //读出ISP/IAP数据寄存器的值送往指定缓存 p_dat++; p_buf++; } }主函数中,检测到KEY1按键按下后,擦除EEPROM第一个扇区,接着从该扇区的起始地址开始连续写入256字节数据,之后分别通过IAP办法和MOVC办法读出数据并通过串口输出。
本例中,从Flash中划分4K字节的EEPROM(共8个扇区)。IAP办法读取数据时,第一个扇区的起始地址是0x0000。利用MOVC办法读取数据时,第一个扇区的起始地址是0xF000。程序代码清单如下。
代码清单:EEPROM读写测试
//定义EEPROM读写软件缓存,大小各为256字节 xdata u8 read_buf[256],write_buf[256]; / 功能描述:主函数 参 数:无 返 回 值:int类型 / int main(void) { u8 btn_val,j; u16 i,test_len; P2M1 &= 0x3F; P2M0 &= 0x3F; //设置P2.6~P2.7为准双向口(指示灯D1和D2) P7M1 &= 0xF9; P7M0 &= 0xF9; //设置P7.1~P7.2为准双向口(指示灯D4和D3) P3M1 &= 0xFE; P3M0 &= 0xFE; //设置P3.0为准双向口(串口1的RxD) P3M1 &= 0xFD; P3M0 |= 0x02; //设置P3.1为推挽输出(串口1的TxD) P3M1 &= 0x3F; P3M0 &= 0x3F; //设置P3.6~P3.7为准双向口(按键KEY2和KEY1) P0M1 &= 0x5F; P0M0 &= 0x5F; //设置P0.5,P0.7为准双向口(按键KEY4和KEY3) //如果按键电路上没有外部上拉电阻,须要开启GPIO的片内上拉。 //开拓板的按键电路设计了上拉电阻,因此,无需开启片内上拉 // P_SW2 |= 0x80; //将EAXFR位置1,以访问在XDATA区域的扩展SFR // P0PU |= 0xA0; //开启P0.5、P0.7的上拉电阻 // P3PU |= 0xC0; //开启P3.6、P3.7的上拉电阻 // P_SW2 &= 0x7F; //将EAXFR位置0,规复访问XRAM uart1_init(); //串口1初始化 EA = 1; //使能总中断 test_len = 256; //EEPROM读写测试长度为256个字节 while(1) { btn_val=buttons_scan(0); //获取开拓板用户按键检测值,不支持连按 //按下KEY1:测试按页写入。扇区0的第一页写入256个字节,之后读出数据并通过串口输出 if(btn_val == BUTTON1_PRESSED) { led_toggle(LED_1); //翻转指示灯D1的状态,指示按键按下 //写之前须要先实行擦除操作,擦除EEPROM第一个扇区 EEPROM_SectorErase(0x0000); //测试数据赋值 j = 0; for(i=0;i<256;i++)write_buf[i] = j++; //写入测试数据 EEPROM_write_bytes(0x0000,write_buf,test_len); //读出测试数据:IAP办法 //在EEPROM的首地址为0x1000处读取5个字节存入buffer数组中 EEPROM_read_bytes_iap(0x0000,read_buf,test_len); //串口打印读取的数据 for(i=0;i<test_len;i++)printf("%02bX ",read_buf[i]); printf("\r\n"); //读出测试数据:MOVC办法 EEPROM_read_bytes_movc(0xF000,read_buf,test_len); //串口打印读取的数据 for(i=0;i<test_len;i++)printf("%02bX ",read_buf[i]); } } } 硬件连接本实验须要利用LED指示灯D1、按键KEY1和USB转串口,按下下图所示短接对应的跳线帽。
图8:D1和KEY1跳线帽短接
图9:USB转串口跳线帽短接
实验步骤解压“…\第3部分:配套例程源码”目录下的压缩文件“实验2-8-1:片内EEPROM读写”,将解压后得到的文件夹拷贝到得当的目录,如“D\STC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程涌现问题)。双击“…\eeprom\Project\object”目录下的工程文件“eeprom.uvproj”。点击编译按钮编译工程,编译成功后天生的HEX文件“eeprom.hex”位于工程的“…\eeprom\project”目录下。打开STC-ISP软件下载程序,下载利用内部IRC时钟,IRC频率选择:24MHz,用户EEPROM大小设置为4K。电脑上打开串口调试助手,选择开拓板对应的串口号,将波特率设置为9600bps。程序运行后,按下KEY1按键,会擦除EEPROM的第一个扇区,接着从第一个扇区起始地址开始连续写入256个字节数据,之后分别利用IAP办法和MOVC办法读出数据并通过串口输出数据。图14:串口吸收的数据