51单片机-DS1302(实时时钟+可调时钟)(可参考主页上一节内容介绍)

作者:王开心

时间:2024.9.10

目的:手撕51

main.c

复制代码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Delay.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 %= 6; //越界清零0-5
	}
	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;}
			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;}
				if(DS1302_Time[2] > 29) {DS1302_Time[2] = 1;}
			}
			else //平年
			{
				if(DS1302_Time[2] < 1) {DS1302_Time[2] = 28;}
				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(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);
				
	
	
}

void main(void)
{
	LCD_Init();
	DS1302_Init();
	Timer0_Init();
	
	
	LCD_ShowString(1,1,"  -  -");
	LCD_ShowString(2,1,"  :  :");
	


	
	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_Rountine(void)  interrupt 1
{
	static unsigned int T0Count ;  //Timer0_Rountine(void) 函数结束之后T0Count保留其原来的值
	
	TL0 = 0x66;		//设置定时初值
	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_MONTH		0x88
#define DS1302_DAY			0x8A
#define DS1302_YEAR			0x8C
#define DS1302_WP			0x8E

//时间数组,索引0~6分别为年、月、日、时、分、秒、星期
unsigned char DS1302_Time[]={19,11,16,12,59,55,6};

/**
  * @brief  DS1302初始化
  * @param  无
  * @retval 无
  */
void DS1302_Init(void)
{
	DS1302_CE=0;
	DS1302_SCLK=0;
}

/**
  * @brief  DS1302写一个字节
  * @param  Command 命令字/地址
  * @param  Data 要写入的数据
  * @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  DS1302读一个字节
  * @param  Command 命令字/地址
  * @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;	//读取后将IO设置为0,否则读出的数据会出错
	return Data;
}

/**
  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  * @param  无
  * @retval 无
  */
void DS1302_SetTime(void)
{
	DS1302_WriteByte(DS1302_WP,0x00);
	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
	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);
}

/**
  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  * @param  无
  * @retval 无
  */
void DS1302_ReadTime(void)
{
	unsigned char Temp;
	Temp=DS1302_ReadByte(DS1302_YEAR);
	DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
	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.h

复制代码
#ifndef __DS1302_H__
#define __DS1302_H__

//外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期
extern char DS1302_Time[];

void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
void TimeSet(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

Key.c

复制代码
#include <REGX52.H>
#include "Delay.h"

/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
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;
}

Key.h

复制代码
#ifndef __KEY_H__
#define __KEY_H__

unsigned char Key();

#endif

Delay.c

复制代码
void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

Delay.h

复制代码
#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

Timer0.c

复制代码
#include <REGX52.H>


//由软件配置的定时器STC-ISP

/**
* @brief 定时器初始化(51单片机软件内置配置的定时器)
  * @param 无
  * @retval 无
  */

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



//void Timer0_Init()
//{
//	/*
//	采用与或式赋值法,可以把不可寻址的位进行寻址,改变其中几位而不影响其他位
//	TMOD = TMOD & 0XF0; //低四位清零,高四位置一
//	TMOD = TMOD | 0X01;//把TMOD的最低位置1,高四位保持不变
//	如上,改变低四位而不改变高四位
//	
//	*/
//	//TMOD = 0x01;  //工作模式寄存器
//	TMOD = TMOD & 0XF0; //低四位清零,高四位置一
//	TMOD = TMOD | 0X01;//把TMOD的最低位置1,高四位保持不变
//	//控制寄存器
//	TF0  = 0;
//	TR0 = 1;
//	
//	/*定时器赋初值  定时1ms,12Mhz的晶振,1us产生一个计数脉冲,
//	而16位的计数器是0~65535个可能,也就是65536us,65536个脉冲
//	如何差生一微秒(1ms=1000us)那么从64535开始记到65535产生一个中断
//	通过配置TL0和TH0控制处置也就是把64535变成16进制TL0是低八位两个十六进制,TH0是高八位的两十六进制*/
//	
//	TL0  = 64535%56;
//	TH0  = 64535/256;
//	
//	ET0  = 1; 
//	EA = 1;
//	PT0 = 0;
//	
//}


/*定时器中断函数模板
void Timer0_Rountine(void)  interrupt 1
{
	static unsigned int T0Count ;  //Timer0_Rountine(void) 函数结束之后T0Count保留其原来的值
	
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	
	T0Count++;
	if(T0Count >= 1000)
	{
		T0Count = 0;
		P2_0 = ~P2_0;
	}	
}
*/

Timer0.h

复制代码
#ifndef __TIMER_H__
#define __TIMER_H__

void Timer0_Init();



#endif
相关推荐
猫猫的小茶馆14 小时前
【ARM】ARM的介绍
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆14 小时前
【PCB工艺】数模电及射频电路基础
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·pcb工艺
点灯小铭15 小时前
基于单片机的智能药物盒设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
梓德原15 小时前
【基础】详细分析带隙型稳压电路的工作原理
单片机·嵌入式硬件·物联网
国科安芯16 小时前
航天医疗领域AS32S601芯片的性能分析与适配性探讨
大数据·网络·人工智能·单片机·嵌入式硬件·fpga开发·性能优化
小李做物联网17 小时前
【物联网毕业设计】60.1基于单片机物联网嵌入式项目程序开发之图像厨房监测系统
stm32·单片机·嵌入式硬件·物联网
贝塔实验室18 小时前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库
@good_good_study18 小时前
STM32 ADC多通道采样实验
stm32·单片机·嵌入式硬件
Darken0318 小时前
什么是“位带”?;在STM32单片机中有什么作用?
stm32·单片机·嵌入式硬件
清风66666619 小时前
基于单片机的智能豆浆机设计(加热打浆熬煮自动控制与防干溢保护)
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业