使用51单片机实现DS1302可调时钟:代码示例

目录

一、介绍

1.DS1302介绍

2.引脚定义和应用电路

3.内部结构框图

4. 寄存器定义

 5.时序定义

6.BCD码

 二、文件内容

a.代码功能

1.代码实现功能

2.工程文件内容

b.代码详细内容

1.main.c

2.DS1302.c

3.DS1302.h

4. Delay.c

5. Delay.h

6.Key.c

7.Key.h

8.LCD1602.c

9.LCD1602.h

10.Timer0.c

11.Timer0.h


一、介绍

1.DS1302介绍

        DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时且具有闰年补偿等多种功能
        RTC(Real Time Clock):实时时钟,是一种集成电路,通常称为时钟芯片

2.引脚定义和应用电路

                   

引脚名

作用

引脚名

作用

VCC2

主电源

CE

芯片使能

VCC1

备用电池

IO

数据输入/输出

GND

电源地

SCLK

串行时钟

X1、X2

32.768KHz晶振

3.内部结构框图

4. 寄存器定义

 5.时序定义

6.BCD码

        BCD码(Binary Coded Decimal):用4位二进制数来表示1位十进制数

        例:0001 0011表示13,1000 0101表示85,0001 1010不合法

        在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法

        BCD码转十进制:DEC=BCD/16*10+ BCD%16;(2位BCD)

        十进制转BCD码: BCD=DEC/10+16+DEC%10;(2位BCD)

 二、文件内容

a.代码功能

1.代码实现功能

        显示年月日、时分秒

        按下按键1选择模式【显示/更改】,按下按键2选择要更改的数,按键3【+】,按键4【-】

2.工程文件内容

b.代码详细内容

1.main.c

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

unsigned char KeyNumber,MODE,TimeSetSelect,TimeSetFlashFlag;
unsigned char YEAR(unsigned char year,month);
void TimeShow();
void TimeSet();
	

void main(){
	LCD_Init();
	DS1302_Init();
	Timer0_Init();
	
	DS1302_SetTime();
	LCD_ShowString(1,1,"  -  -  ");
	LCD_ShowString(2,1,"  :  :  ");
	
	
	while(1){
		KeyNumber = Key();
		if(KeyNumber==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 TimeShow(){
	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(){
	//选择需要更改的位
	if(KeyNumber == 2)
	{
		TimeSetSelect++;
		TimeSetSelect%=6;
	}
	//增加更改位的数,并判断是否越界
	if(KeyNumber == 3)
	{
		DS1302_Time[TimeSetSelect]++;
		if(DS1302_Time[0]>99){DS1302_Time[0]=0;}
		if(DS1302_Time[1]>12){DS1302_Time[1]=1;}
		
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==31)
		{
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==30)
		{
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==29)
		{
			if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==28)
		{
			if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
		}
		
		if(DS1302_Time[3]>24){DS1302_Time[3]=0;}
		if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
		if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
	}
	//减小更改位的数,并判断是否越界
	if(KeyNumber == 4)
	{
		DS1302_Time[TimeSetSelect]--;
		if(DS1302_Time[0]>100){DS1302_Time[0]=99;}
		if(DS1302_Time[1]<1){DS1302_Time[1]=12;}
		
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==31)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
			if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==30)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
			if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==29)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
			if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
		}
		if(YEAR(DS1302_Time[0]+2000,DS1302_Time[1])==28)
		{
			if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
			if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
		}
		
		if(DS1302_Time[3]>100){DS1302_Time[3]=24;}
		if(DS1302_Time[4]>100){DS1302_Time[4]=59;}
		if(DS1302_Time[5]>100){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);}

	/*LCD_ShowNum(2,10,TimeSetSelect,2);
	LCD_ShowNum(2,13,TimeSetFlashFlag,2);*/
}

//闰年判断,月份天数判断
unsigned char YEAR(unsigned char year,month){
	if(year%400==0||year%4==0&&year%100!=0)
	{
		if(month==2)
			return 29;
	}
	else
	{
		if(month==1||month==3||month==5||month==7||month==8||month==10||month==12)
			return 31;
		else if(month==4||month==6||month==9||month==11)
			return 30;
		else
			return 28;
	}
	return 0;
}
//定时器
void Timer0_Routine() interrupt 1
{
	static unsigned int count0 = 0;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	count0++;
	if(count0 == 300){
		count0 = 0;
		TimeSetFlashFlag=!TimeSetFlashFlag;
	}
		
}

2.DS1302.c

#include <REGX52.H>

#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

sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;

unsigned char DS1302_Time[]={22,12,31,23,59,58,2}; 

/**
  * @brief BCD转10进制 
  * @param  
  * @retval 
  */

unsigned char BCD_10(unsigned char Num){
	unsigned char Temp;
	Temp = Num/10*16+Num%10;
	return Temp;
}

/**
  * @brief 10进制转BCD码 
  * @param  
  * @retval 
  */

unsigned char Ten_BCD(unsigned char Num){
	unsigned char Temp;
	Temp = Num/16*10+Num%16;
	return Temp;
}

/**
  * @brief  初始化DS1302
  * @param  
  * @retval 
  */

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

/**
  * @brief  写入字节
  * @param  
  * @retval 
  */

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

/**
  * @brief  读取字节
  * @param  
  * @retval 
  */

unsigned char DS1302_ReadByte(unsigned char Command){

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

/**
  * @brief  将数组内容写入
  * @param  
  * @retval 
  */

void DS1302_SetTime(){
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,BCD_10(DS1302_Time[0]));
	DS1302_WriteByte(DS1302_MONTH,BCD_10(DS1302_Time[1]));
	DS1302_WriteByte(DS1302_DATE,BCD_10(DS1302_Time[2]));
	DS1302_WriteByte(DS1302_HOUR,BCD_10(DS1302_Time[3]));
	DS1302_WriteByte(DS1302_MINUTE,BCD_10(DS1302_Time[4]));
	DS1302_WriteByte(DS1302_SECOND,BCD_10(DS1302_Time[5]));
	DS1302_WriteByte(DS1302_DAY,BCD_10(DS1302_Time[6]));
	DS1302_WriteByte(DS1302_WP,0x80);
}

/**
  * @brief  读出数组内容
  * @param  
  * @retval 
  */

void DS1302_ReadTime(){
	
	DS1302_Time[0]=Ten_BCD(DS1302_ReadByte(DS1302_YEAR));
	DS1302_Time[1]=Ten_BCD(DS1302_ReadByte(DS1302_MONTH));
	DS1302_Time[2]=Ten_BCD(DS1302_ReadByte(DS1302_DATE));
	DS1302_Time[3]=Ten_BCD(DS1302_ReadByte(DS1302_HOUR));
	DS1302_Time[4]=Ten_BCD(DS1302_ReadByte(DS1302_MINUTE));
	DS1302_Time[5]=Ten_BCD(DS1302_ReadByte(DS1302_SECOND));
	DS1302_Time[6]=Ten_BCD(DS1302_ReadByte(DS1302_DAY));
}

3.DS1302.h

#ifndef __SD1302_H__
#define __SD1302_H__

extern unsigned char DS1302_Time[];

void DS1302_Init();
void DS1302_ReadTime();
void DS1302_SetTime();

#endif

4. Delay.c


void Delay(unsigned int t)		//延时函数
{
	
	unsigned char i, j;
	while(t){
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		t--;
	}

	
}

5. Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int t);

#endif

6.Key.c

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

/**
  * @brief 获取独立按键键码 
  * @param  无
  * @retval KeyNumber 键码
  */

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

7.Key.h

#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif

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

9.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

10.Timer0.c

#include <REGX52.H>

void Timer0_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
	PT0 = 0;
}

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

11.Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);	

#endif

模块化编程

物联沃分享整理
物联沃-IOTWORD物联网 » 使用51单片机实现DS1302可调时钟:代码示例

发表评论