<acronym id="xonnx"></acronym>
      <td id="xonnx"></td>
    1. <pre id="xonnx"></pre>

      1. 專注電子技術學習與研究
        當前位置:單片機教程網 >> MCU設計實例 >> 瀏覽文章

        單片機開源項目之按鍵可調時電子鐘(矩陣按鍵+紅外遙控按鍵進行調時)

        作者:寒竹子   來源:本站原創   點擊數:  更新時間:2014年05月02日   【字體:

         此程序是基于51hei單片機開發板上面寫的,如需要移植到自己的電路上,修改相應的端口即可,開發板完整的電路圖下載:  點這里 (注意:只需要看相關部分即可如矩陣鍵盤 數碼管 紅外等,其他部分可以忽略)

        /**
          ************************************************************************
         * @file    :   main.c
         * @author  :   xr
         * @date    :   2014年4月21日 22:23:12 - 2014年4月26日21:22:29
         * @version :   V1.2.3
         * @brief   :   按鍵可調時電子鐘(矩陣按鍵+紅外遙控按鍵進行調時)   單片機STC89C52RC MCU 晶振 11.0592MHZ
         ************************************************************************
         */

        #include <reg52.h>
        #include "main.h"
        /*定義結構體來封裝從DS1302中讀取的時間和日期和設置到DS1302中的時間和日期*/
        struct Time {
         unsigned char year;  //DS1302中只存放的是年的低兩位字節
          unsigned char month;
         unsigned char day;
            unsigned char hour;
         unsigned char min;
         unsigned char sec;
         unsigned char week;
        };
        /*定義結構體時間變量來保存時間和日期*/
        struct Time timeBuf; //此處必須用結構體變量,不能用結構體指針否則寫入失敗!
        unsigned char setTimeIndex = 0; //設置時間狀態及設置光標位置及設置時間位置的索引值(0時正常運行,1-12為設置時間狀態,1-12是設置的位置)
        bit flag200ms = 0;
        unsigned char thr0, tlr0;
        //紅外通信解碼的鍵碼和標準PC機編碼映射表
        unsigned char code IrdCodeMap[] = {0x45, 0x46, 0x47, //開關,Mode, 靜音
                                           0x44, 0x40, 0x43, //播放/暫停 快退, 快進
                                           0x07, 0x15, 0x09, //EQ, 減, 加
                                           0x16, 0x19, 0x0D, //0, 返回, U/SD
                                           0x0C, 0x18, 0x5E, //1, 2, 3
                                           0x08, 0x1C, 0x5A, //4, 5, 6
                                           0x42, 0x52, 0x4A};//7, 8, 9
        //外部變量聲明
        extern bit flagIrd; //紅外數據碼值接收完畢標志位
        extern unsigned char irdCode[4]; //保存NEC協議解碼的四個字節的數據碼(用戶碼+用戶反碼,鍵碼+鍵碼反碼)
        extern void InitLCD1602();
        extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);
        extern void LcdSetCoursor(unsigned char x, unsigned char y);
        extern void LcdOpenCoursor();
        extern void InitDS1302();
        extern void GetTimeFromDS1302(struct Time * time);
        extern void SetTimeToDS1302(struct Time * time);
        extern void KeyDriver();
        extern void KeyScan();
        extern void LcdCoursorRight();
        extern void LcdCoursorLeft();
        void ConfgiTimer0(unsigned int xms);
        void RefreshLcdShowTime();
        extern void ConfigIrdByTimer1();
        void IrdDrive();
        void IrdKeyAction();
        extern void SetTimeBcdByte(unsigned char keyNum);
        /*主程序main()*/
        void main()
        {
            unsigned char psec = 0xFF;//用于檢測sec秒是否變化,若變化則刷新時間顯示
            ConfgiTimer0(1); //定時1ms
            ConfigIrdByTimer1();//配置紅外通信
         InitLCD1602();
            InitDS1302();
           
            /*液晶初始化顯示*/
            LcdShowStr(1, 0, "*");
         LcdShowStr(2, 0, "20  -  -  ");
            LcdShowStr(12, 0, "*");
            LcdShowStr(14, 0, "--");
         LcdShowStr(0, 1, "time: --:--:--");
         
         while (1)
            {
               
                KeyDriver();//檢測按鍵動作
               
                if (flagIrd)
                {
                    flagIrd = 0;
                    IrdKeyAction();//檢測紅外按鍵動作
                }
               
                if (flag200ms == 1 && (setTimeIndex == 0))  //每200ms且setTimeIndex==0處于非設定時間狀態時刷新一次時間顯示
                {
                    flag200ms = 0;
                    GetTimeFromDS1302(&timeBuf); //從DS1302中獲取時間到timeBuf結構體指針變量的成員中
                    if (timeBuf.sec != psec) //當前秒值和上一次的秒值不相等
                    {
                        RefreshLcdShowTime();//刷新時間顯示
                        psec = timeBuf.sec;//備份當前的秒值(秒寄存器值)
                    }
                }
            }   
           
        }
        /*定時器T0配置*/
        void ConfgiTimer0(unsigned int xms)
        {
            unsigned long tmp;
            tmp = 11059200/12;//周期頻率
            tmp = (tmp * xms) / 1000;//定時xms需要的計數值
            tmp = 65536-tmp;//定時裝入的初值
            thr0 = (unsigned char)(tmp >> 8);
            tlr0 = (unsigned char)tmp;
            TMOD &= 0xF0;//清零T0控制位
            TMOD |= 0x01;//T0方式1,16位可設定時模式
            TH0 = thr0;
            TL0 = tlr0;
            TR0 = 1;
            ET0 = 1;
            EA  = 1;
        }
        /*將一個BCD碼字節數據分解顯示到LCD1602的(x, y)坐標上*/
        void LcdShowBCDByte(unsigned char x, unsigned char y, unsigned char bcdbyte)
        {
            unsigned char str[4];
           
            str[0] = (bcdbyte >> 4) + '0';//取BCD碼的高四位字節
            str[1] = (bcdbyte & 0x0F) + '0';//取BCD碼的第四位字節
            str[2] = '\0';
           
            LcdShowStr(x, y, str);
        }
        /*刷新時間顯示到LCD1602液晶上*/
        void RefreshLcdShowTime()
        {
            LcdShowBCDByte(4, 0, timeBuf.year); //顯示年
            LcdShowBCDByte(7, 0, timeBuf.month);
            LcdShowBCDByte(10, 0, timeBuf.day);
            LcdShowBCDByte(6, 1, timeBuf.hour);
            LcdShowBCDByte(9, 1, timeBuf.min);
            LcdShowBCDByte(12, 1, timeBuf.sec);
            LcdShowBCDByte(14, 0, timeBuf.week); //顯示星期
        }
        /************以下函數功能是BCD碼字節的高位和低位數字+1和-1*******************/
        /*遞增BCD碼的高位數字*/
        unsigned char IncrementBCDByteHigh(unsigned char bcdbyte)
        {
            if ((bcdbyte & 0xF0) < 0x90) //取bcdbyte的高四位字節是否小于9
            {
                bcdbyte += 0x10;//高四位字節+1
            }
            else
            {
                //高四位字節數值到9歸零
                bcdbyte &= 0x0F;//0000 1111
            }
           
            return (bcdbyte); //返回修改后的BCD碼值
        }
        /*遞增BCD碼的低位字節數字*/
        unsigned char IncrementBCDByteLow(unsigned char bcdbyte)
        {
            if ((bcdbyte & 0x0F) < 0x09) //取bcdbyte的低四位字節數字
            {
                bcdbyte += 0x01; //低位字節+1
            }
            else
            {
                //到9歸零
                bcdbyte &= 0xF0;//低四位清零
            }
           
            return (bcdbyte);
        }
        /*遞減BCD碼數據字節的高字節數字*/
        unsigned char DescBCDByteHigh(unsigned char bcdbyte)
        {
            if ((bcdbyte & 0xF0) > 0) //取BCD碼字節的高四位字節
            {
                bcdbyte -= 0x10; //高四位字節數字-1
            }
            else
            {
                //到0歸9
                bcdbyte |= 0x90; //或bcdbyte &= 0x9F
            }
           
            return (bcdbyte);
        }
        /*遞減BCD碼數據字節的低字節數字*/
        unsigned char DescBCDByteLow(unsigned char bcdbyte)
        {
            if ((bcdbyte & 0x0F) > 0)
            {
                bcdbyte -= 0x01;//低位數字-1
            }
            else
            {
                //到0歸9
                bcdbyte |= 0x09;
            }
           
            return (bcdbyte);
        }

        /*根據setTimeIndex的值來設置光標閃爍的位置*/
        void LcdRefreshSetCoursor()
        {
            switch (setTimeIndex)
            {
                case 1:  LcdSetCoursor(4, 0);  break;//設置年的十位數字
                case 2:  LcdSetCoursor(5, 0);  break;
                case 3:  LcdSetCoursor(7, 0);  break;
                case 4:  LcdSetCoursor(8, 0);  break;
                case 5:  LcdSetCoursor(10, 0); break;
                case 6:  LcdSetCoursor(11, 0); break;
                case 7:  LcdSetCoursor(6,  1); break;
                case 8:  LcdSetCoursor(7, 1);  break;
                case 9:  LcdSetCoursor(9, 1);  break;
                case 10: LcdSetCoursor(10, 1); break;
                case 11: LcdSetCoursor(12, 1); break;
                case 12: LcdSetCoursor(13, 1); break;
                default: break;
            }
            LcdOpenCoursor();
        }
        /*遞增光標閃爍位置的時間值*/
        void IncTimeBysetTimeIndex()
        {
            switch (setTimeIndex)
            {
                case 1:  timeBuf.year  = IncrementBCDByteHigh(timeBuf.year);  break;//year10++年的十位數字++
                case 2:  timeBuf.year  = IncrementBCDByteLow(timeBuf.year);   break;//年的個位數字++
                case 3:  timeBuf.month = IncrementBCDByteHigh(timeBuf.month); break;//月十位++
                case 4:  timeBuf.month = IncrementBCDByteLow(timeBuf.month);  break;//月個位++
                case 5:  timeBuf.day   = IncrementBCDByteHigh(timeBuf.day);   break;
                case 6:  timeBuf.day   = IncrementBCDByteLow(timeBuf.day);    break;
                case 7:  timeBuf.hour  = IncrementBCDByteHigh(timeBuf.hour);  break;
                case 8:  timeBuf.hour  = IncrementBCDByteLow(timeBuf.hour);   break;
                case 9:  timeBuf.min   = IncrementBCDByteHigh(timeBuf.min);   break;
                case 10: timeBuf.min   = IncrementBCDByteLow(timeBuf.min);    break;
                case 11: timeBuf.sec   = IncrementBCDByteHigh(timeBuf.sec);   break;
                case 12: timeBuf.sec   = IncrementBCDByteLow(timeBuf.sec);    break;
                default: break;
            }
            RefreshLcdShowTime();//刷新時間顯示
            LcdRefreshSetCoursor();//刷新光標閃爍顯示
        }
        /*遞減光標位置的時間值*/
        void DecTimeBySetTimeIndex()
        {
            switch (setTimeIndex)
            {
                case 1:  timeBuf.year  = DescBCDByteHigh(timeBuf.year);  break;//年十位數字遞減
                case 2:  timeBuf.year  = DescBCDByteLow(timeBuf.year);   break;//年個位數字遞減
                case 3:  timeBuf.month = DescBCDByteHigh(timeBuf.month); break;
                case 4:  timeBuf.month = DescBCDByteLow(timeBuf.month);  break;
                case 5:  timeBuf.day   = DescBCDByteHigh(timeBuf.day);   break;
                case 6:  timeBuf.day   = DescBCDByteLow(timeBuf.day);    break;
                case 7:  timeBuf.hour  = DescBCDByteHigh(timeBuf.hour);  break;
                case 8:  timeBuf.hour  = DescBCDByteLow(timeBuf.hour);   break;
                case 9:  timeBuf.min   = DescBCDByteHigh(timeBuf.min);   break;
                case 10: timeBuf.min   = DescBCDByteLow(timeBuf.min);    break;
                case 11: timeBuf.sec   = DescBCDByteHigh(timeBuf.sec);   break;
                case 12: timeBuf.sec   = DescBCDByteLow(timeBuf.sec);    break;
                default: break;
            }
            /*先刷新時間顯示,再刷新光標閃爍顯示*/
            RefreshLcdShowTime();
            LcdRefreshSetCoursor();
        }
        /*紅外按鍵動作函數*/
        void IrdKeyAction()
        {
            if (irdCode[2] == 0x44) // >>||
            {
                if (setTimeIndex == 0) //正常運行狀態
                {
                    setTimeIndex = 1;
                    LcdRefreshSetCoursor();//刷新光標設置顯示    
                }
                else
                {
                    SetTimeToDS1302(&timeBuf); //將時間寫入到DS1302中
                    setTimeIndex = 0;
                    RefreshLcdShowTime();
                }
            }
            else if (irdCode[2] == 0x40) //|<<
            {
                if (setTimeIndex != 0)
                {
                    LcdCoursorLeft();
                }
            }
            else if (irdCode[2] == 0x43) // >>|
            {
                if (setTimeIndex != 0)
                {
                    LcdCoursorRight();
                }
            }
            else if (irdCode[2] == 0x15) //-
            {
                DecTimeBySetTimeIndex();
            }
            else if (irdCode[2] == 0x09) //+
            {
                IncTimeBysetTimeIndex();
            }
            else if (irdCode[2] == 0x19) //EQ
            {
                setTimeIndex = 0;
                RefreshLcdShowTime();
            }
            else if (irdCode[2] == 0x16) //數字鍵0
            {
                SetTimeBcdByte(0);
            }
            else if (irdCode[2] == 0x0C) //數字鍵1
            {
                SetTimeBcdByte(1);
            }
            else if (irdCode[2] == 0x18) //數字鍵2
            {
                SetTimeBcdByte(2);
            }
            else if (irdCode[2] == 0x5E)
            {
                SetTimeBcdByte(3);
            }
            else if (irdCode[2] == 0x08)
            {
                SetTimeBcdByte(4);
            }
            else if (irdCode[2] == 0x1C)
            {
                SetTimeBcdByte(5);
            }
            else if (irdCode[2] == 0x5A)
            {
                SetTimeBcdByte(6);
            }
            else if (irdCode[2] == 0x42)
            {
                SetTimeBcdByte(7);
            }
            else if (irdCode[2] == 0x52)
            {
                SetTimeBcdByte(8);
            }
            else if (irdCode[2] == 0x4A)
            {
                SetTimeBcdByte(9);
            }
            else
            {
            }
        }
        /*T0中斷服務*/
        void Timer0_ISP() interrupt 1
        {
            static unsigned char counter = 0;
           
            TH0 = thr0;
            TL0 = tlr0; //1ms
            counter++;
            KeyScan();//按鍵掃描
           
            if (counter >= 200)
            {
                counter = 0;
                flag200ms = 1;//200ms
            }    
        }

         #ifndef _MAIN_H_
        #define _MAIN_H_
        sbit ADDR0 = P1^0;
        sbit ADDR1 = P1^1;
        sbit ADDR2 = P1^2;
        sbit ADDR3 = P1^3;
        sbit ENLED = P1^4;
        #endif //_MAIN_H_

         /**
          ************************************************************************
         * @file    :   Lcd1602.c
         * @author  :   xr
         * @date    :   2014年4月21日 22:23:12
         * @version :   V1.2.3
         * @brief   :   LCD1602底層驅動   單片機STC89C52RC MCU 晶振 11.0592MHZ
         ************************************************************************
         */
        #include <reg52.h>
        //LCD1602
        sbit LCD1602_RS = P1^0;
        sbit LCD1602_RW = P1^1;
        sbit LCD1602_EN = P1^5;
        #define LCD1602_DB P0
        /*LCD1602忙碌等待*/
        void LCD1602Wait()
        {
         unsigned char sta;//讀取LCD1602狀態字
         
            /*讀取液晶狀態字之前必須將P0口全部拉高*/
         LCD1602_DB = 0xFF;
         
         LCD1602_RS = 0;
         LCD1602_RW = 1;
         LCD1602_EN = 0;
         
         do
         {
          LCD1602_EN = 1;
          sta = LCD1602_DB;//讀狀態字
          LCD1602_EN = 0;
         } while (sta & 0x80); //檢測最高位是否為1,1忙碌,0空閑
        }
        /*LCD1602寫命令*/
        void LCD1602WriteCmd(unsigned char cmd)
        {
         //讀寫前要進行液晶的忙碌等待
         LCD1602Wait();
         
         LCD1602_RS = 0;
         LCD1602_RW = 0;
         LCD1602_EN = 0;
         LCD1602_DB = cmd;
         LCD1602_EN = 1;//高脈沖
         LCD1602_EN = 0;//關閉液晶輸出
        }
        /*LCD1602寫數據*/
        void LCD1602WriteData(unsigned char dat)
        {
         LCD1602Wait();
         
         LCD1602_RS = 1;
         LCD1602_RW = 0;
         LCD1602_EN = 0;
         LCD1602_DB = dat;//送入數據
         LCD1602_EN = 1;//高脈沖
         LCD1602_EN = 0;//關閉液晶輸出
        }
        /*液晶初始化*/
        void InitLCD1602()
        {
         LCD1602WriteCmd(0x38); //寫指令38H
         LCD1602WriteCmd(0x0C); //開顯示不顯示光標
         LCD1602WriteCmd(0x06); //寫入字符時字符指針++且光標++
         LCD1602WriteCmd(0x01); //顯示清屏
        }
        /*在LCD1602的坐標(x, y)位置顯示str*/
        void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str)
        {
         unsigned char addr;
         
         if (y == 0)
         {
          addr = 0x00 + x; //第一行的x位置顯示
         }
         else
         {
          addr = 0x40 + x; //第二行x的位置顯示
         }
         LCD1602WriteCmd(addr + 0x80);
         while (*str != '\0')
         {
          LCD1602WriteData(*str++);
         }
        }
        /*設置光標的位置為(x, y)*/
        void LcdSetCoursor(unsigned char x, unsigned char y)
        {
         unsigned char addr;
         
         if (y == 0)
         {
          addr = 0x00 + x;
         }
         else
         {
          addr = 0x40 + x;
         }
            LCD1602WriteCmd(addr | 0x80); //寫入光標閃爍地址
        }
        /*打開光標顯示*/
        void LcdOpenCoursor()
        {
         LCD1602WriteCmd(0x0F); //顯示光標病使光標閃爍
        }
        /*關閉光標顯示*/
        void LcdCloseCoursor()
        {
         LCD1602WriteCmd(0x0C); //開顯示但不顯示光標
        }

         /**
          ************************************************************************
         * @file    :   Ds1302.c
         * @author  :   xr
         * @date    :   2014年4月21日 22:23:12
         * @version :   V1.2.3
         * @brief   :   DS1302底層驅動   單片機STC89C52RC MCU 晶振 11.0592MHZ
         ************************************************************************
         */
        #include <reg52.h>
        //DS1302
        sbit DS1302_SIO = P3^4;//數據IO
        sbit DS1302_SCK = P3^5;//時鐘線
        sbit DS1302_CE  = P1^7;//片選使能
        /*使用外部數據類型時必須在這個文件中再一次聲明*/
        struct Time {
         unsigned char year;//年在DS1302中只存放的是低兩位,這里用unsigned char型
         unsigned char month;
         unsigned char day;
         unsigned char hour;
         unsigned char min;
         unsigned char sec;
         unsigned char week;
        };
        /*向SIO發送一個字節數據*/
        void DS1302WriteByte(unsigned char byte)
        {
         unsigned char mask = 0x01;//低位在前,逐位發送
         /*初始化DS1302時,將SIO和SCK引腳拉低,空閑*/
         for (mask = 0x01; mask != 0; mask <<= 1)
         {
          if ((byte & mask) != 0)  //上升沿DS1302進行數據的采樣鎖存
          {
           DS1302_SIO = 1;
          }
          else
          {
           DS1302_SIO = 0;
          }
          DS1302_SCK = 1;
          DS1302_SCK = 0;//下降沿主機進行數據輸出
         }
         //主機發送數據完成后要進行釋放SIO數據線
         DS1302_SIO = 1;
        }
        /*讀取SIO數據線上的數據*/
        unsigned char DS1302ReadByte()
        {
         unsigned char mask = 0x01;//低位在先,逐位接收
         unsigned char byte = 0;
         
         for (mask = 0x01; mask != 0; mask <<= 1)
         {
          if (DS1302_SIO != 0) //上升沿主機進行數據采樣鎖存
          {
           byte |= mask; //相應位置1
          }
          else
          {
           byte &= (~mask);//相應位清零
          }
          DS1302_SCK = 1;
          DS1302_SCK = 0;//DS1302在下降沿進行數據的輸出
         }
         
         return (byte);
        }
        /*向DS1302的reg地址寄存器中寫入單個字節數據*/
        void DS1302WriteSingleByte(unsigned char reg, unsigned char dat)
        {
         /*首先打開DS1302片選端,然后寫寄存器地址reg,然后再寫入數據dat*/
         DS1302_CE = 1;
         DS1302WriteByte((reg << 1) | 0x80);//寫入寄存器地址,reg<<1留出讀寫方向位,reg的高位是1,次高位為0
         DS1302WriteByte(dat);              //寫入數據
         DS1302_CE = 0;
         DS1302_SIO = 0;//因為板子上沒有SIO口沒有加上拉電阻,開漏輸出不定態,將SIO=0,單片機IO口會輸出穩定的0態
        }
        /*從DS1302的reg地址寄存器中讀取一個字節的數據*/
        unsigned char DS1302ReadSingleByte(unsigned char reg)
        {
         unsigned char byte = 0;
         
         DS1302_CE = 1;
         DS1302WriteByte((reg << 1) | 0x81); //寫入寄存器地址reg并在讀寫方向上選擇讀
         byte = DS1302ReadByte();
         DS1302_CE = 0;
         DS1302_SIO = 0; //SIO輸出穩定的0狀態
         
         return (byte);
        }
        /*Burst模式寫八個字節到DS1302八個寄存器中*/
        void DS1302BurstWrite(unsigned char * date)
        {
         unsigned char i = 0;
         
         DS1302_CE = 1;
         DS1302WriteByte(0xBE); //寫入突發模式指令(Burst模式)
         for (i = 0; i < 8; i++)
         {
          DS1302WriteByte(date[i]);
         }
         DS1302_CE = 0;
         DS1302_SIO = 0;
        }
        /*Burst模式從DS1302寄存器中連續讀取八個字節的數據*/
        void DS1302BurstRead(unsigned char * date)
        {
         unsigned char i = 0;
         
         DS1302_CE = 1; //片選使能
         DS1302WriteByte(0xBF); //突發讀指令
         for (i = 0; i < 8; i++)
         {
          date[i] = DS1302ReadByte();
         }
         DS1302_CE = 0;
         DS1302_SIO = 0;
        }
        /*從DS1302中獲取當前時間*/
        void GetTimeFromDS1302(struct Time * time)
        {
         unsigned char buff[8];//保存八個寄存器中的數據
         
         DS1302BurstRead(buff); //將DS1302寄存器中的數據讀入buff數組中
         
         time->year  = buff[6];//將年寄存器中的bcd碼取出來存入結構體time->year中
         time->month = buff[4];
         time->week  = buff[5];
         time->day   = buff[3];
         time->hour  = buff[2];
         time->min   = buff[1];
         time->sec   = buff[0];
        }
        /*設置時間,將當前修改后的時間值設置到DS1302中*/
        void SetTimeToDS1302(struct Time * time)
        {
         unsigned char buff[8]; //保存當前修改后的time結構體成員的值
         
         buff[0] = time->sec; //將秒寄存器中的值存入buff[0
         buff[1] = time->min;
         buff[2] = time->hour;
         buff[3] = time->day;
         buff[4] = time->month;
            buff[5] = time->week;
            buff[6] = time->year;
            buff[7] = 0x00;    //WP寫保護寄存器
           
            DS1302BurstWrite(buff); //將buff中的值以Burst模式寫入DS1302中
        }
        /*初始化DS1302*/
        void InitDS1302()
        {
         struct Time code InitTime[] = {0x14, 0x04, 0x22, 0x23, 0x59, 0x59, 0x02}; //2014年4月22日23:59:59
            unsigned char psec; //檢測DS1302的停止位CH
            /*初始化DS1302的通信引腳*/
            DS1302_SCK = 0;
            DS1302_CE  = 0;
           
            psec = DS1302ReadSingleByte(0x00); //0x00<<1 | 0x81=0x81, 讀取秒寄存器
            if ((psec & 0x80) != 0) //最高位CH=1,時鐘停止
            {
                DS1302WriteSingleByte(7, 0x00); //去除寫保護
                SetTimeToDS1302(InitTime); //將初始時間設置到DS1302中
            }   
        }

         /**
          **************************************************************************
          * @file    :   Infrared.c
          * @author  :   xr
          * @date    :   2014年4月23日22:01:12
          * @version :   V1.2.3
          * @brief   :   紅外通信解碼-NEC協議解碼
          **************************************************************************
          */
         
        #include <reg52.h>
        //紅外通信引腳
        sbit IRD = P3^3;//紅外通信引腳,外部中斷1引腳
        bit flagIrd = 0; //紅外數據碼值接收完畢標志位
        unsigned char irdCode[4]; //保存NEC協議解碼的四個字節的數據碼(用戶碼+用戶反碼,鍵碼+鍵碼反碼)
        /*紅外通信-定時器T1計數進行紅外接收數據判定的時間獲取*/
        void ConfigIrdByTimer1()
        {
            TMOD &= 0x0F;//清零T1控制位
            TMOD |= 0x10;//使用T1方式1,16位可設定模式
            TH1 = 0;
            TL1 = 0;//開始時沒有紅外信號,清零T1計數
            TR1 = 0;//開始時關閉T1計數
            ET1 = 0;//關閉T1中斷,只用T1計數功能
            /*外部中斷1配置*/
            IE1 = 0;//外部中斷1標志位清零
            IT1 = 1;//設置外部中斷1為下降沿觸發
            EX1 = 1;//開啟外部中斷
            EA  = 1;//開總中斷
        }
        /*獲取IRD引腳即紅外引腳高電平T1計數值*/
        unsigned int GetHighTimers()
        {
            IRD = 1;//在檢測紅外引腳之前,要先將IRD引腳拉高釋放
            TH1 = 0;
            TL1 = 0;//先清零上一次的T1計數值
            TR1 = 1;//開啟T1計數
            while (IRD)
            {  //高電平T1進行計數
                if (TH1 > 0x40) //0x40 * 256 * (12/11059200) * 1000 = 17.7ms
                {
                    break; //當時間>17.7ms時我們認為是誤碼,強制退出
                }
            }
            TR1 = 0; //關閉T1計數
           
            return (TH1 * 256 + TL1); //返回IRD引腳高電平時T1的計數值
        }
        /*獲得低電平載波時間*/
        unsigned int GetLowTimers()
        {
            IRD = 1;//在檢測紅外引腳之前,要先將IRD引腳釋放
            TH1 = 0;
            TL1 = 0; //清零T1計數值
            TR1 = 1; //開啟T1計數
           
            while (!IRD)
            {
                //超時判斷
                if (TH1 > 0x40) //超出17.7ms就退出
                {
                    break;
                }
            }
            TR1 = 0;//關閉T1計數
           
            return (TH1 * 256 + TL1);//返回T1計數值
        }
        /*外部中斷1服務,判定紅外并接收紅外NEC解碼*/
        void EXINT1_ISP() interrupt 2 //EXINT1中斷標號為2
        {
            unsigned char byte = 0;//接收解碼數據
            unsigned int time = 0;//接收引導碼和空閑與載波的時間
            unsigned char i, j;
           
            time = GetLowTimers();//載波 引導碼(9ms的載波+4.5ms的空閑):范圍(8.5ms-9.5ms)
            if ((time < 7833) ||(time > 8755))  //計數周期=12/11059200 計數時間=(TH1*256+TL1)*12/11059200
            {
                //誤碼,清零中斷標志并退出中斷
                IE1 = 0;
                return;
            }
           
            time = GetHighTimers();//空閑 4.5ms的空閑:范圍:4ms-5ms
            if ((time < 3686) || (time > 4608)) //判斷時必須加上()
            {
                //不符合
                IE1 = 0;//外部中斷標志位
                return; //退出中斷函數
            }       
            //引導碼正確,開始循環接收用戶碼和鍵碼
            for (i = 0; i < 4; i++) //接收4個字節數據
            {
                for (j = 0; j < 8; j++) //接收8位
                {
                    time = GetLowTimers();//載波:560us載波+560us/1.68ms空閑:340us 760us
                    if ((time < 313) || (time > 700))
                    {
                        //誤碼
                        IE1 = 0;//清零外部中斷標志
                        return;
                    }
                    time = GetHighTimers();//空閑
                    if ((time > 313) && (time < 700)) //560us的空閑,比特值'0'time大于340us并且time<780us
                    {
                        byte >>= 1;//低位在先,逐位接收,將數據位右移到低位
                    }
                    else if ((time > 1271) && (time < 1658))  //1.68ms空閑:范圍:1.38ms-1.8ms,比特值是'1'
                    {
                        byte >>= 1;//先右移一位,將這一位移到byte的最高位
                        byte |= 0x80;//再將這一位置1
                    }
                    else
                    {
                        //接收的是誤碼
                        IE1 = 0;//清零外部中斷1標志位
                        return;//退出中斷函數
                    }
                }   
                    irdCode[i] = byte;//接收碼值
            }
            flagIrd = 1;//接收完成標志置1
            IE1 = 0;//清零外部中斷標志
        }

         /**
          ************************************************************************
         * @file    :   Keyboard.c
         * @author  :   xr
         * @date    :   2014年4月21日 22:23:12
         * @version :   V1.2.3
         * @brief   :   按鍵底層驅動   單片機STC89C52RC MCU 晶振 11.0592MHZ
         ************************************************************************
         */
        #include <reg52.h>
        //KEY
        sbit KEY_IN_1  = P2^4;
        sbit KEY_IN_2  = P2^5;
        sbit KEY_IN_3  = P2^6;
        sbit KEY_IN_4  = P2^7;
        sbit KEY_OUT_1 = P2^3;
        sbit KEY_OUT_2 = P2^2;
        sbit KEY_OUT_3 = P2^1;
        sbit KEY_OUT_4 = P2^0;
        static unsigned char volatile keySta[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1},
                                            {1, 1, 1, 1}, {1, 1, 1, 1}}; //16個按鍵當前狀態
        unsigned char pdata keyCodeMap[4][4] = {  //按鍵編碼根據ASCII碼進行編碼
                                            {'1', '2', '3', 0x26}, //數字鍵1,2,3和向上鍵
                                            {'4', '5', '6', 0x25}, //數字鍵4,5,6和向左鍵
                                            {'7', '8', '9', 0x28}, //數字鍵7,8,9和向下鍵
                                            {'0', 0x1B, 0x0D, 0x27} //數字鍵0和ESC鍵,回車鍵,向右鍵
                                        };
                                           
        /*需要使用main.c文件中的struct Time類型的結構體變量timeBuf,需要在重新進行聲明*/
        struct Time {
            unsigned char year;
            unsigned char month;
            unsigned char day;
            unsigned char hour;
            unsigned char min;
            unsigned char sec;
            unsigned char week;
        };                    
        extern struct Time timeBuf;
        extern unsigned char setTimeIndex;//外部變量聲明
        void LcdCoursorRight();
        void LcdCoursorLeft();
        void SetTimeBcdByte(unsigned char keyNum);
        void KeyAction(unsigned char keycode);
        extern void LcdRefreshSetCoursor();
        extern void LcdCloseCoursor();
        extern void IncTimeBysetTimeIndex();
        extern void DecTimeBySetTimeIndex();
        extern GetTimeFromDS1302(struct Time * time);
        extern void SetTimeToDS1302(struct Time * time);
        extern void RefreshLcdShowTime();
        /*按鍵驅動,進行按鍵狀態的判斷,傳遞相應按鍵的鍵碼到按鍵動作函數中*/
        void KeyDriver()
        {
            static unsigned char pdata keyBackup[4][4] = {{1, 1, 1, 1}, {1, 1, 1, 1},
                                                          {1, 1, 1, 1}, {1, 1, 1, 1}}; //按鍵的備份即上一次按鍵的狀態值
            unsigned char i, j;
            for (i = 0; i < 4; i++)
            {
                for (j = 0; j < 4; j++)
                {
                    if (keySta[i][j] != keyBackup[i][j])
                    {
                        //按鍵有動作
                        if (keyBackup[i][j] != 0) //根據上一次的備份值來確定本次的按鍵值,即按鍵的動作
                        {
                            //按鍵按下,執行動作
                            KeyAction(keyCodeMap[i][j]); //傳遞相應的鍵碼
                        }
                        //備份本次按鍵的值
                        keyBackup[i][j] = keySta[i][j];
                    }
                }
            }
        }
        /*按鍵的動態掃描,進行按鍵去抖*/
        void KeyScan()
        {
            static unsigned char keyout = 0;//按鍵行索引,每次都要用到上一次變化的keyout值所以定義為靜態變量
            static unsigned char keybuff[4][4] = {{0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF},
                                                  {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}}; //按鍵掃描緩沖區
            unsigned char i = 0;
           
            /*掃描按鍵的值,并將按鍵的狀態值存入keybuff緩沖區中*/
            keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1; //將第keyout行的第一個按鍵的掃描值存入keybuff[keyout][0]緩沖區中
            keybuff[keyout][1] = (keybuff[keyout][1] << 1) | KEY_IN_2;
            keybuff[keyout][2] = (keybuff[keyout][2] << 1) | KEY_IN_3;
            keybuff[keyout][3] = (keybuff[keyout][3] << 1) | KEY_IN_4;
           
            /*更新掃描消抖后按鍵的狀態*/
            for (i = 0; i < 4; i++) //每行有四個按鍵需要進行判斷
            {
                if ((keybuff[keyout][i] & 0x1F) == 0x00) //5次按鍵掃描值都是0,表示按鍵是穩定按下
                {
                    keySta[keyout][i] = 0;
                }
                else if ((keybuff[keyout][i] & 0x1F) == 0x1F) //5次按鍵的掃描值都是1,表示按鍵是穩定彈起
                {
                    keySta[keyout][i] = 1;
                }
            }
            //行++
            keyout++;
            keyout &= 0x03;//實現到4歸零
           
            /*根據keyout的值來確定選擇哪一行按鍵進行掃描*/
            switch (keyout)
            {
                case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;//掃描第1行
                case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;//掃描第2行
                case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;//掃描第3行
                case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;//掃描第4行
                default: break;
            }
        }
        /*按鍵動作,根據按鍵的鍵碼來執行相應的操作*/
        void KeyAction(unsigned char keycode)
        {
            if (keycode >= '0' && keycode <= '9')
            {
                if (setTimeIndex != 0) //處于設置狀態才相應
                SetTimeBcdByte(keycode - '0'); //數字鍵設置時間
            }
            else if (keycode == 0x26)  //向上鍵進行光標閃爍位置上的數值的++
            {
                IncTimeBysetTimeIndex();//遞增BCD碼的高位或低位數字
            }
            else if (keycode == 0x27) //向右鍵進行進行光標位置的右移
            {
               if (setTimeIndex != 0) //處于設置狀態才相應
               LcdCoursorRight();//遞減BCD碼的高位或低位數字
            }
            else if (keycode == 0x25) //向左鍵進行光標閃爍位置的左移
            {
                if (setTimeIndex != 0)
                LcdCoursorLeft();
            }
            else if (keycode == 0x28) //向下鍵光標閃爍位置上的數值的遞減
            {
                DecTimeBySetTimeIndex();
            }
            else if (keycode == 0x1B) //ESC鍵,取消當前的設置
            {
                setTimeIndex = 0;//時鐘正常運行索引=0
                LcdCloseCoursor();//關閉光標閃爍
            }
            else if (keycode == 0x0D) //回車鍵,進行設置時間和恢復時鐘運行的狀態的切換
            {
                if (setTimeIndex == 0) //正常運行狀態,開始切換到設置狀態
                {
                    setTimeIndex = 1;
                    LcdRefreshSetCoursor();//刷新光標閃爍位置并讓光標閃爍
                }
                else
                {
                    //寫入設置的時間值,并切換到正常運行狀態
                    SetTimeToDS1302(&timeBuf);
                    GetTimeFromDS1302(&timeBuf);
                    setTimeIndex = 0;
                    LcdCloseCoursor();//關閉光標閃爍
                }
            }
        }
        /*setTimeIndex++,實現設置光標閃爍的右移*/
        void LcdCoursorRight()
        {
            if (setTimeIndex < 12)
                setTimeIndex++;//光標設置索引++
            else
                setTimeIndex = 1;       
            LcdRefreshSetCoursor();
        }
        /*setTimeIndex--,實現設置光標閃爍的左移*/
        void LcdCoursorLeft()
        {
            if (setTimeIndex > 1)
                setTimeIndex--;//光標設置索引--
            else
                setTimeIndex = 12;
            LcdRefreshSetCoursor();
        }
        /*設置BCD碼的高位數字*/
        unsigned char SetBCDHighByte(unsigned char bcd, unsigned char keyNum)
        {
            bcd &= 0x0F;//清零高位
            bcd |= (keyNum << 4); //keyNum移至高位
           
            return (bcd);
        }
        /*設置BCD碼的低位數字*/
        unsigned char SetBCDLowByte(unsigned char bcd, unsigned char keyNum)
        {
            bcd &= 0xF0;//低位清零
            bcd |= keyNum; //將keyNum數字放到低位
           
            return (bcd); //返回改變后的bcd字節數據
        }
        /*通過按鍵值設置時間值(設置時間值的高位和低位數字)*/
        void SetTimeBcdByte(unsigned char keyNum) //傳遞按鍵的數值
        {
            switch (setTimeIndex)
            {
                case 1: timeBuf.year  = SetBCDHighByte(timeBuf.year, keyNum); break;//設置年的高位BCD字節
                case 2: timeBuf.year  = SetBCDLowByte(timeBuf.year, keyNum); break; //設置年的低位BCD字節
                case 3: timeBuf.month = SetBCDHighByte(timeBuf.month, keyNum); break;
                case 4: timeBuf.month = SetBCDLowByte(timeBuf.month, keyNum); break;
                case 5: timeBuf.day   = SetBCDHighByte(timeBuf.day, keyNum); break;
                case 6: timeBuf.day   = SetBCDLowByte(timeBuf.day, keyNum); break;
                case 7: timeBuf.hour  = SetBCDHighByte(timeBuf.hour, keyNum); break;
                case 8: timeBuf.hour  = SetBCDLowByte(timeBuf.hour, keyNum); break;
                case 9: timeBuf.min   = SetBCDHighByte(timeBuf.min, keyNum); break;
                case 10: timeBuf.min  = SetBCDLowByte(timeBuf.min, keyNum); break;
                case 11: timeBuf.sec  = SetBCDHighByte(timeBuf.sec, keyNum); break;
                case 12: timeBuf.sec  = SetBCDLowByte(timeBuf.sec, keyNum); break;
            }
            RefreshLcdShowTime();   //刷新時間值顯示
            LcdCoursorRight();      //光標閃爍右移
            LcdRefreshSetCoursor(); //刷新光標閃爍
        }

         
        關閉窗口

        相關文章

        欧美性色欧美精品视频,99热这里只有精品mp4,日韩高清亚洲日韩精品一区二区,2020国自产拍精品高潮