远程温控系统设计

摘要

本系统以集成测温芯片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 <REG51.H>

#include <string.h>

#include <absacc.h>

#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 <absacc.h>

#include <reg51.h>

#include <intrins.h>

#include <stdlib.h>

#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;

} } }

}}

相关推荐
weixin_4493108415 分钟前
高效集成:聚水潭采购数据同步到MySQL
android·数据库·mysql
Cachel wood1 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
standxy1 小时前
如何将钉钉新收款单数据高效集成到MySQL
数据库·mysql·钉钉
Narutolxy2 小时前
MySQL 权限困境:从权限丢失到权限重生的完整解决方案20241108
数据库·mysql
Venchill2 小时前
安装和卸载Mysql(压缩版)
数据库·mysql
Humbunklung2 小时前
一种EF(EntityFramework) MySQL修改表名去掉dbo前缀的方法
数据库·mysql·c#
PGCCC3 小时前
【PGCCC】postgresql 缓存池并发设计
数据库·缓存·postgresql
小爬虫程序猿3 小时前
如何利用Python解析API返回的数据结构?
数据结构·数据库·python
wowocpp4 小时前
查看 磁盘文件系统格式 linux ubuntu blkid ext4
linux·数据库·ubuntu
Ai 编码助手10 小时前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql