神农鼎 发表于 2023-9-26 19:50:40

视频讲解:EEPROM/DataFlash, 8H系列数据手册 EEPROM 内容

视频讲解: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~6msEEPROM操作所需时间是硬件自动控制的,用户只需要正确设置IAP_TPS寄存器即可。IAP_TPS=系统工作频率/1000000(小数部分四舍五入进行取整)例如:系统工作频率为12MHz,则IAP_TPS设置为12又例如:系统工作频率为22.1184MHz,则IAP_TPS设置为22再例如:系统工作频率为5.5296MHz,则IAP_TPS设置为6
18.2         EEPROM相关的寄存器
符号描述地址位地址与符号复位值
B7B6B5B4B3B2B1B0
IAP_DATAIAP数据寄存器C2H
1111,1111
IAP_ADDRHIAP高地址寄存器C3H
0000,0000
IAP_ADDRLIAP低地址寄存器C4H
0000,0000
IAP_CMDIAP命令寄存器C5H------CMDxxxx,xx00
IAP_TRIGIAP触发寄存器C6H
0000,0000
IAP_CONTRIAP控制寄存器C7HIAPENSWBSSWRSTCMD_FAIL----0000,xxxx
IAP_TPSIAP等待时间控制寄存器F5H--IAPTPSxx00,0000

18.2.1          EEPROM数据寄存器(IAP_DATA)
符号地址B7B6B5B4B3B2B1B0
IAP_DATAC2H

在进行EEPROM的读操作时,命令执行完成后读出的EEPROM数据保存在IAP_DATA寄存器中。在进行EEPROM的写操作时,在执行写命令前,必须将待写入的数据存放在IAP_DATA寄存器中,再发送写命令。擦除EEPROM命令与IAP_DATA寄存器无关。18.2.2          EEPROM地址寄存器(IAP_ADDR)
符号地址B7B6B5B4B3B2B1B0
IAP_ADDRHC3H

IAP_ADDRLC4H

EEPROM进行读、写、擦除操作的目标地址寄存器。IAP_ADDRH保存地址的高字节,IAP_ADDRL保存地址的低字节18.2.3          EEPROM命令寄存器(IAP_CMD)
符号地址B7B6B5B4B3B2B1B0
IAP_CMDC5H------CMD
CMD:发送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)
符号地址B7B6B5B4B3B2B1B0
IAP_TRIGC6H

设置完成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)
符号地址B7B6B5B4B3B2B1B0
IAP_CONTRC7HIAPENSWBSSWRSTCMD_FAIL----
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)
符号地址B7B6B5B4B3B2B1B0
IAP_TPSF5H--IAPTPS
需要根据工作频率进行设置若工作频率为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.gifSTC8H1K16的程序空间为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的详细大小和地址请参考下表
型号大小扇区IAP方式读/写/擦除MOVC读取
起始地址结束地址起始地址结束地址
STC8H1K1612K240000h2FFFh4000h6FFFh
STC8H1K244K80000h0FFFh6000h6FFFh
STC8H1K28用户自定义
STC8H1K33用户自定义
STC8H1K084K80000h0FFFh2000h2FFFh
STC8H1K12用户自定义
STC8H1K17用户自定义
STC8H3K32S432K640000h7FFFh8000hFFFFh
STC8H3K48S416K320000h3FFFhC000hFFFFh
STC8H3K60S44K80000h0FFFhF000hFFFFh
STC8H3K64S4用户自定义
STC8H3K32S232K640000h7FFFh8000hFFFFh
STC8H3K48S216K320000h3FFFhC000hFFFFh
STC8H3K60S24K80000h0FFFhF000hFFFFh
STC8H3K64S2用户自定义
STC8H8K32U32K640000h7FFFh8000hFFFFh
STC8H8K48U16K320000h3FFFhC000hFFFFh
STC8H8K60U4K80000h0FFFhF000hFFFFh
STC8H8K64U用户自定义
STC8H4K32TL32K640000h7FFFh8000hFFFFh
STC8H4K48TL16K320000h3FFFhC000hFFFFh
STC8H4K60TL4K80000h0FFFhF000hFFFFh
STC8H4K64TL用户自定义
STC8H4K32TLCD32K640000h7FFFh8000hFFFFh
STC8H4K48TLCD16K320000h3FFFhC000hFFFFh
STC8H4K60TLCD4K80000h0FFFhF000hFFFFh
STC8H4K64TLCD用户自定义
STC8H1K08T4K80000h0FFFh2000h2FFFh
STC8H1K12T用户自定义
STC8H1K17T用户自定义
STC8H2K08U4K80000h0FFFh2000h2FFFh
STC8H2K12U用户自定义
STC8H2K17U用户自定义

:这个为特殊型号,这个型号的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.0592MHzP_SW2                DATA                  0BAHIAP_DATA         DATA                  0C2HIAP_ADDRH   DATA                  0C3HIAP_ADDRL      DATA                  0C4HIAP_CMD          DATA                  0C5HIAP_TRIG          DATA                  0C6HIAP_CONTR      DATA                  0C7HIAP_TPS            DATA                  0F5HP1M1                  DATA                  091HP1M0                  DATA                  092HP0M1                  DATA                  093HP0M0                  DATA                  094HP2M1                  DATA                  095HP2M0                  DATA                  096HP3M1                  DATA                  0B1HP3M0                  DATA                  0B2HP4M1                  DATA                  0B3HP4M0                  DATA                  0B4HP5M1                  DATA                  0C9HP5M0                  DATA                  0CAH                            ORG                   0000H                            LJMP                  MAIN                            ORG                   0100HIAP_IDLE:                            MOV                   IAP_CONTR,#0                           ;关闭IAP功能                            MOV                   IAP_CMD,#0                                 ;清除命令寄存器                            MOV                   IAP_TRIG,#0                                 ;清除触发寄存器                            MOV                   IAP_ADDRH,#80H                     ;将地址设置到非IAP区域                            MOV                   IAP_ADDRL,#0                            RETIAP_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功能                            RETIAP_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功能                            RETIAP_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功能                            RETMAIN:                            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                                       //STC8H1K16voidIapIdle(){         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.0592MHzP_SW2                DATA                  0BAHIAP_DATA         DATA                  0C2HIAP_ADDRH   DATA                  0C3HIAP_ADDRL      DATA                  0C4HIAP_CMD          DATA                  0C5HIAP_TRIG          DATA                  0C6HIAP_CONTR      DATA                  0C7HIAP_TPS            DATA                  0F5HIAP_OFFSET    EQU                   4000H                                             ;STC8H1K16P1M1                  DATA                  091HP1M0                  DATA                  092HP0M1                  DATA                  093HP0M0                  DATA                  094HP2M1                  DATA                  095HP2M0                  DATA                  096HP3M1                  DATA                  0B1HP3M0                  DATA                  0B2HP4M1                  DATA                  0B3HP4M0                  DATA                  0B4HP5M1                  DATA                  0C9HP5M0                  DATA                  0CAH                            ORG                   0000H                            LJMP                  MAIN                            ORG                   0100HIAP_IDLE:                            MOV                   IAP_CONTR,#0                           ;关闭IAP功能                            MOV                   IAP_CMD,#0                                 ;清除命令寄存器                            MOV                   IAP_TRIG,#0                                 ;清除触发寄存器                            MOV                   IAP_ADDRH,#80H                     ;将地址设置到非IAP区域                            MOV                   IAP_ADDRL,#0                            RETIAP_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读取数据                            RETIAP_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功能                            RETIAP_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功能                            RETMAIN:                            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.0592MHzP_SW2                DATA                  0BAHAUXR               DATA                  8EHT2H                     DATA                  0D6HT2L                     DATA                  0D7HIAP_DATA         DATA                  0C2HIAP_ADDRH   DATA                  0C3HIAP_ADDRL      DATA                  0C4HIAP_CMD          DATA                  0C5HIAP_TRIG          DATA                  0C6HIAP_CONTR      DATA                  0C7HIAP_TPS            DATA                  0F5HP1M1                  DATA                  091HP1M0                  DATA                  092HP0M1                  DATA                  093HP0M0                  DATA                  094HP2M1                  DATA                  095HP2M0                  DATA                  096HP3M1                  DATA                  0B1HP3M0                  DATA                  0B2HP4M1                  DATA                  0B3HP4M0                  DATA                  0B4HP5M1                  DATA                  0C9HP5M0                  DATA                  0CAH                            ORG                   0000H                            LJMP                  MAIN                            ORG                   0100HUART_INIT:                            MOV                   SCON,#5AH                            MOV                   T2L,#0E8H                                     ;65536-11059200/115200/4=0FFE8H                            MOV                   T2H,#0FFH                            MOV                   AUXR,#15H                            RETUART_SEND:                            JNB                  TI,$                            CLR                  TI                            MOV                   SBUF,A                            RETIAP_IDLE:                            MOV                   IAP_CONTR,#0                           ;关闭IAP功能                            MOV                   IAP_CMD,#0                                 ;清除命令寄存器                            MOV                   IAP_TRIG,#0                                 ;清除触发寄存器                            MOV                   IAP_ADDRH,#80H                     ;将地址设置到非IAP区域                            MOV                   IAP_ADDRL,#0                            RETIAP_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功能                            RETIAP_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功能                            RETIAP_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功能                            RETMAIN:                            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   codeT_Strings[]={"去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。"};/************* 本地变量声明   **************/u8   xdata tmp;u8   xdata RX1_Buffer;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 == '0')&& ((RX1_Buffer == 'x') || (RX1_Buffer == '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}
//========================================================================//函数:voiddelay_ms(u8 ms)//描述:延时函数。//参数:ms,要延时的ms数,这里只支持1~255ms.自动适应主时钟.//返回:none.//版本:VER1.0//日期:2013-4-1//备注://========================================================================voiddelay_ms(u8 ms){         u16 i;         do         {                   i = MAIN_Fosc / 10000;                   while(--i)    ;         }while(--ms);}
//使用MOVC读EEPROMvoidEEPROM_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== ' ')                                     {                                             addr= GetAddress();                                             if(addr< 63488)                           //限制在0~123扇区                                             {                                                      if(RX1_Buffer== 'E')   //PC请求擦除一个扇区                                                      {                                                               EEPROM_SectorErase(addr);                                                               PrintString1("扇区擦除完成!\r\n");                                                      }
                                                      elseif(RX1_Buffer == 'W')                //PC请求写入EEPROM64字节数据                                                      {                                                               EEPROM_write_n(addr,T_Strings,64);                                                               PrintString1("写入操作完成!\r\n");                                                      }
                                                      elseif(RX1_Buffer == '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 == '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 = 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   codeT_StringD[]={"去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。"};u8   codeT_StringW[]={"横看成岭侧成峰,远近高低各不同。不识庐山真面目,只缘身在此山中。"};/************* 本地变量声明   **************/u8   xdata tmp;                                                                      //通用数据u8   xdata SaveTmp;                                                             //要写入的数组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<<8) +(u16)tmp;                              //读取记录的累加和         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 = (u8)(ChckSum >> 8);         SaveTmp = (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;                                                                     //清除口令}


神农鼎 发表于 2023-10-5 13:34:49

EEPROM 的正确使用是,结合比较器检测温压块前端电压
1,上电后将要修改的读到 RAM 中
2,掉电时及时写回EEPROM中

17.4.7   比较器作外部掉电检测(掉电过程中应及时保存用户数据到EEPROM中)
https://www.stcaimcu.com/data/attachment/forum/202309/26/192049ow1tv38jv292zqzn.jpg
上图中电阻R1和R2对稳压块7805的前端电压进行分压,分压后的电压作为比较器CMP+的外部输入与内部1.19V参考信号源进行比较。一般当交流电在220V时,稳压块7805前端的直流电压为11V,但当交流电压降到160V时,稳压块7805前端的直流电压为8.5V。当稳压块7805前端的直流电压低于或等于8.5V时,该前端输入的直流电压被电阻R1和R2分压到比较器正极输入端CMP+,CMP+端输入电压低于内部1.19V参考信号源,此时可产生比较器中断,这样在掉电检测时就有充足的时间将数据保存到EEPROM中。当稳压块7805前端的直流电压高于8.5V时,该前端输入的直流电压被电阻R1和R2分压到比较器正极输入端CMP+,CMP+端输入电压高于内部1.19V参考信号源,此时CPU可继续正常工作。内部1.19V参考信号源即为内部BandGap经过OP后的电压REFV(芯片在出厂时,内部参考信号源调整为1.19V)。具体的数值要通过读取内部1.19V参考信号源在内部RAM区或者Flash程序存储器(ROM)区所占用的地址的值获得。对于STC8H系列,内部1.19V参考信号源值在RAM和Flash 程序存储器(ROM)中的存储地址请参考 “存储器中的特殊参数”章节
要保证时间足够,就是调整外部电压偏低检测中断的提前量,如
1, 交流220V, 7805稳压电路前端输入是 11V;
2, 交流210V, 7805稳压电路前端输入是 10.5V;
3, 交流200V, 7805稳压电路前端输入是 10V;
4, 交流190V, 7805稳压电路前端输入是 9.5V;
5, 交流180V, 7805稳压电路前端输入是 9V;
6, 交流170V, 7805稳压电路前端输入是 8.5V;
7, 交流160V, 7805稳压电路前端输入是 8V;
===你这个要提前的量放在 11V/10.5V/10V/9.5V/9V/8.5V ?
===哪个电压点产生外部电压偏低检测中断时间充分的一个实践测试选择

DebugLab 发表于 2023-10-1 11:30:21

float类型变量怎么读写到EEPROM(float怎么拆分成8位,8位怎么还原成float不会)

神农鼎 发表于 2023-10-1 11:52:17

等节后上班了,请同事写个演示程序供参考

zhp 发表于 2023-10-9 10:42:07

本帖最后由 zhp 于 2023-10-9 10:47 编辑

DebugLab 发表于 2023-10-1 11:30
float类型变量怎么读写到EEPROM(float怎么拆分成8位,8位怎么还原成float不会) ...
float变量在RAM中为4字节存储,比如:浮点数1.234f,在RAM存储形式为3FH, 9DH, F3H, B6H
写入到EEPROM时不必写入float变量的具体值,只需要写入float变量在RAM中存储内容即可
如果要将浮点数1.234f写入到EEPROM,只需要将3FH, 9DH, F3H, B6H这4个字节写入即可
float的写入和读取范例如下:
    float x;
    float y;
      
    x = 1.234f;
    EEPROM_write_n(0,      //EEPROM写入地址
             (u8 *)&x,   //源float变量存储首地址
             4);       //float变量存储为4字节

    EEPROM_read_n(0,      //EEPROM读取地址
            (u8 *)&y,   //目标float变量存储首地址
            4);       //float变量存储为4字节


DebugLab 发表于 2023-10-9 22:30:39

zhp 发表于 2023-10-9 10:42
float变量在RAM中为4字节存储,比如:浮点数1.234f,在RAM存储形式为3FH, 9DH, F3H, B6H
写入到EEPROM时不 ...

哦,是指针的方式,,如果用memcpy能不能和数组之间传递呢

zhp 发表于 2023-10-10 09:58:56

DebugLab 发表于 2023-10-9 22:30
哦,是指针的方式,,如果用memcpy能不能和数组之间传递呢

可以
memcpy传递的参数也是变量地址(指针)

jingyesiyu 发表于 2023-11-21 08:52:40

老师您好,能分享一些上面C语言程序中“config.h”和“EEPROM.h”头文件吗?

1280354690 发表于 2024-3-6 09:45:03

连续写多个数据后,读取时只能读取到最后一个数据怎么回事

电子DIY小家 发表于 2024-3-6 15:17:12

1280354690 发表于 2024-3-6 09:45
连续写多个数据后,读取时只能读取到最后一个数据怎么回事

用官方示例的eeprom读写函数的吗?
页: [1] 2 3
查看完整版本: 视频讲解:EEPROM/DataFlash, 8H系列数据手册 EEPROM 内容