基于51单片机的八路温度检测系统利用AT89C51作为系统主控,利用8个DS18B20进行温度的获取,使用LCD1602进行温度显示,利用蜂鸣器和Led灯进行报警,使用按键进行显示切换和阈值调节,实现整个系统功能。
一、原理图绘制
1、51单片机最小系统板
本次设计利用AT89C51作为系统主控,于是我们需要设计51单片机最小系统板,大致分为晶振电路、复位电路、芯片三部分,电路图如下:
2、LCD显示模块
LCD模块比较简单,它是一种并口传输的模块,优点是简单,但是IO口占用比较多,如下图:
3、报警模块
报警模块使用Led灯和蜂鸣器,其中LED使用低电平点亮的连接方式,蜂鸣器使用PNP的连接方式,为低电平点亮。如下:
4、八路温度模块
因为Wire总线支持多设备挂载,于是,我们只需要将这八个DS18B20的数据IO口连接到同一个单片机IO口就行了。如下图:
5、按键模块
按键模块非常简单,只需要将按键一端连接到GND,另一端连接到IO口就行了,如下图:
二、原理分析
1、OneWire协议
Oneware单总线是Maxim全资子公司Dallas的一项专有技术。与目前多数标准串行数据通信方式,如SPI / I2C不同,它采用单根信号线,既传输时钟,又传输数据,而且数据传输是双向的。它具有节省I/O口线资源、结构简单、成本低廉、便于总线扩展和维护等诸多优点。
(1)复位和应答:
(2)读写时序
综合上面时序,我们写出如下代码:
#include <REGX52.H>
sbit OneWire_DQ=P3^7;
unsigned char OneWire_Init()
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1;
OneWire_DQ=0;
i = 247;while (--i); //delay 500us
OneWire_DQ=1;
i = 32;while (--i); //delay 70us
AckBit=OneWire_DQ;
i = 247;while (--i); //delay 500us
return AckBit;
}
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0;
i=4;while(--i); //delay 10us
OneWire_DQ=Bit;
i=24;while(--i); //delay 50us
OneWire_DQ=1;
}
unsigned char OneWire_ReceiveBit()
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i=2;while(--i); //delay 5us
OneWire_DQ=1;
i=2;while(--i); //dealy 5us
Bit=OneWire_DQ;
i=24;while(--i); //delay 50us
return Bit;
}
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
unsigned char OneWire_ReceiveByte()
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
}
return Byte;
}
2、DS18B20读取
DS18B20的指令一般分三步:
(1) 初始化
(2)ROM指令
(3)功能指令
本次设计也大致遵循该步骤,由于此前已经进行过初始化,所以本次设计读取数据的步骤大致上是 匹配ROM -> 读暂存存储器 -> 读出数据。如下代码:
//通过rom匹配
void DS18B20_Read_Temperature(u8 *rom,u16 *dat){
u8 LSB = 0,MSB = 0;
u16 Temp = 0;
//温度转换
DS18B20_Match_Rom(rom);//匹配ROM
OneWire_SendByte(0X44);//温度变换
// delay(750);
DS18B20_Match_Rom(rom);
OneWire_SendByte(0XBE);//读暂存存储器
LSB = OneWire_ReceiveByte();
MSB = OneWire_ReceiveByte();
Temp = (MSB<<8) | LSB;//数据合并为16位
if((Temp&0XF800) == 0XF800){ //负温度;s=1 正温度: s=0
*dat =(((~Temp+0X01)* -0.0625)+0.5)*10.0;
}
else{
*dat =((Temp*0.0625)+0.5)*10.0;
}
}
三、程序设计
1、获取DS18B20的ROM
常规的DS18B20在读取数据的时候经常会执行 DS18B20_SKIP_ROM 指令,而本次设计需要实现OneWire总线挂载多设备,这一步是不能执行的。 但是在使用ROM进行多设备读取的时候首先需要获取每个DS18B20的ROM,每个DS18B20出厂的时候都有一个唯一的ROM,一般会在其丝印上。如下图
但是这个丝印一般比较小,并不方便查看,有没有什么方法可以让DS18B20自动将其ROM发送给我们呢,答案是肯定的。如下代码:
void DS18B20_ReadRom(unsigned char *rom)
{
unsigned char i=0;
OneWire_Init();//启动信号
OneWire_SendByte(0x33);//读ROM
for(i=0;i<8;i++){
rom[i] = OneWire_ReceiveByte();//读取一个
}
}
我们可以借助串口首先将每个DS18B20的ROM信息读出来放入数组,然后我们再通过DS18B20的ROM读出这八个传感器的数据。
2、DS18B20读取数据
获取到DS18B20的数据后,我们可以使用该ROM去获取每一个DS18B20的数据了,通过如下代码:
//通过rom匹配
void DS18B20_Read_Temperature(u8 *rom,u16 *dat){
u8 LSB = 0,MSB = 0;
u16 Temp = 0;
//温度转换
DS18B20_Match_Rom(rom);//匹配ROM
OneWire_SendByte(0X44);//温度变换
// delay(750);
DS18B20_Match_Rom(rom);
OneWire_SendByte(0XBE);//读暂存存储器
LSB = OneWire_ReceiveByte();
MSB = OneWire_ReceiveByte();
Temp = (MSB<<8) | LSB;//数据合并为16位
if((Temp&0XF800) == 0XF800){ //负温度;s=1 正温度: s=0
*dat =(((~Temp+0X01)* -0.0625)+0.5)*10.0;
}
else{
*dat =((Temp*0.0625)+0.5)*10.0;
}
}
3、主程序设计
住程序比较简单,就是将DS18B20的数据读出,然后显示。将按键调节显示、DS18B20温度的阈值。
#include <REGX52.H>
#include "LCD1602.H"
#include "delay.h"
#include "DS18B20.h"
#include "UART.h"
#include "KEY.h"
unsigned char i=0;
unsigned char room[8];
sbit Led=P1^4;
sbit Buz=P1^5;
unsigned char code room1[8] = {0x28, 0x12, 0x1A, 0x81, 0x25, 0x23, 0x0B, 0xA9 };
unsigned char code room2[8] = {0x28, 0x5C, 0x01, 0xB0, 0x25, 0x23, 0x0B, 0x04 };
unsigned char code room3[8] = {0x28, 0xEE, 0x75, 0xAD, 0x25, 0x23, 0x0B, 0xBA };
unsigned char code room4[8] = {0x28, 0x45, 0x1B, 0xC6, 0x25, 0x23, 0x0B, 0xA9 };
unsigned char code room5[8] = {0x28, 0x6F, 0x02, 0x99, 0x25, 0x23, 0x0B, 0x1D };
unsigned char code room6[8] = {0x28, 0x2D, 0xAD, 0xB4, 0x25, 0x23, 0x0B, 0xB7 };
unsigned char code room7[8] = {0x28, 0x96, 0x0C, 0x82, 0x25, 0x23, 0x0B, 0xF7 };
unsigned char code room8[8] = {0x28, 0xC5, 0xD0, 0x86, 0x25, 0x23, 0x0B, 0xF1 };
u8 keyNum=0;
u8 mode=0;
u8 flag=1;
u16 tem1=0;
u16 tem2=0;
u16 tem3=0;
u16 tem4=0;
u16 tem5=0;
u16 tem6=0;
u16 tem7=0;
u16 tem8=0;
u16 tem_th=31;
u16 cnt=0;
void main()
{
LCD_Init();
UART_Init();
while(1)
{
keyNum = KEY();
if(keyNum==1){
mode++;
mode%=2;
}
if(mode==0){
//清除显示
if(flag==1){
LCD_Clear();
flag=0;
}
//修改报警值
if(keyNum==2){
tem_th--;
}
else if(keyNum==3){
tem_th++;
}
//显示阈值信息
LCD_ShowString(1, 14, "th:");
LCD_ShowNum(2, 14, tem_th, 2);
}
else if(mode==1){
DS18B20_Read_Temperature(room1, &tem1);
tem1 /=10;
LCD_ShowNum(1, 1, tem1, 2);
DS18B20_Read_Temperature(room2, &tem2);
tem2 /=10;
LCD_ShowNum(1, 4, tem2, 2);
DS18B20_Read_Temperature(room3, &tem3);
tem3 /=10;
LCD_ShowNum(1, 7, tem3, 2);
DS18B20_Read_Temperature(room4, &tem4);
tem4 /=10;
LCD_ShowNum(1, 10, tem4, 2);
DS18B20_Read_Temperature(room5, &tem5);
tem5 /=10;
LCD_ShowNum(2, 1, tem5, 2);
DS18B20_Read_Temperature(room6, &tem6);
tem6 /=10;
LCD_ShowNum(2, 4, tem6, 2);
DS18B20_Read_Temperature(room7, &tem7);
tem7 /=10;
LCD_ShowNum(2, 7, tem7, 2);
DS18B20_Read_Temperature(room8, &tem8);
tem8 /=10;
LCD_ShowNum(2, 10, tem8, 2);
LCD_ShowString(1, 14, "th:");
LCD_ShowNum(2, 14, tem_th, 2);
if(tem1 >= tem_th || tem2 >= tem_th || tem3 >= tem_th || tem4 >= tem_th || \
tem5 >= tem_th || tem6 >= tem_th || tem7 >= tem_th || tem8 >= tem_th \
){
if(++cnt>=2){
cnt=0;
Led=~Led;
}
Buz=0;
}
else{
Led=1;
Buz=1;
}
delay(10);
flag=1;
}
}
}
四、PCB设计
五、项目总结
通过仿真、代码、PCB设计,本次简单项目已经完成,通过八路DS18B20获取八路温度,使用按键调节阈值,八路中任意一路超过阈值,LED亮起、蜂鸣器进行报警。演示地址如下: