视频讲解:EEPROM/DataFlash 应用,STC8H/STC8G/STC8C/STC8A8K64D4
https://v.stcai.com/sv/18b476c5-18ada751b0e/18b476c5-18ada751b0e.mp4
18 IAP/EEPROM/DATA-FLASHSTC8H系列单片机内部集成了大容量的EEPROM。利用ISP/IAP技术可将内部Data Flash当EEPROM,擦写次数在10万次以上。EEPROM可分为若干个扇区,每个扇区包含512字节。 注意:EEPROM的写操作只能将字节中的1写为0,当需要将字节中的0写为1,则必须执行扇区擦除操作。EEPROM的读/写操作是以1字节为单位进行,而EEPROM擦除操作是以1扇区(512字节)为单位进行,在执行擦除操作时,如果目标扇区中有需要保留的数据,则必须预先将这些数据读取到RAM中暂存,待擦除完成后再将保存的数据和需要更新的数据一起再写回EEPROM/DATA-FLASH。 所以在使用EEPROM时,建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不一定要用满。数据存储器的擦除操作是按扇区进行的(每扇区512字节)。 EEPROM可用于保存一些需要在应用过程中修改并且掉电不丢失的参数数据。在用户程序中,可以对EEPROM进行字节读/字节编程/扇区擦除操作。在工作电压偏低时,建议不要进行EEPROM操作,以免发送数据丢失的情况。 18.1 EEPROM操作时间n 读取1字节:4个系统时钟(使用MOVC指令读取更方便快捷) n 编程1字节:约30~40us(实际的编程时间为6~7.5us,但还需要加上状态转换时间和各种控制信号的SETUP和HOLD时间) n 擦除1扇区(512字节):约4~6ms EEPROM操作所需时间是硬件自动控制的,用户只需要正确设置IAP_TPS寄存器即可。 IAP_TPS=系统工作频率/1000000(小数部分四舍五入进行取整) 例如:系统工作频率为12MHz,则IAP_TPS设置为12 又例如:系统工作频率为22.1184MHz,则IAP_TPS设置为22 再例如:系统工作频率为5.5296MHz,则IAP_TPS设置为6
18.2 EEPROM相关的寄存器
18.2.1 EEPROM数据寄存器(IAP_DATA)在进行EEPROM的读操作时,命令执行完成后读出的EEPROM数据保存在IAP_DATA寄存器中。在进行EEPROM的写操作时,在执行写命令前,必须将待写入的数据存放在IAP_DATA寄存器中,再发送写命令。擦除EEPROM命令与IAP_DATA寄存器无关。 18.2.2 EEPROM地址寄存器(IAP_ADDR)EEPROM进行读、写、擦除操作的目标地址寄存器。IAP_ADDRH保存地址的高字节,IAP_ADDRL保存地址的低字节 18.2.3 EEPROM命令寄存器(IAP_CMD)CMD[1:0]:发送EEPROM操作命令 00:空操作 01:读EEPROM命令。读取目标地址所在的1字节。 10:写EEPROM命令。写目标地址所在的1字节。注意:写操作只能将目标字节中的1写为0,而不能将0写为1。一般当目标字节不为FFH时,必须先擦除。 11:擦除EEPROM。擦除目标地址所在的1页(1扇区/512字节)。注意:擦除操作会一次擦除1个扇区(512字节),整个扇区的内容全部变成FFH。 18.2.4 EEPROM触发寄存器(IAP_TRIG)设置完成EEPROM读、写、擦除的命令寄存器、地址寄存器、数据寄存器以及控制寄存器后,需要向触发寄存器IAP_TRIG依次写入5AH、A5H(顺序不能交换)两个触发命令来触发相应的读、写、擦除操作。操作完成后,EEPROM地址寄存器IAP_ADDRH、IAP_ADDRL和EEPROM命令寄存器IAP_CMD的内容不变。如果接下来要对下一个地址的数据进行操作,需手动更新地址寄存器IAP_ADDRH和寄存器IAP_ADDRL的值。 注意:每次EEPROM操作时,都要对IAP_TRIG先写入5AH,再写入A5H,相应的命令才会生效。写完触发命令后,CPU会处于IDLE等待状态,直到相应的IAP操作执行完成后CPU才会从IDLE状态返回正常状态继续执行CPU指令。 18.2.5 EEPROM控制寄存器(IAP_CONTR)IAPEN:EEPROM操作使能控制位 0:禁止EEPROM操作 1:使能EEPROM操作 SWBS:软件复位选择控制位,(需要与SWRST配合使用) 0:软件复位后从用户代码开始执行程序 1:软件复位后从系统ISP监控代码区开始执行程序 SWRST:软件复位控制位 0:无动作 1:产生软件复位 CMD_FAIL:EEPROM操作失败状态位,需要软件清零 0:EEPROM操作正确 1:EEPROM操作失败
18.2.6 EEPROM等待时间控制寄存器(IAP_TPS)需要根据工作频率进行设置 若工作频率为12MHz,则需要将IAP_TPS设置为12;若工作频率为24MHz,则需要将IAP_TPS设置为24,其他频率以此类推。
18.3 EEPROM大小及地址STC8H系列单片机内部均有用于保存用户数据的EEPROM。内部的EEPROM有3操作方式:读、写和擦除,其中擦除操作是以扇区为单位进行操作,每扇区为512字节,即每执行一次擦除命令就会擦除一个扇区,而读数据和写数据都是以字节为单位进行操作的,即每执行一次读或者写命令时只能读出或者写入一个字节。 STC8H系列单片机内部的EEPROM的访问方式有两种:IAP方式和MOVC方式。IAP方式可对EEPROM执行读、写、擦除操作,但MOVC只能对EEPROM进行读操作,而不能进行写和擦除操作。无论是使用IAP方式还是使用MOVC方式访问EEPROM,首先都需要设置正确的目标地址。IAP方式时,目标地址与EEPROM实际的物理地址是一致的,均是从地址0000H开始访问,但若要使用MOVC指令进行读取EEPROM数据时,目标地址必须是在EEPROM实际的物理地址的基础上还有加上程序大小的偏移。下面以STC8H1K16这个型号为例,对目标地址进行详细说明:
file:///C:/Users/HP/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif STC8H1K16的程序空间为16K字节(0000h~3FFFh),EEPROM空间为12K(0000h~2FFFh)。当需要对EEPROM物理地址1234h的单元进行读、写、擦除时,若使用IAP方式进行访问时,设置的目标地址为1234h,即IAP_ADDRH设置12h,IAP_ADDRL设置34h,然后设置相应的触发命令即可对1234h单元进行正确操作了。但若是使用MOVC方式读取EEPROM的1234h单元,则必须在1234h的基础上还有加上ROM空间的大小4000h,即必须将DPTR设置为5234h,然后才能使用MOVC指令进行读取。 注意:由于擦除是以512字节为单位进行操作的,所以执行擦除操作时所设置的目标地址的低9位是无意义的。例如:执行擦除命令时,设置地址1234H/1200H/1300H/13FFH,最终执行擦除的动作都是相同的,都是擦除1200H~13FFH这512字节。 不同型号内部EEPROM的大小及访问地址会存在差异,针对各个型号EEPROM的详细大小和地址请参考下表
[1]:这个为特殊型号,这个型号的EEPROM大小是可用在ISP下载时用户自己设置的。如下图所示:
file:///C:/Users/HP/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg 用户可用根据自己的需要在整个FLASH空间中规划出任意不超过FLASH大小的EEPROM空间,但需要注意:EEPROM总是从后向前进行规划的。 例如:STC8H1K28这个型号的FLASH为28K,此时若用户想分出其中的8K作为EEPROM使用,则EEPROM的物理地址则为28K的最后8K,物理地址为5000h~6FFFh,当然,用户若使用IAP的方式进行访问,目标地址仍然从0000h开始,到1FFFh结束,当使用MOVC读取则需要从5000h开始,到6FFFh结束。注意:STC8H1K28这个型号的程序空间为28K,即整个28K的范围均可运行程序,即使在ISP下载时将28K最后的8K设置为EEPROM使用,但这分配的8K空间仍然可以运行程序。其它可自定义EEPROM大小的型号与此类似。
18.4 范例程序
18.4.1 EEPROM基本操作
C语言代码
//测试工作频率为11.0592MHz #include "stc8h.h" #include "intrins.h" void IapIdle() { IAP_CONTR= 0;//关闭IAP功能 IAP_CMD= 0;//清除命令寄存器 IAP_TRIG= 0;//清除触发寄存器 IAP_ADDRH= 0x80;//将地址设置到非IAP区域 IAP_ADDRL= 0; } char IapRead(int addr) { chardat; IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 1;//设置IAP读命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_(); dat= IAP_DATA;//读IAP数据 IapIdle();//关闭IAP功能 returndat; } void IapProgram(int addr, char dat) { IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 2;//设置IAP写命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_DATA= dat;//写IAP数据 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_(); IapIdle();//关闭IAP功能 } void IapErase(int addr) { IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 3;//设置IAP擦除命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_();// IapIdle();//关闭IAP功能 } void main() { P_SW2|= 0x80; //使能访问XFR P0M0= 0x00; P0M1= 0x00; P1M0= 0x00; P1M1= 0x00; P2M0= 0x00; P2M1= 0x00; P3M0= 0x00; P3M1= 0x00; P4M0= 0x00; P4M1= 0x00; P5M0= 0x00; P5M1= 0x00; IapErase(0x0400); P0= IapRead(0x0400);//P0=0xff IapProgram(0x0400,0x12); P1= IapRead(0x0400);//P1=0x12 while(1); }
汇编代码
;测试工作频率为11.0592MHz P_SW2 DATA 0BAH IAP_DATA DATA 0C2H IAP_ADDRH DATA 0C3H IAP_ADDRL DATA 0C4H IAP_CMD DATA 0C5H IAP_TRIG DATA 0C6H IAP_CONTR DATA 0C7H IAP_TPS DATA 0F5H P1M1 DATA 091H P1M0 DATA 092H P0M1 DATA 093H P0M0 DATA 094H P2M1 DATA 095H P2M0 DATA 096H P3M1 DATA 0B1H P3M0 DATA 0B2H P4M1 DATA 0B3H P4M0 DATA 0B4H P5M1 DATA 0C9H P5M0 DATA 0CAH ORG 0000H LJMP MAIN ORG 0100H IAP_IDLE: MOV IAP_CONTR,#0 ;关闭IAP功能 MOV IAP_CMD,#0 ;清除命令寄存器 MOV IAP_TRIG,#0 ;清除触发寄存器 MOV IAP_ADDRH,#80H ;将地址设置到非IAP区域 MOV IAP_ADDRL,#0 RET IAP_READ: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#1 ;设置IAP读命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP MOV A,IAP_DATA ;读取IAP数据 LCALL IAP_IDLE ;关闭IAP功能 RET IAP_PROGRAM: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#2 ;设置IAP写命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_DATA,A ;写IAP数据 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET IAP_ERASE: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#3 ;设置IAP擦除命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET MAIN: MOV SP, #5FH ORL P_SW2,#80H ;使能访问XFR MOV P0M0, #00H MOV P0M1, #00H MOV P1M0, #00H MOV P1M1, #00H MOV P2M0, #00H MOV P2M1, #00H MOV P3M0, #00H MOV P3M1, #00H MOV P4M0, #00H MOV P4M1, #00H MOV P5M0, #00H MOV P5M1, #00H MOV DPTR,#0400H LCALL IAP_ERASE MOV DPTR,#0400H LCALL IAP_READ MOV P0,A ;P0=0FFH MOV DPTR,#0400H MOV A,#12H LCALL IAP_PROGRAM MOV DPTR,#0400H LCALL IAP_READ MOV P1,A ;P1=12H SJMP $ END
18.4.2 使用MOVC读取EEPROM
C语言代码
//测试工作频率为11.0592MHz #include "stc8h.h" #include "intrins.h" #define IAP_OFFSET 0x4000H //STC8H1K16 voidIapIdle() { IAP_CONTR = 0;//关闭IAP功能 IAP_CMD = 0;//清除命令寄存器 IAP_TRIG = 0;//清除触发寄存器 IAP_ADDRH = 0x80;//将地址设置到非IAP区域 IAP_ADDRL = 0; } char IapRead(intaddr) { addr += IAP_OFFSET;//使用MOVC读取EEPROM需要加上相应的偏移 return *(char code *)(addr);//使用MOVC读取数据 } voidIapProgram(int addr, char dat) { IAP_CONTR = 0x80;//使能IAP IAP_TPS = 12;//设置等待参数12MHz IAP_CMD = 2;//设置IAP写命令 IAP_ADDRL = addr;//设置IAP低地址 IAP_ADDRH = addr >> 8;//设置IAP高地址 IAP_DATA = dat;//写IAP数据 IAP_TRIG = 0x5a;//写触发命令(0x5a) IAP_TRIG = 0xa5;//写触发命令(0xa5) _nop_(); IapIdle();//关闭IAP功能 } voidIapErase(int addr) { IAP_CONTR = 0x80;//使能IAP IAP_TPS = 12;//设置等待参数12MHz IAP_CMD = 3;//设置IAP擦除命令 IAP_ADDRL = addr;//设置IAP低地址 IAP_ADDRH = addr >> 8;//设置IAP高地址 IAP_TRIG = 0x5a;//写触发命令(0x5a) IAP_TRIG = 0xa5;//写触发命令(0xa5) _nop_();// IapIdle();//关闭IAP功能 } void main() { P_SW2 |= 0x80; //使能访问XFR P0M0 = 0x00; P0M1 = 0x00; P1M0 = 0x00; P1M1 = 0x00; P2M0 = 0x00; P2M1 = 0x00; P3M0 = 0x00; P3M1 = 0x00; P4M0 = 0x00; P4M1 = 0x00; P5M0 = 0x00; P5M1 = 0x00; IapErase(0x0400); P0 = IapRead(0x0400);//P0=0xff IapProgram(0x0400, 0x12); P1 = IapRead(0x0400);//P1=0x12 while (1); }
汇编代码
;测试工作频率为11.0592MHz P_SW2 DATA 0BAH IAP_DATA DATA 0C2H IAP_ADDRH DATA 0C3H IAP_ADDRL DATA 0C4H IAP_CMD DATA 0C5H IAP_TRIG DATA 0C6H IAP_CONTR DATA 0C7H IAP_TPS DATA 0F5H IAP_OFFSET EQU 4000H ;STC8H1K16 P1M1 DATA 091H P1M0 DATA 092H P0M1 DATA 093H P0M0 DATA 094H P2M1 DATA 095H P2M0 DATA 096H P3M1 DATA 0B1H P3M0 DATA 0B2H P4M1 DATA 0B3H P4M0 DATA 0B4H P5M1 DATA 0C9H P5M0 DATA 0CAH ORG 0000H LJMP MAIN ORG 0100H IAP_IDLE: MOV IAP_CONTR,#0 ;关闭IAP功能 MOV IAP_CMD,#0 ;清除命令寄存器 MOV IAP_TRIG,#0 ;清除触发寄存器 MOV IAP_ADDRH,#80H ;将地址设置到非IAP区域 MOV IAP_ADDRL,#0 RET IAP_READ: MOV A,#LOW IAP_OFFSET ;使用MOVC读取EEPROM需要加上相应的偏移 ADD A,DPL MOV DPL,A MOV A,@HIGH IAP_OFFSET ADDC A,DPH MOV DPH,A CLR A MOVC A,@A+DPTR ;使用MOVC读取数据 RET IAP_PROGRAM: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#2 ;设置IAP写命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_DATA,A ;写IAP数据 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET IAP_ERASE: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#3 ;设置IAP擦除命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET MAIN: MOV SP, #5FH ORL P_SW2,#80H ;使能访问XFR MOV P0M0, #00H MOV P0M1, #00H MOV P1M0, #00H MOV P1M1, #00H MOV P2M0, #00H MOV P2M1, #00H MOV P3M0, #00H MOV P3M1, #00H MOV P4M0, #00H MOV P4M1, #00H MOV P5M0, #00H MOV P5M1, #00H MOV DPTR,#0400H LCALL IAP_ERASE MOV DPTR,#0400H LCALL IAP_READ MOV P0,A ;P0=0FFH MOV DPTR,#0400H MOV A,#12H LCALL IAP_PROGRAM MOV DPTR,#0400H LCALL IAP_READ MOV P1,A ;P1=12H SJMP $ END
18.4.3 使用串口送出EEPROM数据
C语言代码
//测试工作频率为11.0592MHz #include "stc8h.h" #include "intrins.h" #define FOSC 11059200UL #define BRT (65536-(FOSC / 115200+2) / 4) //加2操作是为了让Keil编译器 //自动实现四舍五入运算 void UartInit() { SCON= 0x5a; T2L= BRT; T2H= BRT >> 8; AUXR= 0x15; } void UartSend(char dat) { while(!TI); TI= 0; SBUF= dat; } void IapIdle() { IAP_CONTR= 0;//关闭IAP功能 IAP_CMD= 0;//清除命令寄存器 IAP_TRIG= 0;//清除触发寄存器 IAP_ADDRH= 0x80;//将地址设置到非IAP区域 IAP_ADDRL= 0; } char IapRead(int addr) { chardat; IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 1;//设置IAP读命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_(); dat= IAP_DATA;//读IAP数据 IapIdle();//关闭IAP功能 returndat; } void IapProgram(int addr, char dat) { IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 2;//设置IAP写命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_DATA= dat;//写IAP数据 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_(); IapIdle();//关闭IAP功能 } void IapErase(int addr) { IAP_CONTR= 0x80;//使能IAP IAP_TPS= 12;//设置等待参数12MHz IAP_CMD= 3;//设置IAP擦除命令 IAP_ADDRL= addr;//设置IAP低地址 IAP_ADDRH= addr >> 8;//设置IAP高地址 IAP_TRIG= 0x5a;//写触发命令(0x5a) IAP_TRIG= 0xa5;//写触发命令(0xa5) _nop_();// IapIdle();//关闭IAP功能 } void main() { P_SW2|= 0x80; //使能访问XFR P0M0= 0x00; P0M1= 0x00; P1M0= 0x00; P1M1= 0x00; P2M0= 0x00; P2M1= 0x00; P3M0= 0x00; P3M1= 0x00; P4M0= 0x00; P4M1= 0x00; P5M0= 0x00; P5M1= 0x00; UartInit(); IapErase(0x0400); UartSend(IapRead(0x0400)); IapProgram(0x0400,0x12); UartSend(IapRead(0x0400)); while(1); }
汇编代码
;测试工作频率为11.0592MHz P_SW2 DATA 0BAH AUXR DATA 8EH T2H DATA 0D6H T2L DATA 0D7H IAP_DATA DATA 0C2H IAP_ADDRH DATA 0C3H IAP_ADDRL DATA 0C4H IAP_CMD DATA 0C5H IAP_TRIG DATA 0C6H IAP_CONTR DATA 0C7H IAP_TPS DATA 0F5H P1M1 DATA 091H P1M0 DATA 092H P0M1 DATA 093H P0M0 DATA 094H P2M1 DATA 095H P2M0 DATA 096H P3M1 DATA 0B1H P3M0 DATA 0B2H P4M1 DATA 0B3H P4M0 DATA 0B4H P5M1 DATA 0C9H P5M0 DATA 0CAH ORG 0000H LJMP MAIN ORG 0100H UART_INIT: MOV SCON,#5AH MOV T2L,#0E8H ;65536-11059200/115200/4=0FFE8H MOV T2H,#0FFH MOV AUXR,#15H RET UART_SEND: JNB TI,$ CLR TI MOV SBUF,A RET IAP_IDLE: MOV IAP_CONTR,#0 ;关闭IAP功能 MOV IAP_CMD,#0 ;清除命令寄存器 MOV IAP_TRIG,#0 ;清除触发寄存器 MOV IAP_ADDRH,#80H ;将地址设置到非IAP区域 MOV IAP_ADDRL,#0 RET IAP_READ: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#1 ;设置IAP读命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP MOV A,IAP_DATA ;读取IAP数据 LCALL IAP_IDLE ;关闭IAP功能 RET IAP_PROGRAM: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#2 ;设置IAP写命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_DATA,A ;写IAP数据 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET IAP_ERASE: MOV IAP_CONTR,#80H ;使能IAP MOV IAP_TPS,#12 ;设置等待参数12MHz MOV IAP_CMD,#3 ;设置IAP擦除命令 MOV IAP_ADDRL,DPL ;设置IAP低地址 MOV IAP_ADDRH,DPH ;设置IAP高地址 MOV IAP_TRIG,#5AH ;写触发命令(0x5a) MOV IAP_TRIG,#0A5H ;写触发命令(0xa5) NOP LCALL IAP_IDLE ;关闭IAP功能 RET MAIN: MOV SP, #5FH ORL P_SW2,#80H ;使能访问XFR MOV P0M0, #00H MOV P0M1, #00H MOV P1M0, #00H MOV P1M1, #00H MOV P2M0, #00H MOV P2M1, #00H MOV P3M0, #00H MOV P3M1, #00H MOV P4M0, #00H MOV P4M1, #00H MOV P5M0, #00H MOV P5M1, #00H LCALL UART_INIT MOV DPTR,#0400H LCALL IAP_ERASE MOV DPTR,#0400H LCALL IAP_READ LCALL UART_SEND MOV DPTR,#0400H MOV A,#12H LCALL IAP_PROGRAM MOV DPTR,#0400H LCALL IAP_READ LCALL UART_SEND SJMP $ END
18.4.4 串口1读写EEPROM-带MOVC读C语言代码(main.c)
//测试工作频率为11.0592MHz /* 本程序经过测试完全正常, 不提供电话技术支持,如不能理解,请自行补充相关基础. */ /************* 本程序功能说明 ************** STC8G系列EEPROM通用测试程序. 请先别修改程序,直接下载"02-串口1读写EEPROM-带MOVC读"里面的"UART-EEPROM.hex"测试.下载时选择主频11.0592MHZ. PC串口设置:波特率115200,8,n,1. 对EEPROM做扇区擦除、写入64字节、读出64字节的操作。 命令例子: E0 对EEPROM进行扇区擦除操作,E表示擦除,数字0为0扇区(十进制,0~126, 看具体IC). W0 对EEPROM进行写入操作,W表示写入,数字0为0扇区(十进制,0~126, 看具体IC). 从扇区的开始地址连续写64字节. R0 对EEPROM进行IAP读出操作,R表示读出,数字0为0扇区(十进制,0~126, 看具体IC). 从扇区的开始地址连续读64字节. M0 对EEPROM进行MOVC读出操作(操作地址为扇区*512+偏移地址),数字0为0扇区(十进制,0~126, 看具体IC). 从扇区的开始地址连续读64字节. 注意:为了通用,程序不识别扇区是否有效,用户自己根据具体的型号来决定。 日期:2019-6-10 ******************************************/ #include "config.H" #include "EEPROM.h" #define Baudrate1 115200L #define UART1_BUF_LENGTH 10 #define EEADDR_OFFSET (8* 1024) //定义EEPROM用MOVC访问时加的偏移量, //等于FLASHROM的大小.对于IAP或IRC开头的, //则偏移量必须为0 #define TimeOutSet1 5 /************* 本地常量声明 **************/ u8 code T_Strings[]={"去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。"}; /************* 本地变量声明 **************/ u8 xdata tmp[70]; u8 xdata RX1_Buffer[UART1_BUF_LENGTH]; u8 RX1_Cnt; u8 RX1_TimeOut; bit B_TX1_Busy; /************* 本地函数声明 **************/ void UART1_config(void); void TX1_write2buff(u8 dat); //写入发送缓冲 void PrintString1(u8 *puts); //发送一个字符串 /************* 外部函数和变量声明*****************/ /**********************************************/ u8 CheckData(u8 dat) { if((dat >= '0') && (dat<= '9')) return (dat-'0'); if((dat >= 'A') && (dat<= 'F')) return (dat-'A'+10); if((dat >= 'a') && (dat<= 'f')) return (dat-'a'+10); return 0xff; } u16 GetAddress(void) { u16 address; u8 i; address = 0; if(RX1_Cnt < 3) return65535;//error if(RX1_Cnt <= 5) //5个字节以内是扇区操作,十进制, //支持命令: E 0, E 12, E 120 // W 0, W 12, W 120 // R 0, R 12, R 120 { for(i=2; i<RX1_Cnt; i++) { if(CheckData(RX1_Buffer)> 9) return65535; //error address = address *10 + CheckData(RX1_Buffer); } if(address < 124) //限制在0~123扇区 { address <<= 9; return (address); } } else if(RX1_Cnt == 8) //8个字节直接地址操作,十六进制, //支持命令: E 0x1234, W 0x12b3, R 0x0A00 { if((RX1_Buffer[2] == '0')&& ((RX1_Buffer[3] == 'x') || (RX1_Buffer[3] == 'X'))) { for(i=4; i<8;i++) { if(CheckData(RX1_Buffer)> 0x0F) return 65535; //error address =(address << 4) + CheckData(RX1_Buffer); } if(address <63488) return (address); //限制在0~123扇区 } }
return 65535; //error }
//======================================================================== //函数:void delay_ms(u8 ms) //描述:延时函数。 //参数:ms,要延时的ms数,这里只支持1~255ms.自动适应主时钟. //返回:none. //版本:VER1.0 //日期:2013-4-1 //备注: //======================================================================== void delay_ms(u8 ms) { u16 i; do { i = MAIN_Fosc / 10000; while(--i) ; }while(--ms); }
//使用MOVC读EEPROM voidEEPROM_MOVC_read_n(u16 EE_address, u8 *DataAddress, u16 number) { u8 code *pc;
pc = EE_address + EEADDR_OFFSET; do { *DataAddress = *pc; //读出的数据 DataAddress++; pc++; }while(--number); }
/*********************主函数*************************/ voidmain(void) { u8 i; u16 addr;
UART1_config(); //选择波特率,2: 使用Timer2做波特率, //其它值:使用Timer1做波特率. EA = 1; //允许总中断
PrintString1("STC8系列MCU用串口1测试EEPROM程序!\r\n"); //UART1发送一个字符串
while(1) { delay_ms(1); if(RX1_TimeOut > 0) //超时计数 { if(--RX1_TimeOut ==0) { if(RX1_Buffer[1]== ' ') { addr= GetAddress(); if(addr< 63488) //限制在0~123扇区 { if(RX1_Buffer[0]== 'E') //PC请求擦除一个扇区 { EEPROM_SectorErase(addr); PrintString1("扇区擦除完成!\r\n"); }
elseif(RX1_Buffer[0] == 'W') //PC请求写入EEPROM64字节数据 { EEPROM_write_n(addr,T_Strings,64); PrintString1("写入操作完成!\r\n"); }
elseif(RX1_Buffer[0] == 'R') //PC请求返回64字节EEPROM数据 { PrintString1("IAP读出的数据如下:\r\n"); EEPROM_read_n(addr,tmp,64); for(i=0;i<64; i++) TX1_write2buff(tmp); //将数据返回给串口 TX1_write2buff(0x0d); TX1_write2buff(0x0a); } elseif(RX1_Buffer[0] == 'M') //PC请求返回64字节EEPROM数据 { PrintString1("MOVC读出的数据如下:\r\n"); EEPROM_MOVC_read_n(addr,tmp,64); for(i=0;i<64; i++) TX1_write2buff(tmp); //将数据返回给串口 TX1_write2buff(0x0d); TX1_write2buff(0x0a); } else PrintString1("命令错误!\r\n"); } else PrintString1("命令错误!\r\n"); }
RX1_Cnt =0; } } } } /**********************************************/
/***************发送一个字节*******************************/ voidTX1_write2buff(u8 dat) //写入发送缓冲 { B_TX1_Busy = 1; //标志发送忙 SBUF = dat; //发送一个字节 while(B_TX1_Busy); //等待发送完毕 }
//======================================================================== //函数:void PrintString1(u8 *puts) //描述:串口1发送字符串函数。 //参数:puts: 字符串指针. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== voidPrintString1(u8 *puts) //发送一个字符串 { for (; *puts != 0; puts++) //遇到停止符0结束 { TX1_write2buff(*puts); } }
//======================================================================== //函数:void UART1_config(void) //描述:UART1初始化函数。 //参数:none. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== void UART1_config(void) { TR1 = 0; AUXR &= ~0x01; //S1 BRTUse Timer1; AUXR |= (1<<6); //Timer1 set as 1T mode TMOD &= ~(1<<6); //Timer1 set As Timer TMOD &= ~0x30; //Timer1_16bitAutoReload; TH1 = (u8)((65536L-(MAIN_Fosc / 4) /Baudrate1) >> 8); TL1 = (u8)(65536L-(MAIN_Fosc / 4) /Baudrate1); ET1 = 0; //禁止Timer1中断 INT_CLKO &= ~0x02; //Timer1不输出高速时钟 TR1 = 1; //运行Timer1
S1_USE_P30P31(); P3n_standard(0x03); //切换到P3.0 P3.1 //S1_USE_P36P37(); P3n_standard(0xc0); //切换到P3.6 P3.7 //S1_USE_P16P17(); P1n_standard(0xc0); //切换到P1.6 P1.7
SCON = (SCON & 0x3f) | 0x40; //UART1模式, 0x00: 同步移位输出, // 0x40: 8位数据,可变波特率, // 0x80: 9位数据,固定波特率, // 0xc0:9位数据,可变波特率 // PS = 1; //高优先级中断 ES = 1; //允许中断 REN = 1; //允许接收
B_TX1_Busy = 0; RX1_Cnt = 0; }
//======================================================================== //函数:void UART1_int (void) interrupt UART1_VECTOR //描述:UART1中断函数。 //参数:nine. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== voidUART1_int (void) interrupt 4 { if(RI) { RI = 0; if(RX1_Cnt >=UART1_BUF_LENGTH) RX1_Cnt = 0; //防溢出 RX1_Buffer[RX1_Cnt++] = SBUF; RX1_TimeOut = TimeOutSet1; }
if(TI) { TI = 0; B_TX1_Busy = 0; } }
C语言代码(EEPROM.c)
//测试工作频率为11.0592MHz // 本程序是STC系列的内置EEPROM读写程序。 #include"config.h" #include"eeprom.h" //======================================================================== //函数:void IAP_Disable(void) //描述:禁止访问ISP/IAP. //参数:non. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== void DisableEEPROM(void) { IAP_CONTR = 0; //禁止ISP/IAP操作 IAP_TPS = 0; IAP_CMD = 0; //去除ISP/IAP命令 IAP_TRIG = 0; //防止ISP/IAP命令误触发 IAP_ADDRH = 0xff; //清0地址高字节 IAP_ADDRL = 0xff; //清0地址低字节,指向非EEPROM区,防止误操作 } //======================================================================== //函数:void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number) //描述:从指定EEPROM首地址读出n个字节放指定的缓冲. //参数:EE_address: 读出EEPROM的首地址. // DataAddress: 读出数据放缓冲的首地址. // number: 读出的字节长度. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== voidEEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number) { EA = 0; //禁止中断 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS = (u8)(MAIN_Fosc / 1000000L); //工作频率设置 IAP_READ(); //送字节读命令,命令不需改变时,不需重新送命令 do { IAP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL = EE_address % 256; //送地址低字节 IAP_TRIG(); //先送5AH,再送A5H到ISP/IAP触发寄存器, //每次都需要如此 //送完A5H后,ISP/IAP命令立即被触发启动 //CPU等待IAP完成后,才会继续执行程序。 _nop_(); _nop_(); _nop_(); *DataAddress = IAP_DATA; //读出的数据送往 EE_address++; DataAddress++; }while(--number); DisableEEPROM(); EA = 1; //重新允许中断 } /********************扇区擦除函数*****************/ //======================================================================== //函数:void EEPROM_SectorErase(u16 EE_address) //描述:把指定地址的EEPROM扇区擦除. //参数:EE_address: 要擦除的扇区EEPROM的地址. //返回:non. //版本:V1.0, 2013-5-10 //======================================================================== voidEEPROM_SectorErase(u16 EE_address) { EA = 0; //禁止中断 //只有扇区擦除,没有字节擦除,512字节/扇区。 //扇区中任意一个字节地址都是扇区地址。 IAP_ADDRH = EE_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL = EE_address % 256; //送扇区地址低字节 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS = (u8)(MAIN_Fosc / 1000000L); //工作频率设置 IAP_ERASE(); //送扇区擦除命令,命令不需改变时,不需重新送命令 IAP_TRIG(); _nop_(); _nop_(); _nop_(); DisableEEPROM(); EA = 1; //重新允许中断 } //======================================================================== //函数:void EEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number) //描述:把缓冲的n个字节写入指定首地址的EEPROM. //参数:EE_address: 写入EEPROM的首地址. // DataAddress: 写入源数据的缓冲的首地址. // number: 写入的字节长度. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== voidEEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number) { EA = 0; //禁止中断 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS = (u8)(MAIN_Fosc / 1000000L); //工作频率设置 IAP_WRITE(); //送字节写命令,命令不需改变时,不需重新送命令 do { IAP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL = EE_address % 256; //送地址低字节 IAP_DATA = *DataAddress; //送数据到IAP_DATA,只有数据改变时才需重新送 IAP_TRIG(); _nop_(); _nop_(); _nop_(); EE_address++; DataAddress++; }while(--number); DisableEEPROM(); EA = 1; //重新允许中断 }
18.4.5 口令擦除写入-多扇区备份-串口1操作C语言代码(main.c)
//测试工作频率为11.0592MHz /* 本程序经过测试完全正常, 不提供电话技术支持,如不能理解,请自行补充相关基础. */ /************* 本程序功能说明 ************** STC8G系列STC8H系列STC8C系列EEPROM通用测试程序,演示多扇区备份、有扇区错误则用正确扇区数据写入、全部扇区错误(比如第一次运行程序)则写入默认值. 每次写都写入3个扇区,即冗余备份. 每个扇区写一条记录,写入完成后读出保存的数据和校验值跟源数据和校验值比较,并从串口1(P3.0P3.1)返回结果(正确或错误提示). 每条记录自校验,64字节数据,2字节校验值,校验值= 64字节数据累加和^ 0x5555. ^0x5555是为了保证写入的66个数据不全部为0. 如果有扇区错误,则将正确扇区的数据写入错误扇区,如果3个扇区都错误,则均写入默认值. 擦除、写入、读出操作前均需要设置口令,如果口令不对则退出操作,并且每次退出操作都会清除口令. 下载时选择主频11.0592MHZ. PC串口设置:波特率115200,8,n,1. 对EEPROM做扇区擦除、写入64字节、读出64字节的操作。 命令例子: 使用串口助手发单个字符,大小写均可. 发E或e: 对EEPROM进行扇区擦除操作,E表示擦除,会擦除扇区0、1、2. 发W或w: 对EEPROM进行写入操作,W表示写入,会写入扇区0、1、2,每个扇区连续写64字节,扇区0写入0x0000~0x003f,扇区1写入0x0200~0x023f,扇区0写入0x0400~0x043f. 发R或r: 对EEPROM进行读出操作,R表示读出,会读出扇区0、1、2,每个扇区连续读64字节,扇区0读出0x0000~0x003f,扇区1读出0x0200~0x023f,扇区0读出0x0400~0x043f. 注意:为了通用,程序不识别扇区是否有效,用户自己根据具体的型号来决定。 日期:2021-11-5 ******************************************/ #include "config.H" #include "EEPROM.h" #define Baudrate1 115200L /************* 本地常量声明 **************/ u8 code T_StringD[]={"去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。"}; u8 code T_StringW[]={"横看成岭侧成峰,远近高低各不同。不识庐山真面目,只缘身在此山中。"}; /************* 本地变量声明 **************/ u8 xdata tmp[70]; //通用数据 u8 xdata SaveTmp[70]; //要写入的数组 bit B_TX1_Busy; u8 cmd; //串口单字符命令 /************* 本地函数声明 **************/ void UART1_config(void); void TX1_write2buff(u8 dat); //写入发送缓冲 void PrintString1(u8 *puts); //发送一个字符串 /************* 外部函数和变量声明*****************/ /************* 读取EEPROM记录,并且校验,返回校验结果,0为正确,1为错误*****************/ u8 ReadRecord(u16 addr) { u8 i; u16 ChckSum; //计算的累加和 u16 j; //读取的累加和 for(i=0; i<66; i++) tmp = 0; //清除缓冲 PassWord = D_PASSWORD; //给定口令 EEPROM_read_n(addr,tmp,66); //读出扇区0 for(ChckSum=0, i=0; i<64; i++) ChckSum+= tmp; //计算累加和 j = ((u16)tmp[64]<<8) +(u16)tmp[65]; //读取记录的累加和 j ^= 0x5555; //隔位取反,避免全0 if(ChckSum != j) return 1; //累加和错误,返回1 return 0; //累加和正确,返回0 }
/************* 写入EEPROM记录,并且校验,返回校验结果,0为正确,1为错误*****************/ u8 SaveRecord(u16 addr) { u8 i; u16 ChckSum; //计算的累加和
for(ChckSum=0, i=0; i<64; i++) ChckSum += SaveTmp; //计算累加和 ChckSum ^= 0x5555; //隔位取反,避免全0 SaveTmp[64] = (u8)(ChckSum >> 8); SaveTmp[65] = (u8)ChckSum;
PassWord = D_PASSWORD; //给定口令 EEPROM_SectorErase(addr); //擦除一个扇区 PassWord = D_PASSWORD; //给定口令 EEPROM_write_n(addr, SaveTmp, 66); //写入扇区
for(i=0; i<66; i++) tmp= 0; //清除缓冲 PassWord = D_PASSWORD; //给定口令 EEPROM_read_n(addr,tmp,66); //读出扇区0 for(i=0; i<66; i++) //数据比较 { if(SaveTmp != tmp) return 1; //数据有错误,返回1 } return 0; //累加和正确,返回0 }
/*********************主函数*************************/ voidmain(void) { u8 i; u8 status; //状态
UART1_config(); // 选择波特率,2: 使用Timer2做波特率, //其它值:使用Timer1做波特率. EA = 1; //允许总中断
PrintString1("STC8G-8H-8C系列MCU用串口1测试EEPROM程序!\r\n"); //UART1发送一个字符串
//上电读取3个扇区并校验,如果有扇区错误则将正确的 //扇区写入错误扇区,如果3个扇区都错误,则写入默认值 status = 0; if(ReadRecord(0x0000) == 0) //读扇区0 { status |= 0x01; //正确则标记status.0=1 for(i=0; i<64; i++) SaveTmp = tmp; //保存在写缓冲 } if(ReadRecord(0x0200) == 0) //读扇区1 { status |= 0x02; //正确则标记status.1=1 for(i=0; i<64; i++) SaveTmp = tmp; //保存在写缓冲 } if(ReadRecord(0x0400) == 0) //读扇区2 { status |= 0x04; //正确则标记status.2=1 for(i=0; i<64; i++) SaveTmp = tmp; //保存在写缓冲 }
if(status == 0) //所有扇区都错误,则写入默认值 { for(i=0; i<64; i++) SaveTmp = T_StringD; //读取默认值 } else PrintString1("上电读取3个扇区数据均正确!\r\n"); //UART1发送一个字符串提示
if((status & 0x01) == 0) //扇区0错误,则写入默认值 { if(SaveRecord(0x0000) == 0) PrintString1("写入扇区0正确!\r\n"); //写入记录0扇区正确 else PrintString1("写入扇区0错误!\r\n"); //写入记录0扇区错误 } if((status & 0x02) == 0) //扇区1错误,则写入默认值 { if(SaveRecord(0x0200) == 0) PrintString1("写入扇区1正确!\r\n"); //写入记录1扇区正确 else PrintString1("写入扇区1错误!\r\n"); //写入记录1扇区错误 } if((status & 0x04) == 0) //扇区2错误,则写入默认值 { if(SaveRecord(0x0400) == 0) PrintString1("写入扇区2正确!\r\n"); //写入记录2扇区正确 else PrintString1("写入扇区2错误!\r\n"); //写入记录2扇区错误 }
while(1) { if(cmd != 0) //有串口命令 { if((cmd >= 'a')&& (cmd <= 'z')) cmd -= 0x20; //小写转大写
if(cmd == 'E') //PC请求擦除一个扇区 { PassWord =D_PASSWORD; //给定口令 EEPROM_SectorErase(0x0000); //擦除一个扇区 PassWord =D_PASSWORD; //给定口令 EEPROM_SectorErase(0x0200); //擦除一个扇区 PassWord =D_PASSWORD; //给定口令 EEPROM_SectorErase(0x0400); //擦除一个扇区 PrintString1("扇区擦除完成!\r\n"); }
else if(cmd == 'W') //PC请求写入EEPROM64字节数据 { for(i=0;i<64; i++) SaveTmp = T_StringW; //写入数值 if(SaveRecord(0x0000)== 0) PrintString1("写入扇区0正确!\r\n"); //写入记录0扇区正确 else PrintString1("写入扇区0错误!\r\n"); //写入记录0扇区错误 if(SaveRecord(0x0200) == 0) PrintString1("写入扇区1正确!\r\n"); //写入记录1扇区正确 else PrintString1("写入扇区1错误!\r\n"); //写入记录1扇区错误 if(SaveRecord(0x0400)== 0) PrintString1("写入扇区2正确!\r\n"); //写入记录2扇区正确 else PrintString1("写入扇区2错误!\r\n"); //写入记录2扇区错误 }
else if(cmd == 'R') //PC请求返回64字节EEPROM数据 { if(ReadRecord(0x0000)== 0) //读出扇区0的数据 { PrintString1("读出扇区0的数据如下:\r\n"); for(i=0;i<64; i++) TX1_write2buff(tmp); //将数据返回给串口 TX1_write2buff(0x0d); //回车换行 TX1_write2buff(0x0a); } else PrintString1("读出扇区0的数据错误!\r\n");
if(ReadRecord(0x0200)== 0) //读出扇区1的数据 { PrintString1("读出扇区1的数据如下:\r\n"); for(i=0;i<64; i++) TX1_write2buff(tmp); //将数据返回给串口 TX1_write2buff(0x0d); //回车换行 TX1_write2buff(0x0a); } else PrintString1("读出扇区1的数据错误!\r\n");
if(ReadRecord(0x0400)== 0) //读出扇区2的数据 { PrintString1("读出扇区2的数据如下:\r\n"); for(i=0;i<64; i++) TX1_write2buff(tmp); //将数据返回给串口 TX1_write2buff(0x0d); //回车换行 TX1_write2buff(0x0a); } else PrintString1("读出扇区2的数据错误!\r\n"); } else PrintString1("命令错误!\r\n"); cmd = 0; } } } /**********************************************/
/***************发送一个字节 *******************************/ voidTX1_write2buff(u8 dat) //写入发送缓冲 { B_TX1_Busy = 1; //标志发送忙 SBUF = dat; //发送一个字节 while(B_TX1_Busy); //等待发送完毕 }
//======================================================================== //函数:void PrintString1(u8 *puts) //描述:串口1发送字符串函数。 //参数:puts: 字符串指针. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== voidPrintString1(u8 *puts) //发送一个字符串 { for (; *puts != 0; puts++) //遇到停止符0结束 { TX1_write2buff(*puts); } }
//======================================================================== //函数:void UART1_config(void) //描述:UART1初始化函数。 //参数:none. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== void UART1_config(void) { TR1 = 0; AUXR &= ~0x01; //S1 BRTUse Timer1; AUXR |= (1<<6); //Timer1set as 1T mode TMOD &= ~(1<<6); //Timer1 set As Timer TMOD &= ~0x30; //Timer1_16bitAutoReload; TH1 = (u8)((65536L-(MAIN_Fosc / 4) /Baudrate1) >> 8); TL1 = (u8)(65536L-(MAIN_Fosc / 4) /Baudrate1); ET1 = 0; // 禁止Timer1中断 INT_CLKO &= ~0x02; //Timer1不输出高速时钟 TR1 = 1; //运行Timer1
S1_USE_P30P31(); P3n_standard(0x03); //切换到P3.0 P3.1 //S1_USE_P36P37(); P3n_standard(0xc0); //切换到P3.6 P3.7 //S1_USE_P16P17(); P1n_standard(0xc0); //切换到P1.6 P1.7
SCON = (SCON & 0x3f) | 0x40; //UART1模式, 0x00: 同步移位输出, // 0x40: 8位数据,可变波特率, // 0x80: 9位数据,固定波特率, // 0xc0: 9位数据,可变波特率 // PS = 1; //高优先级中断 ES = 1; //允许中断 REN = 1; //允许接收
B_TX1_Busy = 0; }
//======================================================================== //函数:void UART1_int (void) interrupt UART1_VECTOR //描述:UART1中断函数。 //参数:nine. //返回:none. //版本:VER1.0 //日期:2014-11-28 //备注: //======================================================================== voidUART1_int (void) interrupt 4 { if(RI) { RI = 0; cmd = SBUF; }
if(TI) { TI = 0; B_TX1_Busy = 0; } }
C语言代码(EEPROM.c)
//测试工作频率为11.0592MHz // 本程序是STC系列的内置EEPROM读写程序。 #include "config.h" #include "EEPROM.h" u32 PassWord; //擦除写入时需要的口令 //======================================================================== //函数:void IAP_Disable(void) //描述:禁止访问ISP/IAP. //参数:non. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== void DisableEEPROM(void) { IAP_CONTR = 0; //禁止ISP/IAP操作 IAP_TPS = 0; IAP_CMD = 0; //去除ISP/IAP命令 IAP_TRIG = 0; //防止ISP/IAP命令误触发 IAP_ADDRH = 0xff; //清0地址高字节 IAP_ADDRL = 0xff; //清0地址低字节,指向非EEPROM区,防止误操作 } //======================================================================== //函数:void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number) //描述:从指定EEPROM首地址读出n个字节放指定的缓冲. //参数:EE_address: 读出EEPROM的首地址. // DataAddress: 读出数据放缓冲的首地址. // number: 读出的字节长度. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== voidEEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number) { if(PassWord == D_PASSWORD) //口令正确才会操作EEPROM { EA = 0; //禁止中断 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS = (u8)(MAIN_Fosc /1000000L); //工作频率设置 IAP_READ(); //送字节读命令,命令不需改变时,不需重新送命令 do { IAP_ADDRH =EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL =EE_address % 256; //送地址低字节 if(PassWord ==D_PASSWORD) //口令口令正确才触发操作 { IAP_TRIG =0x5A; //先送5AH,再送A5H到ISP/IAP触发寄存器, //每次都需要如此 IAP_TRIG =0xA5; //送完A5H后,ISP/IAP命令立即被触发启动 } //CPU等待IAP完成后,才会继续执行程序。 _nop_(); _nop_(); _nop_(); *DataAddress = IAP_DATA; //读出的数据送往 EE_address++; DataAddress++; }while(--number); DisableEEPROM(); EA = 1; //重新允许中断 } PassWord = 0; //清除口令 } /********************扇区擦除函数*****************/ //======================================================================== //函数:void EEPROM_SectorErase(u16 EE_address) //描述:把指定地址的EEPROM扇区擦除. //参数:EE_address: 要擦除的扇区EEPROM的地址. //返回:non. //版本:V1.0, 2013-5-10 //======================================================================== voidEEPROM_SectorErase(u16 EE_address) { if(PassWord == D_PASSWORD) //口令正确才会操作EEPROM { EA = 0; //禁止中断 //只有扇区擦除,没有字节擦除,512字节/扇区。 //扇区中任意一个字节地址都是扇区地址。 IAP_ADDRH = EE_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL = EE_address % 256; //送扇区地址低字节 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS= (u8)(MAIN_Fosc / 1000000L); //工作频率设置 IAP_ERASE(); //送扇区擦除命令,命令不需改变时,不需重新送命令 if(PassWord ==D_PASSWORD) //口令口令正确才触发操作 { IAP_TRIG =0x5A; //先送5AH,再送A5H到ISP/IAP触发寄存器, //每次都需要如此 IAP_TRIG =0xA5; //送完A5H后,ISP/IAP命令立即被触发启动 } //CPU等待IAP完成后,才会继续执行程序。 _nop_(); _nop_(); _nop_(); DisableEEPROM(); EA = 1; //重新允许中断 } PassWord = 0; //清除口令 } //======================================================================== //函数:void EEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number) //描述:把缓冲的n个字节写入指定首地址的EEPROM. //参数:EE_address: 写入EEPROM的首地址. // DataAddress: 写入源数据的缓冲的首地址. // number: 写入的字节长度. //返回:non. //版本:V1.0, 2012-10-22 //======================================================================== voidEEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number) { if(PassWord == D_PASSWORD) //口令正确才会操作EEPROM { EA = 0; //禁止中断 IAP_CONTR = IAP_EN; //允许ISP/IAP操作 IAP_TPS = (u8)(MAIN_Fosc /1000000L); //工作频率设置 IAP_WRITE(); //送字节写命令,命令不需改变时,不需重新送命令 do { IAP_ADDRH =EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址) IAP_ADDRL =EE_address % 256; //送地址低字节 IAP_DATA = *DataAddress; //送数据到IAP_DATA,只有数据改变时才需重新送 if(PassWord ==D_PASSWORD) //口令正确才触发操作 { IAP_TRIG =0x5A; //先送5AH,再送A5H到ISP/IAP触发寄存器, //每次都需要如此 IAP_TRIG =0xA5; //送完A5H后,ISP/IAP命令立即被触发启动 } //CPU等待IAP完成后,才会继续执行程序。 _nop_(); _nop_(); _nop_(); EE_address++; DataAddress++; }while(--number); DisableEEPROM(); EA = 1; //重新允许中断 } PassWord = 0; //清除口令 }
|