目录
一、功能介绍
二、主要模块介绍
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.1 Independentkey.h
3.4.2 Independentkey.c
3.5 T0定时器模块
3.5.1 Timer0_Iint.h
3.5.2 Timer0_Iint.c
3.6 延时函数
3.6.1 Delay.h
3.6.2 Delay.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的寄存器中。有两个变量,一个是要操作的命令字(写秒钟还是写分钟...),另一个是要写入的数据。
- 初始化时CE=0;SCLK=0。
- 使能端CE=1;先SCLK=1由0->1,再SCLK=0由1->0,模拟一个写入脉冲周期。
- 命令字和数据写入完成后要把使能端CE=0;SCLK=0。
单字节读,分两个过程,一个是写入命令字,一个是读出数据。
- 初始化时CE=0;SCLK=0。
- 同样也是使能端CE=1;SCLK先置0(因为初始化的时候SCLK=0,这里置0是方便循环),再置1,模拟上升沿信号,写入控制字。读出数据时SCLK先置1(这里也是方便循环),再置0,模拟下降沿信号。
- 命令字和数据写入完成后要把使能端CE=0;SCLK=0。
单字节读写中,SCLK的设置方式不同,主要是因为时序脉冲的不同,单字节读只有15个脉冲,单字节写有16个脉冲,平均分成命令字写和数据的读/写两部分。
三、程序汇总
3.1测试文件test.c
#include <REGX52.H>
#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]<0){DS1302_time[0]=99;}
//月:1-12月
if(DS1302_time[1]>12){DS1302_time[1]%=13;DS1302_time[1]++;}
if(DS1302_time[1]<1){DS1302_time[1]=12;}
//日:分闰年二月,分大小月
if(DS1302_time[0]%4==0)//闰年二月 1-29号
{
if(DS1302_time[1]==2)
{
if(DS1302_time[2]>29){DS1302_time[2]%=30;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=29;}
}
}
if(DS1302_time[0]%4!=0)//不是闰年的二月 1-28号
{
if(DS1302_time[1]==2)
{
if(DS1302_time[2]>28){DS1302_time[2]%=29;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=28;}
}
}
//大月: 1,3,5,7,8,10,12 1-31号
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]%=32;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=31;}
}
//小月:4,6,9,11 1-30号
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]%=31;DS1302_time[2]++;}
if(DS1302_time[2]<1){DS1302_time[2]=30;}
}
//时钟 0-23
if(DS1302_time[3]>23){DS1302_time[3]%=24;}
if(DS1302_time[3]<0){DS1302_time[3]=23;}
//分钟 0-59
if(DS1302_time[4]>59){DS1302_time[4]%=60;}
if(DS1302_time[4]<0){DS1302_time[4]=59;}
//秒钟 0-59
if(DS1302_time[5]>59){DS1302_time[5]%=60;}
if(DS1302_time[5]<0){DS1302_time[5]=59;}
//星期 1-7
if(DS1302_time[6]>7){DS1302_time[6]%=7;}
if(DS1302_time[6]<1){DS1302_time[6]=7;}
}
int main()
{
unsigned char keynum=0;
unsigned char mode=0;
LCD_Init();
DS1302_Init();
settime();
Timer0_Init();
while(1)
{
keynum=Independentkey();
if(keynum!=0)
{
if(keynum==1)//选择位
{
count++;
if(count>=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 <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @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();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @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();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围: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));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @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;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围: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指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围: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(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;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 0x8E
extern 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 <REGX52.H>
#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转十进制23
DS1302_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.1 Independentkey.h
#ifndef __INDEPENDENTKEY_H__
#define __INDEPENDENTKEY_H__
unsigned char Independentkey();
#endif
3.4.2 Independentkey.c
#include <REGX51.H>
#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.2 Timer0_Iint.c
#include <REGX51.H>
void Timer0_Init()//定时器0,工作方式1
{
TMOD=0x01;
TH0=0xfc;//1ms
TL0=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.2 Delay.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。文章来源:https://www.toymoban.com/news/detail-431848.html
DS1302可调时钟演示文章来源地址https://www.toymoban.com/news/detail-431848.html
到了这里,关于51单片机——DS1302可调时钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!