以下代码来自学习江科大自化协和手把手教你学51单片机后的整合
//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();
}