利用51单片机(STC89C52)和DS1302时钟芯片实现实时时钟功能,并在LCD1602液晶屏上显示年月日时分秒。
拓展:在此基础上实现,实现可调时钟的功能,可以通过按键实现时钟的年月日时分秒可以调节。
LCD1602液晶屏的显示功能和DS1302时钟芯片的相应功能已经在上文中分别实现,详细内容可以查看本文最下方的参考。
本程序在DS1302中做了一下小改动,在DS1302_ReadByte写字节函数中,添加如下代码:
Command|=0x01;//写地址+1=读地址
众所周知,DS1302中读的地址(Read)是写的地址+1(这么表述可能不太准确,但意思是这样的),如下图所示,因此利用Command|=0x01;语句来使函数DS1302_WriteByte和DS1302_ReadByte输入的变量Command为同一地址,只是在函数内容进行重新修改,以此来提升代码的易用性。
为表示方便,可以宏定义相关地址(秒、分、时、日、月、周、年、写保护):
//地址
#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 //写保护
定义数组存储设置的年、月、日、时、分、秒:
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码的内容可以参考文章(文章)第六部分。
将时间从时钟芯片读出函数代码如下:
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;
}
实现可调时钟需要四个按键,分别是:
由于郭天祥的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;
}
显示时钟函数代码如下:
//显示时钟
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);
}
设置时钟函数代码如下:
//设置时钟
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(年、月、日、时、分、秒)时,清零重新开始选择。
年月日时分秒越界判断由于代码量较大,这里不再展示,代码可到本文最后获取。
对应位闪烁需要用到定时器,基本思路是:定义一个变量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,如下图所示。
如果没有看懂,完整代码可以参考:https://download.csdn.net/download/didi_ya/85187911
ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~
参考: