专注电子技术学习与研究
当前位置:单片机教程网 >> 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;//清零外部中断标志

 
关闭窗口

相关文章