51单片机实验一:实现时钟功能(基础)
一、试验的基本功用:
1.完成时钟体系,在51单片机的液晶显现屏中显现:年月日、时分秒,并且随时刻进行跳动
2.完成可调功用,能够调整日期和时刻
二、硬件上的预备:
1.DS1302时钟芯片的引脚
CE芯片使能:操控信号通讯的逻辑操控端,相当于逻辑操控的“与门”,51端口P3_5
IO数据输入输出:信号通讯端口,发送指令和数据,51端口P3_4
SCLK:时序操控端.51端口P3_6
x1,x2:使DS1302能够不经过写入数据,而自动进行时刻增加
2.DS1302通讯的时序定义
分为读写对应的不同的时序进程,内容也分为指令和数据部分
当CE上沿,整个通讯有用,为总控。当传指令时,SCLK上沿表明一位数据有用。
当写数据时,SCLK上沿表明一位数据有用。当读数据时,SCLK下沿表明一位数据有用
3.寄存器定义
对应的读写功用指令,例如:0x81为读取DS1302的存储空间的内容等。可读写年月日,时分秒
三、代码完成的功用函数(当头文件)
DS1302S触及的函数
1.void DS1302_Init(void) 调控总操控端,完成CE上沿
2.void DS1302_WriteByte(unsigned char Command,Data) 完成写入数据到寄存器中(包括指令和数据)
3.unsigned char DS1302_ReadByte(unsigned char Command)完成读入数据到电脑中(包括指令和数据)留意的数据是下沿有用
4.void DS1302_SetTime(void)完成设置DS1302初始时刻,首要调用存储数组的值
5.void DS1302_ReadTime(void)完成读取DS1302寄存器的时刻
6.void TimeShow(void)完成显现时刻到51液晶显现屏上
7.void TimeSet(unsigned char KeyNum)完成设置DS1302的值,首要经过外部设备进行改动数组的值
8.void TimeSet(unsigned char KeyNum);完成调控中心数组的数据,完成时钟调控功用
9.void Timer0_Routine() interrupt 1调用守时器的中断代码,完成调空的数据守时闪耀
由于触及LCD1602液晶显现和独立按键、守时器,所以调用LCD1602.h和keyboad.h,Timer0.h等头文件
四、代码演示和解说说明
1.端口的调用和地址的重置命名
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 0x8E //写保护状况
将51端口和写入指令的指令进行重置命名,完成愈加清晰化及调用愈加便利,提高代码的阅览性
2.变量定义
unsigned char DS1302_Time[] = {23,3,21,10,29,30,2};
unsigned char TimeSelect,TimeSetFlashFlag;
DS1302_Time[]:中心存储变量,将从DS1302的数据读出的贮存空间,显现数据将其读出即可
TimeSelect:用于调控时钟数据,即更改DS1302_Time[]的数据
TimeSetFlashFlag:调控的数字进行闪耀的操控端
3.配置总操控端CE,完成初始化
void DS1302_Init(void)
{
DS1302_CE = 0;
DS1302_SCLK = 0;
}
将使能端先置零,为使能端上沿做预备,也防止了CE开端为1的或许
4.写入数据到DS1302寄存器中
void DS1302_WriteByte(unsigned char Command,Data)
{
unsigned char i;
DS1302_CE = 1; //使能端置1,表时序进程有用
//写入指令指令
for (i = 0;i < 8;i++)
{
DS1302_IO = Command & (0x01<<i);//传入一位比特位放入IO,0x01与Command进行位运算,Command对应的位为1就是1,0就是0
DS1302_SCLK = 1; //开端SCLK就是0,置1代表数据有用,再置0康复状况
DS1302_SCLK = 0;
}
//写入数据
for(i = 0;i<8;i++)
{
DS1302_IO = Data & (0x01<<i);
DS1302_SCLK = 1;
DS1302_SCLK = 0;
}
DS1302_CE = 0;
}
写入数据到DS1302存储器件中,写入指令指令和数据都是向上沿有用
程序将信号输入到I0口上,DS1302接纳数据无需关怀
指令和数据是分隔的传入IO口
写到哪里和读到哪里,是由写指令操控
5.读入数据到电脑中
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i ,Data = 0x00;
//第0位表明读写,0为写,1位读
Command |= 0x01;//设置读的功用,能运用define
DS1302_CE = 1;
for(i = 0;i < 8;i++)
{
DS1302_IO = Command & (0x01<<i);
DS1302_SCLK = 0; //与上述相同,改动0/1方位,但作用不变,使读数据的下沿有用,不发生抵触
DS1302_SCLK = 1;
}
//读出数据
for(i = 0;i < 8;i++)
{
DS1302_SCLK = 1;
DS1302_SCLK = 0;
if(DS1302_IO) {Data |= (0x01<<i);} //当DS1302_IO为1时,Data对应的方方位1,当DS1302_IO为0时,Data为0
}
DS1302_CE = 0;
DS1302_IO = 0; //能处理数据不稳定的问题
return Data;
}
读出数据,包括写入指令指令(上沿有用),读出数据(下沿有用)
无需关怀DS1302怎么宣布数据的,当指令写入读数据时,DS1302开端写入数据,只需接纳即可
写到哪里和读到哪里,是由写指令操控
6.设置DS1302初始时刻,首要调用存储数组的值
void DS1302_SetTime(void)
{ //十进制转BCD码后写入
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);
}
调用前面的DS1302_WriteByte(),将数组的数据全部写入DS1302中
DS1302接纳的数据为16进制数据,所以要将十进制数据转成十六进制数据
7.完成读取DS1302寄存器的时刻
void DS1302_ReadTime(void)
{ //BCD码转十进制后读取
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的数据读到数组里,调用DS1302_ReadTime()
数组采用的十进制输入,是因为LCD1602调用的是显现十进制数,所以将读出来十六进制转十进制
8.完成显现时刻到51液晶显现屏上
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);//显现秒
}
调用LCD1602功用显现中心数组的数据,即完成显现时钟的功用部分
9.完成调控中心数组的数据,完成时钟调控功用
void TimeSet(unsigned char KeyNum)
{
//选择数组方位
if(KeyNum == 2)
{
TimeSelect++;
TimeSelect %= 6;
}
//加对应的值
if(KeyNum == 3)
{
DS1302_Time[TimeSelect]++;
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;}//闰年2月
}
else
{
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
}
}
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[TimeSelect]--;
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;}//闰年2月
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
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(TimeSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");}
else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
if(TimeSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
if(TimeSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
if(TimeSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1," ");}
else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
if(TimeSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4," ");}
else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
if(TimeSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7," ");}
else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
}
经过独立按键相应,来完成对应的功用
例如: KeyNum = 2对应的TimeSelect就会加1,对应LCD1602的数据闪耀方位改动
KeyNum = 3 和 KeyNum = 4对应的数组的DS1302_Time[TimeSelect]的数值加1或减1
KeyNum = 1 改动时钟形式:调控和不调控
处理时刻进位的问题(调控的进位):需要留意的是月份分大月和小月,大月31进,小月30进。
年又份润年和平年,润年2月29天,平年28。
再每次加1之,判别是否越界.KeyNum = 3 或 4 都要判别一下,代码重复,可封装
10.完成调空的数据守时闪耀
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18;
TH0 = 0xFC;
T0Count++;
if(T0Count>=500)
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag;
}
}
完成守时闪耀,守时可满足,规守时刻掉用中断函数,闪耀接纳空白和 显现交替,所以再判别越界结束,
履行这部分代码,先将LCD1602显现空白,再显现数据即可
五、mian函数运行
#include "DS1302.h"
#include "LCD1602.h"
#include "Timer0.h"
#include "Keyboard.h"
#include <REGX52.H>
unsigned char KeyNum,Mode;
void main()
{
LCD_Init();
DS1302_Init();
Timer0_Init();
LCD_ShowString(1,1," - - ");//?????????
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
while(1)
{
KeyNum = SmallKey();
if(KeyNum == 1) //????,????
{
if(Mode == 0){Mode = 1;}
else if (Mode == 1){Mode = 0;}
}
switch(Mode)
{
case 0:TimeShow();break;
case 1:TimeSet(KeyNum);break;
}
}
}
初始化模块的功用,使对应的模块能够使用,调用对应的功用的模块