Protues 基于51单片机的DS18B20温度采集:键盘输入温度上下限_超出设定温度范围报警_8数码管显示

   日期:2020-07-11     浏览:79    评论:0    
核心提示:1、设计单片机与16按键的矩阵式键盘接口以及8数码管的接口电路,测试显示和按键功能。2、设计单片机与DS18B20的接口电路,实现数字温度信息的采集,然后编程处理采集到的数据,得到温度值。3、将采集的温度信息的显示在数码管上。4、设计声光报警电路,设置温度的上下限值,实现报警功能5,将温度上下限显示在数码管上。

功能实现:
1、设计单片机与16按键的矩阵式键盘接口以及8数码管的接口电路,测试显示和按键功能。
2、设计单片机与DS18B20的接口电路,实现数字温度信息的采集,然后编程处理采集到的数据,得到温度值。
3、将采集的温度信息的显示在数码管上。
4、设计声光报警电路,设置温度的上下限值,实现报警功能
5,将温度上下限显示在数码管上。

protues仿真图

代码较长分开说明:

一、 定义与函数声明

#include <reg52.h>
#include <intrins.h> // 因为此文件中用到了延时函数_nop_()(延时一个机器周期),所以要包含_nop_()的头文件、
#define u16 unsigned int
#define u8 unsigned char
char temperatureH = 100, temperatureL = -10; //最高温,最低温 char范围-128~127
// 键盘定义
u8 line, row, Sort = 0, kvalue = 0, kscan, kscanA = 0, kscanB = 0;
u8 keyRowLine();
void getKeyVaule();
void setH();
void setL();
void showH();
void showL();
void dealKey();
// 测温定义
sbit SPEAK = P3 ^ 1;
sbit DQ = P3 ^ 7;
u8 flag = 0, EX = 0, setting = 0, led_active = 0;
u16 n = 0;
u8 dat[10] = {0, 0, 0, 12, 5, 6, 7, 8, 9};
u8 dispcode[] =
    {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
     0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71,
     0x40, 0x76, 0xb8, 0xb7, 0xbe}; //'-' 'H' 'L' 'n' 'u'
// *****************函数声明*******************
void delay5(u8);
u8 initialize();
void write1Byte(u8 come);
u8 read1Byte();
u16 getTemperature();
void Temp_treatment();
void LED_4();
void alarm();

唯一要注意的是延时函数_nop_()为延时一个机器周期的作用

、代码逻辑概括 主函数

// *****************主函数**********************
void main()
{
    u8 i;
    delay5(1000);
    while (1)
    {
        getKeyVaule();    //键盘函数
        dealKey();        //处理按键
        Temp_treatment(); //温度函数
        for (i = 0; i < 30; i++)
        {
            LED_4(); //显示LED
            delay5(1000);
            if (EX)
            {
                alarm(); //报警
                delay5(1000);
            }
        }
    }
}

for (i = 0; i < 30; i++) 语句是为了在一次while (1)循环中LED显示次数增多,增强LED显示稳定性。
EX代表温度超过范围,会使蜂鸣器报警。

、精确延时5us 延时函数

// *****************延时函数********************
void delay5(u8 n)
{
    do
    { //搭配11.059MHZ 精确延时5us
        _nop_();
        _nop_();
        _nop_();
        n--;
    } while (n);
}

、51单片机想向18B20打招呼 初始化函数

// *****************初始化函数******************
u8 initialize()
{
    u8 Prence;
    DQ = 0;      //拉低总线
    delay5(120); //600us
    DQ = 1;      //总线控制给18b20
    delay5(16);  //80us
    if (DQ)
    {
        Prence = 0;
    }
    else
    {
        Prence = 1;
    }
    delay5(80); //400us
    return Prence;
}

初始化时序图

黑色实线: 51控制总线 灰色实线: 18B20控制总线
Prence为设定的标志位,防止无人应答进入死循环。

、51单片机向18B20发指令 写函数

// *****************写函数**********************
void write1Byte(u8 come)
{
    u8 i;
    for (i = 0; i < 8; i++)
    {
        DQ = 0;           //51控制总线
        DQ = come & 0x01; //低位开始写,1us
        delay5(12);       //60us
        DQ = 1;           //18b20控制总线
        come = come >> 1;
        delay5(5);
    }
}

写时序图

无论读写,都是从低字节,低位开始的。

、51单片机从18B20读取温度 读函数

// *****************读函数**********************
u8 read1Byte()
{
    u8 i, value = 0;
    for (i = 0; i < 8; i++)
    {
        DQ = 0;             //51控制总线
        delay5(1);          //5us
        DQ = 1;             //释放总线
        value = value >> 1; //从低位开始
        _nop_();            //一个机器周期的时间
        if (DQ)
        {
            value = value | 0x80;
        }
        delay5(11);
    }
    return value;
}

读时序图

if (DQ)
        {
            value = value | 0x80;
        }

整个函数中这一句话是最难理解的,指当18B20传来的数为1时,执行语句,为0时不执行,毕竟51和18B20只有DQ连着,所以18B20传什么,DQ就是什么。直接举个栗子。

// DS18B20 1110 0011 0x80=1000 0000
// 51 i=0 1111 0001 i=1
// 51 1111 1000 i=2
// 51 0111 1100 i=3
// 51 0011 1110 i=4
// 51 0001 1111 i=5
// 51 1000 1111 i=6
// 51 1100 0111 i=7
// 51 1110 0011 i=8

经过一个读函数,51成功读到了18B20的一个字节。

、准备工作完成开始 测温

// *****************测温函数******************
u16 getTemperature()
{
    // 启动温度转换
    u8 low, high, Temp;
    initialize();
    delay5(10);
    write1Byte(0xcc);//单片工作
    write1Byte(0x44);//启动温度转换
    initialize();
    write1Byte(0xcc);
    write1Byte(0xbe);//读取
    low = read1Byte();
    high = read1Byte();
    Temp = (high << 4) | (low >> 4);
    if (Temp > 127)
    { //温度为负值,将补码转换成原码
        Temp = ~Temp + 1;
        flag = 1;
    }
    else
    {
        flag = 0;
    }
    return (Temp);
}

解释一下下面语句如何获得温度的整数值

    Temp = (high << 4) | (low >> 4);
//和
if (Temp > 127)
    { //温度为负值,将补码转换成原码
        Temp = ~Temp + 1;
        flag = 1;
    }

首先,温度的数据放在DS18B20的前2个字节

所以,low位温度值低位,high为温度值高位。
这2个字节各个位的含义:

    // 2字节 16位 从低位开始
    // 1~4位 温度的小数部分
    // 5~11位 温度的整数部分
    // 12~16位 符号位
    Temp = (high << 4) | (low >> 4);
    //0111 0000 | 0000 1101= 0111 1101
    // 即十进制的125
    //如果Temp为正数,Temp最大为 0111 1111 即127。补码就是原码
    //Temp > 127,则温度为负数,符号位为1,将补码转换成原码

求小数部分,这个实验中没用到,拓展一下

    /
        delay5(1000);
        line = P2;
        if (line != 0x0f)
        {
            
            line = line & 0x0f;
            P2 = 0xf0;
            // delay1ms(1); //等待变化时间
            row = P2;
            row = row & 0xf0;
            kscan = line | row;
            before = P2;
            while (P2 == before)
            {
                
            }
            return kscan;
        }
    }
    return 0xff;
}

下面这个语句有利有弊,可以防止按一次键盘,产生多次按下的效果,但是按键按下时,LED会黑一下。自己权衡吧。

            before = P2;
            while (P2 == before)
            {
                
            }
根据按键调整标志位

// 获得键编码,调整标志位
void getKeyVaule()
{
    u8 Kscan;
    Kscan = keyRowLine();
    switch (Kscan)
    {
    case 0xee:
        setting = 1; //显示最低温
        break;
    case 0xde:
        setting = 2; //显示最高温
        break;
    case 0xbe:
        setting = 3; //设置最低温
        break;
    case 0x7e:
        setting = 4; //设置最高温
        break;
    case 0xed:       //归零
        dat[0] = 0;
        dat[1] = 0;
        dat[2] = 0;
        dat[3] = 12;
        Sort = 0;
        break;
    case 0xdd:       //输入负号
        dat[0] = 16;
        Sort = 1;
        break;
    case 0xbd:
        dat[Sort] = 0;
        Sort++;
        break;
    case 0x7d:
        dat[Sort] = 1;
        Sort++;
        break;
    case 0xeb:
        dat[Sort] = 2;
        Sort++;
        break;
    case 0xdb:
        dat[Sort] = 3;
        Sort++;
        break;
    case 0xbb:
        dat[Sort] = 4;
        Sort++;
        break;
    case 0x7b:
        dat[Sort] = 5;
        Sort++;
        break;
    case 0xe7:
        dat[Sort] = 6;
        Sort++;
        break;
    case 0xd7:
        dat[Sort] = 7;
        Sort++;
        break;
    case 0xb7:
        dat[Sort] = 8;
        Sort++;
        break;
    case 0x77:
        dat[Sort] = 9;
        Sort++;
        break;
    default:
        break;
    }
    if (Sort >= 3)
    {
        Sort = 0;
    }
}

Sort取值0~2,使输入在数码管1 ~3 位循环

按键响应函数
// *****************按键响应函数********************
void dealKey()
{
    if (setting == 1)
    {
        showL();//显示温度下限
    }
    else if (setting == 2)
    {
        showH();//显示温度上限
    }
    else if (setting == 3)
    {
        setL();//设定温度下限
    }
    else if (setting == 4)
    {
        setH();//设定温度上限
    }
}

设定温度上下限函数差不多,这里就说说设定温度上限函数

void setH()
{
    temperatureH = 0;
    temperatureH = 10 * dat[1] + dat[2];
    if (dat[0] == 16)
    {
        temperatureH = 0 - temperatureH;
    }
    else
    {
        temperatureH = 100 * dat[0] + temperatureH;
    }
    Sort = 0;//*
    setting = 0;//*
    dat[3] = 17;
}

首先判断最高位是不是负号,然后将数码管的值赋给全局变量temperatureH即可。

    Sort = 0;//*
    setting = 0;//*

这两句话非常重要,setting也是全局变量,如果不置成0,按键响应函数将不断重复上一次执行的功能。Sort = 0;是为了保证,按键响应后,键盘输入从数码管第一位开始。

如果设置温度上限成功,数码管第4位(输入状态)会显示‘H’

//************显示温度下限***************
void showL()
{
    char LowTemperature = temperatureL;//*
    if (LowTemperature < 0)
    {
        LowTemperature = 0 - LowTemperature;
        dat[0] = 16;
        dat[1] = LowTemperature / 10;
        dat[2] = LowTemperature % 10;
    }
    else
    {
        dat[0] = LowTemperature / 100;
        dat[1] = (LowTemperature / 10) % 10;
        dat[2] = LowTemperature % 10;
    }
    Sort = 0;
    setting = 0;
    dat[3] = 20;
}

本函数需要注意的语句

 char LowTemperature = temperatureL;//*

本函数首先会让负数转为正数,temperatureL是全局变量,如果直接用temperatureL进行showL()操作,可能改变设置好的温度下限。
因此,采用局部变量LowTemperature对temperatureL进行替换,局部变量的改变不会影响全局变量。

最后显示成功如图(U的上面开口,下面封闭,用来表示下限,同理‘n’表示上限。

暂时就这样吧!

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服