MCU-51:单片机实时时钟

一、什么是时钟

1.1 实时时钟

real time clock,真实时间,就是所谓的xx年x月x日x时x分x秒星期x
RTC是SoC中一个内部外设,RTC有自己独立的晶振提供RTC时钟源,内部有一些寄存器用来记录时间(年月日时分秒星期)。一般情况下为了在系统关机时时间仍然在走,还会给RTC提供一个电池供电。
RTC在早期的单片机应用中是外置的,后来因为这个功能比较基础比较常用,比较高端的单片机或者SOC就将RTC直接内置了。

1.2 时序

时序:字面意思,时序就是时间顺序,实际上在通信中时序就是通信线上按照时间顺序发生的电平变化,以及这些变化对通信的意义就叫时序。
注意,时序的横轴的时间,所以对于字节是先发低位还是先发高位,可以看谁在前谁在后,在前的先发,在后的后发。

二、DS1302实时时钟

2.1 DS1302介绍

DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时,且具有闰年补偿等多种功能

在这里插入图片描述

2.2 引脚定义和应用电路

在这里插入图片描述
DS1302 与单片机之间能简单地采用同步串行的方式进行通信,仅需用到三个口线:
RST 复位
I/O 数据线
SCLK 串行时钟

Vcc2为主电源,VCC1为后备电源。
X1和X2是振荡源,外接32.768kHz晶振。
RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据传送的方法。
当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc>2.0V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。

I/O为串行数据输入输出端(双向)。SCLK为时钟输入端。
1、时钟操作可通过AM/PM 指示决定采用24 或12 小时格式;
2、DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关;
3、寄存器中存放的数据位为BCD码形式;
操作DS1302的大致过程,就是将各种数据写入DS1302的寄存器,以设置它当前的时间和格式。然后使DS1302开始运作,DS1302时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。

DS1302的寄存器样式如下,我们看到:
第7 位永远都是1;
第6 位,1表示RAM,寻址内部存储器地址;0表示CK,寻址内部寄存器;
第5位到第1位,为RAM或者寄存器的地址;
最低位,高电平表示RD:即下一步操作将要“读”;低电平表示W:即下一步操作将要“写”。
在这里插入图片描述
SEC:秒寄存器,注意具体右边内容:低四位为SEC,高的次三位为10SEC,最高位CH为DS1302 的运行标志,当CH=0时,DS1302内部时钟运行,反之CH=1时停止;

MIN:分寄存器;

HR:时寄存器,最高位为12/24 小时的格式选择位,该位为1时表示12小时格式。当设置为12小时显示格式时,第5位的高电平表示下午(PM);而当设置为24 小时格式时,第5位为具体的时间数据。

DATE:日寄存器;

MONTH:月寄存器;

DAY:周寄存器,注意一周只有7天,所以该寄存器只有低三位有效,周一到周日;

YEAR:年寄存器;

CONTROL:写保护寄存器,当该寄存器最高位WP为1时,DS1302 只读不写,所以要在往DS1302 写数据之前确保WP为0;

TRICKLE CHARGE REGISTER:涓细电流充电设置寄存器,我们知道,当DS1302掉电时,可以马上调用外部电源保护时间数据。该寄存器就是配置备用电源的充电选项的。其中高四位(4个TCS)只有在1010 的情况下才能使用充电选项;低四位的情况,与DS1302内部电路有关。

CLOCK BURST:批量读写操作设置寄存器,设置该寄存器后,可以对DS1302的各个寄存器进行连续写入。DS1302的另外一种读写方式。笔者还没用过,感兴趣的朋友可以尝试。

寄存器定义表:
在这里插入图片描述
单字节读写时序图如下:
在这里插入图片描述

三、代码演示

3.1 数字时钟

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"

unsigned char Second,Minute;

void main()
{ 
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");
	
	
	DS1302_WriteByte(0x8e,0x00);//关闭写保护
	
	DS1302_SetTime();

	
        while(1)
        {
					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);
        
         }
}

DS1302.c

#include <REGX52.H>

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_MONTHE  0x88
#define DS1302_DAY  0x8A
#define DS1302_YEAR  0x8C
#define DS1302_WP  0x8E

unsigned char DS1302_Time[]={22,12,29,16,35,55,6};

void DS1302_Init()
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	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=Data&(0x01<<i);
	DS1302_SCLK=1;
	DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

unsigned DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command=Command|0x01;
	DS1302_CE=1;
	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)
	{
		Data=Data|(0x01<<i);
	}
	}
	DS1302_CE=0;
	DS1302_IO=0;
	return Data;
}

void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
	DS1302_WriteByte(DS1302_MONTHE,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);
}

void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MONTHE);
	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.h

#ifndef __DS1302_H__
#define __DS1302_H__

extern unsigned char DS1302_Time[];

void DS1302_Init();
void DS1302_WriteByte(unsigned char Command,Data);
unsigned DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif

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');
	}
}

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 DS1302可调时钟

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "key.h"
#include "Timer0.h"

unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;

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);
}

void TimeSet(void)
{
	if(KeyNum==2)
	{
		TimeSetSelect++;
		TimeSetSelect=TimeSetSelect%6;//越界清零
	}
	if(KeyNum==3)
	{
		DS1302_Time[TimeSetSelect]++;
		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;
		}
		else
		{
			if(DS1302_Time[2]>28)
		{
			DS1302_Time[2]=1;
		}
		}
			}
		}
		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[TimeSetSelect]--;
		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;
		}
		}
		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;
		}
		}
		else if(DS1302_Time[1]==2)
		{
			if(DS1302_Time[0]%4==0)
			{
				if(DS1302_Time[2]<1)
		{
			DS1302_Time[2]=29;
		}
		else
		{
			if(DS1302_Time[2]<1)
		{
			DS1302_Time[2]=28;
		}
		}
			}
		}
		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(TimeSetSelect==0&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(1,1,"  ");
	}
	else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
	
	if(TimeSetSelect==1&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(1,4,"  ");
	}
	else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
	
	if(TimeSetSelect==2&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(1,7,"  ");
	}
	else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
	
	if(TimeSetSelect==3&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(2,1,"  ");
	}
	else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
	
	if(TimeSetSelect==4&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(2,4,"  ");
	}
	else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
	
	if(TimeSetSelect==5&&TimeSetFlashFlag==1)
	{
		LCD_ShowString(2,7,"  ");
	}
	else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
	
}

void main()
{ 
	LCD_Init();
	DS1302_Init();
	TImer0_Init();
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");
	
	
	DS1302_WriteByte(0x8e,0x00);//关闭写保护
	
	DS1302_SetTime();

	
        while(1)
        {
					KeyNum=Key();
					if(KeyNum==1)
					{
						if(MODE==0)
						{
							MODE=1;
						}
						else if(MODE==1)
						{
							MODE=0;
							DS1302_SetTime();
						}
					}
					switch(MODE)
					{
						case 0:TimeShow();break;
						case 1:TimeSet();break;
					}
					
        
         }
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count;
	TL0=0x18;//设置定时初值
	TH0=0xFC;//设置定时初值
	T0Count++;
	if(T0Count>=500)
	{
		T0Count=0;
	TimeSetFlashFlag=!TimeSetFlashFlag;
	}
}

DS1302.c

#include <REGX52.H>

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_MONTHE  0x88
#define DS1302_DAY  0x8A
#define DS1302_YEAR  0x8C
#define DS1302_WP  0x8E

char DS1302_Time[]={22,12,29,16,35,55,6};

void DS1302_Init()
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

void DS1302_WriteByte(unsigned char Command,Data)
{
	unsigned char i;
	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=Data&(0x01<<i);
	DS1302_SCLK=1;
	DS1302_SCLK=0;
	}
	DS1302_CE=0;
}

unsigned DS1302_ReadByte(unsigned char Command)
{
	unsigned char i,Data=0x00;
	Command=Command|0x01;
	DS1302_CE=1;
	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)
	{
		Data=Data|(0x01<<i);
	}
	}
	DS1302_CE=0;
	DS1302_IO=0;
	return Data;
}

void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
	DS1302_WriteByte(DS1302_MONTHE,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);
}

void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;
	Temp=DS1302_ReadByte(DS1302_MONTHE);
	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;
}

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');
	}
}

key.c

#include <REGX52.H>
#include "Delay.h"

unsigned char Key()
{
	unsigned char KeyNumber=0;
	if(P3_1==0)
	{
		Delay(20);while(P2_1==0);Delay(20);KeyNumber=1;
	}
  if(P3_0==0)
	{
		Delay(20);while(P2_0==0);Delay(20);KeyNumber=2;
	}
	if(P3_2==0)
	{
		Delay(20);while(P2_2==0);Delay(20);KeyNumber=3;
	}
	if(P3_3==0)
	{
		Delay(20);while(P2_3==0);Delay(20);KeyNumber=4;
	}
		
	return KeyNumber;
	
}

Timer0.c

#include <REGX52.H>
//简介:定时器0初始化,1毫秒@12.000MHZ
void TImer0_Init()
{
	//TMOD=0x01;//0000 0001
	TMOD=TMOD&0xF0;//把TM0D的低四位清零,高四位保持不变
	TMOD=TMOD|0x01;//把TM0D的最低位置置1,高四位保持不变
	TL0=0x18;//设置定时初值
	TH0=0xFC;//设置定时初值
	TF0=0;//清除TF0标志
	TR0=1;//计时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}


//定时器中断函数模板
//void Timer0_Routine() interrupt 1
//{
//	static unsigned int T0Count;
//	TL0=0x18;//设置定时初值
//	TH0=0xFC;//设置定时初值
//	T0Count++;
//	if(T0Count>=1000)
//	{
//		T0Count=0;
//	P2_0=~P2_0;
//	}
//}

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void TImer0_Init(void);

#endif

key.h

#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif

DS1302.h

#ifndef __DS1302_H__
#define __DS1302_H__

extern  char DS1302_Time[];

void DS1302_Init();
void DS1302_WriteByte(unsigned char Command,Data);
unsigned DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);

#endif

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

注意:一定要看

千万不要直接复制代码,因为是模块化编写,模块的代码没有往上复制,大家一定要自己动手跟着教学视屏自己写!!!!!

  • 4
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
单片机简介 一台能够工作的计算机要有这样几个部件构成:CPU(进行运算、控制)、 RAM (数据存储)、ROM(程序存储)、输入/输出设备(例如:串行口、并行输出口 等)。在个人计算机上这些部件被分成若干块芯片,安装一个称之为主板的印刷 线路板上。而在单片机中,这些部件全部被做到一块集成电路芯片中,所以就称 为单片机(也称微控制器 MCU),而且有一些单片机中除了上述部份外,还集成 了其它部份如 A/D,D/A 等。体积不大,一般用 40 脚封装, 当然功能多一些 单片机也有引脚比较多的, 如 68 引脚, 功能少的只有 10 多个或 20 多个引 脚,有的甚至只 8 只引脚。 1.2 51 单片机简介 51 单片机是对所有兼容 Intel 8031 指令系统的单片机的统称。该系列单片 机的始祖是 Intel 的 8004 单片机,后来随着 Flash rom 技术的发展,8004 单片 机取得了长足的进展,成为应用最广泛的 8 位单片机之一,其代表型号是 ATMEL 公司的 AT89 系列,它广泛应用于工业测控系统之中。很多公司都有 51 系列的兼 容机型推出,今后很长的一段时间内将占有大量市场。51 单片机是基础入门的 一个单片机,还是应用最广泛的一种。需要注意的是 51 系列的单片机一般不具 备自编程能力。 80C51 是 MCS-51 系列中的一个典型品种;其它厂商以 8051 为基核开发出的 CMOS 工艺单片机产品统称为 80C51 系列。 当前常用的 80C51 系列单片机主要产 品有: Intel(英特尔)的:i80C31、i80C51、i87C51,i80C32、i80C52、i87C52 等; ATMEL(艾德梅尔)的:AT89C51、AT89C52、AT89C2051,AT89S51(RC),AT89S52 (RC)等; Philips(飞利浦)、华邦、Dallas(达拉斯)、Siemens(西门子)等公司的许多产品
单片机课程设计报告 设计课题:基于MCS-51单片机的时钟设计 专业班级: 小组成员: 指导教师: 设计时间: 目录 设计任务与要求 3 1.基本设计要求 3 2.选作项目 4 相关硬件结构概述 4 1.硬件结构原理 4 2.相关电路图: 7 软件结构概述 8 程序清单 10 程序调试 17 课程总结 17 参考文献 19 设计任务与要求 1.基本设计要求 (1)在ZY15MCU12BD型综合单片机实验箱的硬件结构上编写软件完成设计。也可以在其它 MCS—51单片机硬件板上完成,或自行设计硬件并制做完成。 (2)程序的首地址应使目标机可以直接运行,即从0000H开始。在主程序的开始部分必须 设置一个合适的栈底。程序放置的地址须连续且靠前,不要在中间留下大量的空闲地址 ,以使目标机可以使用较少的硬件资源。 (3)6位LED数码管从左到右分别显示时、分、秒(各占用2位),采用24小时标准计时制 。开始计时时为000000,到235959后又变成000000。 (4)在键盘上选定3个键分别作为小时、分、秒的调校键。每按一次键,对应的显示值便 加1。 分、秒加到59后再按键即变为00;小时加到23后再按键即变为00。在调校时均不向上一 单位进位 (例如分加到59后变为00,但小时不发生改变)。 (5) 软件设计必须使用MCS- 51片内定时器,采用定时中断结构,不得使用软件延时法。 (6)上机调试通过。 2.选作项目 有能力的同学,在做完以上设计后,可选作此项设计内容。 (1)另设三个键,分别作小时、分、秒的减1调校。 (2)在以上设计的基础上,修改程序制作一个电子秒表。分、秒各占用2位显示,1/10秒 、1/100秒各占用1位显示。设定二个键分别作启动/停止、清零。 (3)在做完(2)后,将时钟与秒表合二为一,并且在同时使用时互不影响。即可在时钟与 秒表之间任意切换,而不影响走时、计秒。 相关硬件结构概述 硬件结构原理 该课程设计基于STC89C52单片机为核心完成的,外加少量其他辅助器件,硬件结构比 较简单。 由于本电路结构比较简单,单片机I/O口完全够用,故我们将6位8段LED直接和单片机 相连。硬件结构中的6位8段LED显示器,采用动态方式驱动。我们采用的是共阳电路,一 位LED显示器显示内容一段时间,然后下一位LED显示器显示内容一段时间……,周而复始 。只要刷新频率不小于50Hz,就可以获得清晰稳定的显示效果。 为了增加和位选相连接的I/O口的驱动能力,直接接上上拉电阻。在本设计中,只使用了 6个按键。复位键,模式设定键、2个上下调节键。 1、定时器/计数器 定时器/计数器简称定时器,其作用主要用于生产各种时间间隔、记录外部脉冲与事件的 数量等,是微机中最常用、最基本的部件之一。89C52单片机有2个16位的定时器/计数器 0(T0)和定时器1(T1).T0由2个定时寄存器TH0和Th0构成,T1则由Th1和TL1构成,他 们分别映射在特殊功能寄存器中,从而可以通过对特殊功能寄存器的读写来实现对这两 个定时器的操作。作为定时器时,每个机器周期定时寄存器自动加1,所以定时器也可以 看做是计量机器周期的计数器。由于每个机器周期为12个时钟震荡周期,所以定时器的 分辨率是时钟震荡频率的1/12.作为计数器时,只要在单片机外部引脚T0(T1)有从1到 0电平的负跳变,计数器自动加1.计数的最高频率一般为振荡频率的1/24,例如,选用1 2MHz晶振,则最高计数频率为0.5MHz。 2、中断系统 当中央处理器CPU正在执行某程序时,由于某种原因,外界向CPU发出暂停目前工作去 处理更重要的事情的请求,程序被打断;CPU响应该请求并转入相应的处理程序,处理程 序完成以后,再返回原来程序被打断的位置,继续原来的工作,这一过程称为中断。实 现中断功能的部件称为中断系统,或称为中断控制逻辑系统。 89C52单片机的中断系统简单实用。其基本特点是:有6个固定的可屏蔽的中断源,4 个在片内,2个在片外,他们在程序存储器中各有固定的中断入口地址,由此进入中断服 务程序;6个中断源由两级有先级,可形成中断嵌套;2个特殊功能寄存器用于中断控制 和条件设置的编程。 若某个中断源通过编程设置,处于被打开的状态,并满足中断响应的条件,而且满足如 下3个条件时,单片机响应此中断: 1)当前正在执行的那条指令已被执行完。 2)当前未响应同级或高级中断。 3不在操作IE、IP中断控制寄存器或执行RETI中断指令。 在正常情况下,从中断请求信号有效开始,到中断得到响应,通常需要3个机器周期到8 个机器周期。 中断得到响应后,自动清除中断请求标志(对串行I\O端口的中断标志,要用软件清除) ,将断点即程序计数器之值(PC)压入堆栈(以备恢复用);然后把相应的中断入口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello xiǎo lěi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值