1. 程式人生 > >LCD顯示時鐘,可斷電儲存,調整時間,調整鬧鐘

LCD顯示時鐘,可斷電儲存,調整時間,調整鬧鐘

/*接線:
P2.2   ---> j42.b1(蜂鳴器)
P2.1   ---> iic.SDA
P2.0   ---> iic.SCL
P1     ---> j24(矩陣鍵盤)


矩陣鍵盤按鍵:+、-


問題:
1、對應修改位置閃爍時不能通過按鍵賦值
*/


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


void DelayUs2x(unsigned char t);
void DelayMs(unsigned char t);


unsigned char KeyScan(void);
unsigned char KeyPro(void);
 
void LCD_Write_Com(unsigned char com);
void LCD_Write_Data(unsigned char Data);
void LCD_Clear(void) ;
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s);
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data);
void LCD_Init(void);


void Start_I2c();
void Stop_I2c();
void Ack_I2c(void);
void NoAck_I2c(void);
void SendByte(unsigned char c);
unsigned char  RcvByte();
bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);
bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no);


void time_to_str();//時間轉為字串
void str_to_time();//字串轉為時間
void clock();
  
unsigned char hour,min,sec;
unsigned char clock_h = 0,clock_m = 0;
unsigned char store[6];
unsigned char local = 0;
unsigned char revise_num;


sbit SPK=P2^2;                                              
sbit SDA=P2^1;            //模擬I2C資料傳送位SCL
sbit SCL=P2^0;            //模擬I2C時鐘控制位SDA


sbit RS = P2^4;   //定義埠 
sbit RW = P2^5;
sbit EN = P2^6; 
                           
bit ack;             //應答標誌位


#define  _Nop()  _nop_()        //定義空指令
#define RS_CLR RS=0 
#define RS_SET RS=1


#define RW_CLR RW=0 
#define RW_SET RW=1 


#define EN_CLR EN=0
#define EN_SET EN=1


#define DataPort P0
#define KeyPort  P1


void main()
{
    unsigned char i;
    LCD_Init();
   
    TMOD = 0x01;//初始化計數器
    TH0 = 0x3c;//50ms
TL0 = 0xb0;
EA = 1;
ET0 = 1;
TR0 = 1; 


/*hour = 23; //初始化時間設定
min = 59;
sec = 49;*/


IRcvStr(0xae,4,store,5);  //呼叫儲存資料
str_to_time();


while(1)
{   
 
LCD_Write_String(1,0,"time:");
LCD_Write_String(0,1,"clock:");


if(local != 1)
LCD_Write_Char(7,0,(hour/10)+ '0');
else
{
while(local == 1)
{
   LCD_Write_Char(8,0,(hour%10)+ '0');
   LCD_Write_Char(7,0,' ');
DelayMs(255);
LCD_Write_Char(7,0,(hour/10)+ '0');
DelayMs(255);
hour+=10;
if(hour >= 24 && hour <= 29)
hour-=20;
if(hour >=30)
hour -= 30;
}
//hour-=10;
}


if(local != 2)
LCD_Write_Char(8,0,(hour%10)+ '0');
else
{
while(local == 2)
{
   LCD_Write_Char(7,0,(hour/10)+ '0');
   LCD_Write_Char(8,0,' ');
DelayMs(255);
LCD_Write_Char(8,0,(hour%10)+ '0');
DelayMs(255);


hour++;
if(hour>=24)
hour-=24;
}
hour--;
}


LCD_Write_Char(9,0,':');


if(local != 3)
LCD_Write_Char(10,0,(min/10)+ '0');
else
{
while(local == 3)
{
LCD_Write_Char(11,0,(min%10)+ '0');
   LCD_Write_Char(10,0,' ');
DelayMs(255);
LCD_Write_Char(10,0,(min/10)+ '0');
DelayMs(255);


min += 10;
if(min >= 60)
min -= 60;
}
//min-=10;
}


if(local != 4)
LCD_Write_Char(11,0,(min%10)+ '0');
else
{
while(local == 4)
{
LCD_Write_Char(10,0,(min/10)+ '0');
   LCD_Write_Char(11,0,' ');
DelayMs(255);
LCD_Write_Char(11,0,(min%10)+ '0');
DelayMs(255);


min++;
if(min >= 60 )
min-=60;
}
min--;
}


LCD_Write_Char(12,0,':');


if(local != 5)
LCD_Write_Char(13,0,(sec/10)+ '0');
else
{
while(local == 5)
{
   LCD_Write_Char(14,0,(sec%10)+ '0');
   LCD_Write_Char(13,0,' ');
DelayMs(255);
LCD_Write_Char(13,0,(sec/10)+ '0');
DelayMs(255);


sec+=10;
if(sec >= 60)
sec-=60;
}
//sec-=10;
}


if(local != 6)
LCD_Write_Char(14,0,(sec%10)+ '0');
else
{
while(local == 6)
{
LCD_Write_Char(13,0,(sec/10)+ '0');
   LCD_Write_Char(14,0,' ');
DelayMs(255);
LCD_Write_Char(14,0,(sec%10)+ '0');
DelayMs(255);
sec++;
if(sec >= 60)
sec-=60;
}
sec--;
}


if(local != 7)
LCD_Write_Char(7,1,(clock_h/10)+ '0');
else
{
while(local == 7)
{
   LCD_Write_Char(8,1,(clock_h%10)+ '0');
   LCD_Write_Char(7,1,' ');
DelayMs(255);
LCD_Write_Char(7,1,(clock_h/10)+ '0');
DelayMs(255);
clock_h+=10;
if(clock_h >= 24 && hour <= 29)
clock_h-=20;
if(clock_h >=30)
clock_h -= 30;
}
//clock_h-=10;
}


if(local != 8)
LCD_Write_Char(8,1,(clock_h%10)+ '0');
else
{
while(local == 8)
{
   LCD_Write_Char(7,1,(clock_h/10)+ '0');
   LCD_Write_Char(8,1,' ');
DelayMs(255);
LCD_Write_Char(8,1,(clock_h%10)+ '0');
DelayMs(255);


clock_h++;
if(clock_h>=24)
clock_h-=24;
}
clock_h--;
}


LCD_Write_Char(9,1,':');


if(local != 9)
LCD_Write_Char(10,1,(clock_m/10)+ '0');
else
{
while(local == 9)
{
LCD_Write_Char(11,1,(clock_m%10)+ '0');
   LCD_Write_Char(10,1,' ');
DelayMs(255);
LCD_Write_Char(10,1,(clock_m/10)+ '0');
DelayMs(255);


clock_m += 10;
if(clock_m >= 60)
clock_m -= 60;
}
//clock_m-=10;
}


if(local != 10)
LCD_Write_Char(11,1,(clock_m%10)+ '0');
else
{
while(local == 10)
{
LCD_Write_Char(10,1,(clock_m/10)+ '0');
   LCD_Write_Char(11,1,' ');
DelayMs(255);
LCD_Write_Char(11,1,(clock_m%10)+ '0');
DelayMs(255);


clock_m++;
if(clock_m >= 60 )
clock_m-=60;
}
clock_m--;
}


//LCD_Write_String(2,1,store);
time_to_str();
ISendStr(0xae,4,store,5); //寫入24c02
 
        


if(sec >= 60)
{
sec = 0;
min++;
}
if(min >= 60)
{
min = 0;
hour++;
for(i=0;i<200;i++)
{
DelayMs(1);  
SPK=!SPK;
}
}
if(hour >= 24)
{
hour -= 24;
//min = 0;
//sec = 0;
}
         
clock();
}
}


void DelayUs2x(unsigned char t)
{   
    while(--t);
}


void DelayMs(unsigned char t)
{    
    while(t--)
    {
        //大致延時1mS
        DelayUs2x(245);
   DelayUs2x(245);
    }
}


void time_to_str()//時間轉為字串
{
    store[0] = hour;
store[1] = min;
store[2] = sec;
store[3] = clock_h;
store[4] = clock_m;
store[5] = '\0';
}


void str_to_time()//字串轉為時間
{
    hour = store[0];
min  = store[1];
sec  = store[2];
clock_h = store[3];
clock_m = store[4];
}


/*------------------------------------------------
              寫入命令函式
------------------------------------------------*/
 void LCD_Write_Com(unsigned char com) 
 {  
DelayMs(5);
RS_CLR; 
RW_CLR; 
EN_SET; 
DataPort= com; 
_nop_(); 
EN_CLR;
 }
/*------------------------------------------------
              寫入資料函式
------------------------------------------------*/
 void LCD_Write_Data(unsigned char Data) 
 { 
DelayMs(5);
RS_SET; 
RW_CLR; 
EN_SET; 
DataPort= Data; 
_nop_();
EN_CLR;
 }




 void LCD_Clear(void)  //清屏
 { 
LCD_Write_Com(0x01); 
DelayMs(5);
 }


void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)  //寫入字串
{     
    if (y == 0) 
 {     
   LCD_Write_Com(0x80 + x);     
 }
    else 
 {     
   LCD_Write_Com(0xC0 + x);     
 }  
     
    while (*s) 
 {     
       LCD_Write_Data( *s);     
       s ++;     
 }
}


void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)  //寫入字元
{     
    if (y == 0) 
 {     
   LCD_Write_Com(0x80 + x);     
 }    
    else 
 {     
   LCD_Write_Com(0xC0 + x);     
 } 
      
    LCD_Write_Data( Data);  
}


void LCD_Init(void) //初始化
{
    LCD_Write_Com(0x38);    /*顯示模式設定*/ 
    DelayMs(5); 
    LCD_Write_Com(0x38); 
    DelayMs(5); 
    LCD_Write_Com(0x38); 
    DelayMs(5); 
    LCD_Write_Com(0x38);  
    LCD_Write_Com(0x08);    /*顯示關閉*/ 
    LCD_Write_Com(0x01);    /*顯示清屏*/ 
    LCD_Write_Com(0x06);    /*顯示游標移動設定*/ 
    DelayMs(5); 
    LCD_Write_Com(0x0C);    /*顯示開及游標設定*/
}




void Start_I2c()   //啟動匯流排
{
  SDA=1;   //傳送起始條件的資料訊號
  _Nop();
  SCL=1;
  _Nop();    //起始條件建立時間大於4.7us,延時
  _Nop();
  _Nop();
  _Nop();
  _Nop();    
  SDA=0;     //傳送起始訊號
  _Nop();    //起始條件鎖定時間大於4μ
  _Nop();
  _Nop();
  _Nop();
  _Nop();       
  SCL=0;    //鉗住I2C匯流排,準備傳送或接收資料
  _Nop();
  _Nop();
}
/*------------------------------------------------
                    結束匯流排
------------------------------------------------*/
void Stop_I2c()
{
  SDA=0;    //傳送結束條件的資料訊號
  _Nop();   //傳送結束條件的時鐘訊號
  SCL=1;    //結束條件建立時間大於4μ
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SDA=1;    //傳送I2C匯流排結束訊號
  _Nop();
  _Nop();
  _Nop();
  _Nop();
}








/*----------------------------------------------------------------
                 位元組資料傳送函式               
函式原型: void  SendByte(unsigned char c);
功能:  將資料c傳送出去,可以是地址,也可以是資料,發完後等待應答,並對
     此狀態位進行操作.(不應答或非應答都使ack=0 假)     
     傳送資料正常,ack=1; ack=0表示被控器無應答或損壞。
------------------------------------------------------------------*/
void  SendByte(unsigned char c)
{
 unsigned char BitCnt;
 
 for(BitCnt=0;BitCnt<8;BitCnt++)  //要傳送的資料長度為8位
    {
     if((c<<BitCnt)&0x80)SDA=1;   //判斷髮送位
       else  SDA=0;                
     _Nop();
     SCL=1;               //置時鐘線為高,通知被控器開始接收資料位
      _Nop(); 
      _Nop();             //保證時鐘高電平週期大於4μ
      _Nop();
      _Nop();
      _Nop();         
     SCL=0; 
    }
    
    _Nop();
    _Nop();
    SDA=1;               //8位傳送完後釋放資料線,準備接收應答位
    _Nop();
    _Nop();   
    SCL=1;
    _Nop();
    _Nop();
    _Nop();
    if(SDA==1)ack=0;     
       else ack=1;        //判斷是否接收到應答訊號
    SCL=0;
    _Nop();
    _Nop();
}














/*----------------------------------------------------------------
                 位元組資料傳送函式               
函式原型: unsigned char  RcvByte();
功能:  用來接收從器件傳來的資料,並判斷匯流排錯誤(不發應答訊號),
     發完後請用應答函式。  
------------------------------------------------------------------*/
unsigned char  RcvByte()
{
  unsigned char retc;
  unsigned char BitCnt;
  
  retc=0; 
  SDA=1;             //置資料線為輸入方式
  for(BitCnt=0;BitCnt<8;BitCnt++)
      {
        _Nop();           
        SCL=0;       //置時鐘線為低,準備接收資料位
        _Nop();
        _Nop();      //時鐘低電平週期大於4.7us
        _Nop();
        _Nop();
        _Nop();
        SCL=1;       //置時鐘線為高使資料線上資料有效
        _Nop();
        _Nop();
        retc=retc<<1;
        if(SDA==1)retc=retc+1; //讀資料位,接收的資料位放入retc中
        _Nop();
        _Nop(); 
      }
  SCL=0;    
  _Nop();
  _Nop();
  return(retc);
}






/*----------------------------------------------------------------
                     應答子函式
原型:  void Ack_I2c(void);
 
----------------------------------------------------------------*/
void Ack_I2c(void)
{
  
  SDA=0;     
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();              //時鐘低電平週期大於4μ
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;               //清時鐘線,鉗住I2C匯流排以便繼續接收
  _Nop();
  _Nop();    
}
/*----------------------------------------------------------------
                     非應答子函式
原型:  void NoAck_I2c(void);
 
----------------------------------------------------------------*/
void NoAck_I2c(void)
{
  
  SDA=1;
  _Nop();
  _Nop();
  _Nop();      
  SCL=1;
  _Nop();
  _Nop();              //時鐘低電平週期大於4μ
  _Nop();
  _Nop();
  _Nop();  
  SCL=0;                //清時鐘線,鉗住I2C匯流排以便繼續接收
  _Nop();
  _Nop();    
}




/*----------------------------------------------------------------
                    向有子地址器件傳送多位元組資料函式               
函式原型: bit  ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);  
功能:     從啟動匯流排到傳送地址,子地址,資料,結束匯流排的全過程,從器件
          地址sla,子地址suba,傳送內容是s指向的內容,傳送no個位元組。
           如果返回1表示操作成功,否則操作有誤。
注意:    使用前必須已結束匯流排。
----------------------------------------------------------------*/
bit ISendStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
{
   unsigned char i;


   Start_I2c();               //啟動匯流排
   SendByte(sla);             //傳送器件地址
     if(ack==0)return(0);
   SendByte(suba);            //傳送器件子地址
     if(ack==0)return(0);


   for(i=0;i<no;i++)
    {   
     SendByte(*s);            //傳送資料
       if(ack==0)return(0);
     s++;
    } 
 Stop_I2c();                  //結束匯流排
  return(1);
}


/*----------------------------------------------------------------
                    向有子地址器件讀取多位元組資料函式               
函式原型: bit  ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no);  
功能:     從啟動匯流排到傳送地址,子地址,讀資料,結束匯流排的全過程,從器件
          地址sla,子地址suba,讀出的內容放入s指向的儲存區,讀no個位元組。
           如果返回1表示操作成功,否則操作有誤。
注意:    使用前必須已結束匯流排。
----------------------------------------------------------------*/
bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
{
   unsigned char i;


   Start_I2c();               //啟動匯流排
   SendByte(sla);             //傳送器件地址
     if(ack==0)return(0);
   SendByte(suba);            //傳送器件子地址
     if(ack==0)return(0);


   Start_I2c();
   SendByte(sla+1);
      if(ack==0)return(0);


   for(i=0;i<no-1;i++)
    {   
     *s=RcvByte();              //傳送資料
      Ack_I2c();                //傳送就答位 
     s++;
    } 
   *s=RcvByte();
    NoAck_I2c();                 //傳送非應位
   Stop_I2c();                    //結束匯流排
  return(1);
}




/*------------------------------------------------
        按鍵掃描函式,返回掃描鍵值
------------------------------------------------*/
unsigned char KeyScan(void)  //鍵盤掃描函式,使用行列逐級掃描法
{
unsigned char Val;
KeyPort=0xf0;//高四位置高,低四位拉低
if(KeyPort!=0xf0)//表示有按鍵按下
{
   DelayMs(10);  //去抖
if(KeyPort!=0xf0)
{           //表示有按鍵按下
   KeyPort=0xfe; //檢測第一行
if(KeyPort!=0xfe)
 {
   Val=KeyPort&0xf0;
    Val+=0x0e;
 while(KeyPort!=0xfe);
DelayMs(10); //去抖
while(KeyPort!=0xfe);
    return Val;
   }
   
       KeyPort=0xfd; //檢測第二行
if(KeyPort!=0xfd)
 {
Val=KeyPort&0xf0;
    Val+=0x0d;
 while(KeyPort!=0xfd);
DelayMs(10); //去抖
while(KeyPort!=0xfd);
    return Val;
   }
   
   KeyPort=0xfb; //檢測第三行
if(KeyPort!=0xfb)
 {
Val=KeyPort&0xf0;
    Val+=0x0b;
 while(KeyPort!=0xfb);
DelayMs(10); //去抖
while(KeyPort!=0xfb);
    return Val;
   }
   
   KeyPort=0xf7; //檢測第四行
if(KeyPort!=0xf7)
 {
Val=KeyPort&0xf0;
    Val+=0x07;
 while(KeyPort!=0xf7);
DelayMs(10); //去抖
while(KeyPort!=0xf7);
    return Val;
   }
   }
}
return 0xff;
}


/*------------------------------------------------
         按鍵值處理函式,返回掃鍵值
------------------------------------------------*/
unsigned char KeyPro(void)
{
switch(KeyScan())
{
case 0x7e:return 1;break;// 按下相應的鍵顯示相對應的碼值
case 0x7d:return 4;break;
case 0x7b:return 7;break;
case 0x77:return 0;break;
case 0xbe:return 2;break;
case 0xbd:return 5;break;
case 0xbb:return 8;break;
case 0xb7:return '.';break;
case 0xde:return 3;break;
case 0xdd:return 6;break;
case 0xdb:return 9;break;
case 0xd7:return '=';break;
case 0xee:return '+';break;
case 0xed:return '-';break;
case 0xeb:return '*';break;
case 0xe7:return '/';break;
default:return 0xff;break;
}
}


void clock()
{
    unsigned int i;
    if(hour == clock_h && min == clock_m && sec <=3)
{
   for(i=0;i<1000;i++)
{
DelayMs(1);  
SPK=!SPK;
}
}
}


void ISR_T0(void) interrupt 1
{
unsigned char i,num;
TH0 = 0x3c; //50ms
TL0 = 0xb0;


num = KeyPro();
if(num == '+')
{
local++;
if(local == 11)
local = 0;
}
else if( num == '-')
{
   local--;
if(local == 0xff)
local = 10;
}


if( local == 0 )
{
   i++;
   if(i == 20)
{
i = 0;
sec++;
SPK=!SPK;


}
}
}