• 远程温控系统设计


    摘要

    本系统以集成测温芯片DS18B20为核心,基于两个单片机的串口通信实现远程温控目的。设计综合利用单片机的可编程性,灵活利用其中的2个定时计数器,完成温度采集、运算控制、输出显示、主从机通信等功能。 DS18B20能够较高精度和较大范围的进行温度测量,保证了系统设计的精度要求;运算控制部分主要使用单片机小系统对采集的数据进行处理,方便快捷;输出显示部分使用八段式四位一体共阴极数码管实现,简单明了。系统性能指标均达到了设计要求。整个系统电路简单,操作方便,用户界面友好。

        远程数字测温系统

    实验目的:

    1、了解电子系统的设计方法  

    2、学习 DS18B20 数字温度传感器的测温原理;

    3、掌握MCS-51单片机工作原理及其应用技术;

    4、掌握串口通信协议及其编程方法。

    5、学会用EDA软件(Protel99se或ORCAD)进行电路原理图和PCB图的绘制。

    6、学习用PSPICE、 Multisim 8等仿真软件进行电路设计和仿真。

    实验任务和要求:

       (一)实验任务

      制作一个数字测温系统,包括主站和从站部分,主从站控制核心均采用AT89S51单片机。

       基本部分:

    1、从站单片机采用DS18B20数字温度传感器测量温度,数码管实时显示温度,测温范围:0°C~100°C,误差:±0.1°C。

    2、主站能够接收从站传送过来的温度并同步显示。

    3、主站能够设定一个阈值温度,当温度达到或超过此阈值温度时发出声光报警信号,并且能够手动解除报警。

      发挥部分:

    1、从站可以检测多点温度,数码管顺序显示各点温度,显示时间≧3秒,并且能够设定报警温度范围,当某点温度低于温度下限时LED1亮,当某点温度高于温度上限时LED2亮。

    2、主从站之间采用232/485通信协议。

    系统方案设计与选择:

    1、系统总体构成图和系统功能

    2、控制单元电路

    控制部分的选择较多,但是作为温度计,在成本上最合适的是单片机,对于题目要求的控制能力也能胜任,利用AT89S52自身强大的功能和优异的可扩展性,配上电路实验箱、四位一体数码管和按键等少量外围电路,就能搭建合适本次实验的小系统。从而大大缩短设计流程,把设计的重点放在温度探测单元,串行通信协议两个部分。

    3、稳压电源电路

       89S52以及74LS04等芯片都工作在+5V的电压之下,因此可以直接采用电路实验箱,省略外围电源的设计。

    4、温度探测单元

    方案一:本设计是测温电路,可以使用热敏电阻之类的器件利用其感温效应,将被测温度变化的电压或电流采集过来,进行A/D转换后,就可以用单片机进行数据的处理,在显示电路上,就可以将被测温度显示出来,这种设计需要用到A/D转换电路,感温电路比较麻烦。

     方案二:考虑到用温度传感器。在单片机电路设计中,大多都是使用传感器,所以可以用实验室熟悉的单片机小系统,采用一只温度传感器DS18B20,直接读取被测温度值,再进行转换,就可以满足设计要求。该数字温度计提供12位(二进制)温度读数,指示器件的温度。信息经过单线接口送 入DSl8B20或从DSl8B20送出,因此从主机CPU到DSl820仅需一条线(和地线)。DSl8B20的电源可以由数据线本身提供而不需要外部电源。因为每一个DSl820在出厂时已经给定了唯一的序号,因此任意多个DSl8B20可以存放在同一条单线总线上。这允许在许多不同的地方放置温度敏感器件。DSl8B20的测量范围从-55到+125,可在l s(典型值)内把温度变换成数字。

    比较以上两种方案,方案二其电路比较简单,各项功能通过编程容易实现,能达到实验提出的精度要求,故采用方案二。   

    5、键盘控制功能

       合理设计键盘功能,以实现更加人性化的人机交互。初步将键盘功能定为复位、温度报警上下限设定、警报解除等功能。

    6、时间—温度数字显示

    考虑到对系统成本的控制,我们选择使用八段式四位一体共阴极数码管。用数码管显示,设计简洁,实现容易。由于本次实验要求显示的数据量不是很大,数码管足以达到设计要求。

    7、温度控制报警输出

       如果当前温度跳出设定上下限范围,系统会自动做出一些反应,即由当前温度产生报警输出,如屏幕显示、声光、语音等。基于本系统外围电路的设计,我们选用LED和蜂鸣器组成声光报警系统。

    8、串行通信单元

    MCS-51系列的单片机都带有串口,在两个MCS-51单片机应用系统相距较近的情况下直接利用串口进行互连就可以了。如果通信距离较长,则可以利用RS-232或者RS-485等标准总线接口来进行双机通信。

    理论分析与计算

    为达到精度要求,我们采用DS18B20的12位存储温度值,最高位为符号。位下图为18B20的温度存储方式,负温度S=1,正温度S=0。

    各位的权值如下:

    Bit7

    Bit6

    Bit5

    Bit4

    Bit3

    Bit2

    Bit1

    Bit0

    LSByte

    2^3

    2^2

    2^1

    2^0

    2^-1

    2^-2

    2^-3

    2^-4

    Bit15

    Bit14

    Bit13

    Bit12

    Bit11

    Bit10

     Bit9

    Bit8

    MSByte

    S

    S

    S

    S

    S

    2^6

    2^5

    2^4

    当我们选用12位存储温度值时,精度可以达到0.0625,如果通过更精确的测温装置,用最小二乘法拟和出误差曲线,可校正误差,进一步提高精度。

    系统设计

    1、整体设计

    初始化后,单片机控制单元从DS18B20中读取时间和温度,调用数码管显示出来;在主循环中不断扫描键盘,通过按键可实现不同的功能,分别对应调超温报警上下限、解除报警的功能。

    2、测温模块设计

    DS18B20 的内部结构框图如下所示。DS18B20内部结构主要由4部分组成:64位光刻的ROM,温度传感器,非挥发的温度报警触发器TH和TL和配置寄存器。

    DS18B20的读写时序:

    DS18B20所有通信都必须先初始化。初始化时,单片机控制发出复位脉冲,DS18B20在其后发出存在脉冲,存在脉冲让控制器知道该DS18B20已在总线上并作好了操作准备。

    写流程:为产生写‘0’时序,在将总线拉低后,总线控制器在整个时序内必须持续总线为60us。为产生写‘1’时序,在将总线拉低后,总线控制器必须在15us内释放总线。

    读流程:读时序从控制设备将总线拉低1us后释放总线开始,所有读时序必须最少持续60us。每个读时序之间必须有至少1us的恢复时间,

    基于上述特性,在单片机控制下的DS18B20读温度的子程序设计流程如图:

    3、串行通信过程

    (1)接收数据的过程。

    在进行通信时,当CPU允许接收时,即SCON的REN位设置1时,外界数据通过引脚RXD(P3.0)串行输入,数据的最低位首先进入移位寄储器,一帧接收完毕后再并行送入接收数据缓冲寄存器SBUF中,同时将接收控制位即中断标志位RI置位,向CPU发出中断请求。

    CPU响应中断后读取输入的数据,同时用软件将RI位清除,准备开始下一帧的输入过程,直到所有数据接受完毕。

    (2)发送数据的过程。

    CPU要发送数据时,将数据并行写入发送数据缓冲寄存器SBUF中,同时启动数据由TXD(P3.1)引脚串行发送,当一帧数据发送完毕,即发送缓冲器空时,由硬件自动将发送中断标志位TI置位,向CPU发送中断请求。CPU响应中断后用软件将TI位清除,同时又将下一帧写入SBUF,重复上述过程直到所有数据发送完毕。

    系统软件设计

       系统软件基于单片机开发系统keilC51开发,采用C语言,本系统软件流程图如下图所示,单片机通过扫描用户键盘输入进入相应功能模块:

    测试方法与测试结果

    1、温度显示及报时测试

       系统加电后,四位一体数码管第一位显示多路温度探头的序号,后三位显示相应的温度,整数部分两位,小数部分一位,精确到0.1。每三秒自动切换一路温度探头,并显示相应的温度。用手摸住DS18B20芯片,液晶温度数字发生变化,显示随环境温度增加,频率为1Hz,表示温度升高。主站和从站温度同步显示,没有延迟现象。各项设计达到指标要求。

    2、温度设定及报警测试

      按一下左键为报警高温上限值十位设定,按两下左键为报警高温上限值个位设定,按三下左键为报警低温下限值十位设定,按四下左键为报警低温下限值个位设定,按五下左键回到正常显示模式。数码管显示报警温度上限,温度上下调节正常,范围在0-99摄氏度。当温度超出温限时,高温警报与低温警报各自对应的二极管灯亮,同时蜂鸣器发出报警信号。按下主站的报警解除温度控制模块及报警设计达到指标要求。

    附录:

    1. 系统原理图

    源程序:主站:

    #include

    #include

    #include

    #define uchar unsigned char

    sbit WARNLED = P3^7;

    #define COUNT0 (-8000)

    int x,count = 0,temp;

    uchar u8,u1,mhigh,mlow,shigh,slow,state;

    bit ks,warn;

    uchar led[4]={0};

    uchar code segtab[19] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x76,0x38,0x7c,0x5e,0x79,0x71,0x73,0x00,0xff}; //七段码段码表

                          // "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "H", "L", "B", "D", "E", "F", "P" ,"black"

    uchar lednum = 0;

    bit light,ws;

    sbit first_key= P1^0 ;    sbit second_key= P1^1;     bit first_getkey = 0,second_getkey = 0,control_readkey = 0; bit getkey1= 0,getkey2 = 0; /*******************************************

    键盘扫描函数

    原型:   void readkey(void);

    功能:  当获得有效按键时,令getkey=1,keynum为按键值

    *****************************************/

    void readkey(void)

    {   

      if( first_key != 1)      {

         if(first_getkey == 0)

          {

        first_getkey = 1;

        }

       else       {

        if(keyon1 == 0)  

        {

             

    getkey1 = 1;                keyon1 = 1;         }

          }

        }

      else

        {

         first_getkey = 0;

         keyon1 = 0;        }

    if( second_key != 1)      {

         if(second_getkey == 0)

          {

        second_getkey = 1;

        }

       else       {

        if(keyon2 == 0)  /    {

             

    getkey2 = 1;                keyon2 = 1;           }

          }

        }

      else

        {

         second_getkey = 0;

         keyon2 = 0;         }}

    void leddisp(void)

    {

    P2 = (1 << lednum);

    if (state == 0 && lednum == 2) P0 = segtab[led[lednum]] | 0x80;

    else P0 = segtab[led[lednum]];

        if (lednum == 3) lednum = 0; else lednum++;

    }

    void fj()

    {

    uchar j;

    for (j = 0; j < 4; j++)

     {

      led[3-j] = x % 10;

    x /= 10;

     }

    }

    void INTT0() interrupt 1

    {

    TH0 = COUNT0 >> 8;  //定时器中断时间间隔 4ms

      TL0 = COUNT0 & 0xff;

    if (count == 750)

    {

    count = 0;

    if (state == 0)

    {

    x = temp;

    fj();

    //led[3] = 17;

    }

    }

    else count++;

    if (state)

    {

    if (state == 1)

    x = shigh;

    else

    x = slow;

    fj();

    if (count % 50 == 0)

    {

    light = ~light;

    }

    if (light) led[3-(uchar)ks]=17;

    led[0] = 9 + state;

    led[1] = 17;

    }

    leddisp();

    if(control_readkey == 1)  //每两次定时中断扫描一次键盘

        {

         readkey();

        }

      control_readkey = !control_readkey;

    }

    void time_delay(unsigned char time)          //延时程序要求大于10us

    {

        time=time-10;

    time=time/6;

    while(time!=0)

    time--;

    }

    void send_data(uchar d8,uchar d1)

    {

    TB8 = d1;

    SBUF = d8;

    while (!TI);

    TI = 0;

    }

    void recieve_data()

    {

    while (!RI);

    u8 = SBUF;

    u1 = RB8;

    RI = 0;

    }

    void INTES() interrupt 4

    {

    u8 = SBUF;

    u1 = RB8;

    RI = 0;

    if (u8 & 0x80)

    {

    shigh = u8 & 0x7f;

    recieve_data();

    slow = u8 & 0x7f;

    }

    else

    {

    temp = u8;

    recieve_data();

    temp = temp * 10 + u8;

    count = 0;

    }

    }

    void main()

    {

    TMOD = 0x01;

    SCON = 0x90;

    PCON = 0x80;

    IP = 0X02;

    TH0 = COUNT0 >> 8;  //定时器中断时间间隔 4ms

      TL0 = COUNT0 & 0xff;

    TCON = 0x10;

    ET0 = 1;

      EA = 1; ES = 1; RI = 0; TI = 0;

    count = 0;

    WARNLED = 0;

    shigh = mhigh = 99; slow = mlow = 0;

    state = 0; ks = 0; warn = 0; ws = 1;

    while (1)

    {

    if (temp > shigh * 10)

    {

    if (ws) WARNLED = 1;

    else WARNLED = 0;

    }

    else

    if (temp < slow * 10)

    {

    if (ws) WARNLED = 1;

    else WARNLED = 0;

    }

    else

    {

    WARNLED = 0;

    }

    if (getkey1)

    {

    getkey1 = 0;

    if (!ks)

    {

    state += 1;

    if (state >= 3) {state = 0; EA = 0; send_data(shigh | 0x80,1); time_delay(10); send_data(slow | 0x80,1); EA = 1; x = temp; fj(); ks = ~ks;}

    }

    ks = ~ks;

    }

    if (getkey2)

    {

    getkey2 = 0;

    if (state == 0) ws = ~ws;

    if (state == 1) {

    if (ks)

    {

    if (shigh / 10 == 9) shigh -= 90;

    else shigh += 10;

    }

    else

    {

    if (shigh % 10 == 9) shigh -= 9;

    else shigh += 1;

    }

    }

    if (state == 2) {

    if (ks)

    {

    if (slow / 10 == 9) slow -= 90;

    else slow += 10;

    }

    else

    {

    if (slow % 10 == 9) slow -= 9;

    else slow += 1;

    }

    }

    }

    }

    }

    从站:

    #include

    #include

    #include

    #include

    #define uchar unsigned char

    #define COUNT0 (-8000)

    #define BUSY (DQ==0)

    sbit DQ = P3^2;

    sbit LED1 = P2^7;

    sbit LED2 = P3^7;

    sbit L0 = P2^0;

    sbit L1 = P2^1;

    sbit L2 = P2^2;

    sbit L3 = P2^3;

    uchar TMP;//温度整数部分

    uchar TMP_d;     //温度小数部分

    uchar f;               //标志位 0正 1负

    int x,count = 0,temp;

    uchar u8,u1,mhigh,mlow,shigh,slow,state;

    bit ks,warn;

    uchar led[4]={0};

    uchar code segtab[19] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x76,0x38,0x7c,0x5e,0x79,0x71,0x73,0x00,0xff}; //七段码段码表

                          // "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "H", "L", "B", "D", "E", "F", "P" ,"black"

    uchar lednum = 0;

    bit light,ws;

    /*扫描键盘使用的变量 */

    sbit first_key= P1^0 ;     //键盘第一行控制

    sbit second_key= P1^1;     //键盘第二行控制

    bit first_getkey = 0,second_getkey = 0,control_readkey = 0;  //读键盘过程中的标志位

    bit getkey1= 0,getkey2 = 0;//获得有效键值标志位 等于1时代表得到一个有效键值

    bit keyon1 = 0,keyon2 = 0 ; //防止按键冲突标志位

    bit flag;

    unsigned char code ROM[16]={0x28,0x05,0xC0,0x27,0x01,0x00,0x00,0x8A,0x28,0x3B,0x66,0x27,0x01,0x00,0x00,0xDB};

    void time_delay(unsigned char time)          //延时程序要求大于10us

    {

        time=time-10;

    time=time/6;

    while(time!=0)

    time--;

    }

    void nNop(unsigned char i)

    {

    for(;i>0;i--);

    }

    void ds_reset(void)                        //ds18b20初始化

    {

    unsigned char i=0;

    unsigned char idata count=0;

    DQ=0;

    time_delay(200);

    time_delay(250);

    time_delay(250);

    time_delay(200);

    DQ=1;

    return;

    }

    void check_pre(void)                     //检查器件是否存在

    {

    while(DQ);

    while(~DQ);

    time_delay(30);

    }

    void wr_ds18b20(char d)             // 写字节到ds18b20

    {

    signed char idata i=0;

    unsigned char idata k;

    bit q;

    for(k=1;k<=8;k++)

    {

    q=(bit)(d & 0x01);

            d=d>>1;

    if(q==1)

    {

    DQ=0;

    _nop_();

    _nop_();

    _nop_();

    _nop_();

    DQ=1;

    time_delay(120);

    }

    else

    {

    DQ=0;

    time_delay(100);

    DQ=1;

    _nop_();

    _nop_();

    _nop_();

    _nop_();

    }

    }

    }

    bit read_bit_ds18b20(void)

    {

    idata char i=0;

    bit j;

    DQ=0;

    _nop_();

    _nop_();

    DQ=1;

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    _nop_();

        _nop_();

    j=DQ;

    time_delay(100);

    return j;

    }

    unsigned char read_ds18b20()

    {

    unsigned char idata i,t,d=0;

    for(i=1;i<=8;i++)

    {

    t=read_bit_ds18b20();                   //假设先送高位被读

    d=t<<(i-1)|d;

    }

    return d;

    }

    void get_temperature(void)          //获得温度值 整数+小数

    {

    unsigned char idata a=0,b=0;

    unsigned char idata i,j;

    ds_reset();

        check_pre();

    wr_ds18b20(0xcc);

    wr_ds18b20(0x44);

    while(BUSY);

    ds_reset();

        check_pre();

    wr_ds18b20(0xcc);

    wr_ds18b20(0xbe);

    a=read_ds18b20();

    b=read_ds18b20();

    i=b;

    i=(i>>4);

    if(i==0)

    {

    f=0;

    TMP=((a>>4)|(b<<4));

    a=(a&0x0f);

    TMP_d=a;

    }

    else

    {

    f=1;

    a=~a;

    a=(a+1);

    b=~b;

    b=(b+1);

    j=a;

    a=a>>4;

    b=b<<4;

    TMP=(a|b);

    j=(j&0x0f);

    TMP_d=j;

    }

    }

    /*******************************************

    键盘扫描函数

    原型:   void readkey(void);

    功能:  当获得有效按键时,令getkey=1,keynum为按键值

    *******************************************

    void readkey(void)

    {   

      if( first_key != 1)      {

         if(first_getkey == 0)

          {

        first_getkey = 1;

        }

       else       {

        if(keyon1 == 0)  

             

    getkey1 = 1;                keyon1 = 1;           }

          }

        }

      else

        {

         first_getkey = 0;

         keyon1 = 0;         }

    if( second_key != 1)      {

         if(second_getkey == 0)

          {

        second_getkey = 1;

        }

       else        {

        if(keyon2 == 0)  

        {

             

    getkey2 = 1;                keyon2 = 1;           }

          }

        }

      else

        {

         second_getkey = 0;

         keyon2 = 0;         }

    }

    void leddisp(void)

    {

    switch (lednum)

    {

    case 0: L3 = 0; L0 = 1; break;

    case 1: L0 = 0; L1 = 1; break;

    case 2: L1 = 0; L2 = 1; break;

    case 3: L2 = 0; L3 = 1; break;

    }

    if (state == 0 && lednum == 2) P0 = segtab[led[lednum]] | 0x80;

    else P0 = segtab[led[lednum]];

        if (lednum == 3) lednum = 0; else lednum++;

    }

    void fj()

    {

    uchar j;

    for (j = 0; j < 4; j++)

     {

      led[3-j] = x % 10;

    x /= 10;

     }

    }

    void send_data(uchar d8,uchar d1)

    {

    TB8 = d1;

    SBUF = d8;

    while (!TI);

    TI = 0;

    }

    void INTT0() interrupt 1

    {

    TH0 = COUNT0 >> 8;  //定时器中断时间间隔 4ms

      TL0 = COUNT0 & 0xff;

    if (count == 750)

    {

    count = 0;

    flag = 1;

    if (state == 0)

    {

    x = temp;

    fj();

    //led[3] = 17;

    }

    }

    else count++;

    if (state)

    {

    if (state == 1)

    x = shigh;

    else

    x = slow;

    fj();

    if (count % 50 == 0)

    {

    light = ~light;

    }  

    if (light) led[3-(uchar)ks]=17;

    led[0] = 9 + state;

    led[1] = 17;

    }

    leddisp();

    if(control_readkey == 1)  //每两次定时中断扫描一次键盘

        {

         readkey();

        }

      control_readkey = !control_readkey;

    }

    void recieve_data()

    {

    while (!RI);

    u8 = SBUF;

    u1 = RB8;

    RI = 0;

    }

    void INTES() interrupt 4

    {

    u8 = SBUF;

    u1 = RB8;

    RI = 0;

    if (u8 & 0x80)

    {

    shigh = u8 & 0x7f;

    recieve_data();

    slow = u8 & 0x7f;

    }

    }

    void get()

    {

    TMP = rand()%100;

    TMP_d = rand()%10;

    }

    void main()

    {

    TMOD = 0x01;

    SCON = 0x90;

    PCON = 0x80;

    IP = 0X02;

    TH0 = COUNT0 >> 8;  //定时器中断时间间隔 4ms

      TL0 = COUNT0 & 0xff;

    //TH1 = TL1 = 0xfc;

    TCON = 0x10; //TR1 = 1;

    ET0 = 1;

      EA = 1; ES = 1; RI = 0; TI = 0;

    count = 0;

    shigh = mhigh = 99; slow = mlow = 0;

    state = 0; ks = 0; warn = 0;

    LED1 = 0; LED2 = 0;

    while (1)

    {

    if (flag)

    {

    get_temperature();

    //get();

    EA = 0;

    send_data(TMP,0);

    time_delay(10);

    send_data(TMP_d,0);

    EA = 1;

    flag = 0;

    count = 0;

    }

    temp = TMP * 10 + TMP_d;

    if (temp > shigh * 10)

    {

    LED2 = 0;

    LED1 = 1;

    }

    else if (temp < slow * 10)

    {

    LED2 = 1;

    LED1 = 0;

    }

    else

    {

    LED1 = LED2 = 0;

    }

    if (getkey1)

    {

    getkey1 = 0;

    if (!ks)

    {

    state += 1;

    if (state >= 3) {state = 0; EA = 0; send_data(shigh | 0x80,1); time_delay(10); send_data(slow | 0x80,1); EA = 1; count = 750; ks = ~ks;}

    }

    ks = ~ks;

    }

    if (getkey2)

    {

    getkey2 = 0;

    if (state == 1) {

    if (ks)

    {

    if (shigh / 10 == 9) shigh -= 90;

    else shigh += 10;

    }

    else

    {

    if (shigh % 10 == 9) shigh -= 9;

    else shigh += 1;

    }

    }

    if (state == 2) {

    if (ks)

    {

    if (slow / 10 == 9) slow -= 90;

    else slow += 10;

    }

    else

    {

    if (slow % 10 == 9) slow -= 9;

    else slow += 1;

    } } }

    }}

  • 相关阅读:
    基于 Istio 的灰度发布架构方案实践之路
    SP11 FCTRL - Factorial
    膜拜,Alibaba最新发布SprinBoot:进阶原理实战与面试题分析指南
    GESP一级 - 第一章 - 第3节 - 计算机软件系统 - 习题
    【Linux入门】— 腾讯云服务器的搭建
    【附源码】计算机毕业设计java租车信息管理系统设计与实现
    UE5两大核心技术介绍:Nanite与Lumen
    机器学习-4
    【LeetCode-940 hard】不同的子序列 II
    Scala开发环境搭建
  • 原文地址:https://blog.csdn.net/qq_61141142/article/details/132820407