51单片机案例(1)——利用DS1302实现实时时钟和可调时钟的功能

目录

    • 一、案例描述
    • 二、实时时钟程序实现
      • 2.1 LCD1602和DS1302功能实现
      • 2.2 宏定义相关地址
      • 2.3 写入时间
      • 2.4 读出时间
    • 三、拓展——可调时钟程序实现
      • 3.1 按键功能
      • 3.2 显示时钟
      • 3.3 设置时钟
      • 3.4 调节时对应位闪烁
    • 四、效果演示

一、案例描述

利用51单片机(STC89C52)和DS1302时钟芯片实现实时时钟功能,并在LCD1602液晶屏上显示年月日时分秒。
拓展:在此基础上实现,实现可调时钟的功能,可以通过按键实现时钟的年月日时分秒可以调节。

二、实时时钟程序实现

2.1 LCD1602和DS1302功能实现

LCD1602液晶屏的显示功能和DS1302时钟芯片的相应功能已经在上文中分别实现,详细内容可以查看本文最下方的参考
本程序在DS1302中做了一下小改动,在DS1302_ReadByte写字节函数中,添加如下代码:

Command|=0x01;//写地址+1=读地址

众所周知,DS1302中读的地址(Read)是写的地址+1(这么表述可能不太准确,但意思是这样的),如下图所示,因此利用Command|=0x01;语句来使函数DS1302_WriteByte和DS1302_ReadByte输入的变量Command为同一地址,只是在函数内容进行重新修改,以此来提升代码的易用性。

51单片机案例(1)——利用DS1302实现实时时钟和可调时钟的功能_第1张图片

2.2 宏定义相关地址

为表示方便,可以宏定义相关地址(秒、分、时、日、月、周、年、写保护):

//地址
#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     0x8E	//写保护

2.3 写入时间

定义数组存储设置的年、月、日、时、分、秒:

unsigned char DS1302_Time[]={22,4,20,12,59,55,3};

将时间写入时钟芯片函数如下:

void DS1302_SetTime(){
	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);//打开写保护
}

【注】:这里/10*16+%10表示将十进制转BCD码,关于BCD码的内容可以参考文章(文章)第六部分

2.4 读出时间

将时间从时钟芯片读出函数代码如下:

void DS1302_ReadTime(){
	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;
}

三、拓展——可调时钟程序实现

3.1 按键功能

实现可调时钟需要四个按键,分别是:

  1. 调节/确定;
  2. 移动;
  3. 加一;
  4. 减一;

由于郭天祥的51单片机的四个独立按键S2,S3,S4,S5和LCD1602存在引脚冲突(共用引脚P3),因此这里选择矩阵键盘第四列的四个按键S9,S13,S17,S21作为上述的四个按键。
代码如下:

//延时函数
void Delay(int xms)  //@12.000MHz
{
	unsigned char i, j;
	while(xms){
		i = 2;
		j = 239;
		do{
			while (--j);
		} while (--i);
		xms--;
	}
}

//矩阵键盘读取按键键码
unsigned char MatrixKey(){
	unsigned char KeyNumber=0;
	P3=0xff;
	P3_7=0;
	//检测S9被按下
	if(P3_0==0){Delay(5);while(P3_0==0);Delay(5);KeyNumber=1;}
	//检测S13被按下
	if(P3_1==0){Delay(5);while(P3_1==0);Delay(5);KeyNumber=2;}        
	//检测S17被按下
	if(P3_2==0){Delay(5);while(P3_2==0);Delay(5);KeyNumber=3;}        
	//检测S21被按下
	if(P3_3==0){Delay(5);while(P3_3==0);Delay(5);KeyNumber=4;}        
	
	return KeyNumber;
}

【注】:如果你的独立按键与LCD1602不冲突,也可以根据你的单片机板子进行选择,单片机按键引脚进行自行调整,以你的板子为准,独立按键代码如下:

/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
	unsigned char KeyNumber=0;
	P3=0xff;
	if(P3_4==0){Delay(5);while(P3_4==0);Delay(5);KeyNumber=1;}
	if(P3_5==0){Delay(5);while(P3_5==0);Delay(5);KeyNumber=2;}
	if(P3_6==0){Delay(5);while(P3_6==0);Delay(5);KeyNumber=3;}
	if(P3_7==0){Delay(5);while(P3_7==0);Delay(5);KeyNumber=4;}
	
	return KeyNumber;
}

3.2 显示时钟

显示时钟函数代码如下:

//显示时钟
void TimeShow(){
	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.3 设置时钟

设置时钟函数代码如下:

//设置时钟
void TimeSet(){
	if(KeyNum==2){
		TimeSetSelect++;
		TimeSetSelect%=6;//相当于if(TimeSetSelect>5)TimeSetSelect=0;
	}
	//+
	if(KeyNum==3)
	{
	DS1302_Time[TimeSetSelect]++;
	...//年月日时分秒越界判断
	}
	if(KeyNum==4)
	{
	DS1302_Time[TimeSetSelect]--;
	...//年月日时分秒越界判断
	}

如果第二个按键按下,时钟设置选择位TimeSetSelect,当TimeSetSelect超过5(年、月、日、时、分、秒)时,清零重新开始选择。
年月日时分秒越界判断由于代码量较大,这里不再展示,代码可到本文最后获取。

3.4 调节时对应位闪烁

对应位闪烁需要用到定时器,基本思路是:定义一个变量FlashFlag,实现定时1010翻转。
初始化定时器:

//定时器初始化
void Timer0Init()  //1毫秒@12.000MHz
{
	TMOD &= 0xF0;                //设置定时器模式
	TMOD |= 0x01;                //设置定时器模式
	TL0 = 0x18;                //设置定时初值
	TH0 = 0xFC;                //设置定时初值
	TF0 = 0;                //清除TF0标志
	TR0 = 1;                //定时器0开始计时
	//中断
	ET0=1;
	EA=1;
	PT0=0;
}

定时器中断:

unsigned int T0Count;
//中断
void Timer0_Rountine() interrupt 1
{
		
        TL0 = 0x18;                //设置定时初值
        TH0 = 0xFC;                //设置定时初值
        T0Count++;
        if(T0Count>=500){	//每隔500ms取反
			FlashFlag=!FlashFlag;
        }        
}

最后在设置时钟函数TimeSet()中加入闪烁判断相关语句,如果时钟设置选择位TimeSetSelect在某位上而且闪烁标志位FlashFlag为1则显示,显示500ms后FlashFlag为0,此时熄灭,这样就实现了闪烁功能,相关代码如下:

	//闪烁判断
	if(TimeSetSelect==0 && FlashFlag==1){
		LCD_ShowString(1,1,"  ");
	}
	else{LCD_ShowNum(1,1,DS1302_Time[0],2);}
	if(TimeSetSelect==1 && FlashFlag==1){
		LCD_ShowString(1,4,"  ");
	}
	else{LCD_ShowNum(1,4,DS1302_Time[1],2);}
	if(TimeSetSelect==2 && FlashFlag==1){
		LCD_ShowString(1,7,"  ");
	}
	else{LCD_ShowNum(1,7,DS1302_Time[2],2);}
	if(TimeSetSelect==3 && FlashFlag==1){
		LCD_ShowString(2,1,"  ");
	}
	else{LCD_ShowNum(2,1,DS1302_Time[3],2);}
	if(TimeSetSelect==4 && FlashFlag==1){
		LCD_ShowString(2,4,"  ");
	}
	else{LCD_ShowNum(2,4,DS1302_Time[4],2);}
	if(TimeSetSelect==5 && FlashFlag==1){
		LCD_ShowString(2,7,"  ");
	}
	else{LCD_ShowNum(2,7,DS1302_Time[5],2);}

四、效果演示

完整效果如下:

51单片机案例之利用DS1302实现实时时钟和可调时钟的功能

这里介绍一下我的接线,由于郭天祥51单片机板子没有DS1302时钟芯片而我又需要用到,因此这里花两块钱买了一个DS1302,将DS1302的CLK连接P2.6,DAT连接P2.4,RST连接P2.5,如下图所示。
51单片机案例(1)——利用DS1302实现实时时钟和可调时钟的功能_第2张图片

如果没有看懂,完整代码可以参考:https://download.csdn.net/download/didi_ya/85187911

ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~

参考:

  1. 单片机常用芯片总结(一)——LCD1602液晶屏
  2. 单片机常用芯片总结(二)——DS1302时钟芯片

你可能感兴趣的