目录

一、功能介绍

二、主要模块介绍

2.1 DS1302模块

2.1.1 DS1302芯片介绍

2.1.2 DS1302的使用

三、程序汇总

3.1测试文件test.c

3.2 LCD1602模块程序

3.2.1 LCD1602.h

3.2.2 LCD1602.c

3.3 DS1302模块程序

3.3.1 DS1302.h

3.3.2 DS1302.c

3.4 独立按键模块程序

3.4.1Independentkey.h

3.4.2Independentkey.c

3.5 T0定时器模块

3.5.1 Timer0_Iint.h

3.5.2Timer0_Iint.c

3.6 延时函数

3.6.1 Delay.h

3.6.2Delay.c

四、效果演示


一、功能介绍

在LCD1602上显示时钟,其中的年,月,日和时钟,分钟,秒钟还有星期都是可调的。

二、主要模块介绍

2.1 DS1302模块

为什么要用DS1302芯片来计时呢?定时器不可以吗?

  • 首先,用单独的芯片完成计时功能比用定时器中断更加的精准。DS1302内置了单独的晶振,频率为32.769KHz,32769=2^15,经过15次2分频,得到1Hz频率,也就是每计数一次就是1秒钟。
  • 再者,不影响CPU的性能。只需要把设置的时间写入到DS1302的寄存器中,就可以开始计时了,获取时间也是一样,只需要通过DS1302的I/O口把时间数据读到单片机的内存中就可以了。

2.1.1 DS1302芯片介绍

单片机的CPU三个引脚分别连接DS1302的使能端CE、数据输入/输出端I/O、串行时钟端SCLK三个端口。

  • VCC2:主电源引脚
  • VCC1:备用电源
  • X1、X2:DS1302 外部晶振引脚,通常需外接 32.768K 晶振
  • GND:电源地
  • CE:使能引脚,也是复位引脚(新版本功能变)。
  • I/O:串行数据引脚,数据输出或者输入都从这个引脚
  • SCLK:串行时钟引脚

2.1.2 DS1302的使用

操作 DS1302 的大致过程,就是将各种数据写入 DS1302 的寄存器,以设置 它当前的时间的格式。然后使 DS1302 开始运作,DS1302 时钟会按照设置情况 运转,再用单片机将其寄存器内的数据读出。

1.日历/时钟寄存器

DS1302 共有 12 个寄存器,其中有 7 个与日历、时钟相关,存放的数据为 BCD 码形式。

2.控制命令字

命令字简单来说就是,控制时钟寄存器的读写。比如要写入秒钟,那么操作的命令字就是0x80对应上面图中的80h;要读出秒钟,操作的命令字是0x81,对应81h。以此类推,可以控制所有时钟数据的读写。

3.时序图

上面是单字节读,下面是单字节写。根据时序图来写入数据和读出数据。

单字节写,是把单片机内存里的数据,也就是把定义的变量写入DS1302的寄存器中。有两个变量,一个是要操作的命令字(写秒钟还是写分钟…),另一个是要写入的数据。

  1. 初始化时CE=0;SCLK=0。
  2. 使能端CE=1;先SCLK=1由0->1,再SCLK=0由1->0,模拟一个写入脉冲周期。
  3. 命令字和数据写入完成后要把使能端CE=0;SCLK=0。

单字节读,分两个过程,一个是写入命令字,一个是读出数据。

  1. 初始化时CE=0;SCLK=0。
  2. 同样也是使能端CE=1;SCLK先置0(因为初始化的时候SCLK=0,这里置0是方便循环),再置1,模拟上升沿信号,写入控制字。读出数据时SCLK先置1(这里也是方便循环),再置0,模拟下降沿信号。
  3. 命令字和数据写入完成后要把使能端CE=0;SCLK=0。

单字节读写中,SCLK的设置方式不同,主要是因为时序脉冲的不同,单字节读只有15个脉冲,单字节写有16个脉冲,平均分成命令字写和数据的读/写两部分。

三、程序汇总

3.1测试文件test.c

#include #include"LCD1602.h"#include"DS1302.h"#include"Independentkey.h"#include"Timer0_Init.h"unsigned char count;unsigned char flash;void showtime(){LCD_ShowNum(1,1,DS1302_time[0],2);LCD_ShowString(1,3,"-");LCD_ShowNum(1,4,DS1302_time[1],2);LCD_ShowString(1,6,"-");LCD_ShowNum(1,7,DS1302_time[2],2);LCD_ShowNum(2,1,DS1302_time[3],2);LCD_ShowString(2,3,":");LCD_ShowNum(2,4,DS1302_time[4],2);LCD_ShowString(2,6,":");LCD_ShowNum(2,7,DS1302_time[5],2);LCD_ShowString(2,10,"week:");LCD_ShowNum(2,15,DS1302_time[6],1);}void num_flash(){if(count==0){if(flash==0){LCD_ShowString(1,1,"");}if(flash!=0){LCD_ShowNum(1,1,DS1302_time[count],2);}LCD_ShowNum(2,15,DS1302_time[6],1);}if(count==1){if(flash==0){LCD_ShowString(1,4,"");}if(flash!=0){LCD_ShowNum(1,4,DS1302_time[count],2);}LCD_ShowNum(1,1,DS1302_time[0],2);}if(count==2){if(flash==0){LCD_ShowString(1,7,"");}if(flash!=0){LCD_ShowNum(1,7,DS1302_time[count],2);}LCD_ShowNum(1,4,DS1302_time[1],2);}if(count==3){if(flash==0){LCD_ShowString(2,1,"");}if(flash!=0){LCD_ShowNum(2,1,DS1302_time[count],2);}LCD_ShowNum(1,7,DS1302_time[2],2);}if(count==4){if(flash==0){LCD_ShowString(2,4,"");}if(flash!=0){LCD_ShowNum(2,4,DS1302_time[count],2);}LCD_ShowNum(2,1,DS1302_time[3],2);}if(count==5){if(flash==0){LCD_ShowString(2,7,"");}if(flash!=0){LCD_ShowNum(2,7,DS1302_time[count],2);}LCD_ShowNum(2,4,DS1302_time[4],2);}if(count==6){if(flash==0){LCD_ShowString(2,15," ");}if(flash!=0){LCD_ShowNum(2,15,DS1302_time[count],1);}LCD_ShowNum(2,7,DS1302_time[5],2);}}//定时500ms中断void Timer0_ISR() interrupt 1{static int num=0;num++;TH0=0xfc;TL0=0x18;if(num==500)//每隔500ms闪烁一下{num=0;flash=!flash;} }void setlimit(){//年:2000-2099年if(DS1302_time[0]>99){DS1302_time[0]%=100;}if(DS1302_time[0]12){DS1302_time[1]%=13;DS1302_time[1]++;}if(DS1302_time[1]29){DS1302_time[2]%=30;DS1302_time[2]++;}if(DS1302_time[2]28){DS1302_time[2]%=29;DS1302_time[2]++;}if(DS1302_time[2]31){DS1302_time[2]%=32;DS1302_time[2]++;} if(DS1302_time[2]30){DS1302_time[2]%=31;DS1302_time[2]++;}if(DS1302_time[2]23){DS1302_time[3]%=24;}if(DS1302_time[3]59){DS1302_time[4]%=60;}if(DS1302_time[4]59){DS1302_time[5]%=60;}if(DS1302_time[5]7){DS1302_time[6]%=7;}if(DS1302_time[6]=7){count=0;}}if(keynum==2)//加一{DS1302_time[count]++;setlimit();//对DS1302_time[count]进行调整settime();}if(keynum==3)//减一{DS1302_time[count]--;setlimit();settime();}if(keynum==4)//设置时间和走时显示切换 {mode=!mode;}}if(mode==0)//走时模式{readtime();showtime();}if(mode==1)//设置时间模式{num_flash();}}}

3.2 LCD1602模块程序

3.2.1 LCD1602.h

#ifndef __LCD1602_H__#define __LCD1602_H__//用户调用函数:void LCD_Init();void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif

3.2.2 LCD1602.c

#include //引脚配置:sbit LCD_RS=P2^6;sbit LCD_RW=P2^5;sbit LCD_EN=P2^7;#define LCD_DataPort P0//函数定义:/*** @briefLCD1602延时函数,12MHz调用可延时1ms* @param无* @retval 无*/void LCD_Delay(){unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);}/*** @briefLCD1602写命令* @paramCommand 要写入的命令* @retval 无*/void LCD_WriteCommand(unsigned char Command){LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();}/*** @briefLCD1602写数据* @paramData 要写入的数据* @retval 无*/void LCD_WriteData(unsigned char Data){LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();}/*** @briefLCD1602设置光标位置* @paramLine 行位置,范围:1~2* @paramColumn 列位置,范围:1~16* @retval 无*/void LCD_SetCursor(unsigned char Line,unsigned char Column){if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}}/*** @briefLCD1602初始化函数* @param无* @retval 无*/void LCD_Init(){LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏}/*** @brief在LCD1602指定位置上显示一个字符* @paramLine 行位置,范围:1~2* @paramColumn 列位置,范围:1~16* @paramChar 要显示的字符* @retval 无*/void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char){LCD_SetCursor(Line,Column);LCD_WriteData(Char);}/*** @brief在LCD1602指定位置开始显示所给字符串* @paramLine 起始行位置,范围:1~2* @paramColumn 起始列位置,范围:1~16* @paramString 要显示的字符串* @retval 无*/void LCD_ShowString(unsigned char Line,unsigned char Column,char *String){unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}}/*** @brief返回值=X的Y次方*/int LCD_Pow(int X,int Y){unsigned char i;int Result=1;for(i=0;i0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}}/*** @brief在LCD1602指定位置开始以有符号十进制显示所给数字* @paramLine 起始行位置,范围:1~2* @paramColumn 起始列位置,范围:1~16* @paramNumber 要显示的数字,范围:-32768~32767* @paramLength 要显示数字的长度,范围:1~5* @retval 无*/void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length){unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}}/*** @brief在LCD1602指定位置开始以十六进制显示所给数字* @paramLine 起始行位置,范围:1~2* @paramColumn 起始列位置,范围:1~16* @paramNumber 要显示的数字,范围:0~0xFFFF* @paramLength 要显示数字的长度,范围:1~4* @retval 无*/void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}}

3.3 DS1302模块程序

3.3.1 DS1302.h

#ifndef __DS1302_H__#define __DS1302_H__#define Year_command 0x8C#define Day_command 0x8A#define Month_command 0x88#define Date_command 0x86#define Hour_command 0x84#define Minute_command 0x82#define Second_command 0x80#define Wp_command 0x8Eextern char DS1302_time[];void DS1302_Init();void DS1302_writebyte(unsigned char command,unsigned char byte);unsigned char DS1302_readbyte(unsigned char command);void settime();void readtime();#endif

3.3.2 DS1302.c

这里要注意的是:

  • 设置要写入的时间时要16进制转10进制
  • 读出时间时要记得8421BCD码转10进制,因为DS1302寄存器存的是数据的BCD码。
#include #include"DS1302.h"sbit DS1302_SCLK=P3^6;sbit DS1302_IO=P3^4;sbit DS1302_CE=P3^5;char DS1302_time[]={23,2,24,12,0,0,5};//2023/2/24,12:00:00,星期5/**函数名:DS1302_Init()*函数功能:初始化DS1302*输入: 无*输出: 无*/void DS1302_Init(){DS1302_CE=0;DS1302_SCLK=0;}/**函数名:DS1302_writebyte(unsigned char command,unsigned char byte)*函数功能:DS1302的单字节写*输入: command:命令字 byte:要写入的数据*输出: 无*/void DS1302_writebyte(unsigned char command,unsigned char byte){unsigned char i=0;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=byte&(0x01<<i); DS1302_SCLK=1; DS1302_SCLK=0; } DS1302_CE=0;}/**函数名:DS1302_readbyte(unsigned char command)*函数功能:DS1302的单字节读*输入: command:命令字*输出: byte:要读出的数据*/unsigned char DS1302_readbyte(unsigned char command){unsigned char i=0;unsigned char byte=0x00;DS1302_CE=1;command++;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){byte|=(0x01<<i);}}DS1302_CE=0;DS1302_IO=0;return byte;}/**函数名:settime()*函数功能:把命令字和DS1302_time[]数组中的年、月、日、时钟、分钟、秒钟、星期一起写入DS1302*输入: 无*输出: 无*/void settime(){DS1302_writebyte(Wp_command,0x00);DS1302_writebyte(Year_command,DS1302_time[0]/10*16+DS1302_time[0]%10);//16进制0x23转十进制23DS1302_writebyte(Day_command,DS1302_time[6]/10*16+DS1302_time[6]%10);DS1302_writebyte(Month_command,DS1302_time[1]/10*16+DS1302_time[1]%10);DS1302_writebyte(Date_command,DS1302_time[2]/10*16+DS1302_time[2]%10);DS1302_writebyte(Hour_command,DS1302_time[3]/10*16+DS1302_time[3]%10);DS1302_writebyte(Minute_command,DS1302_time[4]/10*16+DS1302_time[4]%10);DS1302_writebyte(Second_command,DS1302_time[5]/10*16+DS1302_time[5]%10);}/**函数名:readtime()*函数功能:把年、月、日、时钟、分钟、秒钟、星期一起读出到数组DS1302_time[]中*输入: 无*输出: 无*/void readtime(){unsigned char tmp=0;tmp=DS1302_readbyte(Year_command);//BCD码转十进制数DS1302_time[0]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Month_command);DS1302_time[1]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Date_command);DS1302_time[2]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Hour_command);DS1302_time[3]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Minute_command);DS1302_time[4]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Second_command);DS1302_time[5]=tmp/16*10+tmp%16;tmp=DS1302_readbyte(Day_command);DS1302_time[6]=tmp/16*10+tmp%16;} 

3.4 独立按键模块程序

3.4.1Independentkey.h

#ifndef __INDEPENDENTKEY_H__#define__INDEPENDENTKEY_H__unsigned char Independentkey();#endif

3.4.2Independentkey.c

#include #include"Delay.h"unsigned char Independentkey(){unsigned char keynum=0;if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);keynum=1;}if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);keynum=2;}if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);keynum=3;}if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);keynum=4;}return keynum;}

3.5 T0定时器模块

3.5.1 Timer0_Iint.h

#ifndef __TIMER0_INIT_H__#define __TIMER0_INIT_H__void Timer0_Init();#endif

3.5.2Timer0_Iint.c

#include void Timer0_Init()//定时器0,工作方式1{TMOD=0x01;TH0=0xfc;//1msTL0=0x18;EA=1;ET0=1;TR0=1;}//定时1s中断//void Timer0_ISR() interrupt 1//{//static int count=0;//count++;//TH0=0xfc;//TL0=0x18;//if(count==1000)//{//count=0;////} //}

3.6 延时函数

3.6.1 Delay.h

#ifndef __DELAY_H__#define __DELAY_H__void Delay(unsigned int Xms);#endif

3.6.2Delay.c

​void Delay(unsigned int Xms)//@12.000MHz{unsigned char i, j;while(Xms--){i = 12;j = 169;do{while (--j);} while (--i);}}​

四、效果演示

把23年2月24日12:00:00 星期5 改成23年3月1日13:55:00 星期3。

DS1302可调时钟演示