51 microcontroller - DS1302 adjustable clock

Table of contents

1. Function introduction

2. Introduction to main modules

2.1 DS1302 module

2.1.1 Introduction to DS1302 chip

2.1.2 Use of DS1302

3. Summary of procedures

3.1 Test file test.c

3.2 LCD1602 module program

3.2.1 LCD1602.h

3.2.2 LCD1602.c

3.3 DS1302 module program

3.3.1 DS1302.h

3.3.2 DS1302.c

3.4 Independent button module program

3.4.1 Independentkey.h

3.4.2 Independentkey.c

3.5 T0 timer module

3.5.1 Timer0_Iint.h

3.5.2 Timer0_Iint.c

3.6 Delay function

3.6.1 Delay.h

3.6.2 Delay.c

4. Effect Demonstration


1. Function introduction

     The clock is displayed on the LCD1602, in which the year, month, day and clock, minutes, seconds and day of the week are all adjustable.

2. Introduction to main modules

2.1 DS1302 module

Why use the DS1302 chip for timing? Isn't it possible to use a timer?

  • First of all, using a separate chip to complete the timing function is more accurate than using a timer interrupt. DS1302 has a built-in separate crystal oscillator with a frequency of 32.769KHz, 32769=2^15. After 15 times of frequency division by 2, a frequency of 1Hz is obtained, which means that each count is 1 second.
  • Furthermore, it does not affect the performance of the CPU. You only need to write the set time into the register of DS1302 to start timing. The same is true for obtaining the time. You only need to read the time data into the memory of the microcontroller through the I/O port of DS1302.

2.1.1 Introduction to DS1302 chip

       The three pins of the CPU of the microcontroller are respectively connected to the three ports of the enable terminal CE, the data input/output terminal I/O, and the serial clock terminal SCLK of the DS1302.

  • VCC2: Main power pin
  • VCC1: backup power supply
  • X1, X2: DS1302 external crystal oscillator pins, usually require an external 32.768K crystal oscillator
  • GND: power ground
  • CE: Enable pin, also a reset pin (the function changes in the new version).
  • I/O: Serial data pin, data output or input is from this pin
  • SCLK: serial clock pin 

9cef50459ec54016b9a23dc71740500c.png

2.1.2 Use of DS1302

        The general process of operating DS1302 is to write various data into the registers of DS1302 to set its current time format. Then let DS1302 start operating, the DS1302 clock will run according to the setting, and then use the microcontroller to read the data in its register.

1. Calendar/clock register

        DS1302 has a total of 12 registers, 7 of which are related to calendar and clock, and the data stored are in BCD code form.

2.Control command words

        Simply put, the command word controls the reading and writing of the clock register. For example, if you want to write seconds, the command word of the operation is 0x80, which corresponds to 80h in the figure above; if you want to read the seconds, the command word of the operation is 0x81, which corresponds to 81h. By analogy, the reading and writing of all clock data can be controlled.

 3. Timing diagram

        The top is a single-byte read, and the bottom is a single-byte write. Write data and read data according to the timing diagram.

        Single-byte writing is to write the data in the microcontroller memory, that is, to write the defined variables into the register of DS1302. There are two variables, one is the command word to be operated (write seconds or minutes...), and the other is the data to be written.

  1. CE=0; SCLK=0 during initialization.
  2. Enable terminal CE=1; first SCLK=1 from 0->1, then SCLK=0 from 1->0, simulating a write pulse cycle.
  3. After the command word and data writing is completed, the enable terminal CE=0; SCLK=0.

        Single-byte reading is divided into two processes, one is to write the command word, and the other is to read the data.

  1. CE=0; SCLK=0 during initialization.
  2. The same is true for the enable terminal CE=1; SCLK is first set to 0 (because SCLK=0 during initialization, setting 0 here is convenient for looping), and then set to 1 to simulate the rising edge signal and write the control word. When reading data, SCLK is first set to 1 (this is also convenient for looping), and then set to 0 to simulate the falling edge signal.
  3. After the command word and data writing is completed, the enable terminal CE=0; SCLK=0.

        In single-byte reading and writing, SCLK is set in different ways, mainly because of the difference in timing pulses. There are only 15 pulses for single-byte reading and 16 pulses for single-byte writing. They are evenly divided into command word writing and data reading/writing. Two parts.

3. Summary of procedures

3.1 Test file 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 module program

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 module program

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

What to note here is:

  • When setting the time to be written, convert hexadecimal to decimal .
  • When reading the time, remember to convert the 8421BCD code to decimal , because the DS1302 register stores the BCD code of the data.
#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 Independent button module program

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 timer module

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 Delay function

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

​

4. Effect Demonstration

        Change Friday, February 24, 23, 12:00:00 to Wednesday, March 1, 23, 13:55:00.

DS1302 Adjustable Clock Demonstration

Guess you like

Origin blog.csdn.net/ssssshhbh/article/details/129170766