基于清翔單片機開發板80c51的萬年日歷。
部分代碼和使用方法如下
附件有代碼和代碼說明
功能:帶鬧鐘的萬年歷
鬧鐘 萬年歷 調節時間 調節鬧鐘 可以區分閏年,28 29 30 31.
思路:利用定時器中斷進行計時以此來進行秒數加一,分鐘加一……。注意區分滿十進一,滿60分鐘進一,還是小時的滿24天數進一,以及判斷月份的天數,閏年否。
按鍵掃描函數:進行時間的日期的調節。按下S2進入時間調節,
再按矩陣按鍵的S6-S19中的一個,分別對應選中要調節2018 0501 12:31:53 中的某一位
再按矩陣的S6-S15分別對應把剛選中的那一位設置為1或2或3.....0
(操作原理圖)
鬧鐘:鬧鐘設置及檢測函數
按下s3時顯示鬧鐘如1231
再按s4一下對應調節第一位數據,此時按s2對應數據在0——9循環變化
s4按兩下對應調節第二位,再s2調節數據大小
s4三下第三位,s4四下第四位
再按s4又到第一位,一次循環
s5用于鬧鐘的開啟關閉。
當鬧鐘響時,可以按s4關閉蜂鳴器,不干擾時,響一分鐘
LCD1602:液晶顯示函數,根據課本操作時序圖進行數據的寫入
代碼具體說明如下
由于代碼模塊性較強,內容多,分若干個.c,.h文件

1.Main.c
/****************************************************
程序說明
帶鬧鐘的萬年歷
鬧鐘 萬年歷 調節時間 調節鬧鐘 可以區分閏年,28 29 30 31.
*****************************************************/
#include <reg52.h>
#include<stdlib.h>
#include<Lcd1602.h>
#include<main.h>
#include<key_Scan.h>
#include<Clock_Set.h>
#include<W_Time.h>
sbit dula = P2^6;// 數碼管位選段選用于關閉數碼管顯示
sbit wela = P2^7;//數碼管位選段選用于關閉數碼管顯示
/*******************************************************
變量說明
*flag0是定義在了萬年c歷.c中的作為判斷一秒鐘中斷產生的標志位。
*ClockFlag2(Clock-Set.c)用于在鬧鐘響著的時候(鬧鐘中斷時)關閉或開啟鬧鐘給beep賦值變量。
*Clock[5]用于儲存設置的鬧鐘的點數
*Y M D :把字符串型的年月日轉或為int型時分別付給他三個,再利用Y M D判斷閏年否,28 29 30 31天?
*ClockFlag=1;用于判斷顯示時間還是鬧鐘的標志位
***********************************************************/
extern uchar flag0;
extern int ClockFlag2;
extern uchar Clock[5];
int Y,M,D,ClockFlag=1;
uchar num=0; //定時器中斷
uchar time0[]={"2027-12-31"}; //儲存時間的
uchar time1[]={"23:59:53"};
void T0_init() //定時器0初始化
{
TMOD=0x01;
TH0=(65536-45872)/256; //裝初值50ms單位
TL0=(65536-45872)%256;
EA=1; //開總中斷
ET0=1; //開啟定時器0中斷
TR0=1; //開啟定時器0
}
void main()
{
int i=0,n=0,j;
char str1[3],str2[3]; //用于儲存字符串中的月份或日子
for(j=5;j<=6;j++) //把字符串中的月份拿出來
{str1[ i]=time0[j];
i++;
}
for(j=8;j<=9;j++) //把字符串中的日子拿出來
{str2[n]=time0[j];
n++;
}
Y=atoi(time0); //利用stdlib.h中的atoi()函數把年月日轉化為int型,以此來寫算法判斷閏年,28 29 30 31 ?
M=atoi(str1);
D=atoi(str2);
dula = 0;
wela = 0;//關閉數碼管顯示
T0_init();//定時器0初始化
LCD_Init();//1602初始化
while(1)
{
W_Time(); //萬年歷函數
Clock_Check(); //鬧鐘檢測函數,看是否有鬧鐘產生
key_scan(); //用于調整時間的按鍵掃描函數
ClockSet(); //鬧鐘設置函數
if(ClockFlag==1) //用于判斷顯示時間還是鬧鐘的標志位
{
LCD_displaystr(0, 0, &time0[0]); //顯示時間
LCD_displaystr(0, 1, &time1[0]);
}
if(ClockFlag<0)
{
LCD_displaystr(0, 1, &Clock[0]); //顯示鬧鐘
}
}
}
void T0_time() interrupt 1 //定時器0中斷服務函數
{
TH0=(65536-45872)/256;
TL0=(65536-45872)%256;
num++;
if(num==20)
{
num=0;
flag0=1;
}
}
2.main.h
#define uchar unsigned char
#define uint unsigned int
3.萬年歷.c
/*********************************************************************************
萬年歷函數
通過定時器0產生一秒中斷,判斷flag0進入函數。進行秒數加一,進位時看是否達到六十秒,六十分鐘,24小時。
第一個if(flag0)進行時間的判斷,若到了24小時則進入if(flag1)天數加一及年月判斷
可以區分閏年及月份的具體天數
具體說明在下面。
*********************************************************************************/
#include <reg52.h>
#include<main.h>
extern uchar time0[];
extern uchar time1[];
uchar flag0=0,flag1=0; //flag1用于判斷是否該天數加一了
extern int Y,M,D;
void W_Time()
{
if(flag0==1) //一秒中斷發生
{
if(time1[0]=='1'||time1[0]=='0') //小時處未到20點以上
{
time1[7]++; //秒數加一
if(time1[7]>'9')
{
time1[7]='0';
time1[6]++; //秒數進位
if(time1[6]=='6') //達到了60秒
{
time1[6]='0';
time1[4]++; //分鐘加一
if(time1[4]>'9')
{
time1[4]='0';
time1[3]++; //分鐘進位
if(time1[3]=='6') //達到了60分鐘
{
time1[3]='0';
time1[1]++; //小時加一
if(time1[1]>'9')
{
time1[1]='0';
time1[0]++; //小時進位
}
}
}
}
}
}
if(time1[0]=='2') //小時處20點以上了
{
time1[7]++; //以下分別是秒數,分鐘,小時加一進位,方法同上
if(time1[7]>'9')
{
time1[7]='0';
time1[6]++;
if(time1[6]=='6')
{
time1[6]='0';
time1[4]++;
if(time1[4]>'9')
{
time1[4]='0';
time1[3]++;
if(time1[3]=='6')
{
time1[3]='0';
time1[1]++;
if(time1[1]=='4') //判斷是否到了24小時,則天數該加一了
{
time1[1]='0';
time1[0]='0';
flag1=1; //天數加一標志位
}
}
}
}
}
}
if(flag1==1) //達到了24小時,天數加一
{
if(M==0x02) // M=2,二月
{
if((Y%4==0&&Y%100!=0)||(Y%100==0)) //看是否為閏年,此時為閏年
{
if(time0[8]=='0'||time0[8]=='1') //天數在20天以下
{ time0[9]++; //天數加一
if(time0[9]>'9')
{
time0[9]='0';
time0[8]++; //天數進位
}
}
if(time0[8]=='2') //20天以上
{ time0[9]++; //天數加一及到了28天月份加一
if(time0[9]>'8')
{
time0[9]='1';
time0[8]='0';
time0[6]='3'; //月份加一
}
}
}
else // 不是閏年,天數29天
{
if(time0[8]=='0'||time0[8]=='1') //天數在20天以下
{ time0[9]++; //天數加一
if(time0[9]>'9')
{
time0[9]='0';
time0[8]++; //天數進位
}
}
if(time0[8]=='2') //天數在20以上,當29天時月份要加一了
{ time0[9]++;
if(time0[9]>'9')
{
time0[9]='1';
time0[8]='0';
time0[6]='3'; //月份加一
}
}
}
}
if(M==0x04||M==0x06||M==0x09||M==0x0b) //4 6 9 11月天數30
{
if(time0[8]=='0'||time0[8]=='1'||time0[8]=='2') //30天以下
{ time0[9]++; //天數加一
if(time0[9]>'9')
{
time0[9]='0';
time0[8]++; //天數進位
}
}
if(time0[8]=='3') //第30天,月份加一
{
time0[9]='1';
time0[8]='0';
time0[6]++; //月份加一
}
}
if(M==0x01||M==0x03||M==0x05||M==0x07||M==0x08||M==0x0a) //1 3 5 7 8 10 月 31天
{
if(time0[8]=='0'||time0[8]=='1'||time0[8]=='2') //天數小于30天
{ time0[9]++;
if(time0[9]>'9')
{
time0[9]='0';
time0[8]++;
}
}
if(time0[8]=='3'&&time0[9]=='0')
time0[9]++; //30號時繼續加一
if(time0[8]=='3'&&time0[9]=='1') //31號了,月份加一
{
time0[9]='1';
time0[8]='0';
time0[6]++;
}
}
if(M==0x0c) //12月,31天
{
if(time0[8]=='0'||time0[8]=='1'||time0[8]=='2')
{ time0[9]++;
if(time0[9]>'9')
{
time0[9]='0';
time0[8]++;
}
}
if(time0[8]=='3'&&time0[9]=='0')
time0[9]++;
if(time0[8]=='3'&&time0[9]=='1') //12-31號了,年份加一,日子變為01-01
{
time0[9]='1';
time0[8]='0';
time0[6]='1';
time0[5]='0';
time0[3]++;
if(time0[3]>'9')
{
time0[3]='0';
time0[2]++; //年份滿十年進位
if(time0[2]>'9')
{
time0[1]++; //年份滿十年進位
time0[2]='0';
if(time0[1]>'9')
{
time0[1]='0';
time0[0]++; //年份滿十年進位
}
}
}
}
}
flag1=0; //清0
}
flag0=0;//清0
}
}
4.W_Time.h
void W_Time();
5.Key_Scan.c
/******************************************************
按鍵函數
可以調節時間及日期
按下S2進入時間調節,
再按矩陣按鍵的S6-S19中的一個,分別對應選中要調節2018 0501 12:31:53 中的某一位
再按矩陣的S6-S15分別對應把剛選中的那一位設置為1或2或3.....0
按鍵原理及操作方法將word文檔
******************************************************/
#include <reg52.h>
#include<main.h>
#include<Lcd1602.h>
sbit S2=P3^0;
/**********************************************
變量說明
flag第一次進入矩陣按鍵的標志位,即此時矩陣按鍵是用于選擇你要調節時間日期的哪一位
flag3第二次進入矩陣按鍵的標志位,即此時矩陣按鍵是用于設置剛才選擇的那一位為數字幾
TimeSet是Time0或者Time1數組的下標
Key_Flag=0作為作為檢測S2的標志位,當檢測矩陣按鍵時Key—Flag=1。因為S6和s2為一個I/O口,這樣做為了穩定,否則S6也會有s2的功能
*********************************************/
int flag=0,temp,TimeSet,flag3=0;
uchar Key_Flag=0;
extern uchar time0[];
extern uchar time1[];
void delayms(uint xms) //延時函數
{
uint i,j;
for(i=xms;i>0;i--)
for(j=110;j>0;j--);
}
void key_scan()
{
if(Key_Flag==0) //檢測S2,S2按下進入時間調節
{
if(S2==0)
{
delayms(20);
if(!S2)
{
while(!S2);
flag=1;
}
}
}
while(flag==1) //以下是第一次按矩陣按鍵,設置要調節時間的哪一位
{
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee:
TimeSet=0; //S6對應為要調節Time0的0位,即2018的2
flag=0; //跳出第一次檢測矩陣案件的while循環
flag3=1; //進入第二次矩陣按鍵的標志位,即開始設置選中的那一位為數字幾
break;
case 0xde: //S7 2018 12 31 12:31:54 中的0
TimeSet=1;
flag=0;
flag3=1;
break;
case 0xbe: //S8 2018 12 31 12:31:54 中的1
TimeSet=2; //以下均同上
flag=0;
flag3=1;
break;
case 0x7e: //s9
TimeSet=3;
flag=0;
flag3=1;
break;
}
while(temp!=0xf0) //等待按鍵釋放
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: //s10
TimeSet=5;
flag=0;
flag3=1;
break;
case 0xdd: //s11
TimeSet=6;
flag=0;
flag3=1;
break;
case 0xbd: //s12
TimeSet=8;
flag=0;
flag3=1;
break;
case 0x7d: //s13
TimeSet=9;
flag=0;
flag3=1;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: //s14
TimeSet=11;
flag=0;
flag3=1;
break;
case 0xdb: //s15
TimeSet=12;
flag=0;
flag3=1;
break;
case 0xbb: //s16
TimeSet=14;
flag=0;
flag3=1;
break;
case 0x7b: //s17
TimeSet=15;
flag=0;
flag3=1;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xf7;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xe7: //s18
TimeSet=17;
flag=0;
flag3=1;
break;
case 0xd7: //s19
TimeSet=18;
flag=0;
flag3=1;
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
}
}
P3=0xff; //最后把P3的I/O口拉高,防止對后續有共同I/O口的按鍵影響
if(S2==0) //S2按下時退出時間設置
{
delayms(20);
if(!S2)
{
while(!S2);
flag=0;
}
}
}
while(flag3==1)
{ //第二次矩陣按鍵檢測標志位,即開始設置被選中的那一位為數字幾
P3=0xfe;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xee: //s6 設置為1
time0[TimeSet]='1';
break;
case 0xde: //s7 2
time0[TimeSet]='2';
break;
case 0xbe: //s8 3
time0[TimeSet]='3';
break;
case 0x7e: //s9 4
time0[TimeSet]='4';
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
LCD_displaystr(0, 0, &time0[0]); //隨時顯示時間,觀察你設置的幾,是否設置正確
LCD_displaystr(0, 1, &time1[0]);
}
}
P3=0xfd;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xed: //s10 5
time0[TimeSet]='5';
break;
case 0xdd: //s11 6
time0[TimeSet]='6';
break;
case 0xbd: //s12 7
time0[TimeSet]='7';
break;
case 0x7d: //s13 8
time0[TimeSet]='8';
break;
}
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
LCD_displaystr(0, 0, &time0[0]); //同上
LCD_displaystr(0, 1, &time1[0]);
}
}
P3=0xfb;
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
delayms(10);
temp=P3;
temp=temp&0xf0;
if(temp!=0xf0)
{
temp=P3;
switch(temp)
{
case 0xeb: //s14 9
time0[TimeSet]='9';
break;
case 0xdb:
time0[TimeSet]='0'; //s15 0
break; }
while(temp!=0xf0)
{
temp=P3;
temp=temp&0xf0;
}
LCD_displaystr(0, 0, &time0[0]);
LCD_displaystr(0, 1, &time1[0]);
}
}
P3=0xff; //同上
if(S2==0) //按下s2跳出時間設置
{
delayms(20);
if(!S2)
{
while(!S2);
flag3=0;
}
}
}
}
5.Key_Scan.h
#include<reg52.h>
#include<main.h>
void key_scan();
void delayms(uint xms);
6.lcd1602.c
/***********************************************
lcd1602函數
此函數實現液晶屏上的字符顯示
仿照課本函數,學習后,根據時序圖自己又打了一遍
***********************************************/
#include <reg52.h>
#include<main.h>
sbit LCDRS = P3^5; //RS端
sbit LCDRW = P3^6; //RW端
sbit LCDEN = P3^4; //EN端
/************************************
判斷液晶忙函數,因為lcd接收數據是有速度限制的,如果單片機工作較快,會造成顯示不正常的結果
************************************/
void If_Busy()
{
uchar busy;
P0 = 0xff;//拉高數據總線,復位
LCDRS = 0; //拉低RS
LCDRW = 1; //拉高RW
do
{
LCDEN = 1;//使能EN
busy = P0;//讀數據
LCDEN = 0; //拉低使能,等待下一次
}while(busy & 0x80); //判斷狀態字BIT7位是否為1,為1則表示忙,程序等待
}
void LCD_Write_data(uchar date) //寫數據函數
{
If_Busy(); //忙則等待
LCDRS = 1; //由基本操作時序知,選擇寫數據模式要拉高它
LCDRW = 0; //同上
P0=date; //寫入數據
LCDEN = 1; //拉高使能端
LCDEN = 0; //拉低使能以便于等待下一次
}
void LCD_Write_com(uchar com) //寫命令函數
{ If_Busy(); //忙則等待
LCDRS = 0; //選擇寫命令模式
LCDRW = 0; //拉低RW操作,由時序圖可知
P0 = com;//寫入命令
LCDEN = 1; //拉高使能端 數據被傳輸到LCD1602內
LCDEN = 0; //拉低使能以便于等待下一次
}
/******************************************
在指定位置顯示字符串函數,L對應一行中的哪個位置,H對應第一行還是第二行
******************************************/
void LCD_displaystr(uchar L, uchar H, uchar *str)
{
if(H) L |= 0x40;
L |= 0x80;
LCD_Write_com(L);
while(*str != '\0')
{
LCD_Write_data(*str++);
}
}
void LCD_Init() //初始化
{
LCD_Write_com(0x38); // 設置16*2顯示,5*7點陣,8位數據接口
LCD_Write_com(0x0c); //開顯示
LCD_Write_com(0x06); //讀寫一字節后地址指針加1
LCD_Write_com(0x01); //清除顯示
}
7.Lcd1602.h
#include<reg52.h>
#include<main.h>
void If_Busy();
void LCD_Write_com (uchar com);
void LCD_Write_data(uchar date);
void LCD_displaystr(uchar L, uchar H, uchar *str);
void LCD_Init();
8.Clock_Set.c
/*****************************************************
鬧鐘設置及檢測函數
按下s3時顯示鬧鐘如1231
再按s4一下對應調節第一位數據,此時按s2對應數據在0——9循環變化
s4按兩下對應調節第二位,再s2調節數據大小
s4三下第三位,s4四下第四位
再按s4又到第一位,一次循環
s5用于鬧鐘的開啟關閉。
當鬧鐘響時,可以按s4關閉蜂鳴器,不干擾時,響一分鐘
*****************************************************/
#include <reg52.h>
#include<main.h>
#include<Lcd1602.h>
sbit S2=P3^0;
sbit S3=P3^1;
sbit S4=P3^2;
sbit S5=P3^3;
sbit beep=P2^3;
extern void delayms(uint xms);
/***********************************************
變量說明
ClockFlag3作為是否檢測s4的標志位,即是否進入鬧鐘時間調節
Clock_Set=1作為是否開啟鬧鐘的標志位,通過s5讓其取反,以此控制鬧鐘的開啟關閉
ClockFlag2=1用于開啟beep,通過s4取反判斷大于0否,以此給beep賦值
ClockFlag1作為給鬧鐘第幾位賦值的標志位,即儲存鬧鐘數據數組的下標
***********************************************/
int ClockFlag3=1,Clock_Set=1,i,j,ClockFlag2=1;
uchar Clock[5]={"0800"},ClockFlag1=0;
uchar code table[]={'0','1','2','3','4','5','6','7','8','9'}; //0——9循環
extern uchar time1[];
extern uchar time0[];
extern int ClockFlag;
extern uchar Key_Flag;
/***********************************************************
鬧鐘設置函數
***********************************************************/
void ClockSet()
{ if(Key_Flag==0) //同Key_Scan.c文件中此變量的解釋說明
{
if(S3==0) //按下s3時顯示鬧鐘如1231,再按下又退出
{
delayms(20);
if(!S3)
{
while(!S3);
LCD_Write_com(0x01); //清除顯示
ClockFlag=~ClockFlag;
ClockFlag3=~ClockFlag3; //ClockFlag3作為是否檢測s4的標志位,即是否進入鬧鐘時間調節
}
}
}
if(ClockFlag3<0) //開始鬧鐘調節,選擇調節哪一位
{
if(S4==0)
{
delayms(10);
if(!S4)
{
while(!S4);
Key_Flag=1;
ClockFlag1++; //記錄按下s4的次數,依次對應每一位
if(ClockFlag1>4)ClockFlag1=1;
}
}
if(ClockFlag1) //進入被選擇的這一位的數值調整0——9循環
{
if(S2==0)
{
delayms(20);
if(!S2)
{ while(!S2);
Clock[ClockFlag1-1]=table[ i]; //0——9循環
i++;
if(i==10)i=0;
}
}
}
if(S3==0) //完成調節退出
{
delayms(20);
if(!S3)
{
while(!S3);
LCD_Write_com(0x01); //清除顯示
ClockFlag=~ClockFlag;
ClockFlag3=~ClockFlag3;
Key_Flag=0;
}
}
}
}
/******************************
檢測鬧鐘函數
******************************/
void Clock_Check()
{
if(S5==0)//開啟鬧鐘
{
delayms(20);
if(!S5)
{ while(!S5);
Clock_Set=~Clock_Set;
}
}
if(Clock_Set<0) //鬧鐘開啟并進行檢測是否到點
{
P1=0xfe; //鬧鐘開啟時亮燈,作為提示
if(time1[0]==Clock[0]&&time1[1]==Clock[1]&&time1[3]==Clock[2]&&time1[4]==Clock[3]) //到點
{
for(j=0;j<10;j++)
beep=ClockFlag2>0?0:1; //響
if(S4==0) //開關蜂鳴器
{
delayms(20);
if(!S4)
{ while(!S4);
ClockFlag2=~ClockFlag2;
}
}
}
beep=1;P1=0xff;
}
}
9.Clock_Set.h
#include <reg52.h>
#include<main.h>
void ClockSet();
void Clock_Check();
完整的Word格式文檔51黑下載地址:
代碼說明2,萬年歷.docx
(161.71 KB, 下載次數: 46)
2018-11-22 00:23 上傳
點擊文件名下載附件
|