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

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

        單片機開源項目之基于DS18B20的帶記憶功能溫度報警系統源碼

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

         此程序是基于51hei單片機開發板上面做的,如需要移植到自己的電路上,修改相應的端口即可,開發板完整的電路圖下載:  點這里 (注意:只需要看相關部分即可如 溫感 數碼管 蜂鳴器的使用的端口的連線,其他部分可以忽略)

         /**
          ***********************************************************************************************************
          * @file      :  main.c
          * @author    :  徐冉
          * @date      :  2014年4月27日08:40:23 ~ 2014年5月1日23:03:58
          * @version   :  V1.2.3
          * @brief     :  基于DS18B20的帶記憶功能溫度報警系統  單片機STC89C52RC MCU 晶振 : 11.0592MHZ
          * @note      :  溫度報警值可由開發板鍵盤和紅外遙控器按鍵進行調整,調整后下次上電將保存上一次的調整值
          * ------------  溫度報警系統有三種狀態:當溫度達到設定的高溫預警值范圍時LED點陣將顯示"火",同時蜂鳴器
          * ------------  以8000HZ的頻率發聲報警,且LED小燈全亮;當溫度達到設定的低溫預警值時LED點陣屏將顯示“水”,
          * ------------  蜂鳴器將以4000HZ的頻率報警,且LED小燈全亮;當溫度處于正常溫度值時,LED點陣屏顯示“心形”,
          * ------------  蜂鳴器處于關閉狀態,且LED小燈全部熄滅。
          ***********************************************************************************************************
          */
        #include <reg52.h>

        //74HC138
        sbit ADDR3 = P1^3;
        sbit ENLED = P1^4;
        //蜂鳴器
        sbit BUZZ = P1^6;
        //數碼管編碼表
        unsigned char code LedTable[] = {
                        0xC0,  //"0"
                        0xF9,  //"1"
                        0xA4,  //"2"
                        0xB0,  //"3"
                        0x99,  //"4"
                        0x92,  //"5"
                        0x82,  //"6"
                        0xF8,  //"7"
                        0x80,  //"8"
                        0x90,  //"9"
                        0x9C,  // '小0'
                        0xC6,  //'C'
                        0xBF   //"-"
                    };
        //數碼管顯示緩沖區+LED獨立小燈
        unsigned char idata LedBuff[] = {0xC6, 0x9C, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F};
        //點陣取模
        unsigned char pdata LedCode[3][8] = {
                                    {0xFF,0x99,0x00,0x00,0x00,0x81,0xC3,0xE7}, //heart
                                    {0xF7,0xD5,0xD5,0xE6,0xF7,0xEB,0xDD,0xBE}, //fire
                                    {0xF7,0x77,0xA0,0xC3,0xE5,0xD6,0xB7,0x71}  //water
                                   };
         
        /**************************local values definition***********************************/
        //溫度狀態
        unsigned char idata tempSta = 0;//0表示正常溫度,1表示高溫,2表示低溫
        bit flag2s = 0;
        unsigned char setTempIndex = 0;//0-正常運行狀態 1-8報警溫度設定索引
        unsigned char thr0, tlr0;//T0定時器重載值
        unsigned int counter = 0;//計數器
        unsigned char idata thr1, tlr1;//00H-70H內存不夠,使用70H-FFH內存
        signed int temp;//存儲溫度值
        bit buzzflag = 0;//蜂鳴器啟動標志
        //定義報警溫度的上限值和下限值范圍(以下溫度值是*10之后的溫度值)溫度值分正負且都是int型
        signed int shangxianHigh = 300, shangxianLow = 280;//溫度上限值高溫度值和低溫度值
        signed int xiaxianHigh = 200, xiaxianLow = 180;    //溫度下限值的高溫度值和低溫度值
        signed char num[8] = {0, 0, 0, 0, 0, 0, 0, 0};//保存報警溫度值十位數和個位數
        extern bit flagIrd;         //紅外解碼完成標志
        extern unsigned char IrdCode[4];//裝載紅外解碼值
        /**************************local function definition***********************************/
        void ConfigTimer0(unsigned int xms);
        void TempToLedBuf(signed int temp);
        void TempertureWarning(signed int temp);
        void ConfigBuzzFr(unsigned int fr);
        void ReadE2PROMToNumArrary();
        /**************************extern function definition***********************************/
        extern void KeyDrive();
        extern void KeyScan();
        extern void LCD1602RefreshCoursor();
        extern void InitLCD1602();
        extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);
        extern bit StartConvertTemp();
        extern bit ReadDS18B20Temperture(signed int * temperture);
        extern void WriteEEPROMByte(unsigned char addr, unsigned char dat);
        extern unsigned char ReadEEPROMByte(unsigned char addr);
        extern void LEDRefreshPause();
        extern void ContinueRefreshLED();
        extern void ConfigInfrared();
        extern void IrdKeyDrive();
        /*主函數main()*/
        void main(void)
        {
            ADDR3 = 1;
            ENLED = 0;//選擇LED
            InitLCD1602();
            ConfigTimer0(1);//T0定時1ms
            ConfigInfrared();
            //ConfigBuzzFr(1000);//默認設定蜂鳴器頻率為1000
           
            StartConvertTemp();//啟動一次溫度轉換
           
            LcdShowStr(0, 0, "high T: XX-XX 'C");
            LcdShowStr(0, 1, "low  T: XX-XX 'C");//LCD初始化顯示
           
            ReadE2PROMToNumArrary();
            EA = 1; //打開總中斷
           
            while (1)
            {
                KeyDrive(); //檢測按鍵動作
                if (flagIrd)
                {
                    flagIrd = 0;
                    IrdKeyDrive();//紅外按鍵檢測
                }
                if (flag2s)
                {
                    flag2s = 0;
                   
                    if (ReadDS18B20Temperture(&temp))
                    {
                        TempToLedBuf(temp); //將溫度值轉換成數碼管數字并檢測溫度是否超限,超限啟動聲光報警!
                        StartConvertTemp();//再次啟動溫度轉換
                    }
                }
            }
        }
        /*將eeprom的存儲的報警溫度值的各個位數字讀取到num[8]數組中*/
        void ReadE2PROMToNumArrary()
        {
            unsigned char str[4];
           
            //數碼管初始化顯示0.00
            LedBuff[2] = LedTable[0];
            LedBuff[3] = LedTable[0];
            LedBuff[4] = LedTable[0];
            LedBuff[3] &= 0x7F;//add point
           
            /*讀取eeprom中的數據*/
            num[0] = ReadEEPROMByte(0x00);
            num[1] = ReadEEPROMByte(0x01);
            num[2] = ReadEEPROMByte(0x02);
            num[3] = ReadEEPROMByte(0x03);
            num[4] = ReadEEPROMByte(0x04);
            num[5] = ReadEEPROMByte(0x05);
            num[6] = ReadEEPROMByte(0x06);
            num[7] = ReadEEPROMByte(0x07);
            LedBuff[6] = 0x7F;//讀取成功指示
           
            /*初始化更新設定的報警溫度值*/
            shangxianLow  = (num[0] * 100 + num[1] * 10);
            shangxianHigh = (num[2] * 100 + num[3] * 10);
            xiaxianLow    = (num[4] * 100 + num[5] * 10);
            xiaxianHigh   = (num[6] * 100 + num[7] * 10);
           
            /*將報警溫度值顯示到LCD1602上*/
            str[0] = num[0] + '0';
            str[1] = num[1] + '0';
            str[2] = '\0';
            LcdShowStr(8, 0, str);
            str[0] = num[2] + '0';
            str[1] = num[3] + '0';
            str[2] = '\0';
            LcdShowStr(11, 0, str);
            str[0] = num[4] + '0';
            str[1] = num[5] + '0';
            str[2] = '\0';
            LcdShowStr(8, 1, str);
            str[0] = num[6] + '0';
            str[1] = num[7] + '0';
            str[3] = '\0';
            LcdShowStr(11, 1, str);  
        }
        /*將溫度值轉換為有效數字存儲到LedBuff中*/
        void TempToLedBuf(signed int temp)
        {
            unsigned char buf[6];//緩沖區
            signed char i = 0;   //i必須是有符號型數據
            if (temp < 0) //負溫度值
            {
                buf[5] = LedTable[12];//添加負號
                //保留一位小數
                temp = (~temp + 1) * 0.0625 * 10;//負值,讀取到的16位二進制數值取反+1再*分辨率0.0625
                for (i = 0; i < 4; i++) //溫度值最高3位數值+小數位
                {
                    buf[i] = temp % 10;
                    temp /= 10;
                }
                //去掉無效位數字
                for (i = 3; i > 1; i--)
                {
                    if (buf[i] == 0)
                    {
                        buf[i] = 0xFF;//去掉高位0不顯示
                    }
                    else
                    {
                        break;//遇到第一個有效數字就退出
                    }
                }
                //轉換為實際的有效數字
                for (; i >= 0; i--)
                {
                    buf[i] = LedTable[buf[i]];
                }
                //將實際數字拷貝到數碼管緩沖區中
                for (i = 0; i < 4; i++)
                {
                    LedBuff[i+2] = buf[i];
                }
            }
            else
            {  
                //溫度值大于等于0,保留一位小數位
                temp = (temp * 0.0625 * 10);//直接用讀取到的16位二進制數值*分辨率即是實際的溫度值
                TempertureWarning(temp);    //溫度報警檢測必須函數必須放在前面
               
                for (i = 0; i < 4; i++)
                {
                    buf[i] = temp % 10;
                    temp /= 10;
                }
                //去掉高位的0
                for (i = 3; i > 1; i--)
                {
                    if (buf[i] == 0)
                    {
                        buf[i] = 0xFF;
                    }
                    else
                    {
                        break;//遇到第一個有效數字就退出
                    }
                }
                //轉換為實際的有效數字
                for (; i >= 0; i--)
                {
                    buf[i] = LedTable[buf[i]];
                }
                //拷貝到緩沖區
                for (i = 0; i < 4; i++)
                {
                    LedBuff[i+2] = buf[i]; //最低兩位顯示溫度標示符
                }
            }
            //在相應的位置點上小數點
            LedBuff[3] &= 0x7F;//0 111 1111
        }
        /*刷新LCD設置位置上的數字顯示*/
        void RefreshLCDDisplay()
        {
            unsigned char str[3];//字符串緩沖區
            switch (setTempIndex)
            {
                case 1: str[0] = num[0] + '0'; str[1] = '\0'; LcdShowStr(8, 0, str);  break;
                case 2: str[0] = num[1] + '0'; str[1] = '\0'; LcdShowStr(9, 0, str);  break;//shangxianLow的十位和個位數字顯示刷新
                case 3: str[0] = num[2] + '0'; str[1] = '\0'; LcdShowStr(11, 0, str); break;
                case 4: str[0] = num[3] + '0'; str[1] = '\0'; LcdShowStr(12, 0, str); break;
                case 5: str[0] = num[4] + '0'; str[1] = '\0'; LcdShowStr(8, 1, str);  break;
                case 6: str[0] = num[5] + '0'; str[1] = '\0'; LcdShowStr(9, 1, str);  break;
                case 7: str[0] = num[6] + '0'; str[1] = '\0'; LcdShowStr(11, 1, str); break;
                case 8: str[0] = num[7] + '0'; str[1] = '\0'; LcdShowStr(12, 1, str); break;
                default: break;
            }
            LCD1602RefreshCoursor();
        }
        /*響應按鍵值來設置溫度報警值*/
        void SetTempByKeyNum(unsigned char keyNum)
        {
            switch (setTempIndex)
            {
                case 1: num[0] = keyNum; break;
                case 2: num[1] = keyNum; break;
                case 3: num[2] = keyNum; break;
                case 4: num[3] = keyNum; break;
                case 5: num[4] = keyNum; break;
                case 6: num[5] = keyNum; break;
                case 7: num[6] = keyNum; break;
                case 8: num[7] = keyNum; break;
                default: break;
            }
            RefreshLCDDisplay();
            setTempIndex++;//光標右移
            if (setTempIndex > 8)
            setTempIndex = 1;//設置光標回返
            LCD1602RefreshCoursor();  
        }
        /*光標閃爍位置數字遞增*/
        void IncCoursorNum()
        {
            switch (setTempIndex)
            {
                case 1:  {if (num[0] < 9) num[0]++; else num[0] = 0; break;} //十位數字++后重新設置溫度值
                case 2:  {if (num[1] < 9) num[1]++; else num[1] = 0; break;}//shangxianLow是小數*10之后的值
                case 3:  {if (num[2] < 9) num[2]++; else num[2] = 0; break;}
                case 4:  {if (num[3] < 9) num[3]++; else num[3] = 0; break;}
                case 5:  {if (num[4] < 9) num[4]++; else num[4] = 0; break;}
                case 6:  {if (num[5] < 9) num[5]++; else num[5] = 0; break;}
                case 7:  {if (num[6] < 9) num[6]++; else num[6] = 0; break;}
                case 8:  {if (num[7] < 9) num[7]++; else num[7] = 0; break;}
                default: break;
            }
           RefreshLCDDisplay();//刷新設置位置上的數字顯示
           LCD1602RefreshCoursor();//再次刷新光標顯示
        }
        /*光標閃爍位置數字遞減*/
        void DecCoursorNum()
        {
            switch (setTempIndex)  //根據光標閃爍的索引來遞減相應位置的數字
            {
                case 1:  {if (num[0] > 0) --num[0]; else num[0] = 9; break;} //十位數字++后重新設置溫度值
                case 2:  {if (num[1] > 0) --num[1]; else num[1] = 9; break;}//shangxianLow是小數*10之后的值
                case 3:  {if (num[2] > 0) --num[2]; else num[2] = 9; break;}
                case 4:  {if (num[3] > 0) --num[3]; else num[3] = 9; break;}
                case 5:  {if (num[4] > 0) --num[4]; else num[4] = 9; break;}
                case 6:  {if (num[5] > 0) --num[5]; else num[5] = 9; break;}
                case 7:  {if (num[6] > 0) --num[6]; else num[6] = 9; break;}
                case 8:  {if (num[7] > 0) --num[7]; else num[7] = 9; break;}
                default: break;
            }
            RefreshLCDDisplay();
            LCD1602RefreshCoursor();//刷新光標閃爍
        }
        /*設置報警溫度值*/
        void SetWarningTemperture()
        {
            /*設置溫度報警值*/
            shangxianLow  = (num[0] * 100 + num[1] * 10);
            shangxianHigh = (num[2] * 100 + num[3] * 10);
            xiaxianLow    = (num[4] * 100 + num[5] * 10);
            xiaxianHigh   = (num[6] * 100 + num[7] * 10);
           
            /*將數據保存到eeprom中*/
            WriteEEPROMByte(0x00, num[0]);
            WriteEEPROMByte(0x01, num[1]);
            WriteEEPROMByte(0x02, num[2]);
            WriteEEPROMByte(0x03, num[3]);
            WriteEEPROMByte(0x04, num[4]);
            WriteEEPROMByte(0x05, num[5]);
            WriteEEPROMByte(0x06, num[6]);
            WriteEEPROMByte(0x07, num[7]);
            LedBuff[6] = 0xFE;//寫入成功指示
        }
        /*溫度報警檢測*/
        void TempertureWarning(signed int temp)
        {
            if ((temp > xiaxianLow) && (temp < xiaxianHigh)) //溫度在18.0-20.0度進行低溫報警
            {
                buzzflag = 1;//啟動報警器
                ConfigBuzzFr(4000); //蜂鳴器發聲頻率2000HZ
                LedBuff[6] = 0x00;//低溫亮8個LED小燈
                tempSta = 2;//water
            }
            else if ((temp > shangxianLow) && (temp < shangxianHigh)) //溫度在28.0-30.0度進行高溫報警
            {
                buzzflag = 1;
                ConfigBuzzFr(8000);//蜂鳴器發聲頻率5000HZ
                LedBuff[6] = 0x00;//高溫亮八個LED小燈
                tempSta = 1;//fire
            }
            else
            {
                buzzflag = 0;//關閉報警器
                LedBuff[6] = 0xFF;//關閉報警燈
                tempSta = 0;//heart
            }
        }
        /*配置T0定時器,由于刷新時間的要求,定時xms時間*/
        void ConfigTimer0(unsigned int xms)
        {
            unsigned long tmp;
            tmp = 11059200/12;//周期頻率
            tmp = (tmp * xms) / 1000;//定時xms需要的計數值
            tmp = 65536 - tmp;//定時xms的定時器裝入初值
            thr0 = (unsigned char)(tmp >> 8);
            tlr0 = (unsigned char)tmp;
            TMOD &= 0xF0;//清零T0控制位
            TMOD |= 0x01;//T0方式1,16位定時器可以設定
            TH0 = thr0;
            TL0 = tlr0;
            TR0 = 1;//開啟T0定時器
            ET0 = 1;//開定時器T0中斷
        }
        /*數碼管刷新*/
        void RefreshLEDChar()
        {
            static unsigned char index = 0;
            P0 = 0xFF;//消隱
            ADDR3 = 1;
            if ((ENLED == 0) && (ADDR3 == 1))
            {
                P1 &= 0xF8;//清零P1口低三位
                P1 |= index;//index控制三八譯碼器選擇地址
                P0 = LedBuff[index++];
                if (index > 6) //刷新LEDS0-LEDS6,6個數碼管+LED小燈
                {
                    index = 0;
                }
            }
        }
        /*刷新點陣屏*/
        void RefreshLed()
        {
            static unsigned char index = 0;
            P0 = 0xFF;   
            ADDR3 = 0;
            if ((ENLED == 0) && (ADDR3 == 0))
            {
                P1 &= 0xF8;//清零P1口低三位
                P1 |= index;
                P0 = LedCode[tempSta][index++];//tempSta == 0表示溫度正常
                if (index > 7)
                index = 0;
                //index &= 0x07;//到8歸0
            }
        }
        /*配置蜂鳴器發聲頻率fr*/
        void ConfigBuzzFr(unsigned int fr)
        {
            unsigned long tmp = 0;
            tmp = 11059200/12; //周期頻率
            tmp = tmp / fr;    //設定fr頻率需要的計數值
            tmp = 65536 - tmp;//設定fr頻率需要裝入的計數初值
            thr1 = (unsigned char)(tmp >> 8);
            tlr1 = (unsigned char)tmp;       //計數值高低字節
            TMOD &= 0x0F; //清零T1控制位
            TMOD |= 0x10; //配置T1工作方式1,16位定時器模式
            TH1 = thr1;
            TL1 = tlr1;
            TR1 = 1;//開啟T1
            ET1 = 1;//開啟定時器T1中斷
        }
        /*定時器T0中斷服務*/
        void Timer0_ISP() interrupt 1
        {  
            static bit biv = 0;
            TH0 = thr0;
            TL0 = tlr0;//重新裝入初值
            counter++;
            KeyScan();        //掃描按鍵
            RefreshLEDChar(); //1ms數碼管顯示刷新    
            if (!biv)         //2ms
            {
                RefreshLed();//點陣刷新 
            }
            biv = ~biv;           //分頻
           
            if (counter >= 2000)
            {
                counter = 0;//2s
                flag2s = 1;
            }
        }
        /*定時器T1中斷服務配置蜂鳴器的發聲頻率*/
        void Timer1_ISP() interrupt 3
        {
            TH1 = thr1;
            TL1 = tlr1;
            if (buzzflag)
            {
                BUZZ = ~BUZZ;//以frHZ驅動蜂鳴器發聲
            }
        }

         /**
          ***********************************************************************************************
          * @file      :  lcd1602.c
          * @author    :  xr
          * @date      :  2014年4月27日08:40:23
          * @version   :  V1.2.3
          * @brief     :  LCD1602驅動
          ***********************************************************************************************
          */
        #include <reg52.h>
        //LCD1602
        sbit LCD1602_RS = P1^0;
        sbit LCD1602_RW = P1^1;
        sbit LCD1602_EN = P1^5;
        //74HC138
        sbit ADDR0 = P1^0;
        sbit ADDR1 = P1^1;
        sbit ADDR2 = P1^2;
        sbit ADDR3 = P1^3;
        sbit ENLED = P1^4;
        bit tmpADDR0 = 0;
        bit tmpADDR1 = 0;//地址選擇緩沖區
        /**************************local function definition***********************************/
        void ContinueRefreshLED();
        void LEDRefreshPause();
        /*LCD1602忙碌等待*/
        void LCD1602Wait()
        {
            unsigned char sta;
            P0 = 0xFF;//讀狀態前先拉高P0口
           
            /*讀狀態*/
            LCD1602_RS = 0;
            LCD1602_RW = 1;
            LCD1602_EN = 0;
           
            do
            {
                LCD1602_EN = 1;
                sta = P0;
                LCD1602_EN = 0;//關閉液晶輸出使能,避免液晶輸出數據影響總線上的其他器件
            } while (sta & 0x80); //狀態字的最高位如果為1則表示液晶忙碌,禁止操作
        }
        /*暫停LED掃描*/
        void LEDRefreshPause()
        {
            ENLED = 1;//關閉LED使能
            tmpADDR0 = ADDR0;//因為LED和LCD同時使用了P1^0和P1^1引腳,所以要暫時保存ADDR0和ADDR1的數據即LED掃描地址值
            tmpADDR1 = ADDR1;
            P0 = 0xFF;  //數碼管+LED小燈去抖動
        }
        /*繼續掃描LED*/
        void ContinueRefreshLED()
        {
            ADDR0 = tmpADDR0;
            ADDR1 = tmpADDR1;//恢復原來LED掃描的地址選擇值
            ENLED = 0;//選擇LED
            P0 = 0xFF; //數碼管和LED去抖
        }
        /*LCD1602寫命令*/
        void LCD1602WriteCmd(unsigned char cmd)
        {  
            LEDRefreshPause();//暫停LED掃描
            //讀寫之前都要進行液晶的忙碌等待
            LCD1602Wait();
           
            LCD1602_RS = 0;
            LCD1602_RW = 0;
            LCD1602_EN = 0;
            P0 = cmd;//送入命令
            LCD1602_EN = 1;//高脈沖
            LCD1602_EN = 0;//恢復原電平狀態,關閉液晶輸出
           
            ContinueRefreshLED();//繼續掃描LED(數碼管+獨立LED小燈)
        }
        /*LCD1602寫數據*/
        void LCD1602WriteData(unsigned char dat)
        {
            LEDRefreshPause();//暫停LED掃描
            LCD1602Wait();
           
            LCD1602_RS = 1;
            LCD1602_RW = 0;
            LCD1602_EN = 0;
            P0 = dat;
            LCD1602_EN = 1;//高脈沖
            LCD1602_EN = 0;
           
            ContinueRefreshLED();//繼續掃描LED
        }
        /*液晶初始化*/
        void InitLCD1602()
        {
            LCD1602WriteCmd(0x38);
            LCD1602WriteCmd(0x0C);
            LCD1602WriteCmd(0x06);
            LCD1602WriteCmd(0x01);
        }
        /*設置光標位置*/
        void LCD1602SetCoursor(unsigned char x, unsigned char y)
        {
            unsigned char addr;
           
            if (y == 0) //第一行
            {
                addr = 0x00 + x;//x為地址偏移
            }
            else
            {
                addr = 0x40 + x;
            }
            LEDRefreshPause();
            LCD1602WriteCmd(addr + 0x80);//寫光標地址
            ContinueRefreshLED();
        }
        /*打開光標閃爍*/
        void OpenCoursor()
        {
            LCD1602WriteCmd(0x0F);//顯示光標,且光標閃爍
        }
        /*關閉光標*/
        void CloseCoursor()
        {      
            LCD1602WriteCmd(0x0C);//關閉光標
        }
        /*寫str字符串到液晶的坐標(x, y)上*/
        void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str)
        {
            unsigned char addr;
           
            if (y == 0)
            {
                addr = 0x00 + x;
            }
            else
            {
                addr = 0x40 + x;
            }
            LEDRefreshPause();
            LCD1602WriteCmd(addr + 0x80);
            ContinueRefreshLED();
            while (*str != '\0')
            {
                LEDRefreshPause();
                LCD1602WriteData(*str++);
                ContinueRefreshLED();
            }
        }

        /**
          ***********************************************************************************************
          * @file      :  ds18b20.c
          * @author    :  xr
          * @date      :  2014年4月27日08:40:23
          * @version   :  V1.2.3
          * @brief     :  DS18B20驅動
          ***********************************************************************************************
          */
        #include <reg52.h>
        #include <intrins.h>
        //DS18B20_IO
        sbit DS18B20_IO = P3^2;//1ware總線引腳
        /*DS18B20操作時間,延時x*10us*/
        void DelayX10us(unsigned char x10us)
        {
            do
            {
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_();
                _nop_(); //大約延時10us
            } while (x10us--);
        }
        /*復位DS18B20,讀取DS18B20存在脈沖*/
        bit ReadDS18B20Ack()
        {
            bit ack = 0;
           
            EA = 0;//先關閉所有中斷,以避免中斷時間影響DS18B20操作
            DS18B20_IO = 0;//拉低
            DelayX10us(70); //延時700us
            DS18B20_IO = 1;
            DelayX10us(6); //延時60us一定可以讀取到DS18B20的應答
            ack = DS18B20_IO;//讀取存在脈沖
            while (!DS18B20_IO);//等待DS18B20復位結束
            EA = 1;//打開中斷
           
            return (ack);
        }
        /*寫一個字節數據到DS18B20總線上*/
        void WriteDS18B20Byte(unsigned char byte)
        {
            unsigned char mask = 0x01;//最低位開始寫
            EA = 0;//關閉中斷
            for (mask = 0x01; mask != 0; mask <<= 1)
            {
                DS18B20_IO = 0;//拉低
                _nop_();
                _nop_();//延時2us
                if ((byte & mask) != 0)
                {
                    DS18B20_IO = 1;//發送1
                }
                else
                {
                    DS18B20_IO = 0;//發送0
                } //拉低延時到把數據送入總線上的時間不得超過15us,15us后DS18B20進行對總線進行采樣
                DelayX10us(6);//延時60us,等待DS18B20采樣完成
                DS18B20_IO = 1;//釋放總線
            }  
            EA = 1;//打開中斷
        }
        /*從DS18B20讀一個字節的數據*/
        unsigned char ReadDS18B20Byte()
        {
            unsigned char mask = 0x01;//接收數據探測掩碼
            unsigned char byte = 0;
           
            EA = 0;//關閉中斷
            for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,逐位接收
            {
                DS18B20_IO = 0;
                _nop_();
                _nop_();//先拉低總線2us
                DS18B20_IO = 1;
                _nop_();
                _nop_();//再拉高總線2us,然后主機進行對DS18B20_IO總線采樣
                if (DS18B20_IO != 0)
                {
                    byte |= mask;//相應位置1
                }
                else
                {
                    byte &= (~mask);//相應位清零
                }//主機在將DS18B20拉低再拉高,然后主機進行采樣,時間不超過15us
                DelayX10us(6); //延時60us,這時總線將被上拉電阻拉高
            }
            EA = 1;//開中斷
           
            return (byte); //返回接收到的數據
        }   
        /*啟動溫度轉換Convert Temperature*/
        bit StartConvertTemp()
        {
            bit ack;
           
            ack = ReadDS18B20Ack();//復位總線,并讀取DS18B20存在脈沖
           
            if (ack == 0) //讀取到復位脈沖
            {
                WriteDS18B20Byte(0xCC);//跳過ROM尋址
                WriteDS18B20Byte(0x44);//啟動溫度轉換
            }
           
            return (~ack);//ack==0表示溫度啟動轉換成功
        }
        /*讀取溫度值,將溫度值保存到*temperture中,并返回應答ack*/
        bit ReadDS18B20Temperture(signed int * temperture)
        {
            unsigned char lsb, msb;
            bit ack = 0;
           
            ack = ReadDS18B20Ack();//復位總線,并讀取DS18B20存在脈沖
           
            if (ack == 0)
            {
                WriteDS18B20Byte(0xCC);//跳過ROM
                WriteDS18B20Byte(0xBE);//讀暫存數據寄存器
                lsb = ReadDS18B20Byte();//讀溫度值低字節數據
                msb = ReadDS18B20Byte();//讀溫度值高字節數據
                /*將溫度值的高低字節合并成int型數據并存入*temperture中*/
                *temperture = ((unsigned int)(msb) << 8) + lsb;
            }
           
            return (~ack);//ack==0則表示溫度值讀取成功
        }

        /**
          ***********************************************************************************************
          * @file      :  main.c
          * @author    :  xr
          * @date      :  2014年4月27日20:26:26
          * @version   :  V1.2.3
          * @brief     :  按鍵驅動程序
          ***********************************************************************************************
          */
        #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;
        //按鍵當前狀態
        unsigned char keySta[4][4] = {1, 1, 1, 1, 1, 1, 1, 1,
                                      1, 1, 1, 1, 1, 1, 1, 1};
        //按鍵到PC標準鍵盤編碼
        unsigned char keyCodeMap[4][4] = {
                            '1', '2', '3', 0x26, //數字鍵123,向上鍵
                            '4', '5', '6', 0x25, //數字鍵456,向左鍵
                            '7', '8', '9', 0x28, //數字鍵789,向下鍵
                            '0', 0x1B, 0x0D, 0x27 //數字鍵0,向右鍵
                        };
        /*紅外遙控器鍵碼對應標準PC鍵盤編碼,21個鍵碼*/
        unsigned char code IrdKeyCode[21][2] = {
                                        {0x45, 0x00}, {0x46, 0x00}, {0x47, 0x00}, //前者為紅外遙控器鍵碼,后者為對應的標準PC鍵盤編碼,0x00表示無對應值
                                        {0x44, 0x00}, {0x40, 0x25}, {0x43, 0x27}, //后退->向左鍵,前進->向右鍵
                                        {0x07, 0x1B}, {0x15, 0x28}, {0x09, 0x26}, //EQ->ESC,-->向下鍵,+->向上鍵
                                        {0x16, '0'},  {0x19, 0x00}, {0x0D, 0x0D}, //0->0, U/SD->回車鍵
                                        {0x0C, '1'},  {0x18, '2'},  {0x5E, '3'}, //1->1, 2->2, 3->3
                                        {0x08, '4'},  {0x1C, '5'},  {0x5A, '6'}, //4->4, 5->5, 6->6
                                        {0x42, '7'},  {0x52, '8'},  {0x4A, '9'}  //7->7, 8->8, 9->9
                                    };
        /***************************local function definition************************************/
        void KeyAction(unsigned char keycode);
        /***************************extern function and values definition************************************/
        extern unsigned char setTempIndex;
        extern bit flagIrd;         //紅外解碼完成標志
        extern unsigned char IrdCode[4];//裝載紅外解碼值
        extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char * str);
        extern void LCD1602SetCoursor(unsigned char x, unsigned char y);
        extern void OpenCoursor();
        extern void CloseCoursor();
        extern void ContinueRefreshLED();
        extern void LEDRefreshPause();
        extern void IncCoursorNum();
        extern void DecCoursorNum();
        extern void SetWarningTemperture();
        extern void SetTempByKeyNum(unsigned char keyNum);
                       
        /*按鍵驅動函數,檢測按鍵動作*/
        void KeyDrive()
        {
            static unsigned char 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 IrdKeyDrive()
        {
            unsigned char i = 0;
            for (i = 0; i < 21; i++)  //遍歷紅外鍵碼表
            {
                if (IrdCode[2] == IrdKeyCode[i][0]) //IrdKeyCode[i][0]為紅外鍵碼,IrdKeyCode[i][1]為對應的PC鍵盤編碼
                {
                    //在表中找到對應的鍵碼
                    KeyAction(IrdKeyCode[i][1]); //執行相應的動作
                    break;
                }
            }
        }
        /*刷新LCD1602光標顯示*/
        void LCD1602RefreshCoursor()
        {
            switch (setTempIndex)
            {
                case 1: LCD1602SetCoursor(8, 0);  break;//shangxianLow的十位數字
                case 2: LCD1602SetCoursor(9, 0);  break; //shangxianLow的個位數字
                case 3: LCD1602SetCoursor(11, 0); break; //shangxianHigh的十位數字
                case 4: LCD1602SetCoursor(12, 0); break;
                case 5: LCD1602SetCoursor(8, 1);  break;
                case 6: LCD1602SetCoursor(9, 1);  break;
                case 7: LCD1602SetCoursor(11, 1); break;
                case 8: LCD1602SetCoursor(12, 1); break;
                default: break;
            }
           OpenCoursor();//打開光標
        }
        /*光標閃爍左移*/
        void LCDCoursorLeft()
        {
            if (setTempIndex > 1)
            {
                setTempIndex--;
            }
            else
            {
               setTempIndex = 8;
            }
            LCD1602RefreshCoursor();//刷新光標顯示
        }
        /*光標右移*/
        void LCDCoursorRight()
        {
            if (setTempIndex < 8)
            {
                setTempIndex++;
            }
            else
            {
                setTempIndex = 1;
            }
            LCD1602RefreshCoursor();//刷新光標顯示
        }
        /*按鍵動作函數,根據keycode的值執行相應的動作*/
        void KeyAction(unsigned char keycode)
        {
            if (keycode >= '0' && keycode <= '9')
            {
                if (setTempIndex != 0) //處于設置狀態
                {
                    SetTempByKeyNum(keycode - '0'); //0-9
                }
            }
            else if (keycode == 0x25) //<-
            {
                if (setTempIndex != 0) //處于設置狀態時響應
                {
                    LCDCoursorLeft();
                }
            }
            else if (keycode == 0x27) //->
            {
                if (setTempIndex != 0)
                {
                    LCDCoursorRight();
                }
            }
            else if (keycode == 0x26) //UP
            {
                if (setTempIndex != 0) //處于設置狀態時響應
                {
                    IncCoursorNum();
                }
            }
            else if (keycode == 0x28)//Down
            {
                if (setTempIndex != 0)
                {
                    DecCoursorNum();
                }
            }
            else if (keycode == 0x1B) //ESC
            {
                setTempIndex = 0;  
                CloseCoursor();
            }
            else if (keycode == 0x0D) //Enter
            {
                if (setTempIndex == 0)
                {
                    setTempIndex = 1;
                    LCD1602RefreshCoursor();
                }
                else
                {
                    setTempIndex = 0;
                    SetWarningTemperture();//設定報警溫度值,并將設定的報警值保存到eeprom中
                    CloseCoursor();
                }   
            }
        }
        /*按鍵掃描函數,定時器消抖*/
        void KeyScan()
        {
            static unsigned char keyout = 0;//按鍵行索引
            static unsigned char keybuff[4][4] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                                                  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
                                                  //16個按鍵的掃描緩沖區
            unsigned char i = 0;
           
            //掃描一行按鍵,將掃描值存入keybuff中
            keybuff[keyout][0] = (keybuff[keyout][0] << 1) | KEY_IN_1;
            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) //五次檢測都是0
                {
                    keySta[keyout][i] = 0;//按鍵按下
                }
                else if ((keybuff[keyout][i] & 0x1F) == 0x1F)//五次都是1           
                {
                    keySta[keyout][i] = 1;//按鍵彈起
                }
            }
            //檢測下一行按鍵
            keyout++;
            keyout &= 0x03;//0x11按位與實現到4歸零
            //根據keyout的值來選擇檢測哪一行
            switch (keyout)
            {
                case 0: KEY_OUT_1 = 0; KEY_OUT_4 = 1; break;
                case 1: KEY_OUT_2 = 0; KEY_OUT_1 = 1; break;
                case 2: KEY_OUT_3 = 0; KEY_OUT_2 = 1; break;
                case 3: KEY_OUT_4 = 0; KEY_OUT_3 = 1; break;
                default: break;
            }
        }

         /**
          ***********************************************************************************************
          * @file      :  I2C.c
          * @author    :  xr
          * @date      :  2014年4月28日19:18:15
          * @version   :  V1.2.3
          * @brief     :  I2C通信底層驅動
          ***********************************************************************************************
          */
        #include <reg52.h>
        #include <intrins.h>
        //I2C
        sbit SDA = P3^6;
        sbit SCL = P3^7;
        /*I2C通信延時函數宏定義*/
        #define nops()  { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } //延時大約5us
         
        /*I2C起始信號*/
        void I2CStart()
        {
            SDA = 1;
            SCL = 1; //先拉高SDA和SCL
            nops();
            SDA = 0;//拉低SDA,在SCL為高電平期間SDA1-0的跳變被當做I2C的起始信號
            nops();
            SCL = 0;//拉低SCL,準備好發送數據
        }
        /*I2C停止信號*/
        void I2CStop()
        {
            SDA = 0;
            SCL = 0; //首先確保SDA和SCL為低電平,即忙碌
            nops();
            SCL = 1; //拉高SCL
            nops();
            SDA = 1;//在SCL為高電平期間SDA0-1的變化被當做是停止信號
            nops();
        }
        /*向I2C通信總線發送一個字節數據,并返回從機應答信號ack*/
        bit I2CWriteByte(unsigned char byte)
        {
            bit ack = 0;
            unsigned char mask = 0x80;//從高位開始發送
           
            //I2CStart中已將SCL拉低,可以直接發送數據了
            for (mask = 0x80; mask != 0; mask >>= 1)
            {
                if ((byte & mask) != 0) //送入數據
                {
                    SDA = 1;//發送1
                }
                else
                {
                    SDA = 0;//發送0
                }
                nops();//等待數據發送完成
                SCL = 1;//拉高SCL使數據線SDA穩定不變
                nops();
                SCL = 0;//等待下一位的發送
            }
            /*主機釋放SDA數據線,從機開始發送應答ack*/
            SDA = 1;
            nops();
            SCL = 1;   //拉高SCL以接收從機應答
            ack = SDA;//接收從機應答ack
            nops();
            SCL = 0;//拉低SCL等待下一次的數據發送
           
            return (~ack); //ack==0表示寫成功
        }
        ///*從I2C總線上接收一個字節數據,主機返回ack 0,返回值:讀取到的數據byte*/
        //unsigned char I2CReadByteAck()
        //{
        //    unsigned char byte = 0;
        //    unsigned char mask = 0x80;//高位在先,逐位接收
        //   
        //    //主機釋放SDA數據線
        //    SDA = 1;
        //    //I2CStart中已將SCL拉低,等待從機發送數據
        //    for (mask = 0x80; mask != 0; mask >>= 1)
        //    {
        //        nops();//延時>4.7us等待從機將數據發送到I2C總線上
        //        SCL = 1;//拉高SCL使SDA線上的數據穩定
        //        if (SDA != 0)
        //        {
        //            byte |= mask;//相應位置1
        //        }
        //        else
        //        {
        //            byte &= (~mask);//相應位清零
        //        }
        //        nops();//等待數據讀取成功
        //        SCL = 0;//拉低SCL等待從機發送下一位數據
        //    }
        //    SDA = 0;//主機發送應答位0,繼續接收數據
        //    nops();
        //    SCL = 1;//拉高SCL從機等待主機的應答
        //    nops();
        //    SCL = 0;//最后拉低SCL以進行下一次數據的發送和接收
        //   
        //    return (byte);
        //}
        /*從I2C總線上接收一個字節的數據,主機返回非應答1,返回值:接收到的數據byte*/
        unsigned char I2CReadByteNAck()
        {
            unsigned char byte = 0;
            unsigned char mask =  0x80;//高位在前,逐位接收
           
            //主機先釋放SDA等待從機發送數據
            SDA = 1;
            //I2CStart起始信號已將SCL拉低,從機開始發送數據
            for (mask = 0x80; mask != 0; mask >>= 1)
            {
                nops();//等待從機將數據發送到I2C總線上
                SCL = 1;//使SDA上的數據穩定
                if (SDA != 0)
                {
                    byte |= mask;
                }
                else
                {
                    byte &= (~mask);
                }
                nops();//等待主機采樣數據成功
                SCL = 0;//等待從機發送下一位數據
            }
            SDA = 1;//主機發送非應答1,停止接收數據
            nops();
            SCL = 1;//拉高SCL從機以等待主機的非應答信號
            nops();
            SCL = 0;//拉低SCL等待下一次的I2C通信
           
            return (byte);
        }

        /**
          ***********************************************************************************************
          * @file      :  eeprom.c
          * @author    :  xr
          * @date      :  2014年4月28日19:18:15
          * @version   :  V1.2.3
          * @brief     :  I2C通信協議讀寫eeprom存儲器件
          ***********************************************************************************************
          */
        #include <reg52.h>
        //器件地址宏定義
        #define EEPROM_ADDR 0x50 //1010 A2A1A0 七位器件地址
        /***************************extern function definition************************************/
        /*I2C底層驅動*/
        extern void I2CStart();
        extern void I2CStop();
        extern bit I2CWriteByte(unsigned char byte);
        //extern unsigned char I2CReadByteAck();
        extern unsigned char I2CReadByteNAck();
           
        /*向EEPROM的addr地址寫一個字節的數據*/
        void WriteEEPROMByte(unsigned char addr, unsigned char dat)
        {
            do
            {
                I2CStart();
                if (I2CWriteByte(EEPROM_ADDR << 1)) //寫eeprom器件地址ADDR并在讀寫方向上選擇寫,從機回復應答ack
                {
                    break;//尋址成功強制退出
                }
                I2CStop();
            } while (1);
            I2CWriteByte(addr);//寫入數據地址addr,并返回應答ack
            I2CWriteByte(dat); //寫入數據dat
            I2CStop();
        }
        /*從EEPROM的地址addr中讀取一個字節數據,讀取成功返回1,否則返回0,指針變量接收數據*/
        unsigned char ReadEEPROMByte(unsigned char addr)
        {
            unsigned char dat = 0;
           
            /*I2C進行EEPROM尋址*/
            do
            {
                I2CStart();
                if (I2CWriteByte(EEPROM_ADDR << 1)) //寫入器件地址ADDR并選擇寫
                {
                    break;
                }
                I2CStop();
            } while (1);
            I2CWriteByte(addr); //寫入要讀取的數據的地址addr
            I2CStart();
            I2CWriteByte((EEPROM_ADDR << 1) | 0x01); //寫器件地址ADDR并在讀寫方向上選擇讀
            dat = I2CReadByteNAck();//讀取數據,主機返回非應答
            I2CStop();
           
            return (dat);
        }

        /**
          ******************************************************************************
          * @file      :   Infrared.c
          * @author    :   xr
          * @date      :   2014年5月1日20:57:45
          * @version   :   V1.2.3
          * @brief     :   紅外通信-基于NEC協議的底層驅動
          ******************************************************************************
          */
        #include <reg52.h>
        //紅外通信引腳
        sbit IRD = P3^3;//外部中斷1引腳
        bit flagIrd = 0;         //紅外解碼完成標志
        unsigned char IrdCode[4];//裝載紅外解碼值
        /*紅外通信T1定時器配置*/
        void ConfigInfrared()
        {
            TMOD &= 0x0F;//清零T1控制位
            TMOD |= 0x10;//十六位計數器模式
            TH1 = 0;
            TL1 = 0;//清零T1計數器
            TR1 = 0;//先關閉T1計數
            ET1 = 0;//不用T1中斷
            //配置外部中斷
            IE1 = 0;//外部中斷1標志位清零
            IT1 = 1;//設置外部中斷1的觸發方式是下降沿觸發(0:低電平觸發,1:下降沿觸發)
            EX1 = 1;//開啟外部中斷1
        }
        /*獲得IRD引腳高電平時間即空閑時間計數值*/
        unsigned int GetHighTimers()
        {
            IRD = 1;//檢測該引腳之前要先釋放IRD引腳
            TH1 = 0;
            TL1 = 0;//每次都要清零T1計數值
            TR1 = 1;//打開T1計數
            while (IRD)
            {
                //超時判斷,超過17ms認為是誤碼,強制退出 TL1加滿256就向TH1進1
                if (TH1 > 0x40)  //(0x40*256)*12/11059200(s)= 17.8ms
                {
                    break;
                }
            }
            TR1 = 0;//關閉T1計數
            return (TH1 * 256 + TL1);//返回T1計數值
        }
        /*獲得IRD引腳低電平時間即載波時間計數值*/
        unsigned int GetLowTimers()
        {
            IRD = 1;//檢測IRD引腳之前要先將IRD引腳拉高釋放
            TH1 = 0;
            TL1 = 0;//清零T1計數值
            TR1 = 1;//開啟T1計數
            while (!IRD)
            {
                if (TH1 > 0x40)  //超過17ms表示是誤碼,強制退出
                {
                    break;
                }
            }
            TR1 = 0;//關閉T1計數
            return (TH1 * 256 + TL1);//返回T1計數值
        }
        /*外部中斷1中斷服務,接收紅外解碼值*/
        void EX1_ISP() interrupt 2
        {
            static unsigned int time = 0;
            unsigned char i = 0, j = 0;
            unsigned char Irbyte = 0;
            /*首先判斷引導碼,引導碼(9ms的載波+4.5ms的空閑)*/
            time = GetLowTimers();//獲取載波
            if ((time < 7833) || (time > 8755))  //8.5ms-9.5ms  time = T1計數值*(12/11059200)(s)
            {
                IE1 = 0;//清零外部中斷標志
                return;//退出中斷服務
            }
            time = GetHighTimers();//獲取空閑
            if ((time < 3686) || (time > 4608))  //4ms-5ms
            {
                IE1 = 0;
                return;
            }
            //引導碼正確,開始接收用戶碼和鍵碼
            /*比特值0:560us載波+560us空閑 比特值1:560us載波+1.68us空閑*/
            for (i = 0; i < 4; i++)  //用戶碼+用戶反碼+鍵碼+鍵碼反碼
            {
                for (j = 0; j < 8; j++)  //解碼值是八位數據
                {
                    time = GetLowTimers();//載波 時間范圍:360us-760us
                    if ((time < 332) || (time > 700))
                    {
                        IE1 = 0;
                        return;
                    }
                    //560us載波正確
                    time = GetHighTimers();//空閑
                    if ((time > 332) && (time < 700))  //'0'
                    {
                        //接收比特0
                        Irbyte >>= 1;//低位在先,逐位接收
                    }
                    else if ((time > 1290) && (time < 1658))    //1.68ms空閑,范圍:1.4ms-1.8ms
                    {
                        //接收比特1
                        Irbyte >>= 1;  //先將這一位移出來
                        Irbyte |= 0x80;//再將此位置1
                    }
                    else
                    {
                        //誤碼
                        IE1 = 0;
                        return;
                    }
                }
                //接收一個字節
                IrdCode[i] = Irbyte;
            }
            flagIrd = 1;//接收完成標志置1
            IE1 = 0;//清零外部中斷標志

         
        關閉窗口

        相關文章

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