一、簡介
本設(shè)計(jì)利用單片機(jī)AT89S52和1602液晶,實(shí)現(xiàn)日歷(年月日)、星期和時間的顯示。具體效果為:
2011-11-11 FRI
11:11:11
二、設(shè)計(jì)圖紙
1、電路原理圖
2、PCB圖
3、三維模型
三、相關(guān)程序
程序簡介:程序啟動后,先初始化液晶和兩個定時器,時鐘開始工作,再進(jìn)入掃描檢測調(diào)整時鐘函數(shù)中,根據(jù)是否有KEY1長按來決定是否調(diào)整時鐘。定時器T0用來產(chǎn)生時鐘計(jì)時,T1用來中斷顯示液晶,按鍵KEY1為多功能按鍵,在時鐘工作時,長按時進(jìn)入時鐘調(diào)整,依次調(diào)整年、月、日、星期、時、分,KEY2和KEY3分別為上下調(diào)整,每次調(diào)整完畢后短按KEY1返回,進(jìn)入下一個調(diào)整。
1、主程序文件
#include "reg51.h"
#include "adjust.h"
#include "lcm.h"
#include "key.h"
//定時器初始化函數(shù)
void Timer_Start()
{
TMOD=0x11;
TH1=0xfc;
TL1=0x18;
TH0=0xfc;
TL0=0x60;
EA=1;
ET1=1;
ET0=1;
TR1=1;
TR0=1;
}
//主函數(shù)
void main()
{
unsigned char Key_Code;
//初始化LCM
LCM_START();
//初始化定時器
Timer_Start();
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_M:Adjust();break;
default:break;
}
}
}
2、延時程序文件
(1)頭文件
#ifndef __DELAY_H__
#define __DELAY_H__
extern void Delay_ms(unsigned int ms);
#endif
(2)程序文件
#include "delay.h"
//延時1毫秒函數(shù)
void Delay_ms(unsigned int ms)
{
unsigned char Temp;
while(ms--)
for(Temp=0;Temp<120;Temp++);
}
2、時鐘程序文件
(1)頭文件
#ifndef __CLOCK_H__
#define __CLOCK_H__
extern unsigned int Second_Count;
extern char Second;
extern char Minute;
extern char Hour;
extern char Day;
extern char Month;
extern int Year;
extern char Week;
extern char Month_Day[12];
extern bit Leap_Year(int Temp_Year);
#endif
(2)程序文件
#include "reg51.h"
//秒計(jì)數(shù)變量
unsigned int Second_Count=0;
//時分秒變量
char Second=53;
char Minute=20;
char Hour =5;
char Day =18;
char Month =4;
int Year =2011;
char Week =1;
//每月天數(shù)數(shù)組
char Month_Day[12]={31,28,31,30,31,30,31,31,30,31,30,31};
//閏年判斷函數(shù)
bit Leap_Year(int Temp_Year)
{
bit Temp;
if(((Temp_Year%4==0)&&(Temp_Year%100!=0))||(Temp_Year%400==0))
{
Temp=1;
}
else
{
Temp=0;
}
return Temp;
}
//定時器0中斷服務(wù)函數(shù)
void Timer0_Serve()interrupt 1
{
TH0=0xfc;
TL0=0x60;
Second_Count++;
if(Second_Count>1000)
{
Second_Count=0;
Second++;
if(Second>=60)
{
Second=0;
Minute++;
if(Minute>=60)
{
Minute=0;
Hour++;
if(Hour>=24)
{
Hour=0;
Week++;if(Week>=7)Week=0;
Day++;
if(Day>Month_Day[Month-1])
{
if((Month!=2)||(!Leap_Year(Year))||(Day>29))
{
Day=1;
Month++;
if(Month>12)
{
Month=1;
Year++;
}
}
}
}
}
}
}
}
3、液晶程序文件
(1)頭文件
#ifndef __LCM_SHOW_H__
#define __LCM_SHOW_H__
extern void LCM_START();
extern void LCM_CMD (unsigned char Temp_Data);
extern void LCM_DATA(unsigned char Temp_Data);
extern void LCM_POS (unsigned char Temp_Data);
#endif
(2)程序
#include "reg51.h"
#include "delay.h"
#include "intrins.h"
//LCM控制端口定義
sbit RS=P2^5;//數(shù)據(jù)/命令選擇端口
sbit RW=P2^6;//讀寫控制端口
sbit EN=P2^7;//使能端口
//定義商標(biāo)字符
char code Logo1[]="WELCOME TO";
char code Logo2[]="MY MCU WORKSHOP!";
//LCM狀態(tài)讀取函數(shù)
bit LCM_Status()
{
bit Temp_Status;
RS=0;
RW=1;
EN=1;
_nop_();
Temp_Status=(bit)(P0&0x80);
_nop_();
EN=0;
return Temp_Status;
}
//LCM寫初始化命令函數(shù)
void LCM_INIT(unsigned char Temp_Data)
{
RS=0;
RW=0;
EN=0;
_nop_();
P0=Temp_Data;
_nop_();
EN=1;
_nop_();
_nop_();
EN=0;
}
//LCM寫命令函數(shù)
void LCM_CMD(unsigned char Temp_Data)
{
while(LCM_Status());
RS=0;
RW=0;
EN=0;
_nop_();
P0=Temp_Data;
_nop_();
EN=1;
_nop_();
_nop_();
EN=0;
}
//LCM寫數(shù)據(jù)函數(shù)
void LCM_DATA(unsigned char Temp_Data)
{
while(LCM_Status());
RS=1;
RW=0;
EN=0;
_nop_();
P0=Temp_Data;
_nop_();
EN=1;
_nop_();
_nop_();
EN=0;
}
//LCM顯示位置設(shè)定函數(shù)
void LCM_POS(unsigned char Temp_Data)
{
LCM_CMD(Temp_Data|0x80);
}
//LCM初始化函數(shù)
void LCM_START()
{
unsigned char Temp;
//寫初始化指令三次但不檢測忙信號
LCM_INIT(0x38);
Delay_ms(5);
LCM_INIT(0x38);
Delay_ms(5);
LCM_INIT(0x38);
Delay_ms(5);
//寫初始化命令字
LCM_CMD(0x38);
Delay_ms(1);
//寫清屏命令字
LCM_CMD(0x01);
Delay_ms(1);
//寫地址指針及光標(biāo)自動移位命令字
LCM_CMD(0x06);
Delay_ms(1);
//寫開顯示命令字
LCM_CMD(0x0c);
Delay_ms(1);
Temp=0;
//設(shè)定顯示位置為第行4列
LCM_POS(0x03);
Delay_ms(1);
//顯示Logo第一行
while(Logo1[Temp]!='\0')
{
LCM_DATA(Logo1[Temp]);
Delay_ms(200);
Temp++;
}
Temp=0;
//設(shè)定顯示位置為第二行1列
LCM_POS(0x40);
Delay_ms(1);
//顯示Logo第二行
while(Logo2[Temp]!='\0')
{
LCM_DATA(Logo2[Temp]);
Delay_ms(200);
Temp++;
}
Delay_ms(2000);
//Logo顯示完了之后清屏
LCM_CMD(0x01);
Delay_ms(1);
}
4、時鐘顯示文件
(1)頭文件
#ifndef __SHOW_H__
#define __SHOW_H__
extern unsigned char Show_Mark;
#endif
(2)程序文件
#include "reg51.h"
#include "lcm.h"
#include "clock.h"
#include "delay.h"
#include "adjust.h"
//顯示計(jì)數(shù)
unsigned int Show_Count=0;
unsigned char Show_Mark=0;
//定義時鐘顯示字符串
char Date[]="2011-04-17 SUN";
char Time[]="12:30:30 ";
//定義二維周字符數(shù)組
char Week_Name[7][3]={"SUN","MON","TUE","WED","THU","FRI","SAT"};
char Adjust_Mark[7][2]={" ","YY","MM","DD","WW","HH","MM"};
//LCM顯示函數(shù)
void LCM_SHOW()
{
unsigned char Temp;
//顯示之前轉(zhuǎn)化日期為顯示字符
Date[0]=Year/1000+0x30;
Date[1]=Year/100%10+0x30;
Date[2]=Year/10%10+0x30;
Date[3]=Year%10+0x30;
Date[5]=Month/10+0x30;
Date[6]=Month%10+0x30;
Date[8]=Day/10+0x30;
Date[9]=Day%10+0x30;
Date[11]=Week_Name[Week][0];
Date[12]=Week_Name[Week][1];
Date[13]=Week_Name[Week][2];
//顯示之前轉(zhuǎn)化時間為顯示字符
Time[0]=Hour/10+0x30;
Time[1]=Hour%10+0x30;
Time[3]=Minute/10+0x30;
Time[4]=Minute%10+0x30;
Time[6]=Second/10+0x30;
Time[7]=Second%10+0x30;
//顯示設(shè)定標(biāo)記
Time[10]=Adjust_Mark[Show_Mark][0];
Time[11]=Adjust_Mark[Show_Mark][1];
Temp=0;
//設(shè)定顯示位置為第一行1列
LCM_POS(0x01);
Delay_ms(1);
//顯示日期和星期
while(Date[Temp]!='\0')
{
LCM_DATA(Date[Temp]);
Temp++;
}
Temp=0;
//設(shè)定顯示位置為第二行4列
LCM_POS(0x44);
Delay_ms(1);
//顯示時間
while(Time[Temp]!='\0')
{
LCM_DATA(Time[Temp]);
Temp++;
}
}
//中斷方式顯示LCM
void Timer1_Serve()interrupt 3
{
TH1=0xfc;
TL1=0x18;
Show_Count++;
if(Show_Count>100)
{
Show_Count=0;
LCM_SHOW();
}
}
5、按鍵掃描文件
(1)頭文件
#ifndef __KEY_SCAN_H__
#define __KEY_SCAN_H__
#define Key_M 0x10
#define Key_R 0x00
#define Key_U 0x01
#define Key_D 0x02
extern unsigned char Key_Scan();
#endif
(2)程序文件
#include "reg51.h"
#include "delay.h"
#include "key.h"
//按鍵端口定義
sbit Key1=P3^4;
sbit Key2=P3^5;
sbit Key3=P3^6;
//鍵盤掃描程序
unsigned char Key_Scan()
{
//先掃描模式選擇按鍵
if(Key1==0)
{
Delay_ms(500); //延時0.5秒,一則為一次短按的最長時間,另則為延時去抖動
if(Key1==0) //判斷長短按鍵
{
while(!Key1);//等待按鍵釋放
Delay_ms(100);//消去釋放抖動
return Key_M;
}
else
{
return Key_R;
}
}
else
if(Key2==0) //掃描加一鍵,有長按不斷調(diào)整功能
{
Delay_ms(300);
return Key_U;
}
else
if(Key3==0)
{
Delay_ms(300); //掃描減一鍵,有長按不斷調(diào)整功能
return Key_D;
}
else return 0xff;
}
6、時鐘調(diào)整文件
(1)頭文件
#ifndef __ADJUST_H__
#define __ADJUST_H__
extern void Adjust();
#endif
(2)程序文件
#include "reg51.h"
#include "Clock.h"
#include "adjust.h"
#include "show.h"
#include "Key.h"
//調(diào)整年函數(shù)
void Adjust_Year()
{
unsigned char Key_Code;
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Year++;break;
case Key_D:Year--;break;
default :break;
}
}
}
//調(diào)整月函數(shù)
void Adjust_Month()
{
unsigned char Key_Code;
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Month++;if(Month>12)Month=1;break;
case Key_D:Month--;if(Month<1)Month=12;break;
default:break;
}
}
}
//調(diào)整日函數(shù)
void Adjust_Day()
{
unsigned char Key_Code;
//調(diào)整日期之前先根據(jù)已經(jīng)調(diào)整的月份修改日期
if(Day>Month_Day[Month-1])
{
if((Month!=2)||(!Leap_Year(Year))||(Day>29))Day=1;
}
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Day++;
if(Day>Month_Day[Month-1])
{
if((Month!=2)||(!Leap_Year(Year))||(Day>29))Day=1;
}
break;
case Key_D:Day--;
if(Day<1)
{
if((Month==2)&&(Leap_Year(Year)))Day=29;
else Day = Month_Day[Month-1];
}
break;
default :break;
}
}
}
//調(diào)整星期函數(shù)
void Adjust_Week()
{
unsigned char Key_Code;
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Week++;if(Week>=7)Week=0;break;
case Key_D:Week--;if(Week<0) Week=6;break;
default :break;
}
}
}
//調(diào)整小時函數(shù)
void Adjust_Hour()
{
unsigned char Key_Code;
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Hour++;if(Hour>=24)Hour= 0;break;
case Key_D:Hour--;if(Hour<0) Hour=23;break;
default:break;
}
}
}
//調(diào)整分鐘閃爍函數(shù)
void Adjust_Minute()
{
unsigned char Key_Code;
while(1)
{
Key_Code=Key_Scan();
switch(Key_Code)
{
case Key_R:return;
case Key_U:Minute++;if(Minute>=60)Minute= 0;break;
case Key_D:Minute--;if(Minute<0) Minute=59;break;
default:break;
}
}
}
//總調(diào)整函數(shù)
void Adjust()
{
//調(diào)整之前先暫停時鐘
TR0=0;
//秒相關(guān)量清零
Second=0;
Second_Count=0;
//調(diào)整年
Show_Mark=1;
Adjust_Year();
//調(diào)整月
Show_Mark=2;
Adjust_Month();
//調(diào)整日
Show_Mark=3;
Adjust_Day();
//調(diào)整周
Show_Mark=4;
Adjust_Week();
//調(diào)整小時
Show_Mark=5;
Adjust_Hour();
//調(diào)整分鐘
Show_Mark=6;
Adjust_Minute();
//調(diào)整完畢重啟時鐘
Show_Mark=0;
TR0=1;
}