基於51單片機的萬年曆(包含鬧鐘,秒錶)實現

開頭嘮一嘮:

          趁着寒假的時間,也趁着課程設計正好是做一個萬年曆。就打算好好從頭到尾來一遍。漲漲知識。首先說的是本人也是小白一顆,大神們能幫忙指正錯誤的話,不勝感激。寫博客只是爲了總結經驗,要是幫到一部分人就更好了。我想是從硬件到軟件都介紹的詳細一點,還想說一說自己遇到的一些問題,可能要寫的長一點。代碼的話我會在後面上傳。好,閒話不多說。進入正題。

概述:

         首先說一下我用到的東西,硬件方面(電路都是自己拿萬能板焊的):一片51單片機,一塊12864液晶,一片ds1302時鐘芯片,四個按鍵。還有些電容、電阻、晶振什麼的,下面講到的時候再說吧。主要的就這麼多吧。再簡單說一下按鍵的功能吧,假設按鍵分別是k1,k2,k3,k4。首先lcd主界面是顯示的當前的日期時間和四路鬧鐘的時間。附圖。k1,k2,k3,k4最開始被按下時分別對應的功能是k1:進入時間設定模式;k2:進入日期設定模式;k3:進入鬧鐘設定模式;k4:進入秒錶計數模式。進入不同的模式後,四個按鍵有都有了新的功能,首先k4一直是退出,就是退出到最開始的選四種模式。k1,k2,k3對於日期和時間設定模式是一樣的功能k1:數值加1,k2:數值減1,k3:更換調的是小時還是分鐘抑或年份還是月份。對於鬧鐘模式,k1:數值加1,k2:更換調的是小時還是分鐘,k3:更換調的是第幾個鬧鐘。對於秒錶模式,k1:第一次按是開始計數,然後再按就是記錄一下當前是多少秒,最多可以記錄9次。k2:暫停/開始,k3:重新計數。有點繞得慌,簡單的的說就是有兩重循環。要是還沒理解,可以看後面的代碼。

 一:硬件電路

       這部分怎麼說,說簡單也挺簡單的。但其中有個梗我現在還沒過去。就是最開始我打算自己焊個下載電路在上面的,結果總是下不進去程序。這部分算是題外話了,但還是想簡單說一說。最開始打算用CH340芯片直接usb轉uart的,結果芯片買回來發現好像沒有直插的。自己腐板子什麼的又嫌太麻煩。最後打算先用usb轉九針串口轉成rs232電平,再用max232轉成uart電平的。照着電路圖一頓焊,結果果然不出我所料,不可能一下就成功下進去程序。就找問題啊,找啊找,找啊找。好像是找到了一個,就是51下程序不是有一個斷電在上電的過程嗎?我是這樣做的,但其中好像有問題,斷的這個電應該只是單片機的電,而不包括max232的電。於是又改電路,改完還是不行。算了,這個我以後搞明白了再來說說吧。

       其餘的應該就不算什麼難的了,找一個51最小系統原理圖照着焊唄,沒什麼太大的問題的。法

       對了,還有幾個小的點,提一提吧。51的P0口是相當於集電極開路的門電路的,記得接上拉電阻。LCD屏導完程序時,最開始如果什麼也不顯示的話,記得調一下3腳接的電位器調一下背光。

 

二.軟件設計

    1.按鍵檢測

        這一部分在我最開始看來是沒有什麼大文章的,也沒有什麼可以值得寫的,有點基礎的人幾分鐘就可以把程序寫出了。可是當自己正真寫的時候,才知道自己不懂得太多,要學的也太多。單片機的IO口最普通的兩種功能,輸入和輸出嘛。記得自己學stm32時,IO口的輸入輸出是要在最開始初始化的是定義的。也就是IO口在同一時刻只能有一種功能吧,總不能又輸入有輸出吧。可是51呢?讓我懵逼,在任何地方,包括啓動文件裏都沒有定義IO口是輸入還是輸出。這讓我很鬱悶,總不會我讓一個IO口輸出一個高電平後,還可以從IO口讀輸入吧,那樣不一直應該讀到的就是我輸出的高電平嗎。直到我好好研究了一波51IO口的內部電路,才明白其中的玄機。

這裏是最簡單的P1口的內部結構圖。有點數電基礎的人大概可以看明白。具體我就不講了。你可以參考這裏http://www.eeworld.com.cn/mcu/article_2017120236473_2.html

由上圖可見,要正確地從引腳上讀入外部信息,必須先使場效應管關斷,以便由外部輸入的信息確定引腳的狀態。爲此,在作引腳讀入前,必須先對該端口寫入l。具有這種操作特點的輸入/輸出端口,稱爲準雙向I/O口。8051單片機的P1、P2、P3都是準雙向口。P0端口由於輸出有三態功能,輸入前,端口線已處於高阻態,無需先寫入l後再作讀操作。弄懂IO口的內部結構之後。我就直接上程序了,慢慢研究吧。註釋的和沒有用到的部分大家就不要糾結了。

/*************************************************************************************************
程序說明:按鍵的檢測程序(基於51單片機),現在只有獨立按鍵檢測函數
Author:	xingcheng
IO說明:按鍵接的
**************************************************************************************************/

#include"key.h"
sbit KeyPort2=P1^5;
sbit KeyPort0=P1^7;						
sbit KeyPort1=P1^6;
sbit KeyPort3=P1^4;	//自己焊的按鍵接的單片機引腳
//sbit KeyPort2=P1^2;
//sbit KeyPort0=P1^0;						
//sbit KeyPort1=P1^1;
//sbit KeyPort3=P1^3;

/************************************************************************
函數名稱:key_scan()
函數功能:4個獨立按鍵檢測
輸入參數:無
返回值:KeyV		通過返回值的不同來確定哪個按鍵被按下
*************************************************************************/ 
uchar key_scan()
{
	uchar KeyV;
	KEYPORT=0xff;		//從51IO口讀數據,一般要先給鎖存器寫1,
						//具體請參考51IO口內部結構						
	if(KeyPort0==0||KeyPort1==0||KeyPort2==0||KeyPort3==0)		//判斷是否有按鍵按下
	//這裏改成if((P3&0xf0)!=0xf0)總是錯,原因可能是P3讀數據不是從引腳讀的
	//而是從鎖存器讀的,一直是0xff
	{
		delay_ms(10);		//防止抖動(拿板子實驗時,發現這裏延不延時並無影響)
		 if(KeyPort0==0)		//判斷哪個按鍵被按下//
		{	
			KeyV=K1;
		}
		else if(KeyPort1==0)
		{
			KeyV= K2;
		}
		else if(KeyPort2==0)
		{	
			KeyV=K3;
		}
		else if(KeyPort3==0)
		{
			KeyV=K4;
		}
		else 
		{
			KeyV= 0;
		}		  			//判斷哪個按鍵被按下//
		
		if(KeyV != 0)		//有按鍵按下時,進行鬆手檢測
              while(KeyPort0==0||KeyPort1==0||KeyPort2==0||KeyPort3==0);
		delay_ms(10);		//延時消抖(拿板子實驗,這裏延時非常必要)
   }

    return KeyV;			//返回KeyV來標識哪一個按鍵被按下
}

/*****************************有時間再完善連按,長按等功能************************/

/*		while((KEYPORT&0Xf0)!=NO_KEY)
		{
			delay_ms(15);
			PressCnt--;
			if(PressCnt==0)
				{
					PressCnt=SHORTCNT;
					return KeyV;
				}
		}
	}
	delay_ms(15);
	if((KEYPORT&0Xf0)==NO_KEY)
	{
		ReleaseCon=0;
		return KeyV;
	}
  */
#ifndef __KEY_H#define _KEY_H

#include<reg52.h>#include"delay.h"

#ifndef uchar #define uchar  unsigned char#endif

#define KEYPORT P3  //   四個按鍵接在了P3口的四個引腳#define NO_KEY   0xf0#define K1    0X01#define K2    0X02#define K3    0X03#define K4   0X04#define KEYSUB    0X02#define KEYADD    0X01#define KEYSET    0X04#define KEYNEXT   0X03 //K1,2,3,4,和這些是一樣的,只是寫.c文件時#define LONGCNT  150#define SHORTCNT 12

 

      uchar key_scan();

#endif


  2.lcd12864

     這個就是真的沒什麼好說的了。就是記得調電位器調背光。對了,還有一個 好坑的地方,不知道各位有沒有解決方法,就是那個光標(一閃一閃的那個)每次移動都是兩個字兩個字的移。上程序。

/***********************************************************************
程序功能:12864液晶驅動程序
其他:   只包括基本的字符串顯示功能
*************************************************************************/
 

#include <LCD12864.h>
#define uchar unsigned char
#define uint  unsigned int
#define LCD_data  P0             //數據口

 
/*******************************************************************
函數名稱:delay(int ms)
函數功能:延時
輸入參數:ms  要延時的ms數                                                                                                                                                                      
返回值:  無
 *******************************************************************/
 void delay(int ms)
 {
     while(ms--)
 {
       uchar i;
  for(i=0;i<250;i++)  
   {
    ; ; ; ;
   }
 }
 } 

 /*******************************************************************
函數名稱:lcd_busy()
函數功能:檢測LCD忙狀態。
輸入參數:無
返回值:  result result爲1時,忙等待;result爲0時,閒,可寫指令數據

 *******************************************************************/
bit lcd_busy()
  {                          
     bit result;
     LCD_RS = 0;
     LCD_RW = 1;
     LCD_EN = 1;
      delay_ms(1);
     result = (bit)(LCD_data&0x80);
     LCD_EN = 0;
     return(result); 
  }
 /*******************************************************************/                                                               
 /*寫指令數據到LCD                                                  */
 /*RS=L,RW=L,E=高脈衝,D0-D7=指令碼。                             */                                                                
 /*******************************************************************/
 void lcd_wcmd(uchar cmd)
 {                          
    while(lcd_busy());
     LCD_RS = 0;
     LCD_RW = 0;
     LCD_EN = 0;
     delay_ms(1);
     LCD_data = cmd;
    delay_ms(1);
     LCD_EN = 1;
    delay_ms(1);
     LCD_EN = 0;  
 }
 /*******************************************************************/
 /*寫顯示數據到LCD                                                  */
 /*RS=H,RW=L,E=高脈衝,D0-D7=數據。                               */
 /*******************************************************************/
 void lcd_wdat(uchar dat)
 {                          
    while(lcd_busy());
     LCD_RS = 1;
     LCD_RW = 0;
     LCD_EN = 0;
     LCD_data = dat;
    delay_ms(1);
     LCD_EN = 1;
   delay_ms(1);
     LCD_EN = 0; 
 }
 /*******************************************************************/
 /*  LCD初始化設定                                                  */
 /*******************************************************************/
 void lcd_init()
 { 
 

    LCD_PSB = 1;         //並口方式
     
     lcd_wcmd(0x34);      //擴充指令操作
     delay_ms(5);
     lcd_wcmd(0x30);      //基本指令操作
     delay_ms(5);
     lcd_wcmd(0x0C);      //顯示開,關光標
     delay(5);
     lcd_wcmd(0x01);      //清除LCD的顯示內容
     delay(5);
 }
 

/*********************************************************/
 /* 設定顯示位置       X:行數                Y:列數                                   */
 /*********************************************************/
 void lcd_pos(uchar X,uchar Y)
 {                          
    uchar  pos;
    if (X==0)
      {X=0x80;}
    else if (X==1)
      {X=0x90;}
    else if (X==2)
      {X=0x88;}
    else if (X==3)
      {X=0x98;}
    pos = X+Y ;  
    lcd_wcmd(pos);     //顯示地址
 }
 
/*********************************************************/
 /* 在設定位置顯示字符(串)                                         */
 /*********************************************************/
 void zifu_dis (uchar X,uchar Y,uchar *dis)
 {
      uchar i;
  lcd_pos(X,Y);   
      i = 0;
     while(dis[i] != '\0')
      {                         //顯示字符
        lcd_wdat(dis[i]);
        i++;
      }
 }

/**************dis_12864.h***************/#ifndef __LCD12864_H__#define __LCD12864_H__

#include"delay.h" #include <reg52.h>#define uchar unsigned char #define uint  unsigned int /*12864端口定義*/ #define LCD_data  P0             //數據口 sbit LCD_RS  =  P2^3;            //寄存器選擇輸入  sbit LCD_RW  =  P2^4;            //液晶讀/寫控制 sbit LCD_EN  =  P2^5;            //液晶使能控制 sbit LCD_PSB =  P3^3;            //串/並方式控制 

/*函數聲明*/ void delay(int ms); void lcd_init();  void beep(); void  dataconv(); void lcd_pos(uchar X,uchar Y);  //確定顯示位置 void zifu_dis (uchar X,uchar Y,uchar *dis); #endif


3.ds1302時鐘

  直接給程序,相應的資料大家可以網上搜的。   


/**************************************************************************
  
                   		THE REAL TIMER DS1302 DRIVER LIB
  
             		COPYRIGHT (c)   2005 BY JJJ.
                       		--  ALL RIGHTS RESERVED  --
  
   File Name:       DS1302.h
   Author:          Jiang Jian Jun
   Created:         2003/7/21
   Modified:		NO
   Revision: 		1.0
   re
***************************************************************************/
#include"ds1302.h"

/***************************************************************************
函數名稱:DS1302InputByte(unsigned char d)
函數功能:實時時鐘寫入一個字節(內部函數)
輸入參數:d			要寫入的數據
返回值:無
***************************************************************************/
void DS1302InputByte(unsigned char d) 	
{ 
    unsigned char i;
    ACC = d;
    for(i=8; i>0; i--)
    {
        DS1302_IO = ACC0;           	//相當於彙編中的 RRC
        DS1302_CLK = 1;
        DS1302_CLK = 0;
        ACC = ACC >> 1; 
    } 
}

/***************************************************************************
函數名稱:DS1302OutputByte(void)
函數功能:實時時鐘讀取一個字節(內部函數)
輸入參數:無
返回值:ACC			讀到的數據
***************************************************************************/
unsigned char DS1302OutputByte(void) 	
{ 
    unsigned char i;
    for(i=8; i>0; i--)
    {
        ACC = ACC >>1;         			//相當於彙編中的 RRC
        ACC7 = DS1302_IO;
        DS1302_CLK = 1;
        DS1302_CLK = 0;
    } 
    return(ACC); 
}

/***************************************************************************
函數名稱:Write1302(unsigned char ucAddr, unsigned char ucDa)	
函數功能:往實時時鐘指定地址寫數據
輸入參數:ucAddr		要寫數據的地址
		  ucDa			要寫入的數據
返回值:無
***************************************************************************/
void Write1302(unsigned char ucAddr, unsigned char ucDa)	
{
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(ucAddr);       	// 地址,命令
    DS1302InputByte(ucDa);       	// 寫1Byte數據
//    DS1302_CLK = 1;
    DS1302_RST = 0;
} 

/***************************************************************************
函數名稱:Read1302(unsigned char ucAddr)	
函數功能:讀取ds1302某地址的數據
輸入參數:ucAddr		要讀數據的地址
返回值:  ucData		讀出的數據
***************************************************************************/
unsigned char Read1302(unsigned char ucAddr)	//讀取ds1302某地址的數據
{
    unsigned char ucData;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(ucAddr|0x01);        // 地址,命令 
    ucData = DS1302OutputByte();         // 讀1Byte數據
//    DS1302_CLK = 1;
    DS1302_RST = 0;
    return(ucData);
}

/***************************************************************************
函數名稱:DS1302_SetProtect(bit flag)        
函數功能:是否寫保護
輸入參數:flag		
返回值:  無 
其他:flag爲1時,0x8E對應的control register最高位爲1,寫保護開啓
***************************************************************************/
void DS1302_SetProtect(bit flag)        //是否寫保護
{
	if(flag)
		Write1302(0x8E,0x80);
	else
		Write1302(0x8E,0x00);
}

/***************************************************************************
函數名稱:DS1302_SetTime(unsigned char Address, unsigned char Value)        
函數功能:向指定寄存器寫時間
輸入參數:Address		寄存器地址
		  Value			要寫入的時間(hex碼)
返回值:  無 
其他:可以先用宏定義定義好year,month,hour等的地址
***************************************************************************/
void DS1302_SetTime(unsigned char Address, unsigned char Value)        // 設置時間函數
{
	DS1302_SetProtect(0);
	Write1302(Address, ((Value/10)<<4 | (Value%10))); 		//將hex碼轉化爲BCD碼
}

/***************************************************************************
函數名稱:DS1302_GetTime(SYSTEMTIME *Time)
函數功能:讀出日期和時間,將它們存入Time這個結構體中
輸入參數:*Time		要存日期和時間的結構體的地址
返回值:  無 
***************************************************************************/
void DS1302_GetTime(SYSTEMTIME *Time)
{
	unsigned char ReadValue;
	ReadValue = Read1302(DS1302_SECOND);
	Time->Second = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);	//八進制轉爲十進制
	ReadValue = Read1302(DS1302_MINUTE);
	Time->Minute = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_HOUR);
	Time->Hour = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_DAY);
	Time->Day = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);	
	ReadValue = Read1302(DS1302_WEEK);
	Time->Week = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_MONTH);
	Time->Month = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);
	ReadValue = Read1302(DS1302_YEAR);
	Time->Year = ((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);	
}

/***************************************************************************
函數名稱:DateToStr(SYSTEMTIME *Time)	 
函數功能:將讀出的日期變成便於顯示的字符形式
輸入參數:*Time		要存字符的結構體
返回值:無 
***************************************************************************/
void DateToStr(SYSTEMTIME *Time)	 
{
	Time->DateString[0] = Time->Year/10+0x30 ;	 //·分離個位和十位
	Time->DateString[1] = Time->Year%10+0x30 ;
	Time->DateString[2] = '-';
	Time->DateString[3] = Time->Month/10+0x30;
	Time->DateString[4] = Time->Month%10+0x30 ;
	Time->DateString[5] = '-';
	Time->DateString[6] = Time->Day/10+0x30 ;
	Time->DateString[7] = Time->Day%10+0x30 ;  //用LCD顯示,要變成ascii碼所以加了0x30,用數碼管顯示的話就不用加了
	Time->DateString[8] = '\0';
}

/***************************************************************************
函數名稱:TimeToStr(SYSTEMTIME *Time)
函數功能:將讀出的時間變成便於顯示的字符形式
輸入參數:*Time		要存字符的結構體
返回值:無 
***************************************************************************/
void TimeToStr(SYSTEMTIME *Time)
{
	Time->TimeString[0] = Time->Hour/10+0x30 ;
	Time->TimeString[1] = Time->Hour%10+0x30 ;
	Time->TimeString[2] = ':';
	Time->TimeString[3] = Time->Minute/10+0x30 ;
	Time->TimeString[4] = Time->Minute%10+0x30 ;
	Time->TimeString[5] = ':';
	Time->TimeString[6] = Time->Second/10+0x30;
	Time->TimeString[7] = Time->Second%10+0x30 ;//用LCD顯示,要變成ascii碼所以加了0x30,用數碼管顯示的話就不用加了
	Time->DateString[8] = '\0';
}

/***************************************************************************
函數名稱:Initial_DS1302(void)
函數功能:初始化ds1302
輸入參數:無
返回值:無 
***************************************************************************/
void Initial_DS1302(void)
{
	unsigned char Second=Read1302(DS1302_SECOND);
	if(Second&0x80)		  
		DS1302_SetTime(DS1302_SECOND,0);
}
				   
/********************************************************************************
void BurstWrite1302(unsigned char *pWClock)	//往ds1302寫入時鐘數據(多字節方式)
{
    unsigned char i;
    Write1302(0x8e,0x00);         	// 控制命令,WP=0,寫操作
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(0xbe);        	// 0xbe:時鐘多字節寫命令
    for (i = 8; i>0; i--)     		//8Byte = 7Byte 時鐘數據 + 1Byte 控制
    {
        DS1302InputByte(*pWClock); 	// 寫1Byte數據
        pWClock++;
    }
    DS1302_CLK = 1;
    DS1302_RST = 0;
} 

void BurstRead1302(unsigned char *pRClock)	//讀取ds1302時鐘數據(時鐘多字節方式)
{
    unsigned char i;
    DS1302_RST = 0;
    DS1302_CLK = 0;
    DS1302_RST = 1;
    DS1302InputByte(0xbf);             	// 0xbf:時鐘多字節讀命令 
    for (i=8; i>0; i--) 
    {
       *pRClock = DS1302OutputByte();   // 讀1Byte數據 
       pRClock++;
    }
    DS1302_CLK = 1;
    DS1302_RST = 0;
}

void DS1302_TimeStop(bit flag)           // 是否將時鐘停止
{
	unsigned char Data;
	Data=Read1302(DS1302_SECOND);
	DS1302_SetProtect(0);
	if(flag)
		Write1302(DS1302_SECOND, Data|0x80);
	else
		Write1302(DS1302_SECOND, Data&0x7F);
}
********************************************************************************/
#ifndef _DS1302_H#define _DS1302_H

#include<reg52.h>#include"delay.h"#include<intrins.h>

#ifndef uchar #define uchar unsigned char#endifsbit  DS1302_CLK = P3^6;            //實時時鐘時鐘線引腳sbit  DS1302_IO  = P3^5;              //實時時鐘數據線引腳sbit  DS1302_RST = P3^4;              //實時時鐘復位線引腳//sbit  DS1302_CLK = P3^6;            //實時時鐘時鐘線引腳//sbit  DS1302_IO  = P3^4;              //實時時鐘數據線引腳//sbit  DS1302_RST = P3^5;              //實時時鐘復位線引腳

sbit  ACC0 = ACC^0;sbit  ACC7 = ACC^7;

typedef struct __SYSTEMTIME__{ unsigned int Second; unsigned char Minute; unsigned char Hour; unsigned char Week; unsigned char Day; unsigned char Month; unsigned char  Year; unsigned char DateString[9]; unsigned char TimeString[9];

}SYSTEMTIME; //定義的時間類型

#define AM(X) X#define PM(X) (X+12)                      // 轉成24小時制#define DS1302_SECOND 0x80#define DS1302_MINUTE 0x82#define DS1302_HOUR  0x84 #define DS1302_WEEK  0x8A#define DS1302_DAY  0x86#define DS1302_MONTH 0x88#define DS1302_YEAR  0x8C#define DS1302_RAM(X) (0xC0+(X)*2)    //用於計算ds1302RAM地址的宏

void DS1302InputByte(unsigned char d) ;unsigned char DS1302OutputByte(void)  ;void Write1302(unsigned char ucAddr, unsigned char ucDa);void DS1302_SetProtect(bit flag);unsigned char Read1302(unsigned char ucAddr);void DS1302_SetTime(unsigned char Address, unsigned char Value);void DS1302_GetTime(SYSTEMTIME *Time);void DateToStr(SYSTEMTIME *Time);void TimeToStr(SYSTEMTIME *Time);void Initial_DS1302(void);#end


   主要的程序模塊到這裏基本上就算準備好了。完整的程序我壓縮一下上傳到資源吧,(沒辦法想賺點積分,理解理解),其實到這步,大家應該把完整的程序寫出來也不是問題了。

   再來說說其他的吧。在使用keil軟件時,總是報這樣的錯誤*** ERROR L107: ADDRESS SPACE OVERFLOW。也是多方查找才找到問題所在。就是我們所定義變量是定義在51的RAM裏的,而且供變量存儲的只有256或者128個字節(看型號吧),這裏看網上說在變量前面加idata,然而並不管用。還是儘量節省RAM吧。只讀的數組定義前面加上code,全局變量儘量少點。不行就只能換單片機了畢竟51是一個資源很少 的單片機,不適合一些大工程。最後加上張效果圖

 

算了,我還是把,所有的程序也貼上來吧,也不在乎那幾個積分啦 。

下面的是按鍵處理程序(這個纔是核心程序),和主函數。我從KEIL上覆制過來的時候改了一下把edit configuration裏的Encode in ANSI 改成了Chinese GB2312.要不然複製過來時中文是亂碼。你複製到自己的工程裏時應該要改回來吧。

#include"keyProcess.h"
void array2show(ARRAY2SHOW *arrayshow0,uchar wch);	 //函數聲明//
void sec2show(SYSTEMTIME *secshow);
SYSTEMTIME showtime;  
extern SYSTEMTIME CurrentTime;
extern ARRAY2SHOW  Alarmandshow;
/****************************************************************************************************
函數名稱:key_process(uchar mode)
函數功能:按鍵處理函數(調節日期,時間,秒錶,鬧鐘)
輸入參數:mode		用來選擇模式,是修改日期,時間還是鬧鐘
返回值:無
****************************************************************************************************/
void key_process(uchar mode)
{	
	
	uchar Wch=0;
	uchar flag=0;
	uchar AlarmWch=0;
	uchar HourSecWch=0;
	uchar temp=0;
	switch(mode)		//在最外層循環中檢測按鍵,確定要設置什麼
	{
		DS1302_GetTime(&CurrentTime);
		case MODE0:			//設置時間
			showtime=CurrentTime;
			while(1)
			{	 				
				DateToStr(&CurrentTime);
				zifu_dis(1,0,&CurrentTime.DateString[0]); 		//修改時間不影響從1302讀日期顯示
				//(麻煩的思想)TArray3=show2array3(&CurrentTime.TimeString[0]);	//將顯示的字符形式變成可以直接加1的形式
				if(key_scan()==K1||key_scan()==K2||key_scan()==K3||key_scan()==K4)//檢測有沒有按鍵按下,有按鍵按下才執行操作
				{
					switch(key_scan())			//再次檢測按鍵
					{	  
						case K3:				//K3按下,選擇時間的哪一位被更改
								 
								Wch++;
								if(Wch==3)
									Wch=0;
								break;						
						case K1:			   //K1按下,數字加一
							   //(麻煩的思想)TArray3[TimeWch]++;			//轉化成單個字符形式顯示 
								if(Wch==0)
								{
									showtime.Hour++;
									if(showtime.Hour==24)
										showtime.Hour=0;
								}
								else if(Wch==1)
								{
									showtime.Minute++;
									if(showtime.Minute==60)
										showtime.Minute=0;
								}
								else if(Wch==2)
								{
									showtime.Second++;
								 	if(showtime.Second==60)
										showtime.Second=0;
								}
								TimeToStr(&showtime);
								zifu_dis(0,0,&showtime.TimeString[0]); 
								break;
	
						case K2:			   //K2按下,數字減一
								//(麻煩的思想)TArray3[TimeWch]--;
								//(麻煩的思想)zifu_dis(1,0,array32show(TArray3)); 
								if(Wch==0)
								{
									showtime.Hour--;
									if(showtime.Hour==0xff)
										showtime.Hour=0;
								}
								else if(Wch==1)
								{
									showtime.Minute--;
									if(showtime.Minute==0xff)
										showtime.Minute=0;
								}
								else if(Wch==2)
								{
									showtime.Second--;
									if(showtime.Second==0xff)
										showtime.Second=0;
								}
								TimeToStr(&showtime);
								zifu_dis(0,0,&showtime.TimeString[0]); 
								break;
	
						case K4:  			  //K4按下,確定修改,
								flag=1;break;
					 }	 	
			
				}
				 if(flag==1)		 //flag爲1時,確定修改,將1302裏的時間重置,並退到最初的模式檢測
				 {		
					DS1302_SetTime(DS1302_HOUR,showtime.Hour);	
					DS1302_SetTime(DS1302_MINUTE,showtime.Minute);
					DS1302_SetTime(DS1302_SECOND,showtime.Second);
					Wch=0;
					flag=0;
				 	break;
				 } 	
			}
			 break;
		case MODE1:			//設置日期
			showtime=CurrentTime;
			while(1)
			{
				DS1302_GetTime(&CurrentTime);
				TimeToStr(&CurrentTime);
				zifu_dis(0,0,&CurrentTime.TimeString[0]); 		   //修改日期,不影響從1302讀時間顯示
				//(麻煩的思想)DArray3=show2array3(&CurrentTime.DateString);	   //將顯示的字符形式變成可以直接加1的形式
				if(key_scan()==K1||key_scan()==K2||key_scan()==K3||key_scan()==K4)//檢測有沒有按鍵按下,有按鍵按下才執行操作
				{
					switch(key_scan())	//再次檢測按鍵
					{
						case K3:			    //K3按下,選擇日期的哪一位被更改
								Wch++;
								if(Wch==3)
									Wch=0;
								break;
						case K1:			   //K1按下,數字加一
							    //(麻煩的思想)DArray3[DateWch]=DArray3[DateWch]+1;
								//(麻煩的思想)zifu_dis(0,0,array32show(DArray3)); 
								if(Wch==0)
									showtime.Year++;
								else if(Wch==1)
								{
									showtime.Month++;
									if(showtime.Month==13)
										showtime.Month=1;
								}		
								else if(Wch==2)
								{
									showtime.Day++;
									if(showtime.Month==1||showtime.Month==3||showtime.Month==5||showtime.Month==7||showtime.Month==8||showtime.Month==10||showtime.Month==12)
										if(showtime.Day==32)
											showtime.Day=0;
									else if(showtime.Month==2)
										if(showtime.Day=30)
											showtime.Day=0;
									else
										if(showtime.Day==31)
											showtime.Day=0;	
								}
								DateToStr(&showtime);
								zifu_dis(1,0,&showtime.DateString[0]); 
								break;
						case K2:			   //K2按下,數字減一
								//(麻煩的思想)DArray3[DateWch]--;
								//(麻煩的思想)zifu_dis(0,0,array32show(DArray3)); 
								if(Wch==0)
									showtime.Year--;
								else if(Wch==1)
									showtime.Month--;
								else if(Wch==2)
									showtime.Day--;
								DateToStr(&showtime);
								zifu_dis(1,0,&showtime.DateString[0]); 
								break;
						case K4: 
											   //K4按下退出此循環,回到模式檢測循環
								flag=1;
								break;
					 }
				}
				 if(flag==1)	//flag爲1時,確定修改,將1302裏的日期重置,並退到最初的模式檢測
				 {
				 	DS1302_SetTime(DS1302_YEAR,showtime.Year);	
					DS1302_SetTime(DS1302_MONTH,showtime.Month);
					DS1302_SetTime(DS1302_DAY,showtime.Day);
					flag=0;
					Wch=0;
				 	break;
					
				 }
			}
			 break;
		case MODE2:			//設置鬧鐘
			while(1)
			{	
				DS1302_GetTime(&CurrentTime);
				DateToStr(&CurrentTime);
				TimeToStr(&CurrentTime);
				zifu_dis(0,0,&CurrentTime.TimeString[0]); 	 //在設置鬧鐘時不讓時間的顯示停下
				if(key_scan()==K1||key_scan()==K2||key_scan()==K3||key_scan()==K4)//檢測有沒有按鍵按下,有按鍵按下才執行操作
				{
					switch(key_scan())	//再次檢測按鍵
					{
						case K3:			    //K1按下,選擇哪一個鬧鐘被更改
								AlarmWch++;
								if(AlarmWch==4)
									AlarmWch=0;
								break;
						case K2:			   //K2按下,選擇鬧鐘的小時還是秒被更改
							    HourSecWch++;
								if(HourSecWch==2)
									HourSecWch=0;
								break;							   
						case K1:			   //K3按下,數字加1
								Alarmandshow.Alarm[AlarmWch][HourSecWch]++;
								if(Alarmandshow.Alarm[AlarmWch][HourSecWch]==60)
									Alarmandshow.Alarm[AlarmWch][HourSecWch]=0;
								array2show(&Alarmandshow,AlarmWch);
					
								zifu_dis(2+AlarmWch%2,2+AlarmWch/2*3,&Alarmandshow.showstring[0]);
								break;
						case K4: 			   //K4按下退出此循環,回到模式檢測循環
								flag=1;break;
					 }
				}
				 if(flag==1)
				 {
				 	AlarmWch=0;
					HourSecWch=0;		//最好要將AlarmWch,HourSecWch清零,後面要用
				 	flag=0;
				 	break;
					
				 }
			}	
			break;	
		case MODE3:					 //秒錶
				
				while(1)	//此層循環用來顯示秒錶的初始界面	  	
				{
					temp=0;
					showtime.Second=0;
					lcd_init();		
					zifu_dis(0,3,"00.0");
					if(key_scan()==K1)		//K1按下,秒錶開始計時
					{
						while(1)	//此層循環是秒錶開始後的循環
							{		 
								delay_ms(73);		//再算上程序執行的時間,一共爲100ms				 
								sec2show(&showtime);
								zifu_dis(0,3,&showtime.TimeString[0]);	
								if(flag==0)
								showtime.Second++;		//每過100ms,Second++,
								switch(key_scan())
								{
									case K1:
										zifu_dis(temp/3+1,temp*3%9,&showtime.TimeString[0]);			
										temp++;		//讀一下秒錶,記錄下
										if(temp==9)
											temp=0;
										break;
									case K2:
										flag=~flag;
										break;
									case K3:
										flag=2;
										break;
									case K4:
										flag=1;
										break;
								}	
								if(flag==2||flag==1)
								{
									if(flag==2)
										flag=0;
									break;	
								}
							}
					}
					if(flag==1)
					{
						flag=0;
						break;
					}
				}
			lcd_init();
			for(;AlarmWch<4;AlarmWch++)
			{
				//arrayshow.array2[AlarmWch][HourSecWch]=0;
				array2show(&Alarmandshow,AlarmWch);
				zifu_dis(AlarmWch/2+2,AlarmWch%2*3+2,&Alarmandshow.showstring[0]);
			}
			zifu_dis(2,0,"鬧鐘");
			AlarmWch=0;
			HourSecWch=0;
			break;				
	}
}
 
/***********************************************************************************************
函數名稱:array32show(uchar *array3)
函數功能:將存在array[3]裏的小時,分鐘,秒轉換成可以直接顯示的形式
輸入參數:*array3 		array[3]的首地址
返回值:  show			show[9]的首地址,可以直接用來顯示
*************************************************************************************************/
/*uchar *array32show(uchar *array3)
{
	uchar show[5];
	show[0] = *array3/10+0x30 ;
	show[1] = *array3++%10+0x30 ;
	show[2] = ':';
	show[3] = *array3/10+0x30 ;
	show[4] = *array3%10+0x30 ;//用LCD顯示,要變成ascii碼所以加了0x30,用數碼管顯示的話就不用加了
	show[5] = '\0';
	return show;	
} *///沒有用到

/***********************************************************************************************
函數名稱:show2array3(uchar *show)
函數功能:將存在show[]裏的可直接顯示的字符轉換成可以直接加一的array[3]
輸入參數:*show 		show數組的首地址
返回值:  array3		array數組的首地址,可以直接用來做加一操作
*************************************************************************************************/
/*uchar *show2array3(uchar *show)
{
	uchar array3[3];
	array3[0]=(show[0]-0x30)*10+(show[1]-0x30);
	array3[1]=(show[3]-0x30)*10+(show[4]-0x30);		
	array3[2]=(show[6]-0x30)*10+(show[7]-0x30);
	return array3;	
}*/ 

void array2show(ARRAY2SHOW *arrayshow0,uchar wch)
{
	arrayshow0->showstring[0] = arrayshow0->Alarm[wch][0]/10+0x30 ;
	arrayshow0->showstring[1] = arrayshow0->Alarm[wch][0]%10+0x30 ;
	arrayshow0->showstring[2] =':';
	arrayshow0->showstring[3] = arrayshow0->Alarm[wch][1]/10+0x30 ;
	arrayshow0->showstring[4] = arrayshow0->Alarm[wch][1]%10+0x30 ;
	//用LCD顯示,要變成ascii碼所以加了0x30,用數碼管顯示的話就不用加了
	arrayshow0->showstring[5] = '\0';
}

void sec2show(SYSTEMTIME *secshow)
{
	secshow->TimeString[0]=secshow->Second/100+0x30;
	secshow->TimeString[1]=secshow->Second%100/10+0x30;
	secshow->TimeString[2]='.';
	secshow->TimeString[3]=secshow->Second%10+0x30;
	secshow->TimeString[4]='\0';
}

#ifndef __KEYPROCESS_H
#define _KEYPROCESS_H

#include<reg52.h>
#include<stdio.h>
#include"delay.h"
#include"key.h"
#include"ds1302.h"
#include"LCD12864.h"


#ifndef uchar
	#define uchar 	unsigned char
#endif

typedef struct _ARRAYSHOW_
{
	unsigned char   showstring[6];
	unsigned char  Alarm[4][2];
}ARRAY2SHOW;

typedef struct _SHOW_
{
	unsigned char   showstring[6];
	unsigned char  array2[4][2];
}show;

#define MODE0 		0X00
#define MODE1		0X01
#define MODE2 		0X02
#define MODE3		0X03
#define TIMESET		MODE0	
#define DATESET		MODE1
#define ALARMSET	MODE2
#define SECCON		MODE3

void key_process(uchar mode);

#endif

/***********************************************************************************
程序說明:利用12864液晶和ds1302配合按鍵實現 萬年曆,四路可調鬧鐘,秒錶(基於51單片機)
作者:哈爾濱工程大學  黃上城
***********************************************************************************/
#include <reg52.h>
#include<stdio.h>
#include"delay.h"
#include"ds1302.h"
#include"LCD12864.h"
#include"key.h"
#include"buzzer.h"
#include"keyProcess.h"
SYSTEMTIME  CurrentTime;	//存儲當前從ds1302中讀到的時間日期等
ARRAY2SHOW  Alarmandshow;	//存儲鬧鐘的時間,和用於鬧鐘顯示的字符串
char code table[7][20]={{"星期壹"},{"星期貳"},{"星期叄"},{"星期肆"},{"星期伍"},{"星期陸"},{"星期日"}};
sbit led=P1^7;
void main()
{	
	uchar mode;
	Initial_DS1302();				//ds1302初始化
//	DS1302_SetTime(DS1302_HOUR,10);	
//	DS1302_SetTime(DS1302_MINUTE,0);
//	DS1302_SetTime(DS1302_SECOND,0);//向ds1302中寫初始時間
//	DS1302_SetTime(DS1302_YEAR,17);	
//	DS1302_SetTime(DS1302_MONTH,1);
//	DS1302_SetTime(DS1302_DAY,16);	//向ds1302中寫初始日期
	DS1302_SetTime(DS1302_WEEK,3);
	
	lcd_init();						//lcd12864初始化
	zifu_dis(2,0,"鬧鐘");
	zifu_dis(2,2,"00:00");
	zifu_dis(2,5,"00:00");
	zifu_dis(3,2,"00:00");
	zifu_dis(3,5,"00:00");			 //設置鬧鐘的初始顯示
  	
	while(1)
	{
		if(key_scan()==K1||key_scan()==K2||key_scan()==K3||key_scan()==K4)
		{				
			switch (key_scan())
			{	
				case K1: mode=MODE0;break;		//MODE0設置時間
				case K2: mode=MODE1;break;		//MODE0設置日期
				case K3: mode=MODE2;break;		//MODE0設置鬧鐘
				case K4: mode=MODE3;break;		//MODE0設置秒錶
			}
			key_process(mode);				   //按鍵處理函數
		}		
		DS1302_GetTime(&CurrentTime);
		DateToStr(&CurrentTime);
		TimeToStr(&CurrentTime);
		zifu_dis(0,0,&CurrentTime.TimeString[0]); 
		zifu_dis(1,0,&CurrentTime.DateString[0]); 		 //讀出ds1302裏的時間,在lcd上顯示
		zifu_dis(1,4,table[CurrentTime.Week]);
		
		if((CurrentTime.Hour==Alarmandshow.Alarm[0][0]&&CurrentTime.Minute==Alarmandshow.Alarm[0][1])||
		   (CurrentTime.Hour==Alarmandshow.Alarm[1][0]&&CurrentTime.Minute==Alarmandshow.Alarm[1][1])||
		   (CurrentTime.Hour==Alarmandshow.Alarm[2][0]&&CurrentTime.Minute==Alarmandshow.Alarm[2][1])||
		   (CurrentTime.Hour==Alarmandshow.Alarm[3][0]&&CurrentTime.Minute==Alarmandshow.Alarm[3][1]))
		   //檢查所設的鬧鐘時間和現在的時間是否一致,是則響蜂鳴器。
		   
				buzzer_delay();
	}		
}
												
這個是蜂鳴器要用到的,就是一個IO口拉高拉低。
#include"buzzer.h"

void buzzer_on(void)
{
		BuzzerPort=0;
}

void buzzer_off(void)
{
		BuzzerPort=1;
}

void buzzer_delay(void)
{
		BuzzerPort=0;
		delay_ms(400);
		BuzzerPort=1;
		delay_ms(400);
}
#ifndef __BUZZER_H
#define _BUZZER_H

#include<reg52.h>
#include"delay.h"

#ifndef uchar
	#define uchar 	unsigned char
#endif

sbit BuzzerPort=P2^2;

void buzzer_on(void);
void buzzer_off(void);
void buzzer_delay(void);

#endif

 完工~

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

     

 

 

         

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章