一、简介:

VB上位机程序控制DS1302时钟是一种常见的应用,DS1302是一款实时时钟芯片,通常用于计算机、电子设备或其他系统中,以提供时间戳和其他时间相关功能,DS1302时钟芯片通常需要外部电源供电,并且具有有限的存储空间和数据传输速率。因此,在控制DS1302时钟时,需要考虑到这些因素,并采取适当的措施以确保芯片的正常运行和数据的安全。

二、头文件与变量定义:

主要用于初始化一些硬件接口,例如实时时钟(DS1302)和一些控制芯片的引脚设置。具体来说,它包括了一些特殊功能寄存器(SFR)的定义和一些用于连接硬件的位定义。这些定义在微控制器的开发中非常常见,它们使得开发者能够更好地控制硬件并与之交互。
首先包含了头文件reg52.h,它包含了8051微控制器的特殊功能寄存器的定义。然后定义了一些位定义,如实时时钟的时钟线、数据线、复位线等引脚,以及一些控制芯片的输入/输出引脚。
此外,代码中还定义了一个字节型的数组tab,它包含了数字0到9的ASCII码值。这个数组可能用于显示实时时钟的时间。

#include <reg52.h>

sbit T_CLK = P2^4; /*实时时钟时钟线引脚 */
sbit T_IO = P2^3; /*实时时钟数据线引脚 */
sbit T_RST = P2^2; /*实时时钟复位线引脚 */
sbit ACC0=ACC^0;
sbit ACC7=ACC^7;
sbit rs=P2^0;
sbit rw=P2^1;
sbit e=P2^7;
sbit setd=P3^2;
sbit sett=P3^3;
sbit add=P2^5;
sbit enter=P2^6;
sbit speaker=P3^7;
unsigned char code tab[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};

三、各函数模块介绍:

用于处理实时时钟输入字节的函数。函数`v_RTInputByte`接收一个无符号字符类型的参数`ucDa`,并将这个字节的值存储在`ACC`寄存器中。
然后,函数会通过循环8次(每次循环都把ACC寄存器的内容右移一位)来实现对输入字节的循环右移操作。循环右移的操作是用汇编语言中的`RRC`指令实现的,`RRC`是"右循环移位寄存器"的意思,它会把最低位移动到最高位,其余位则向左移动一位。
在每次循环中,都会通过`T_IO = ACC0;`来更新时钟线`T_IO`的值,可能是在启动或结束一次时钟操作。在8次循环后,原始的输入字节被左移8位并重新装载到ACC寄存器中。

3.1 v_RTInputByte

void v_RTInputByte(unsigned char ucDa) 
{ 
    unsigned char i;
    ACC = ucDa;
    for(i=8; i>0; i--)
    {
        T_IO = ACC0; /*相当于汇编中的 RRC */
        T_CLK = 1;
        T_CLK = 0;
        ACC = ACC >> 1; 
    } 
}

3.2 uc_RTOutputByte

函数 uc_RTOutputByte,返回一个无符号字符(unsigned char)类型的值。
函数通过循环将一个8位变量 ACC 的值右移一位,并将最低位的值设置为 T_IO 的值。然后,通过控制时钟线 T_CLK 的状态,进行时钟信号的产生,完成一次数据的传输。具体来说,循环中的操作相当于汇编中的循环右移(RRC)指令。
最后,函数返回变量 ACC 的值。
该函数是用于读取外部设备通过串行通信接口传输的一个字节的数据。
unsigned char uc_RTOutputByte(void) 
{ 
    unsigned char i;
    for(i=8; i>0; i--)
    {
        ACC = ACC >>1; /*相当于汇编中的 RRC */
        ACC7 = T_IO;
        T_CLK = 1;
        T_CLK = 0;
    } 
    return(ACC); 
}

3.3 v_W1302

定义了一个函数 v_W1302,该函数负责向某个设备(可能是一种RTC,例如DS1302实时时钟模块)写入数据。函数接受两个参数,一个是地址(ucAddr),另一个是要写入的数据(ucDa)。

初始化通信界面:

T_RST = 0; 首先将复位(RST)引脚设为低电平。
T_CLK = 0; 然后将时钟(CLK)引脚设为低电平。
T_RST = 1; 接着将复位(RST)引脚设为高电平,以启动与目标设备的通信。

发送地址和命令:

v_RTInputByte(ucAddr); 发送地址或命令给目标设备。这里的 v_RTInputByte 函数可能是用于发送一个字节的数据,但是具体的实现在这段代码中没有给出。
写入数据:

v_RTInputByte(ucDa); 发送实际的数据字节给目标设备。
结束通信:

T_CLK = 1; 把时钟(CLK)引脚设为高电平。
T_RST =0; 最后将复位(RST)引脚设为低电平,结束与目标设备的通信。
这个函数的作用是通过特定的通信协议(可能是SPI或类似的串行通信协议),向一个外部设备写入数据。根据函数名称和参数,可以推测这段代码可能用于操作DS1302或类似的实时时钟(RTC)模块。DS1302等RTC模块常用于提供精确的时间和日期信息,并可通过SPI等串行接口与微控制器通信。

void v_W1302(unsigned char ucAddr, unsigned char ucDa)
{
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(ucAddr); /* 地址,命令 */
    v_RTInputByte(ucDa); /* 写1Byte数据*/
    T_CLK = 1;
    T_RST =0;
}

3.4 uc_R1302

定义了一个函数 uc_R1302,该函数负责从某个设备(可能是一种RTC,例如DS1302实时时钟模块)读取数据。函数接受一个参数 ucAddr,表示要读取的数据的地址。

初始化通信界面:

T_RST = 0; 首先将复位(RST)引脚设为低电平。
T_CLK = 0; 然后将时钟(CLK)引脚设为低电平。
T_RST = 1; 接着将复位(RST)引脚设为高电平,以启动与目标设备的通信。

发送地址和命令:

v_RTInputByte(ucAddr); 发送地址或命令给目标设备。这里的 v_RTInputByte 函数可能是用于发送一个字节的数据,但是具体的实现在这段代码中没有给出。

读取数据:

ucDa = uc_RTOutputByte(); 调用 uc_RTOutputByte 函数从目标设备读取一个字节的数据,并将其保存到变量 ucDa 中。

结束通信:

T_CLK = 1; 把时钟(CLK)引脚设为高电平。
T_RST =0; 最后将复位(RST)引脚设为低电平,结束与目标设备的通信。

返回数据:

return(ucDa); 将读取的数据字节返回给调用者。
这个函数的作用是通过特定的通信协议(可能是SPI或类似的串行通信协议),从一个外部设备读取数据。根据函数名称和参数,可以推测这段代码可能用于操作DS1302或类似的实时时钟(RTC)模块,从中读取特定地址的数据。
unsigned char uc_R1302(unsigned char ucAddr)
{
    unsigned char ucDa;
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(ucAddr); /* 地址,命令 */
    ucDa = uc_RTOutputByte(); /* 读1Byte数据 */
    T_CLK = 1;
    T_RST =0;
    return(ucDa);
}

3.5 v_BurstW1302T

参数解析: 函数接受一个指针*pSecDa作为参数,这个指针指向一个包含待写入数据的数组。这个数组预期包含DS1302实时钟模块的时间和日期数据,以及可能的控制字节。

写保护关闭: 首先,函数使用v_W1302(0x8e,0x00);调用来关闭写保护。0x8e是DS1302的写保护控制寄存器的地址,写入0的操作是为了确保可以向DS1302写入数据。

初始化通信: 通过设置T_RST和T_CLK的状态,初始化与DS1302的通信。T_RST = 0;和T_CLK = 0;用于确保开始前总线是清晰的。随后T_RST = 1;开启DS1302的通信。

发送写命令: 通过调用v_RTInputByte(0xbe);发送0xbe命令至DS1302,这个命令是时钟多字节写操作的命令。这允许连续写入多个字节到DS1302而不需要每次写入都发送地址。

批量写数据: 使用for循环和v_RTInputByte(*pSecDa);将数组pSecDa指向的8字节数据写入到DS1302。这8字节通常包含了秒、分、时、日、月、星期和年的信息,以及一个控制字节。

结束通信: 最后,通过设置T_CLK = 1;和T_RST =0;结束与DS1302的通信。

void v_BurstW1302T(unsigned char *pSecDa)
{
    unsigned char i;
    v_W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(0xbe); /* 0xbe:时钟多字节写命令 */
    for (i=8;i>0;i--) /*8Byte = 7Byte 时钟数据 + 1Byte 控制*/
    {
        v_RTInputByte(*pSecDa);/* 写1Byte数据*/
        pSecDa++;
    }
    T_CLK = 1;
    T_RST =0;
}

3.6 v_BurstR1302T

参数解析: 函数接受一个指针*pSecDa作为参数,这个指针用于存储从DS1302读取的数据。函数将会读取8字节的数据,通常这些数据包含了时间和日期信息。

初始化通信: 通过设置T_RST和T_CLK的状态,初始化与DS1302的通信。首先将T_RST和T_CLK都设置为0以确保通信开始前总线状态清晰,然后将T_RST设置为1以启动与DS1302的通信。

发送读命令: 通过调用v_RTInputByte(0xbf);发送0xbf命令至DS1302,这个命令是时钟多字节读操作的命令。这允许连续读取多个字节从DS1302而不需要每次读取都发送地址。

批量读数据: 使用for循环和*pSecDa = uc_RTOutputByte();从DS1302读取8字节数据,并将这些数据存储到通过pSecDa指针指向的位置。这8字节数据通常包含了秒、分、时、日、月、星期和年的信息。

结束通信: 最后,通过设置T_CLK = 1;和T_RST =0;结束与DS1302的通信。

void v_BurstR1302T(unsigned char *pSecDa)
{
    unsigned char i;
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(0xbf); /* 0xbf:时钟多字节读命令 */
    for (i=8; i>0; i--) 
    {
        *pSecDa = uc_RTOutputByte(); /* 读1Byte数据 */
        pSecDa++;
    }
    T_CLK = 1;
    T_RST =0;
}

3.7 v_BurstW1302R

命令错误: 在注释中提到0xbe:时钟多字节写命令,但在函数中实际使用的命令是0xfe。这可能是一个笔误。根据DS1302的标准文档,0xBE才是进行时间数据多字节写入的正确命令。0xFE似乎不是一个有效的命令,这可能是一个错误。

数据长度: 函数尝试写入31字节的数据到DS1302,这与DS1302的实际能力不符。DS1302通常只有少量的时间和控制寄存器,总共远不到31字节。

写保护: 与之前的函数类似,使用v_W1302(0x8e,0x00);关闭写保护功能,这是正确的。0x8E是DS1302的写保护寄存器地址,写入0x00可以确保我们能够对DS1302进行写操作。

通信初始化和结束: 与其他操作相同,通过控制T_RST和T_CLK来开始和结束与DS1302的通信过程。

批量写数据: 函数尝试使用v_RTInputByte(*pReDa);连续写入31字节的数据。pReDa是一个指向需要写入数据的指针。

void v_BurstW1302R(unsigned char *pReDa)
{
    unsigned char i;
    v_W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(0xfe); /* 0xbe:时钟多字节写命令 */
    for (i=31;i>0;i--) /*31Byte 寄存器数据 */
    {
        v_RTInputByte(*pReDa); /* 写1Byte数据*/
        pReDa++;
    }
    T_CLK = 1;
    T_RST =0;
}

3.8 v_BurstR1302R

命令不一致: 注释中提到的是0xbf:时钟多字节读命令,但在函数调用v_RTInputByte时使用的命令是0xff。这里似乎存在一个不一致。根据DS1302的标准操作,0xBF是正确的多字节读命令。0xFF可能是一个错误或者特定于某种实现的命令,这需要根据实际使用的硬件文档来确定。

数据长度问题: 函数试图从DS1302读取31字节的数据。这个长度对于DS1302来说似乎过长,因为DS1302的寄存器空间并没有这么大。DS1302主要用于存储时间和日期信息,以及少量的控制和RAM数据,总共不会超过几十字节。

通信初始化和结束: 通过控制T_RST和T_CLK来开始和结束与DS1302的通信过程是标准的操作。首先将T_RST和T_CLK置为0以确保从一个已知状态开始,然后将T_RST置为1开始通信,最后通过将T_CLK置为1和T_RST置为0来结束通信。

批量读取数据: 使用for循环和*pReDa = uc_RTOutputByte();从DS1302连续读取31字节的数据,并将这些数据存储到通过pReDa指针指向的位置。这种方式适用于需要从设备中读取大量数据的情况。

void v_BurstR1302R(unsigned char *pReDa)
{
    unsigned char i;
    T_RST = 0;
    T_CLK = 0;
    T_RST = 1;
    v_RTInputByte(0xff); /* 0xbf:时钟多字节读命令 */
    for (i=31; i>0; i--) /*31Byte 寄存器数据 */
    {
        *pReDa = uc_RTOutputByte(); /* 读1Byte数据 */
        pReDa++;
    }
    T_CLK = 1;
    T_RST =0;
}

3.9 v_setd1302

关闭写保护: 通过调用v_W1302(0x8e,0x00);,函数首先关闭DS1302的写保护功能。这是必要的步骤,因为在默认情况下,DS1302可能启用了写保护,防止意外写入。0x8E是写保护寄存器的地址,而0x00是关闭写保护的命令。

逐个写入时间数据: 函数使用一个for循环,从地址0x80开始,逐个写入时间和日期数据。ucAddr变量以2的步长递增,这是因为DS1302的寄存器地址是按位控制的,每个时间日期值的地址间隔为2。循环中,v_W1302(ucAddr,*pSecDa);负责写入数据,pSecDa指针随之递增,指向下一个要写入的数据。

数据顺序: 根据循环逻辑,数据应该按照秒、分、时、日、月、星期、年的顺序排列,每个时间单位占用一个字节。这与DS1302的数据寄存器顺序相匹配。

重新启用写保护: 在所有数据写入完成后,通过v_W1302(0x8e,0x80);重新启用写保护。这是一个好习惯,可以防止后续的意外写入,确保时间数据的稳定性。0x80是启用写保护的命令。

void v_setd1302(unsigned char *pSecDa) 
{
    unsigned char i;
    unsigned char ucAddr = 0x80; 
    v_W1302(0x8e,0x00); /* 控制命令,WP=0,写操作?*/
    for(i =7;i>0;i--)
    { 
        v_W1302(ucAddr,*pSecDa); /* 秒 分 时 日 月 星期 年 */

        pSecDa++;
        ucAddr +=2;
    }
    v_W1302(0x8e,0x80); /* 控制命令,WP=1,写保护?*/
}

3.10 delay

变量声明:函数通过声明一个unsigned char类型的变量y开始。选择这种类型是因为它足以容纳从0到255的值,这是8位无符号数的范围。

循环延时:for循环将y初始化为0,并且一直增加到255(0xFF)。循环中包含一个空语句(;),意味着在循环的每次迭代中不执行任何操作。

延时的目的:这种类型的函数通常用于嵌入式系统编程中引入延时。实际的延时持续时间取决于处理器的时钟速度以及执行每次循环迭代所需的时间,包括循环机制(初始化、增量和条件检查)引入的任何开销。

void delay()
{     unsigned char y;
     for(y=0;y<0xff;y++)
     {;}
}

3.11 wc51r

void wc51r(unsigned char j)//写命令
{
    e=0;rs=0;rw=0;
    e=1;
    P1=j;
    e=0;
    delay();
}

函数参数:wc51r函数接受一个unsigned char类型的参数j,用于传递要写入的命令值。

引脚操作:函数开始通过对引脚进行操作来执行写命令的操作。在这段代码中,e、rs、rw和P1是用于控制的引脚。

引脚操作步骤:函数首先将e、rs、rw引脚置为低电平,然后将e引脚置为高电平,表示向LCD发送命令。接下来,将P1引脚设置为参数j的值,将命令写入LCD。最后,将e引脚置为低电平,完成命令的写入操作。

延时:在命令写入之后,调用了一个延时函数delay()。这个函数的作用是引入一个短暂的延时,以确保命令被正确写入和执行。

四、初始化:

定义了一个名为 init 的函数,其目标是初始化一个设备,基于函数名和调用的模式,这个设备很可能是一个LCD显示屏,特别是那些基于HD44780或兼容芯片的字符LCD。wc51r 函数的确切作用没有在代码片段中给出,但从上下文中可以推测,wc51r 很可能是用来写命令到LCD控制器的函数。下面是各个命令的解释:

wc51r(0x01); - 清除显示屏,移动光标到初始位置。这个命令会清空LCD的显示内容,并把光标复位到左上角。
wc51r(0x38); - 设置显示模式。这个命令配置LCD工作在8位数据传输模式,并设置为2行显示,5x8点阵的字符。
wc51r(0x38); - 同上,可能重复以确保LCD控制器正确设置,有时候在初始化流程中指令需要发送多次以确保设备正确响应。
wc51r(0x0e); - 开启显示并显示光标,光标闪烁。这个命令使LCD显示内容,并把光标设置为闪烁模式,以便于用户定位光标位置。
wc51r(0x06); - 设置光标移动方向和显示移动。这个命令配置LCD在字符写入后自动增加地址,即光标会向右移动,并且不会移动显示内容。
wc51r(0x0c); - 开启显示,隐藏光标。此命令用于开启LCD显示,但不显示光标,这通常是正常使用时的偏好设置。
init 函数通过发送一系列配置命令来初始化LCD显示屏,使其准备好显示文字或其他字符信息。这是在使用LCD显示屏时常见的初始化步骤,确保LCD以适当的模式和配置开始工作。
void init()//初始化
{
    wc51r(0x01);
    wc51r(0x38);
    wc51r(0x38);
    wc51r(0x0e);
    wc51r(0x06);
    wc51r(0x0c);
}

五、写数据

函数 wc51ddr(unsigned char j)
此函数用于向显示屏写入数据。

e=0; rs=1; rw=0; 设置控制线以写入数据。e(使能)被设为低,以准备命令,rs(寄存器选择)被设为高,以选择数据寄存器(与命令寄存器相对),rw(读/写)被设为低,表示写操作。
e=1; 然后将e(使能)线设为高,这准备LCD接收数据。
P1=j; 将数据字节j输出到数据端口P1(在基于8051微控制器的系统中,P1指的是I/O端口之一,通常用于与外设的并行数据通信)。
e=0; 再次将使能线设为低,表示数据写操作的结束。
delay(); 调用延时函数,为LCD处理数据提供必要的时间。

函数 write1602(unsigned char add, unsigned char da)

此函数是一个更高级别的操作,结合了设置DDRAM(显示数据RAM)地址和向显示屏写入数据。它封装了将字符写入LCD特定位置的操作。具体操作如下:

调用wc51r(add);设置DDRAM地址。这是你希望数据在显示屏上出现的位置。地址直接映射到LCD上的特定位置。
调用wc51ddr(da);向显示屏写入实际的数据字节da。这个字节代表你想要显示的字符,根据LCD的字符生成器ROM进行映射。
void wc51ddr(unsigned char j)//写数据
{
    e=0;rs=1;rw=0;
    e=1;
    P1=j;
    e=0;
    delay();
}

void write1602(unsigned char add,unsigned char da)//写入显示数据
{wc51r(add);wc51ddr(da);}

六、主函数

在main函数中进行了一系列的LCD初始化和设置显示内容的操作。具体的步骤包括:

调用init()函数进行初始化。
使用write1602函数将一些固定的字符显示在LCD的指定位置上。
循环调用write1602函数将日期和时间的各个部分显示在LCD的指定位置上。
判断当前时间是否为整点,并根据条件设置蜂鸣器的状态。

serint函数是串口中断服务函数,用于接收来自外部设备(假设是串口)发送的数据。具体的步骤包括:

接收数据并保存在temp变量中。
根据接收到的数据,判断当前是接收到了日期和时间的哪个部分,并将其保存在对应的变量中。
当接收完所有的日期和时间的部分后,将保存的数据转换成合适的格式,并调用v_setd1302函数将其设置到DS1302实时时钟模块中。

void main(void)
{
    unsigned char ucCurtime[7];
    unsigned char i,yearh,yearl,monh,monl,dah,dal,hoh,hol,mih,mil,seh,sel;
    unsigned char ucAddr;
    unsigned int c;

    init();
    write1602(0x85,0x35);
    for(c=0;c<30000;c++);
    write1602(0x86,0x32);
    for(c=0;c<30000;c++);
    write1602(0x87,0x4D);
    for(c=0;c<30000;c++);
    write1602(0x88,0x43);
    for(c=0;c<30000;c++);
    write1602(0x89,0x55);
    for(c=0;c<30000;c++);
    write1602(0xC2,0x51);
    write1602(0xC3,0x51);
    write1602(0xC4,0x3A);
    write1602(0xC5,0x35);
    write1602(0xC6,0x39);
    write1602(0xC7,0x37);
    write1602(0xC8,0x33);
    write1602(0xC9,0x38);
    write1602(0xCA,0x35);
    write1602(0xCB,0x33);
    write1602(0xCC,0x36);
    write1602(0xCD,0x39);
    for(c=0;c<50000;c++);
    init();//LCD初始化





    TMOD=0X20;
    TH1=0XF3;
    TL1=0XF3;
    SCON=0X50;
    PCON=0X00;
    IT0=1;
    EX0=1;
    IT1=1;
    EX1=1;
    TR1=1;
    EA=1;
    ES=1;
    while(1)
    {ucAddr = 0x81;
    for (i=0;i<7;i++)
    {
    ucCurtime[i] = uc_R1302(ucAddr);/*格式为: 秒 分 时 日 月 星期 年 */
    ucAddr += 2;
    }


    yearh=ucCurtime[6]/16;
    yearl=ucCurtime[6]%16;
    monh=ucCurtime[4]/16;
    monl=ucCurtime[4]%16;
    dah=ucCurtime[3]/16;
    dal=ucCurtime[3]%16;

    hoh=ucCurtime[2]/16;
    hol=ucCurtime[2]%16;
    mih=ucCurtime[1]/16;
    mil=ucCurtime[1]%16;
    seh=ucCurtime[0]/16;
    sel=ucCurtime[0]%16;



    write1602(0x80,0x44);
    write1602(0x81,0x61);
    write1602(0x82,0x74);
    write1602(0x83,0x65);
    write1602(0x84,0x3a);//显示date:

    write1602(0x85,tab[yearh]);
    write1602(0x86,tab[yearl]);
    write1602(0x87,0x2d);//显示年
    write1602(0x88,tab[monh]);
    write1602(0x89,tab[monl]);
    write1602(0x8a,0x2d);//显示月
    write1602(0x8b,tab[dah]);
    write1602(0x8c,tab[dal]);

    write1602(0xc0,0x54);
    write1602(0xc1,0x69);
    write1602(0xc2,0x6d);
    write1602(0xc3,0x65);
    write1602(0xc4,0x3a);//显示time:

    write1602(0xc5,tab[hoh]);
    write1602(0xc6,tab[hol]);
    write1602(0xc7,0x3a);//显示小时
    write1602(0xc8,tab[mih]);
    write1602(0xc9,tab[mil]);
    write1602(0xca,0x3a);//显示小时
    write1602(0xcb,tab[seh]);
    write1602(0xcc,tab[sel]);

    if((ucCurtime[1]==0)&(ucCurtime[0]==0|ucCurtime[0]==1|ucCurtime[0]==2))//3秒整点报时
      speaker=0;
    else speaker=1;

    }
}




serint() interrupt 4
{ //EA=0;
  static unsigned char k;
  unsigned char temp,year,month,date,hour,min,sec,week;
  unsigned char stemp[7]={0};
  RI=1;
  temp=SBUF;
  RI=0;
  k++;
  switch (k)
  { case 1:sec=temp;
           break;
    case 2:min=temp;
           break;
    case 3:hour=temp;
           break;
    case 4:date=temp;
           break;
    case 5:month=temp;
           break;
    case 6:week=temp;
           break;
    case 7:year=temp;
           stemp[0]=(sec/10)*16+sec%10;
           stemp[1]=(min/10)*16+min%10;
           stemp[2]=(hour/10)*16+hour%10;
           stemp[3]=(date/10)*16+date%10;
           stemp[4]=(month/10)*16+month%10;
           stemp[5]=(week/10)*16+week%10;
           stemp[6]=(year/10)*16+year%10;
           v_setd1302(stemp);//设定值
           k=0;
           break;
  }
}

七、仿真电路图

**设计DS1302电路:** 首先,我们需要在Proteus中设计一个包含DS1302实时时钟模块的电路。你可以从Proteus的组件库中找到DS1302组件,并把它添加到你的设计中。连接好电源、地线以及与微控制器(例如Arduino或者其他的)的通信线。

**设置Proteus仿真参数:** 在电路设计完成后,你需要设置适当的仿真参数。这可能包括电源电压、时钟频率等等。

**编写VB上位机程序: **使用VB (Visual Basic)编写一个程序来控制DS1302实时时钟模块。程序应当能够通过串口与微控制器通信,并能读取或设置时钟时间。你可能需要使用VB的串口控件(MSComm或SerialPort)来实现这个功能。

**编写微控制器程序:** 在微控制器端,你需要编写一个相应的程序来响应VB程序发出的指令。包括接收到设置时间的指令时,应当能够把新的时间设置到DS1302模块。

**在Proteus中测试: **最后,你可以在Proteus中运行仿真,看看你的电路和程序是否正常工作。通过Proteus的虚拟串口功能,你可以把微控制器的串口与你的电脑连接起来,从而让VB程序能够控制它。


八、运行效果:

开机显示:

时间数据:


相关设置:


九、总结:

通过这个项目学习到了很多知识,学习如何通过编程控制硬件设备的基本引脚,例如在LCD显示屏上写命令的过程。这涉及到对特定引脚的电平进行精确控制。加深对嵌入式系统编程的理解,特别是如何在微控制器上实现基本的输入输出操作。这是嵌入式系统开发的核心技能之一。
通过创建和使用函数(如delay()和wc51r()),学习如何封装代码以增强可读性、重用性和模块化。这对于任何编程项目来说都是一个重要的技能,理解在硬件操作中引入延时的重要性,以及如何实现简单的软件延时。这对于确保硬件操作的稳定性和可靠性至关重要,通过实际编写和测试代码,将理论知识应用于实际问题解决中,增强理解和记忆。
在实验过程中,可能会遇到代码不工作或硬件不响应的情况,需要通过调试和问题解决技能来找出问题所在并修正。