DS1302和LCD1602整合长短按键电子钟

以下代码来自学习江科大自化协和手把手教你学51单片机后的整合

DS1302和LCD1602整合长短按键电子钟_第1张图片

DS1302和LCD1602整合长短按键电子钟_第2张图片 

DS1302和LCD1602整合长短按键电子钟_第3张图片 

DS1302和LCD1602整合长短按键电子钟_第4张图片 

DS1302和LCD1602整合长短按键电子钟_第5张图片 

//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


//LCD1602.c

#include 

//引脚配置:
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;i0;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');
	}
}

//***********************************************************************************//********************
//DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__

char DS1302_data[];
extern unsigned char TimeSetFlashFlag;

void Time_Show();
void LCD_Show();
void LCD_Show_Button();

void DS1302_Init();
void DS1302_WriteByte(unsigned char command,unsigned char Data);
unsigned char DS1302_ReadByte(unsigned char command);
void DS1302_SetTime();
void DS1302_ReadTime();
	
#endif


//DS1302.c
#include 
#include "LCD1602.h"
#include "Key.h"

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

char DS1302_data[] = {21,12,31,23,59,56,2}; //定义年,月,日,时,分,秒,星期

unsigned char TimeSetFlashFlag;	//时间设置闪烁标志位。

#define DS1302_sec 0x80     //写地址最后一位为零,如果是读就让它|0x01,在DS1302_ReadByte()中已经置1了,所以不用再定义0x81
#define DS1302_min 0x82
#define DS1302_hour 0x84
#define DS1302_date 0x86    //日期
#define DS1302_mon 0x88
#define DS1302_day 0x8A     //星期
#define DS1302_year 0x8C
#define DS1302_wp 0x8E      //写保护

/************BCD码转换******************/
//DEC=BCD/16*10+BCD%16
//BCD=DEC/10*16+DEC%10

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

void DS1302_WriteByte(unsigned char command,unsigned char Data)
{
//	DS1302_CE = 1;
//	
//	DS1302_IO = command&0x01;
//	DS1302_SCLK = 1;   //一个上升延写入,
//	DS1302_SCLK = 0;   //随后又下降,为下一个上升延准备.
//	
//	DS1302_IO = command&0x02;
//	DS1302_SCLK = 1;   //一个上升延写入,
//	DS1302_SCLK = 0;   //随后又下降,为下一个上升延准备.
//	.                  查DS1302数据手册得知上升下降最小为50ns,结合单片机
//	.                  执行一条语句的时间为微秒级,所以中间不用延迟.
//	.优化如下:
	unsigned char i;
	DS1302_CE = 1;
	for(i=0;i<8;i++)
	{
		DS1302_IO = command&(0x01<
#include "DS1302.H"

unsigned char MODE = 0;
unsigned char index = 0;
unsigned char KeySta[4][4] = {{1,1,1,1},		
							   {1,1,1,1},
							   {1,1,1,1},
							   {1,1,1,1}
							  };

unsigned char code KeyCodeMap[4][4]={
	{0x31,0x32,0x33,0x26}, //数字键1,数字键2,数字键3,向上键
	{0x34,0x35,0x36,0x25}, //数字键4,数字键5,数字键6,向左键
	{0x37,0x38,0x39,0x28}, //数字键7,数字键8,数字键9,向下键
	{0x30,0x1B,0x0D,0x27}	 //数字键0,Esc键,回车键,向右键
															};
unsigned long pdata KeyDownTime[4][4] = {
{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}
};
void KeyAction(unsigned char keycode)
{

	if((keycode>=0x30)&&(keycode<=0x39))    //如果是数字0~9
	{

	}
	else if(keycode==0x25)		//向左 ,设为切换模式	
	{
		MODE = !MODE;
	}
	else if(keycode==0x26)		//如果是向上键 ,加	
	{
		DS1302_data[index]++;
		if(DS1302_data[0]>99){DS1302_data[0]=0;}//年越界判断
		if(DS1302_data[1]>12){DS1302_data[1]=1;}//月越界判断
		if( DS1302_data[1]==1 || DS1302_data[1]==3 || DS1302_data[1]==5 || DS1302_data[1]==7 || 
			DS1302_data[1]==8 || DS1302_data[1]==10 || DS1302_data[1]==12)//日越界判断
		{
			if(DS1302_data[2]>31){DS1302_data[2]=1;}//大月
		}
		else if(DS1302_data[1]==4 || DS1302_data[1]==6 || DS1302_data[1]==9 || DS1302_data[1]==11)
		{
			if(DS1302_data[2]>30){DS1302_data[2]=1;}//小月
		}
		else if(DS1302_data[1]==2)
		{
			if(DS1302_data[0]%4==0)
			{
				if(DS1302_data[2]>29){DS1302_data[2]=1;}//闰年2月
			}
			else
			{
				if(DS1302_data[2]>28){DS1302_data[2]=1;}//平年2月
			}
		}
		if(DS1302_data[3]>23){DS1302_data[3]=0;}//时越界判断
		if(DS1302_data[4]>59){DS1302_data[4]=0;}//分越界判断
		if(DS1302_data[5]>59){DS1302_data[5]=0;}//秒越界判断
	}
	else if(keycode==0x27)		//如果是向右键 ,切换时间数组索引	
	{
		index++;
		index %= 6;
	}
	else if(keycode==0x28)//如果是向下键,减
	{
		DS1302_data[index]--;
		if(DS1302_data[0]<0){DS1302_data[0]=99;}//年越界判断
		if(DS1302_data[1]<1){DS1302_data[1]=12;}//月越界判断
		if( DS1302_data[1]==1 || DS1302_data[1]==3 || DS1302_data[1]==5 || DS1302_data[1]==7 || 
			DS1302_data[1]==8 || DS1302_data[1]==10 || DS1302_data[1]==12)//日越界判断
		{
			if(DS1302_data[2]<1){DS1302_data[2]=31;}//大月
			if(DS1302_data[2]>31){DS1302_data[2]=1;}
		}
		else if(DS1302_data[1]==4 || DS1302_data[1]==6 || DS1302_data[1]==9 || DS1302_data[1]==11)
		{
			if(DS1302_data[2]<1){DS1302_data[2]=30;}//小月
			if(DS1302_data[2]>30){DS1302_data[2]=1;}
		}
		else if(DS1302_data[1]==2)
		{
			if(DS1302_data[0]%4==0)
			{
				if(DS1302_data[2]<1){DS1302_data[2]=29;}//闰年2月
				if(DS1302_data[2]>29){DS1302_data[2]=1;}
			}
			else
			{
				if(DS1302_data[2]<1){DS1302_data[2]=28;}//平年2月
				if(DS1302_data[2]>28){DS1302_data[2]=1;}
			}
		}
		if(DS1302_data[3]<0){DS1302_data[3]=23;}//时越界判断
		if(DS1302_data[4]<0){DS1302_data[4]=59;}//分越界判断
		if(DS1302_data[5]<0){DS1302_data[5]=59;}//秒越界判断
	}	
	else if(keycode==0x1B)			//如果是回车键
	{
	
	}
}

void KeyDriver()
{
	static unsigned char backup[4][4] = { {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};
	unsigned char i,j;
	static unsigned long pdata TimeAdd[4][4] = {
	{1000,1000,1000,1000},{1000,1000,1000,1000},{1000,1000,1000,1000},{1000,1000,1000,1000}
	};
	for(i=0;i<4;i++)
	{
		for(j=0;j<4;j++)
		{
			if(backup[i][j] != KeySta[i][j])
			{
				if(backup[i][j] == 0)
				{
					KeyAction(KeyCodeMap[i][j]);
				}
				backup[i][j] = KeySta[i][j];
			}
			if(KeyDownTime[i][j] > 0)
			{				//LCD_ShowNum(1,5,KeyDownTime[i][j],5);
				if(KeyDownTime[i][j] >= TimeAdd[i][j])
				{			//LCD_ShowNum(2,8,KeyDownTime[i][j],5);
					KeyAction(KeyCodeMap[i][j]);
					TimeAdd[i][j] += 80;          //设置长按时跳动的速度,越小越快
				}
			}
			else
				TimeAdd[i][j] = 1000;

		}
	}														  
}

void KeyScan()
{
	static unsigned  char keyout = 0;
	unsigned char i = 0;
	static unsigned char keybuf[4][4] = {{0xFF,0xFF,0xFF,0xFF},
										   {0xFF,0xFF,0xFF,0xFF},
	                                       {0xFF,0xFF,0xFF,0xFF},
										   {0xFF,0xFF,0xFF,0xFF}
                                          };
	keybuf[keyout][0] = (keybuf[keyout][0]<<1) | P14; //所以每行要左移8次,4行就左移32次,32毫秒就能跳出抖动
	keybuf[keyout][1] = (keybuf[keyout][1]<<1) | P15;
	keybuf[keyout][2] = (keybuf[keyout][2]<<1) | P16;
	keybuf[keyout][3] = (keybuf[keyout][3]<<1) | P17;
	
	for(i=0;i<4;i++)
	{
		if((keybuf[keyout][i] & 0xFF) == 0x00) //0x00表示后8位为0就判断为0,因为初始值
		{                                      //为0xFF,所以至少要进入8次中断,左移8次
			KeySta[keyout][i] = 0;             //与0xFF才可能等于0x00
			KeyDownTime[keyout][i] += 4;       //所以这这里要加上4毫秒。
		}
		else if((keybuf[keyout][i] & 0xFF) == 0xFF) //0xFF表示8位为1就判断为1
		{
			KeySta[keyout][i] = 1;
			KeyDownTime[keyout][i] = 0;
		}
	}	
	keyout++;
//	if(keyout>=3)
//	{
//		keyout = 0;
//	}
	keyout &= 0x03; 
	switch(keyout)
	{
		//case 0: P00=1;P01=1;P02=1;P03=0;break;
		//case 1: P00=1;P01=1;P02=0;P03=1;break;
		//case 2: P00=1;P01=0;P02=1;P03=1;break;
		//case 3: P00=0;P01=1;P02=1;P03=1;break;
		//优化后如下:
		case 0: P10=1;P13=0; break;
		case 1: P13=1;P12=0; break;
		case 2: P12=1;P11=0; break;
		case 3: P11=1;P10=0; break;		
		default: break;
	}
}	

//***********************************************************************************//***********

mani.c 
#include 
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"

void main()
{	
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值,1毫秒@12.000MHz
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	EA = 1;
	ET0 = 1;
	
	LCD_Init();
	DS1302_Init();
	LCD_ShowString(1,1,"  -  -");
	LCD_ShowString(2,1,"  :  :");
	DS1302_SetTime(); //设置时间,用数组里面的数值设置
	while(1)
	{
		KeyDriver();
		switch(MODE)
		{
			case 0 : Time_Show(); break;
			case 1 : DS1302_SetTime();LCD_Show_Button(); break;                      
		}
	
	}
}

void InterruptTimer0() interrupt 1
{		
	static unsigned int T0_Count = 0;	
	TL0 = 0x18;		
	TH0 = 0xFC;
	T0_Count++;
	if(T0_Count==500)
	{
		T0_Count = 0;
		TimeSetFlashFlag = !TimeSetFlashFlag;	
	}
		
	KeyScan();
}

 

你可能感兴趣的:(51单片机,经验分享)