当前位置: 首页 > article >正文

江科大51单片机笔记【9】DS1302时钟可调时钟(下)

在写代码前,记得把上一节的跳线帽给插回去,不然LCD无法显示

一.DS1302时钟

1.编写DS1302.c文件

(1)重新对端口定义名字

sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

(2)初始化

因为单片机上电默认是1,所以要初始化为0

void DS1302_Init(void)
{DS1302_CE=0;DS1302_SCLK=0;
}

(3)写入函数

在给SCLK赋值的时候

    DS1302_SCLK=1;  
    DS1302_SCLK=0;

在这中间置1后马上置0需要有一个最小的延时,但实际操作后发现不用加延时可以运行,因为我们的单片机运行没有这么快

这里相当于我们已经写入了第0位

//Command是命令字
void DS1302_WriteByte(unsigned char Command,Data)
{DS1302_CE=1;DS1302_IO=Command&0x01;  //取第0位DS1302_SCLK=1;  DS1302_SCLK=0;}

同理第1位即    DS1302_IO=Command&0x02; 

       第2位即    DS1302_IO=Command&0x04; .......

所以我们可以用一个for循环来实现取8位

//Command是命令字
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char iDS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}}

此时我们已经完成写入操作的一半,又发现后面的写入数据和前面的写入指令是一样的,所以我们可以复制for循环的代码思路

//Command是命令字
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char iDS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}for(i=0;i<8;i++){DS1302_IO=Data&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}DS1302_CE=0;
}

记得在最后把CE置0

到这我们就完成时序的写入函数,我们就可以对任何的寄存器进行写入操作

(4)读取函数

虽然该部分的时序跟上面的写入类似,但不能直接照搬,因为在SCLK这条线上只有15个脉冲(写入是16个)因为在最中间的脉冲同时进行上升沿和下降沿的操作

所以我们把SCLK赋值的顺序颠倒一下,先给0再给1,这样当for循环8次后,刚好全是上升沿

这里根据代码理解在图上比划一下就很好理解,第一个for循环里先给0再给1,在循环结束后SCLK依旧是1,但是在第二个for循环里还是要先给1再给0,目的就是为了跳过一个周期适配15个脉冲

unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0x00;DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=0;  DS1302_SCLK=1;}for(i=0;i<8;i++){DS1302_SCLK=1;  DS1302_SCLK=0;if(DS1302_IO){Data |= (0X01<<i);}	}DS1302_CE=0;return Data;
}

一般来说,与操作是为了清零,或操作是为了置1

最后不用忘了return 返回值,因为这是有返回值的函数

(5)测试

写完上面3个函数,我们就已经对时序模拟出来了、

接下来进行测试

先写好声明文件

//DS1302.h#ifndef __DS1302_H_
#define __DS1302_H_void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);#endif

这里提醒一下,DS1302初始化有写保护,只能读不能写,在DS1302初始化之后加一句DS1302_WriteByte(0x8e,0x00);0x8E为写保护寄存器,需要先关闭写保护,

//main文件#include <REGX52.H>
#include " LCD1602.h"
#include " DS1302.h"unsigned char Second;void main()
{DS1302_Init();DS1302_WriteByte(0x8E,0x00);LCD_Init();LCD_ShowString(1,1,"RTC");DS1302_WriteByte(0x80,0x03);Second=DS1302_ReadByte(0x81);LCD_ShowNum(2,1,Second,3);while(1){}}

现象

(6)扩展知识点

BCD码(Binary Coded Decimal):用4位二进制数来表示1位十进制

内部的寄存器不是以二进制来存储的,而是以BCD码来存储

例:0001 0011表示13,1000 0101表示85,0001 1010不合法

在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

BCD码转十进制:DEC=BCD/16*10+BCD%16;(2位BCD)

十进制转BCD码:BCD=DEC/10*16+DEC%16;(2位BCD)

#include <REGX52.H>
#include " LCD1602.h"
#include " DS1302.h"
#include " Delay.h"unsigned char Second;void main()
{DS1302_Init();DS1302_WriteByte(0x8E,0x00);LCD_Init();LCD_ShowString(1,1,"RTC");DS1302_WriteByte(0x80,0x03);while(1){Second=DS1302_ReadByte(0x81);LCD_ShowHexNum(2,1,Second,3);}}

所以我们在这里想让Second自加显示在LCD上,就得使用下面这个函数,而不是ShowNum,否则他会从9突变到16,这是因为ShowNum是以十进制来显示,而时钟寄存器的自加是上面所说的BCD码,而下面这个函数是以十六进制显示的            LCD_ShowHexNum(2,1,Second,3);  十六进制和BCD码有部分兼容

下面解释寄存器里的BCD码

CH是时钟静止,给1静止给0运行,高3位显示10秒,低4位显示秒,分、时、日、月、年都是同理

小时的最高位是选择12/24制,第6位选择AM还是PM

根据前面BCD码转十进制,我们就可以写出

LCD_ShowNum(2,1,Second/16*10+Second%16,3);

这样就可以在LCD上正常显示了

(7)定义数组函数存储读写年月日等

先定义地址

因为写入和读取的地址前7位都是一样的,只有最低位是01之分,这里我们只定义写入的地址,只需要在读的函数里给Command即命令字的最低位置1,这样就不用再重新定义读取的地址了

#define DS1302_SECOND  0x80
#define DS1302_MINUTE  0x82
#define DS1302_HOUR    0x84
#define DS1302_DATE    0x86
#define DS1302_MONTH   0x88
#define DS1302_DAY     0x8A
#define DS1302_YEAR    0x8C
#define DS1302_WP      0x8Eunsigned char DS1302_ReadByte(unsigned char Command)
{Command |=0x01;

下面是完整代码

记得要在.h文件里说明

这里再说一电,声明外部变量时前面必须加extern,数组和函数可以不加因为前面会自带

#include <REGX52.H>//重新对端口定义名字
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;#define DS1302_SECOND  0x80
#define DS1302_MINUTE  0x82
#define DS1302_HOUR    0x84
#define DS1302_DATE    0x86
#define DS1302_MONTH   0x88
#define DS1302_DAY     0x8A
#define DS1302_YEAR    0x8C
#define DS1302_WP      0x8Eunsigned char DS1320_Time[]={25,03,04,12,59,55,2}void DS1302_Init(void)
{DS1302_CE=0;DS1302_SCLK=0;
}//Command是命令字
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char i;DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}for(i=0;i<8;i++){DS1302_IO=Data&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}DS1302_CE=0;
}unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0x00;Command |=0x01;DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=0;  DS1302_SCLK=1;}for(i=0;i<8;i++){DS1302_SCLK=1;  DS1302_SCLK=0;if(DS1302_IO){Data |= (0X01<<i);}	}DS1302_CE=0;DS1302_IO=0;return Data;
}//写入时间是十进制转BCD码
void DS1302_SetTime(void)
{DS1302_WriteByte(DS1302_WP,0x00);DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);DS1302_WriteByte(DS1302_WP,0x80);
}
//读取时间是BCD码转十进制
void DS1302_ReadTime(void)
{unsigned char Temp;Temp=DS1302_ReadByte(DS1302_YEAR);DS1302_Time[0]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_MONTH);DS1302_Time[1]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_DATE);DS1302_Time[2]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_HOUR);DS1302_Time[3]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_MINUTE);DS1302_Time[4]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_SECOND);DS1302_Time[5]=Temp/16*10+Temp%16Temp=DS1302_ReadByte(DS1302_DAY);DS1302_Time[6]=Temp/16*10+Temp%16}

有了这些代码我们就不用在main函数里定义时分秒了,我们只需要调用函数即可

(8)主函数

到这里我们就做好第一个功能了

#include <REGX52.H>
#include " LCD1602.h"
#include " DS1302.h"
#include " Delay.h"void main()
{DS1302_Init();LCD_Init();LCD_ShowString(1,1,"  -  -  ");LCD_ShowString(2,1,"  :  :  ");DS1302_SetTime();while(1){DS1302_ReadTime();LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);}}

下面再对模块化参数进行注释 

#include <REGX52.H>//引脚定义
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;//寄存器写入地址/指令定义
#define DS1302_SECOND  0x80
#define DS1302_MINUTE  0x82
#define DS1302_HOUR    0x84
#define DS1302_DATE    0x86
#define DS1302_MONTH   0x88
#define DS1302_DAY     0x8A
#define DS1302_YEAR    0x8C
#define DS1302_WP      0x8Eunsigned char DS1302_Time[]={25,03,04,12,59,55,2};/*** @brief  DS1302初始化* @param  无* @retval 无*/void DS1302_Init(void)
{DS1302_CE=0;DS1302_SCLK=0;
}/*** @brief  DS1302写一个字节* @param  Command命令字/地址* @param  Data要写入的数据* @retval 无*/
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char i;DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}for(i=0;i<8;i++){DS1302_IO=Data&(0x01<<i);  DS1302_SCLK=1;  DS1302_SCLK=0;}DS1302_CE=0;
}/*** @brief  DS1302读一个字节* @param  Command命令字/地址* @retval 读出的数据*/unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0x00;Command |=0x01;DS1302_CE=1;for(i=0;i<8;i++){DS1302_IO=Command&(0x01<<i);  DS1302_SCLK=0;  DS1302_SCLK=1;}for(i=0;i<8;i++){DS1302_SCLK=1;  DS1302_SCLK=0;if(DS1302_IO){Data |= (0X01<<i);}	}DS1302_CE=0;DS1302_IO=0;return Data;
}/*** @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中* @param  无* @retval 无*/
void DS1302_SetTime(void)
{DS1302_WriteByte(DS1302_WP,0x00);DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//鍗佽繘鍒惰浆BCD鐮佸悗鍐欏叆DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);DS1302_WriteByte(DS1302_WP,0x80);
}/*** @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中* @param  无* @retval 无*/
void DS1302_ReadTime(void)
{unsigned char Temp;Temp=DS1302_ReadByte(DS1302_YEAR);DS1302_Time[0]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_MONTH);DS1302_Time[1]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_DATE);DS1302_Time[2]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_HOUR);DS1302_Time[3]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_MINUTE);DS1302_Time[4]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_SECOND);DS1302_Time[5]=Temp/16*10+Temp%16;Temp=DS1302_ReadByte(DS1302_DAY);DS1302_Time[6]=Temp/16*10+Temp%16;}

二.DS1302可调时钟

这个部分是上一个代码的升级版,这一块会比较难

下面为了节约空间,我没有把定义的变量标出来,都是unsigned char类型

1.按键1切换

定义按键1,切换我们的模式,模式0是时间显示,模式1是时间设置

void main()
{DS1302_Init();LCD_Init();LCD_ShowString(1,1,"  -  -  ");LCD_ShowString(2,1,"  :  :  ");DS1302_SetTime();while(1){KeyNum=Key();if(KeyNum==1){if(MODE==0){MODE=1;}else if(MODE==1){MODE=0;}}switch(MODE){case 0:TimeShow();break;case 1:TimeSet();break;}}}

2.时间显示函数

void TimeShow(void)
{DS1302_ReadTime();LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);
}

3.时间设置函数

按键2让时间设置选择++并且取值范围在0~5

void TimeSet(void)
{if(KeyNum==2){TimeSetSelect++;TimeSetSelect%=6; //大于5越界取余0的进阶写法}if(KeyNum==3){DS1302_Time[TimeSetSelect]++;}if(KeyNum==4){DS1302_Time[TimeSetSelect]--;}LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);LCD_ShowNum(2,10,TimeSetSelect,2);
}

这样就已经实现对时钟的六个位进行选择和加减,但是我们并没有设置越界判断,也就是说月份会一直加到13,14月,所以下面我们对这个函数进行越界判断的优化

这段最麻烦是对日的判断,因为有30和31的区分,而2月又有28和29的区分,所以非常麻烦

void TimeSet(void)
{if(KeyNum==2){TimeSetSelect++;TimeSetSelect%=6; //大于5越界取余0的进阶写法}
if(KeyNum==3){DS1302_Time[TimeSetSelect]++;if(DS1302_Time[0]>99){DS1302_Time[0]=0;}if(DS1302_Time[1]>12){DS1302_Time[1]=1;}if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12){if(DS1302_Time[2]>31){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11){if(DS1302_Time[2]>30){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==2){if(DS1302_Time[0]%4==0){if(DS1302_Time[2]>29){DS1302_Time[2]=1;}}else{if(DS1302_Time[2]>28){DS1302_Time[2]=1;}}}if(DS1302_Time[3]>23){DS1302_Time[3]=0;}if(DS1302_Time[4]>59){DS1302_Time[4]=0;}if(DS1302_Time[5]>59){DS1302_Time[5]=0;}}if(KeyNum==4){DS1302_Time[TimeSetSelect]--;if(DS1302_Time[0]<0){DS1302_Time[0]=99;}if(DS1302_Time[1]<1){DS1302_Time[1]=12;}if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12){if(DS1302_Time[2]<1){DS1302_Time[2]=31;}if(DS1302_Time[2]>31){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11){if(DS1302_Time[2]<1){DS1302_Time[2]=30;}if(DS1302_Time[2]>30){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==2){if(DS1302_Time[0]%4==0){if(DS1302_Time[2]<1){DS1302_Time[2]=29;}if(DS1302_Time[2]>29){DS1302_Time[2]=1;}}else{if(DS1302_Time[2]<1){DS1302_Time[2]=28;}if(DS1302_Time[2]>28){DS1302_Time[2]=1;}}}if(DS1302_Time[3]<0){DS1302_Time[3]=23;}if(DS1302_Time[4]<0){DS1302_Time[4]=59;}if(DS1302_Time[5]<0){DS1302_Time[5]=59;}}LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);LCD_ShowNum(2,10,TimeSetSelect,2);
}

4.选择对应位闪烁

感叹号是逻辑取反,波浪号是按位取反

比如0逻辑取反1,1逻辑取反0;0按位取反0xFE,1按位取反0xFF

我们设置一个变量,让他10101010的变化,然后当选择某个位时,让这个位为1时亮,为0时灭,在这里我们只需要一个标识位,所以用逻辑取反就好

这里要用到之前的定时器中断的函数

	if(TimeSetSelect==0&&TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}else {LCD_ShowNum(1,1,DS1302_Time[0],2);}if(TimeSetSelect==1&&TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}else {LCD_ShowNum(1,4,DS1302_Time[1],2);}if(TimeSetSelect==2&&TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}else {LCD_ShowNum(1,7,DS1302_Time[2],2);}if(TimeSetSelect==3&&TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}else {LCD_ShowNum(2,1,DS1302_Time[3],2);}if(TimeSetSelect==4&&TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}else {LCD_ShowNum(2,4,DS1302_Time[4],2);}if(TimeSetSelect==5&&TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}else {LCD_ShowNum(2,7,DS1302_Time[5],2);}void Timer0_Routine() interrupt 1
{static unsigned int	T0Count;TL0 = 0x18;  //设置定时初值TH0 = 0xFC;  //设置定时初值T0Count++;if(T0Count>=500){T0Count=0;TimeSetFlashFlag=!TimeSetFlashFlag;}}
#include <REGX52.H>
#include " LCD1602.h"
#include " DS1302.h"
#include " Delay.h"
#include " Key.h"
#include " Timer0.h"unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;void TimeShow(void)
{DS1302_ReadTime();LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);
}void TimeSet(void)
{if(KeyNum==2){TimeSetSelect++;TimeSetSelect%=6; //大于5越界取余0的进阶写法}
if(KeyNum==3){DS1302_Time[TimeSetSelect]++;if(DS1302_Time[0]>99){DS1302_Time[0]=0;}if(DS1302_Time[1]>12){DS1302_Time[1]=1;}if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12){if(DS1302_Time[2]>31){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11){if(DS1302_Time[2]>30){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==2){if(DS1302_Time[0]%4==0){if(DS1302_Time[2]>29){DS1302_Time[2]=1;}}else{if(DS1302_Time[2]>28){DS1302_Time[2]=1;}}}if(DS1302_Time[3]>23){DS1302_Time[3]=0;}if(DS1302_Time[4]>59){DS1302_Time[4]=0;}if(DS1302_Time[5]>59){DS1302_Time[5]=0;}}if(KeyNum==4){DS1302_Time[TimeSetSelect]--;if(DS1302_Time[0]<0){DS1302_Time[0]=99;}if(DS1302_Time[1]<1){DS1302_Time[1]=12;}if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12){if(DS1302_Time[2]<1){DS1302_Time[2]=31;}if(DS1302_Time[2]>31){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11){if(DS1302_Time[2]<1){DS1302_Time[2]=30;}if(DS1302_Time[2]>30){DS1302_Time[2]=1;}}else if(DS1302_Time[1]==2){if(DS1302_Time[0]%4==0){if(DS1302_Time[2]<1){DS1302_Time[2]=29;}if(DS1302_Time[2]>29){DS1302_Time[2]=1;}}else{if(DS1302_Time[2]<1){DS1302_Time[2]=28;}if(DS1302_Time[2]>28){DS1302_Time[2]=1;}}}if(DS1302_Time[3]<0){DS1302_Time[3]=23;}if(DS1302_Time[4]<0){DS1302_Time[4]=59;}if(DS1302_Time[5]<0){DS1302_Time[5]=59;}}if(TimeSetSelect==0&&TimeSetFlashFlag==1){LCD_ShowString(1,1,"  ");}else {LCD_ShowNum(1,1,DS1302_Time[0],2);}if(TimeSetSelect==1&&TimeSetFlashFlag==1){LCD_ShowString(1,4,"  ");}else {LCD_ShowNum(1,4,DS1302_Time[1],2);}if(TimeSetSelect==2&&TimeSetFlashFlag==1){LCD_ShowString(1,7,"  ");}else {LCD_ShowNum(1,7,DS1302_Time[2],2);}if(TimeSetSelect==3&&TimeSetFlashFlag==1){LCD_ShowString(2,1,"  ");}else {LCD_ShowNum(2,1,DS1302_Time[3],2);}if(TimeSetSelect==4&&TimeSetFlashFlag==1){LCD_ShowString(2,4,"  ");}else {LCD_ShowNum(2,4,DS1302_Time[4],2);}if(TimeSetSelect==5&&TimeSetFlashFlag==1){LCD_ShowString(2,7,"  ");}else {LCD_ShowNum(2,7,DS1302_Time[5],2);}}void main()
{DS1302_Init();LCD_Init();Timer0Init();LCD_ShowString(1,1,"  -  -  ");LCD_ShowString(2,1,"  :  :  ");DS1302_SetTime();while(1){KeyNum=Key();if(KeyNum==1){if(MODE==0){MODE=1;TimeSetSelect=0;}else if(MODE==1){MODE=0;DS1302_SetTime();}}switch(MODE){case 0:TimeShow();break;case 1:TimeSet();break;}}}void Timer0_Routine() interrupt 1
{static unsigned int	T0Count;TL0 = 0x18;  //设置定时初值TH0 = 0xFC;  //设置定时初值T0Count++;if(T0Count>=500){T0Count=0;TimeSetFlashFlag=!TimeSetFlashFlag;}}

这代码还有一个缺陷就是按下按键的时候会停止时间

就是我们在写Key()函数时,用到了while死循环,但是为了简单应用只能这样

unsigned int Key()
{unsigned char KeyNumber=0;if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}return KeyNumber;	
}

相关文章:

江科大51单片机笔记【9】DS1302时钟可调时钟(下)

在写代码前&#xff0c;记得把上一节的跳线帽给插回去&#xff0c;不然LCD无法显示 一.DS1302时钟 1.编写DS1302.c文件 &#xff08;1&#xff09;重新对端口定义名字 sbit DS1302_SCLKP3^6; sbit DS1302_IOP3^4; sbit DS1302_CEP3^5;&#xff08;2&#xff09;初始化 因为…...

发行思考:全球热销榜的频繁变动

几点杂感&#xff1a; 1、单机游戏销量与在线人数的衰退是剧烈的&#xff0c;有明显的周期性&#xff0c;而在线游戏则稳定很多。 如去年的某明星游戏&#xff0c;最高200多万在线&#xff0c;如今在线人数是48名&#xff0c;3万多。 而近期热门的是MH&#xff0c;在线人数8…...

微信小程序接入deepseek

先上效果 话不多说&#xff0c;直接上代码&#xff08;本人用的hbuilder Xuniapp&#xff09; <template><view class"container"><!-- 聊天内容区域 --><scroll-view class"chat-list" scroll-y :scroll-top"scrollTop":…...

为解决局域网IP、DNS切换的Windows BAT脚本

一、背景 为解决公司普通人员需要切换IP、DNS的情况&#xff0c;于是搞了个windows下的bat脚本&#xff0c;可以对有线网络、无线网络进行切换设置。 脚本内容 echo off title 多网络接口IP切换工具:menu cls echo echo 请选择要配置的网络接口: echo echo 1. 有线网络&am…...

VUE3开发-9、axios前后端跨域问题解决方案

VUE前端解决跨域问题 前端页面需要改写 如果无效&#xff0c;记得重启服务器 后端c#解决跨域问题 前端js取值&#xff0c;后端c#跨域_c# js跨域-CSDN博客...

【计算机网络】计算机网络的性能指标——时延、时延带宽积、往返时延、信道利用率

计算机网络的性能指标 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;&#xff01; 在上一篇内容中我们介绍了计算机网络的三个性能指标——速率、带宽和吞吐量。用大白话来说就是&#xff1a;网速、最高网速和实时网速。 相信大家看到这三个词应该就…...

Kubermetes 部署mysql pod

步骤 1: 创建 PersistentVolume 和 PersistentVolumeClaim 首先为 MySQL 创建一个 PersistentVolume (PV) 和 PersistentVolumeClaim (PVC) 来确保数据的持久性。 mysql-pv.yaml&#xff1a; apiVersion: v1 kind: PersistentVolume metadata:name: mysql-pv-volume spec:cap…...

Docker和DockerCompose基础教程及安装教程

Docker的应用场景 Web 应用的自动化打包和发布。自动化测试和持续集成、发布。在服务型环境中部署和调整数据库或其他的后台应用。从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。 CentOS Docker 安装 使用官方安装脚本自动安装 安装命令…...

网络安全等级保护—定级

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 网络安全等级保护五个保护等级 五个保护等级 受侵害的客体 对客体的侵害程度 一般损害 严重损害 特别严重损害 公民、法人和其他组织的合法权益 第一级 第二级 …...

服务器数据恢复—raid5阵列中硬盘出现坏道的数据恢复流程

服务器故障情况&#xff1a; 某公司一台服务器中有一组多块硬盘组成的磁盘阵列。磁盘阵列中有2块硬盘出现故障离线&#xff0c;服务器崩溃&#xff0c;上层数据丢失。 硬件检测&#xff1a; 硬件工程师对客户服务器内的所有硬盘进行物理故障检测&#xff0c;最终确认这2块硬盘…...

macos 程序 运行

sudo xattr -r -d com.apple.quarantine [/Applications/Name]使用stow 管理配置文件...

UVC for USBCamera in Android

基于UVC 协议&#xff0c;完成USBCamera 开发 文章目录 一、目的&#xff1a;二、USBCamera 技术实现方案难点 三、误区&#xff1a;四、基础补充、资源参考架构图了解Camera相关专栏零散知识了解部分相机源码参考&#xff0c;学习API使用&#xff0c;梳理流程&#xff0c;偏应…...

Docker 安装 Nacos 2.1.1(单机版)

一、拉取镜像 docker pull nacos/nacos-server:v2.1.1 二、新建数据库 官网上下载 对应版本的 nacos zip 包&#xff0c;在 nacos\conf 目录下有 mysql脚本&#xff1a; 新建一个数据库 nacos_config&#xff0c;在数据库中依次执行 nacos-mysql.sql、1.4.0-ipv6_support-up…...

网络安全数据富化 网络数据安全处理规范

本文件规定了网络运营者开展网络数据收集、存储、使用、加工、传输、提供、公开等数据处理的安全 技术与管理要求。 本文件适用于网络运营者规范网络数据处理,以及监管部门、第三方评估机构对网络数据处理进行 监督管理和评估。 部分术语和定义 数据&#xff08;data&#x…...

深入理解 C 语言函数的定义

在 C 语言的编程世界里&#xff0c;函数是构建复杂程序的基石。理解函数的定义与运用&#xff0c;对于编写高效、可维护的代码至关重要。​ 函数定义的基本概念​ 函数是一组执行特定任务的代码块。它将一个复杂的问题分解为一个个小的、可管理的部分&#xff0c;提高了代码的…...

SQL注入练习场:PHPStudy+SQLI-LABS靶场搭建教程(零基础友好版)

注意&#xff1a;文中涉及演示均为模拟测试&#xff0c;切勿用于真实环境&#xff0c;任何未授权测试都是违法行为&#xff01; 一、环境准备 下载PHPStudy 官网下载地址&#xff1a;https://www.xp.cn/php-study&#xff08;选择Windows版&#xff09; 安装时建议选择自定…...

最好用的多语言插件Google Language Translator

Google Language Translator 是一款用于 WordPress 的翻译插件&#xff0c;可以帮助网站快速实现多语言支持。以下是关于该插件的详细介绍&#xff1a; 功能特点 免费与付费版本 免费版本使用 Google Translate 的短语基础机器翻译&#xff0c;能够快速将网站内容翻译成多种…...

计算机组成原理笔记(一)——1.1电子计算机与存储程序控制

电子计算机是一种不需要人工直接干预&#xff0c;能够自动、高速、准确地对各种信息进行处理和存储的电子设备。电子计算机从总体上来说可以分为两大类:电子模拟计算机和电子数字计算机。电子模拟计算机中处理的信息是连续变化的物理量&#xff0c;运算的过程也是连续的;而电子…...

5人3小时复刻Manus?开源OpenManus项目全解剖,我的DeepSeek股票报告这样诞生

大家好,我是大 F,深耕AI算法十余年,互联网大厂技术岗。分享AI算法干货、技术心得。 更多文章可关注《大模型理论和实战》、《DeepSeek技术解析和实战》,一起探索技术的无限可能! OpenManus是什么 1. 项目背景 OpenManus 是由 MetaGPT 核心团队仅用 3 小时复刻而成的开源…...

WordPress报502错误问题解决-php-fpm-84.service loaded failed failed LSB: starts php-fpm

文章目录 问题描述问题排查问题解决 问题描述 服务器环境&#xff1a; php&#xff1a;8.4MySQL&#xff1a;8.0Nginx&#xff1a;1.26.2 在访问站点时&#xff0c;一直报502&#xff0c;而两天前还能正常访问。 问题排查 导致502的问题很多&#xff0c;比如站点访问量太大…...

「Selenium+Python自动化从0到1②|2025浏览器操控7大核心API实战(附高效避坑模板))」

Python 自动化操作浏览器基础方法 在进行 Web 自动化测试时&#xff0c;操作浏览器是必不可少的环节。Python 结合 Selenium 提供了强大的浏览器操作功能&#xff0c;让我们能够轻松地控制浏览器执行各种任务。本文将详细介绍如何使用 Python 和 Selenium 操作浏览器的基本方法…...

uniapp 微信小程序 升级 uniad插件版本号

问题描述&#xff1a; 每次提交代码升级的时候会弹窗提示&#xff1a;uniad插件版本太低… 解决办法 一、使用微信小程序开发工具点击右上角 查看到最新版本&#xff1a;1.3.4 二、在app.json中改为最新的版本即可 "uni-ad": {"version": "1.3.4&q…...

使用Dockerfile打包java项目生成镜像部署到Linux_java项目打docker镜像的dockerfile

比起容器、镜像来说&#xff0c;Dockerfile 非常普通&#xff0c;它就是一个纯文本&#xff0c;里面记录了一系列的构建指令&#xff0c;比如选择基础镜像、拷贝文件、运行脚本等等&#xff0c;每个指令都会生成一个 Layer&#xff0c;而 Docker 顺序执行这个文件里的所有步骤&…...

使用 Apache POI 实现 Excel 单元格合并

在日常工作中&#xff0c;Excel 是一个不可或缺的工具&#xff0c;尤其是在处理大量数据时。为了提升数据的可读性和美观性&#xff0c;我们经常需要对 Excel 中的单元格进行合并操作。本文将介绍如何使用 Apache POI 库在 Java 中实现 Excel 单元格的合并&#xff0c;并提供一…...

低代码开发直聘管理系统

低代码 DeepSeek 组合的方式开发直聘管理系统&#xff0c;兼职是开挂的存在。整个管理后台系统 小程序端接口的输出&#xff0c;只花了两个星期不到。 一、技术栈 后端&#xff1a;SpringBoot mybatis MySQL Redis 前端&#xff1a;Vue elementui 二、整体效果 三、表结…...

Android更新时区版本-ianaVersion

代码路径 源码路径system/timezone 目录结构&#xff1a; apex 包含与Android时区更新APEX文件相关的代码&#xff0c;用于在Android设备上更新时区规则。 debug_tools 包含调试Android设备时区问题的工具。 distro 包含与“分发包”&#xff08;distros&#xff09;相关…...

Android 创建一个全局通用的ViewModel

&#xff08;推荐&#xff09;使用ViewModelStore 代码示例&#xff1a; class MyApplication : Application(), ViewModelStoreOwner {private val mViewModelStore ViewModelStore()override fun onCreate() {super.onCreate()}override val viewModelStore: ViewModelSto…...

利用python实现对Excel文件中数据元组的自定义排序

问题引入&#xff1a; 假设你是一个浙江省水果超市的老板&#xff0c;统筹11个下辖地市的水果产量。假设11个地市生产的水果包括&#xff1a;苹果、香蕉和西瓜。你如何快速得到某种水果产量突出&#xff08;排名前几&#xff09;的地市&#xff1f;产量落后&#xff08;排名后…...

leetcode 0018 四数之和-medium

1 题目&#xff1a;四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#x…...

时序数据库 InfluxDB 3.0 版本性能实测报告:写入吞吐量提升效果验证

亮点总结&#xff1a; TSBS 测试表明&#xff0c;对于少于 100 万台设备的数据集&#xff0c;InfluxDB OSS 3.0 的数据写入速度实际上比 InfluxDB OSS 1.8 更慢。 对于 100 万台及以上设备的数据集&#xff0c;InfluxDB OSS 3.0 的数据写入性能才开始超过 InfluxDB OSS 1.8。…...