ds18b20温度转换指令_【Proteus】DS18B20简易温控器

前言
DS18B20是单片机课设或者仿真项目中一个比较常用的数字温度传感器,因此使用DS18B20仿真一个简易的温控器,超过温度就打开风扇,低于某温度就用继电器打开加热丝。整个程序我都会附在最后,如果懒得复制粘贴,要所有的工程以及仿真的话,就给我点赞赏吧。
整个仿真如图:

首先便是查找DS18B20的手册,编写DS18B20的驱动程序。阅读手册可知:
DS18B20的数据类型
DS18B20提供9位与12位摄氏温度测量进度,上电默认是12位,如果我们就用默认的12位精度的话,那么最后从DS18B20读出来的数据要乘以0.0625,之后得到的就是实际温度,单位摄氏度。测量范围为 -55℃至125℃。
DS18B20的通信方式
DS18B20是单总线接口,也就是说,只需要一个通信接口与单片机进行通信,DS18B20看起来就比较像三极管,但它是两根电源线+一根通信线,每个DS18B20都有一个独一无二的64位序列号,也就是说,在一个总线上可以连接多个DS18B20设备,控制不同的DS18B20来读取多点温度。
DS18B20的外围电路
DS18B20的电路部分也很简单,不需要外围元件,使用3V-5.5V供电。
DS20B20的控制流程
DS18B20上电默认是12位精度,主设备必须向DS18B20发送温度转换命令[44H]才能开始温度转换。当然DS18B20进行温度转换需要时间,当发送完[44H]读命令之后,主设备就可以执行读数据时序了,等待DS18B20转换完成,在DS18B20转换完成之前,都会返回0,转换完成之后,就会返回1。
DS18B20的温度数据是以一个16位二进制补码数的形式存储在温度寄存器中。我们现在就确定使用12位精度的形式,那么首位就为符号位,之后是数据位,如图:

此外要注意这个上电复位时默认是85℃,有些人看到上电85度还以为是错的,因此疯狂修改程序,其实是正常现象,自己程序注意一下即可。
了解了这些,我们就开始着手写DS18B20的驱动程序。
手册上对于事件流程的说明如图:

注意最后一句话:当执行完这些ROM命令之后,主设备必须回到上述步骤中的第一步。也就是说,我们首先需要进行初始化,然后发送ROM命令,然后发送功能命令。之后便是等待ROM命令执行完,如果要发送下一个ROM命令,则又需要重新回到第一步初始化,再发送下一个ROM命令,再发送功能命令。这一点需要记住。
那么我们的整个读取流程就基本确定了:
初始化->跳过读序列号(ROM命令)->启动温度转化(功能命令)->延时等待ROM命令与温度转换执行结束->初始化->跳过读序列号(ROM命令)->读取温度(功能命令)
因为我们就使用一个DS18B20,因此可以跳过这个读序列号。下面我们就按照这个流程来编写各部分程序。
初始化

由时序图可知,初始化程序首先需要将总线拉为低电平至少480us的时间,然后释放总线,总线置为高电平,DS18B20就会检测这个上升沿,检测到上升沿之后,等待15-60us,将总线再拉低至少480us,也就是说,高电平的时间也要持续15-60us。
初始化程序如下:
/@@****************************************************************************
函数功能:延时子程序
****************************************************************************/
void delay(uint k)
{while(k--);
}
/@@****************************************************************************
函数功能:DS18B20初始化子程序
****************************************************************************/
void ds18b20_init(void)
{DQ=1; //DQ先置高delay(16); //延时DQ = 0; //发送复位脉冲delay(80); //延时(>480us)DQ = 1; //拉高数据线delay(16); //等待(15~60us)delay(60);DQ = 1;
}写数据
写数据分为写0和写1,在DS18B20中分为写0时段和写1时段,每个写时段最小必须由60us的时序时间并且要有1us的恢复时间。

在写1时段,在总线拉低之后,主设备必须在15us之内释放总线,总线释放后,上拉电阻将总线拉高;在写0时段,在总线拉低后,整个时段必须一致拉低总线,至少60us。
简单来说,就是这样:在主设备初始化写时段后,DS18B20会在15-60us内对总线进行采样,如果采样时候是高电平,则DS18B20会写入1,反之则写入0。
程序如下:
/****************************************************************************函数功能:向DS18B20写一字节数据入口参数:dat出口参数:****************************************************************************/
void WriteOneChar(uchar dat)
{uchar i=0;DQ=1;delay(1);for(i=8;i>0;i--){DQ=0;DQ=dat&0x01;delay(5);DQ=1;dat>>=1;}
}读数据
主设备在执行完读暂存寄存器[BEh]之后,需要即使生成读时段,才可以读取数据。每个读时段最小必须有60us的持续时间与1us的恢复时间。从DS18B20中发送出来的数据在读时序后仅有15us的有效时间,因此,主设备在开始读时段后的15us之内必须释放总线来读取数据。时序如上图所示。
程序如下:
/****************************************************************************函数功能:向DS18B20读一字节数据入口参数:出口参数:dat****************************************************************************/uchar ReadOneChar(void){uchar i=0;uchar dat=0;DQ=1;delay(1);for (i=8;i>0;i--){DQ=0;dat>>=1;DQ=1;delay(1);if(DQ)dat|=0x80;delay(30);DQ=1;}return dat;
}然后就是根据手册查找一些重要的命令,包括跳过读序列号,读取温度命令等等,这一部分就不再赘述。然后我们就可以按照我们之前总结的读取温度的流程来编写整个读取温度的程序:
/****************************************************************************函数功能:向DS18B20读温度值
入口参数:
出口参数:temperature温度读取流程: 初始化->跳过读序列号->启动温度转化->延时->初始化->跳过读序列号->读取温度并显示****************************************************************************/
float ReadTemperature(void)
{float temperature = 0.0;uint temflag = 0;uint tt = 0;uchar tempL=0; //临时变量低位uchar tempH=0; //临时变量高位ds18b20_init(); //初始化WriteOneChar(0xcc); //跳过读序列号的操作WriteOneChar(0x44); //启动温度转换delay(125); //转换需要一点时间,延时ds18b20_init(); //初始化WriteOneChar(0xcc); //跳过读序列号的操作WriteOneChar(0xbe); //读温度寄存器(头两个值分别为温度的低位和高位)tempL=ReadOneChar(); //读出温度的低位LSBtempH=ReadOneChar(); //读出温度的高位MSB//温度转换,把高低位做相应的运算转化为实际温度tt = tempH<<8;tt = tt|tempL;if(tt&0xf800) //判断是否是负温度{ //是负温度tt = ~tt + 1; temflag = 0;}else{tt = tt; temflag = 1;}temperature = (float)tt;temperature = temperature * 0.0625;
// delay(200);/*负温度*/if(temflag == 0){temperature = -temperature;}/*正温度*/else{temperature = temperature;}return temperature; //返回温度值
}然后就是用1602来显示出来,看一下我们的程序对不对,关于1602的驱动程序,在我之前的文章中有说明,这里就不再说明。
测试主程序如下:
/@@*显示函数*/
void display(void)
{char *string1 = "TEM : ";char *string2 = " C ";char *string3 = "High:";char *string4 = "Low:"; char Display1[16]; //第一行显示数组char Display2[16]; //第二行显示数组//显示温度sprintf((char*)Display1,"%s%d%s",string1,temp,string2); print_string(Display1,1); //显示过温低温点sprintf((char*)Display2,"%s%d %s%d",string3,tempOver,string4,tempLow); print_string(Display2,2);
}
void main()
{lcd_init();SW = 0;Motor = 0;while(1){temp = (int)ReadTemperature(); //读取温度display(); //显示}
}测试结果:

这里的显示程序中我加了阈值显示,这里设置的是高温阈值为30,低温阈值为27。因为为了后续方便,所以我就把阈值设置得比较近,这个可以自行修改。
之后就是比较简单的逻辑了,超过高温阈值就关闭加热丝打开风扇,低于低温阈值就关闭风扇,打开加热丝。主程序如下:
void main()
{lcd_init();SW = 0;Motor = 0;while(1){temp = (int)ReadTemperature(); //读取温度display(); //显示/*过温 打开风扇,关闭加热丝*/if(temp > tempOver){SW = 0; Motor = 1;}/*低温 关闭风扇,打开加热丝*/else if(temp < tempLow){SW = 1;Motor = 0;}}
}关于风扇的电路,我这里就直接用了一个ULN2003,这个是小电流电机的,如果要更换24V大电流电机驱动,或者要加上PWM调速,我后续会更新这方面的文章。
效果如下:

如果要全部的工程,仿真就点赞私聊我喔。
1602程序在之前的文章中有发,因此1602程序就不发了。我的工程目录如下:

所有源代码如下:
main.c
#include
#include
#include "1602.h"
#include "ds18b20.h"
#define uchar unsigned char
#define uint unsigned intsbit SW = P1^0; //继电器控制加热丝
sbit Motor = P1^1; //电机接口int temp = 0; //存储温度
int tempOver = 30; //过温温度
int tempLow = 27; //低温温度
sbit key1 = P3^0; //增加过温温度
sbit key2 = P3^1; //减小过温温度
sbit key3 = P3^0; //增加低温温度
sbit key4 = P3^1; //减小低温温度uint key_result = 0; //保存按键结果void key_delay(uchar t)
{ int j; for(;t!=0; t--) for (j=0;j<255;j++); }/*按键检测 如果按键1被按下就返回1
如果按键2被按下就返回2 如果没有按键按下就返回0*/
uint key_scan(void)
{uint result = 0;/*先将按键电平拉高*/key1 = 1;key2 = 1;/*检测按键1是否被按下*/if(key1 == 0){key_delay(5);if(key1 == 0){result = 1;}}/*检测按键2是否被按下*/if(key2 == 0){key_delay(5);if(key2 == 0){result = 2;}} return result;
}/*显示函数*/
void display(void)
{char *string1 = "TEM : ";char *string2 = " C ";char *string3 = "High:";char *string4 = "Low:"; char Display1[16]; //第一行显示数组char Display2[16]; //第二行显示数组//显示温度sprintf((char*)Display1,"%s%d%s",string1,temp,string2); print_string(Display1,1); //显示过温低温点sprintf((char*)Display2,"%s%d %s%d",string3,tempOver,string4,tempLow); print_string(Display2,2);
}void main()
{lcd_init();SW = 0;Motor = 0;while(1){temp = (int)ReadTemperature(); //读取温度display(); //显示/*过温 打开风扇,关闭加热丝*/if(temp > tempOver){SW = 0; Motor = 1;}/*低温 关闭风扇,打开加热丝*/else if(temp < tempLow){SW = 1;Motor = 0;}}
}
DS18B20源文件:
#include
#include
#include "ds18b20.h"/***************************温度传感器信号引脚******************************************/
sbit DQ=P1^5; //数据传输线接单片机的相应的引脚/****************************************************************************函数功能:延时子程序****************************************************************************/
void delay(uint k)
{while(k--);
}/****************************************************************************函数功能:DS18B20初始化子程序****************************************************************************/
void ds18b20_init(void)
{DQ=1; //DQ先置高delay(16); //延时DQ = 0; //发送复位脉冲delay(80); //延时(>480us)DQ = 1; //拉高数据线delay(16); //等待(15~60us)delay(60);DQ = 1;
}/****************************************************************************函数功能:向DS18B20读一字节数据入口参数:出口参数:dat****************************************************************************/uchar ReadOneChar(void){uchar i=0;uchar dat=0;DQ=1;delay(1);for (i=8;i>0;i--){DQ=0;dat>>=1;DQ=1;delay(1);if(DQ)dat|=0x80;delay(30);DQ=1;}return dat;
}/****************************************************************************函数功能:向DS18B20写一字节数据入口参数:dat出口参数:****************************************************************************/
void WriteOneChar(uchar dat)
{uchar i=0;DQ=1;delay(1);for(i=8;i>0;i--){DQ=0;DQ=dat&0x01;delay(5);DQ=1;dat>>=1;}
}/****************************************************************************函数功能:向DS18B20读温度值
入口参数:
出口参数:temperature温度读取流程: 初始化->跳过读序列号->启动温度转化->延时->初始化->跳过读序列号->读取温度并显示****************************************************************************/
float ReadTemperature(void)
{float temperature = 0.0;uint temflag = 0;uint tt = 0;uchar tempL=0; //临时变量低位uchar tempH=0; //临时变量高位ds18b20_init(); //初始化WriteOneChar(0xcc); //跳过读序列号的操作WriteOneChar(0x44); //启动温度转换delay(125); //转换需要一点时间,延时ds18b20_init(); //初始化WriteOneChar(0xcc); //跳过读序列号的操作WriteOneChar(0xbe); //读温度寄存器(头两个值分别为温度的低位和高位)tempL=ReadOneChar(); //读出温度的低位LSBtempH=ReadOneChar(); //读出温度的高位MSB//温度转换,把高低位做相应的运算转化为实际温度tt = tempH<<8;tt = tt|tempL;if(tt&0xf800) //判断是否是负温度{ //是负温度tt = ~tt + 1; temflag = 0;}else{tt = tt; temflag = 1;}temperature = (float)tt;temperature = temperature * 0.0625;
// delay(200);/*负温度*/if(temflag == 0){temperature = -temperature;}/*正温度*/else{temperature = temperature;}return temperature; //返回温度值
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
