• 專注電子技術學習與研究
    當前位置:單片機教程網 >> 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;//清零外部中斷標志

     
    關閉窗口

    相關文章

    亚洲一区二区制服在线|在绩专区欧美自拍日韩|青春娱乐网97超碰人人射|在线观看国产网址你懂的