电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
【Proteus】DS18B20简易温控器
分 享
扫描二维码分享
【Proteus】DS18B20简易温控器
DS18b20
单片机
Proteus
KVIN
关注
发布时间: 2020-01-09
丨
阅读: 1441
# 前言 DS18B20是单片机课设或者仿真项目中一个比较常用的数字温度传感器,因此使用DS18B20仿真一个简易的温控器,超过温度就打开风扇,低于某温度就用继电器打开加热丝。 整个仿真如图: ![](https://cf01.ickimg.com/bbsimages/202001/27e530c82c30413a8ec74e10b97f3bfd.png) 首先便是查找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位精度的形式,那么首位就为符号位,之后是数据位,如图: ![](https://cf01.ickimg.com/bbsimages/202001/6ec7b03e7d206c3248d41134a73f8be6.png) 此外要注意这个上电复位时默认是85℃,有些人看到上电85度还以为是错的,因此疯狂修改程序,其实是正常现象,自己程序注意一下即可。 了解了这些,我们就开始着手写DS18B20的驱动程序。 手册上对于事件流程的说明如图: ![](https://cf01.ickimg.com/bbsimages/202001/f16a54b14555053ff0634cabb19a23e1.png) 注意最后一句话:当执行完这些ROM命令之后,主设备必须回到上述步骤中的第一步。也就是说,我们首先需要进行初始化,然后发送ROM命令,然后发送功能命令。之后便是等待ROM命令执行完,如果要发送下一个ROM命令,则又需要重新回到第一步初始化,再发送下一个ROM命令,再发送功能命令。这一点需要记住。 那么我们的整个读取流程就基本确定了: 初始化-&amp;amp;gt;跳过读序列号(ROM命令)-&amp;amp;gt;启动温度转化(功能命令)-&amp;amp;gt;延时等待ROM命令与温度转换执行结束-&amp;amp;gt;初始化-&amp;amp;gt;跳过读序列号(ROM命令)-&amp;amp;gt;读取温度(功能命令) 因为我们就使用一个DS18B20,因此可以跳过这个读序列号。下面我们就按照这个流程来编写各部分程序。 ## 初始化 ![](https://cf01.ickimg.com/bbsimages/202001/9db67769c217a6f88285bc4020a82cac.png) 由时序图可知,初始化程序首先需要将总线拉为低电平至少480us的时间,然后释放总线,总线置为高电平,DS18B20就会检测这个上升沿,检测到上升沿之后,等待15-60us,将总线再拉低至少480us,也就是说,高电平的时间也要持续15-60us。 初始化程序如下: ```c /@@**************************************************************************** 函数功能:延时子程序 ****************************************************************************/ void delay(uint k) { while(k--); } /@@**************************************************************************** 函数功能:DS18B20初始化子程序 ****************************************************************************/ void ds18b20_init(void) { DQ=1; //DQ先置高 delay(16); //延时 DQ = 0; //发送复位脉冲 delay(80); //延时(&amp;amp;gt;480us) DQ = 1; //拉高数据线 delay(16); //等待(15~60us) delay(60); DQ = 1; } ``` ## 写数据 写数据分为写0和写1,在DS18B20中分为写0时段和写1时段,每个写时段最小必须由60us的时序时间并且要有1us的恢复时间。 ![](https://cf01.ickimg.com/bbsimages/202001/f49967820a2b8334c25206dea74e21b0.png) 在写1时段,在总线拉低之后,主设备必须在15us之内释放总线,总线释放后,上拉电阻将总线拉高;在写0时段,在总线拉低后,整个时段必须一致拉低总线,至少60us。 简单来说,就是这样:在主设备初始化写时段后,DS18B20会在15-60us内对总线进行采样,如果采样时候是高电平,则DS18B20会写入1,反之则写入0。 程序如下: ```c /@@**************************************************************************** 函数功能:向DS18B20写一字节数据 入口参数:dat 出口参数: ****************************************************************************/ void WriteOneChar(uchar dat) { uchar i=0; DQ=1; delay(1); for(i=8;i&amp;amp;gt;0;i--) { DQ=0; DQ=dat&amp;amp;amp;0x01; delay(5); DQ=1; dat&amp;amp;gt;&amp;amp;gt;=1; } } ``` ## 读数据 主设备在执行完读暂存寄存器[BEh]之后,需要即使生成读时段,才可以读取数据。每个读时段最小必须有60us的持续时间与1us的恢复时间。从DS18B20中发送出来的数据在读时序后仅有15us的有效时间,因此,主设备在开始读时段后的15us之内必须释放总线来读取数据。时序如上图所示。 程序如下: ```c /@@**************************************************************************** 函数功能:向DS18B20读一字节数据 入口参数: 出口参数:dat ****************************************************************************/ uchar ReadOneChar(void) { uchar i=0; uchar dat=0; DQ=1; delay(1); for (i=8;i&amp;amp;gt;0;i--) { DQ=0; dat&amp;amp;gt;&amp;amp;gt;=1; DQ=1; delay(1); if(DQ) dat|=0x80; delay(30); DQ=1; } return dat; } ``` 然后就是根据手册查找一些重要的命令,包括跳过读序列号,读取温度命令等等,这一部分就不再赘述。然后我们就可以按照我们之前总结的读取温度的流程来编写整个读取温度的程序: ```c /@@**************************************************************************** 函数功能:向DS18B20读温度值 入口参数: 出口参数:temperature 温度读取流程: 初始化-&amp;amp;gt;跳过读序列号-&amp;amp;gt;启动温度转化-&amp;amp;gt;延时-&amp;amp;gt;初始化-&amp;amp;gt;跳过读序列号-&amp;amp;gt;读取温度并显示 ****************************************************************************/ 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(); //读出温度的低位LSB tempH=ReadOneChar(); //读出温度的高位MSB //温度转换,把高低位做相应的运算转化为实际温度 tt = tempH&amp;amp;lt;&amp;amp;lt;8; tt = tt|tempL; if(tt&amp;amp;amp;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的驱动程序,在我之前的文章中有说明,这里就不再说明,文章链接:https://www.icxbk.com/article/detail?aid=1067 测试主程序如下: ```c /@@*显示函数*/ 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(); //显示 } } ``` 测试结果: ![](https://cf01.ickimg.com/bbsimages/202001/9a946bc2ab0ddbbd5eb1fcf247c72321.png) 这里的显示程序中我加了阈值显示,这里设置的是高温阈值为30,低温阈值为27。因为为了后续方便,所以我就把阈值设置得比较近,这个可以自行修改。 之后就是比较简单的逻辑了,超过高温阈值就关闭加热丝打开风扇,低于低温阈值就关闭风扇,打开加热丝。主程序如下: ```c void main() { lcd_init(); SW = 0; Motor = 0; while(1) { temp = (int)ReadTemperature(); //读取温度 display(); //显示 /@@*过温 打开风扇,关闭加热丝*/ if(temp &amp;amp;gt; tempOver) { SW = 0; Motor = 1; } /@@*低温 关闭风扇,打开加热丝*/ else if(temp &amp;amp;lt; tempLow) { SW = 1; Motor = 0; } } } ``` 关于风扇的电路,我这里就直接用了一个ULN2003,这个是小电流电机的,如果要更换24V大电流电机驱动,或者要加上PWM调速,请查阅我之前的文章:https://www.icxbk.com/article/detail?aid=1080 效果如下: ![](https://cf01.ickimg.com/bbsimages/202001/5b6fbf3ec1dc27353f1b84a8ff9abe3f.gif) 之后又是同样的环节:如果是懒癌患者,请移步下载区给作者赏点IC币,整个程序工程以及仿真工程都已上传,链接:https://www.icxbk.com/download/detail/48602.html 如果不是懒癌,1602程序在之前的文章中有发,因此1602程序就不发了。我的工程目录如下: ![](https://cf01.ickimg.com/bbsimages/202001/4bc6670f76136e1854263d28467189a1.png) 所有源代码如下: main.c ```c #include &amp;lt;reg52.h&amp;gt; #include &amp;lt;stdio.h&amp;gt; #include "1602.h" #include "ds18b20.h" #define uchar unsigned char #define uint unsigned int sbit 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&amp;amp;lt;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 &amp;amp;gt; tempOver) { SW = 0; Motor = 1; } /@@*低温 关闭风扇,打开加热丝*/ else if(temp &amp;amp;lt; tempLow) { SW = 1; Motor = 0; } } } ``` DS18B20头文件: ```c #include <reg52.h> #include <stdio.h> #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(); //读出温度的低位LSB tempH=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; //返回温度值 } ```
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
KVIN
关注
评论
(3)
登录后可评论,请
登录
或
注册
哈里波菜
49
天前...
用32能在proteus上跑起来吗?
0
回复
发布
KVIN
回复
哈里波菜
47
天前...
简单的可以,碰到复杂功能就死机,需要proteus8以上版本,不太推荐用proteus仿真32
0
回复
发布
KVIN
101
天前...
最后的代码由于跟markdown的一些关键字有歧义,所以排版变形,见谅
0
回复
发布
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交