51单片机实验一:实现时钟功能(基础)

51单片机实验一:实现时钟功能(基础)51单片机实现时钟功能(实现时钟系统,可调功能),提供学习和参考用,欢迎学习,欢迎大佬指正!!!!!

一、实验的基本功能:

 1.实现时钟系统,在51单片机的液晶显示屏中显示:年月日、时分秒,并且随时间进行跳动 2.实现可调功能,能够调整日期和时间 

二、硬件上的准备:

1.DS1302时钟芯片的引脚

微信图片编辑_20230318142743.jpg

微信图片_20230318142252.png

   ·CE芯片使能:控制信号通信的逻辑控制端,相当于逻辑控制的“与门”,51端口P3_5

   ·IO数据输入输出:信号通信端口,发送命令和数据,51端口P3_4

   ·SCLK:时序控制端.51端口P3_6

   ·x1,x2:使DS1302能够不通过写入数据,而自动进行时间增长

2.DS1302通信的时序定义

微信图片_20230318142206.png

 ·分为读写对应的不同的时序过程,内容也分为命令和数据部分 
 ·当CE上沿,整个通信有效,为总控。当传命令时,SCLK上沿表示一位数据有效。 当写数据时,SCLK上沿表示一位数据有效。当读数据时,SCLK下沿表示一位数据有效 

3.寄存器定义

微信图片_20230318142228.png

 对应的读写功能指令,例如: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 = 34 都要判断一下,代码重复,可封装
    

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;
		}
  }
}
·初始化模块的功能,使对应的模块可以使用,调用对应的功能的模块

今天的文章51单片机实验一:实现时钟功能(基础)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16246.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注