找回密码
 立即注册
查看: 1981|回复: 18

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

[复制链接]

该用户从未签到

551

主题

9449

回帖

1万

积分

管理员

积分
13999
发表于 2023-9-26 19:50:40 | 显示全部楼层 |阅读模式
视频讲解EEPROM/DataFlash 应用,STC8H/STC8G/STC8C/STC8A8K64D4
截图202310040853383309.jpg
https://v.stcai.com/sv/18b476c5-18ada751b0e/18b476c5-18ada751b0e.mp4


18      IAP/EEPROM/DATA-FLASH
STC8H系列单片机内部集成了大容量的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相关的寄存器
  
符号
  
描述
地址
位地址与符号
复位值
B7
B6
B5
B4
B3
B2
B1
B0
IAP_DATA
IAP数据寄存器
C2H

1111,1111
IAP_ADDRH
IAP高地址寄存器
C3H

0000,0000
IAP_ADDRL
IAP低地址寄存器
C4H

0000,0000
IAP_CMD
IAP命令寄存器
C5H
-
-
-
-
-
-
CMD[1:0]
xxxx,xx00
IAP_TRIG
IAP触发寄存器
C6H

0000,0000
IAP_CONTR
IAP控制寄存器
C7H
IAPEN
SWBS
SWRST
CMD_FAIL
-
-
-
-
0000,xxxx
IAP_TPS
IAP等待时间控制寄存器
F5H
-
-
IAPTPS[5:0]
xx00,0000

18.2.1          EEPROM数据寄存器(IAP_DATA
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_DATA
C2H

在进行EEPROM的读操作时,命令执行完成后读出的EEPROM数据保存在IAP_DATA寄存器中。在进行EEPROM的写操作时,在执行写命令前,必须将待写入的数据存放在IAP_DATA寄存器中,再发送写命令。擦除EEPROM命令与IAP_DATA寄存器无关。
18.2.2          EEPROM地址寄存器(IAP_ADDR
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_ADDRH
C3H

IAP_ADDRL
C4H

EEPROM进行读、写、擦除操作的目标地址寄存器。IAP_ADDRH保存地址的高字节,IAP_ADDRL保存地址的低字节
18.2.3          EEPROM命令寄存器(IAP_CMD
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_CMD
C5H
-
-
-
-
-
-
CMD[1:0]
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
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_TRIG
C6H

设置完成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
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_CONTR
C7H
IAPEN
SWBS
SWRST
CMD_FAIL
-
-
-
-
IAPENEEPROM操作使能控制位
0:禁止EEPROM操作
1:使能EEPROM操作
SWBS:软件复位选择控制位,(需要与SWRST配合使用)
0:软件复位后从用户代码开始执行程序
1:软件复位后从系统ISP监控代码区开始执行程序
SWRST:软件复位控制位
0:无动作
1:产生软件复位
CMD_FAILEEPROM操作失败状态位,需要软件清零
0:EEPROM操作正确
1:EEPROM操作失败

18.2.6          EEPROM等待时间控制寄存器(IAP_TPS
  
符号
  
地址
B7
B6
B5
B4
B3
B2
B1
B0
IAP_TPS
F5H
-
-
IAPTPS[5:0]
需要根据工作频率进行设置
若工作频率为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这个型号为例,对目标地址进行详细说明:
截图202309261955166752.jpg 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的详细大小和地址请参考下表
  
型号
  
大小
扇区
IAP方式读/写/擦除
MOVC读取
起始地址
结束地址
起始地址
结束地址
STC8H1K16
12K
24
0000h
2FFFh
4000h
6FFFh
STC8H1K24
4K
8
0000h
0FFFh
6000h
6FFFh
STC8H1K28
用户自定义[1]
STC8H1K33
用户自定义[1]
STC8H1K08
4K
8
0000h
0FFFh
2000h
2FFFh
STC8H1K12
用户自定义[1]
STC8H1K17
用户自定义[1]
STC8H3K32S4
32K
64
0000h
7FFFh
8000h
FFFFh
STC8H3K48S4
16K
32
0000h
3FFFh
C000h
FFFFh
STC8H3K60S4
4K
8
0000h
0FFFh
F000h
FFFFh
STC8H3K64S4
用户自定义[1]
STC8H3K32S2
32K
64
0000h
7FFFh
8000h
FFFFh
STC8H3K48S2
16K
32
0000h
3FFFh
C000h
FFFFh
STC8H3K60S2
4K
8
0000h
0FFFh
F000h
FFFFh
STC8H3K64S2
用户自定义[1]
STC8H8K32U
32K
64
0000h
7FFFh
8000h
FFFFh
STC8H8K48U
16K
32
0000h
3FFFh
C000h
FFFFh
STC8H8K60U
4K
8
0000h
0FFFh
F000h
FFFFh
STC8H8K64U
用户自定义[1]
STC8H4K32TL
32K
64
0000h
7FFFh
8000h
FFFFh
STC8H4K48TL
16K
32
0000h
3FFFh
C000h
FFFFh
STC8H4K60TL
4K
8
0000h
0FFFh
F000h
FFFFh
STC8H4K64TL
用户自定义[1]
STC8H4K32TLCD
32K
64
0000h
7FFFh
8000h
FFFFh
STC8H4K48TLCD
16K
32
0000h
3FFFh
C000h
FFFFh
STC8H4K60TLCD
4K
8
0000h
0FFFh
F000h
FFFFh
STC8H4K64TLCD
用户自定义[1]
STC8H1K08T
4K
8
0000h
0FFFh
2000h
2FFFh
STC8H1K12T
用户自定义[1]
STC8H1K17T
用户自定义[1]
STC8H2K08U
4K
8
0000h
0FFFh
2000h
2FFFh
STC8H2K12U
用户自定义[1]
STC8H2K17U
用户自定义[1]

[1]:这个为特殊型号,这个型号的EEPROM大小是可用在ISP下载时用户自己设置的。如下图所示:
截图202309261956187130.jpg 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表示擦除,数字00扇区(十进制,0~126, 看具体IC).
W0     EEPROM进行写入操作,W表示写入,数字00扇区(十进制,0~126, 看具体IC). 从扇区的开始地址连续写64字节.
R0     EEPROM进行IAP读出操作,R表示读出,数字00扇区(十进制,0~126, 看具体IC). 从扇区的开始地址连续读64字节.
M0     EEPROM进行MOVC读出操作(操作地址为扇区*512+偏移地址),数字00扇区(十进制,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)           //定义EEPROMMOVC访问时加的偏移量,
//等于FLASHROM的大小.对于IAPIRC开头的,
//则偏移量必须为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);
}

//使用MOVCEEPROM
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,再送A5HISP/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字节的操作。
命令例子:
使用串口助手发单个字符,大小写均可.
Ee:  EEPROM进行扇区擦除操作,E表示擦除,会擦除扇区012.
Ww:  EEPROM进行写入操作,W表示写入,会写入扇区012,每个扇区连续写64字节,扇区0写入0x0000~0x003f,扇区1写入0x0200~0x023f,扇区0写入0x0400~0x043f.
Rr:  EEPROM进行读出操作,R表示读出,会读出扇区012,每个扇区连续读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,再送A5HISP/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,再送A5HISP/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,再送A5HISP/IAP触发寄存器,
//每次都需要如此
                                     IAP_TRIG =0xA5;                                 //送完A5H后,ISP/IAP命令立即被触发启动
                            }                                                                         //CPU等待IAP完成后,才会继续执行程序。
                            _nop_();
                            _nop_();
                            _nop_();
                            EE_address++;
                            DataAddress++;
                   }while(--number);
                   DisableEEPROM();
                   EA = 1;                                                                       //重新允许中断
         }
         PassWord = 0;                                                                     //清除口令
}



回复 送花

使用道具 举报

该用户从未签到

551

主题

9449

回帖

1万

积分

管理员

积分
13999
 楼主| 发表于 2023-10-5 13:34:49 | 显示全部楼层
EEPROM 的正确使用是,结合比较器检测温压块前端电压
1,上电后将要修改的读到 RAM 中
2,掉电时及时写回EEPROM中


17.4.7   比较器作外部掉电检测(掉电过程中应及时保存用户数据到EEPROM

上图中电阻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 ?
===哪个电压点产生外部电压偏低检测中断时间充分的一个实践测试选择

回复 支持 反对 送花

使用道具 举报

  • TA的每日心情
    开心
    3 小时前
  • 签到天数: 116 天

    [LV.6]常住居民II

    25

    主题

    304

    回帖

    1053

    积分

    荣誉版主

    Cyber Hamster

    积分
    1053
    发表于 2023-10-1 11:30:21 来自手机 | 显示全部楼层
    float类型变量怎么读写到EEPROM(float怎么拆分成8位,8位怎么还原成float不会)

    点评

    zhp
    float变量在RAM中为4字节存储,比如:浮点数1.234f,在RAM存储形式为3FH, 9DH, F3H, B6H 写入到EEPROM时不必写入float变量的具体值,只需要写入float变量在RAM中存储内容即可 如果要将浮点数1.234f写入到EEPROM,  详情 回复 发表于 2023-10-9 10:42
    (=・ω・=)
    回复 支持 1 反对 0 送花

    使用道具 举报

    该用户从未签到

    551

    主题

    9449

    回帖

    1万

    积分

    管理员

    积分
    13999
     楼主| 发表于 2023-10-1 11:52:17 | 显示全部楼层
    等节后上班了,请同事写个演示程序供参考
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    5 天前
  • 签到天数: 16 天

    [LV.4]偶尔看看III

    16

    主题

    721

    回帖

    2570

    积分

    超级版主

    积分
    2570
    发表于 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的写入和读取范例如下:
    1.     float x;
    2.     float y;
    3.         
    4.     x = 1.234f;
    5.     EEPROM_write_n(0,        //EEPROM写入地址
    6.              (u8 *)&x,   //源float变量存储首地址
    7.              4);       //float变量存储为4字节
    8.     EEPROM_read_n(0,        //EEPROM读取地址
    9.             (u8 *)&y,   //目标float变量存储首地址
    10.             4);       //float变量存储为4字节
    复制代码


    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    3 小时前
  • 签到天数: 116 天

    [LV.6]常住居民II

    25

    主题

    304

    回帖

    1053

    积分

    荣誉版主

    Cyber Hamster

    积分
    1053
    发表于 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
    可以 memcpy传递的参数也是变量地址(指针)  详情 回复 发表于 2023-10-10 09:58
    (=・ω・=)
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    5 天前
  • 签到天数: 16 天

    [LV.4]偶尔看看III

    16

    主题

    721

    回帖

    2570

    积分

    超级版主

    积分
    2570
    发表于 2023-10-10 09:58:56 | 显示全部楼层
    DebugLab 发表于 2023-10-9 22:30
    哦,是指针的方式,,如果用memcpy能不能和数组之间传递呢

    可以
    memcpy传递的参数也是变量地址(指针)
    回复 支持 反对 送花

    使用道具 举报

    该用户从未签到

    1

    主题

    2

    回帖

    33

    积分

    新手上路

    积分
    33
    发表于 2023-11-21 08:52:40 | 显示全部楼层
    老师您好,能分享一些上面C语言程序中“config.h”和“EEPROM.h”头文件吗?
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    郁闷
    2024-3-29 10:52
  • 签到天数: 1 天

    [LV.1]初来乍到

    1

    主题

    12

    回帖

    61

    积分

    注册会员

    积分
    61
    发表于 2024-3-6 09:45:03 | 显示全部楼层
    连续写多个数据后,读取时只能读取到最后一个数据怎么回事

    点评

    用官方示例的eeprom读写函数的吗?  详情 回复 发表于 2024-3-6 15:17
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    10 分钟前
  • 签到天数: 100 天

    [LV.6]常住居民II

    41

    主题

    1012

    回帖

    6959

    积分

    荣誉版主

    冲哥视频教程和各种开源资料QQ交流群884047237,可群

    积分
    6959
    QQ
    发表于 2024-3-6 15:17:12 | 显示全部楼层
    1280354690 发表于 2024-3-6 09:45
    连续写多个数据后,读取时只能读取到最后一个数据怎么回事

    用官方示例的eeprom读写函数的吗?
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-5-16 11:52 , Processed in 0.132510 second(s), 72 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表