文章目录
- 一、DS18B20介绍
- 二、硬件设计
- 三、软件设计
- 1.数码管显示函数
- 2.DS18B20 初始化函数及温度读取函数
- 4.主函数
- 四、实验现象
这一节我们来学习精度较高的外部
DS18B20
数字温度传感器,由于此传感器是
单总线接口,所以需要使用
51
单片机的一个
IO
口模拟单总线时序与
DS18B20
通信,将检测的环境温度读取出来。开发板上集成了 1 个
DS18B20
温度传感器接口,需插上
DS18B20
温度传感器后才能测试温度。本节要实现的功能是:系统运行时,插上
DS18B20
温度传感器,数码管显示检测的温度值。
一、DS18B20介绍
DS18B20
是由 DALLAS
半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。
DS18B20
温度传感器具有如下特点:
- 适应电压范围更宽,电压范围:
3.0~5.5V
,在寄生电源方式下可由数据线供电。 - 独特的单线接口方式,
DS18B20
在与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20
的双向通讯。 DS18B20
支持多点组网功能,多个 DS18B20
可以并联在唯一的三线上,实现组网多点测温。DS18B20
在使用中不需要任何外围元件,全部传感元件及转换电路集成在形如一只三极管的集成电路内。- 温范围
-55℃~+125℃
,在-10~+85℃
时精度为±0.5℃
- 可编程的分辨率为
9~12
位,对应的可分辨温度分别为 0.5℃
、0.25℃
、0.125℃
和0.0625℃
,可实现高精度测温。 - 在
9
位分辨率时最多在 93.75ms
内把温度转换为数字,12
位分辨率时最多在 750ms
内把温度值转换为数字,速度更快。 - 测量结果直接输出数字温度信号,以"一根总线"串行传送给
CPU
,同时可传送 CRC
校验码,具有极强的抗干扰纠错能力。 - 负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
DS18B20
外观实物如下图所示:
从 DS18B20
外观图可以看到,当我们正对传感器切面(传感器型号字符那一面)时,传感器的管脚顺序是从左到右排列。管脚 1
为 GND
,管脚 2
为数据DQ
,管脚 3
为 VDD
。如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向,通常我们在开发板上都会标出传感器的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可。
DS18B20
内部结构如下图所示:
ROM
中的 64
位序列号是出厂前被光刻好的,它可以看作是该 DS18B20
的地址序列号。64
位光刻 ROM
的排列是:开始 8 位(28H)是产品类型标号,接着的 48 位是该 DS18B20
自身的序列号,最后 8 位是前面 56 位的循环冗余校验码。光刻 ROM
的作用是使每一个 DS18B20
都各不相同,这样就可以实现一根总线上挂接多个 DS18B20
的目的。
DS18B20
温度传感器的内部存储器包括一个高速的暂存器 RAM
和一个非易失性的可电擦除的 EEPROM
,后者存放高温度和低温度触发器 TH
、TL
和配置寄存器。
配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:
TM R1 R0 1 1 1 1 1
低五位一直都是"1"
,TM
是测试模式位,用于设置 DS18B20
在工作模式还是在测试模式。在 DS18B20
出厂时该位被设置为 0,用户不需要去改动。R1
和R0
用来设置 DS18B20
的精度(分辨率),可设置为 9,10,11 或 12 位,对应的分辨率温度是 0.5℃,0.25℃,0.125℃和 0.0625℃。R0
和 R1
配置如下图:
在初始状态下默认的精度是 12 位,即 R0=1
、R1=1
。
高速暂存存储器由 9 个字节组成,其分配如下:
当温度转换命令(44H
)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。存储的两个字节,高字节的前 5 位是符号位 S
,单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式如下:
如果测得的温度大于 0,这 5 位为‘ 0’,只要将测到的数值乘以 0.0625(默认精度是 12 位)即可得到实际温度;如果温度小于 0,这 5 位为‘ 1’,测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。温度与数据对应关系如下:
比如我们要计算+85
度,数据输出十六进制是 0X0550
,因为高字节的高 5 位为 0,表明检测的温度是正温度,0X0550
对应的十进制为 1360
,将这个值乘以 12 位精度 0.0625,所以可以得到+85
度。
知道了怎么计算温度,接下来我们就来看看如何读取温度数据,由于 DS18B20
是单总线器件,所有的单总线器件都要求采用严格的信号时序以保证数据的完整性。DS18B20
时序包括如下几种:初始化时序、写(0 和 1)时序、 读(0
和 1)时序。 DS18B20
发送所有的命令和数据都是字节的低位在前。这里我们简单介绍这几个信号的时序:
- 初始化时序
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少 480us
(该时间的时间范围可以从 480 到 960 微妙),以产生复位脉冲。接着主机释放总线,外部的上拉电阻将单总线拉高,延时 15~60 us
,并进入接收模式。接着 DS18B20
拉低总线 60~240 us
,以产生低电平应答脉冲,若为低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要480
微妙。初始化时序图如下:
- 写时序
写时序包括写 0 时序和写 1 时序。所有写时序至少需要 60us
,且在 2 次独立的写时序之间至少需要 1us
的恢复时间,两种写时序均起始于主机拉低总线。写 1 时序:主机输出低电平,延时 2us
,然后释放总线,延时 60us
。写 0 时序:主机输出低电平,延时 60us
,然后释放总线,延时 2us
。写时序图如下:
- 读时序
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要 60us
,且在 2 次独立的读时序之间至少需要 1us
的恢复时间。每个读时序都由主机发起,至少拉低总线 1us
。主机在读时序期间必须释放总线,并且在时序起始后的 15us
之内采样总线状态。读时序图如下:
典型的读时序过程为:主机输出低电平延时 2us
,然后主机转入输入模式延时 12us
,然后读取单总线当前的电平,然后延时 50us
。
在了解了单总线时序之后,我们来看看 DS18B20
的典型温度读取过程,DS18B20
的典型温度读取过程:复位→发 SKIP ROM
命令(0XCC
)→发开始转换命令(0X44
)→延时→复位→发送 SKIP ROM
命令(0XCC
)→发读存储器命令(0XBE
)→连续读出两个字节数据(即温度)→结束。
二、硬件设计
本实验使用到硬件资源如下:
动态数码管电路在前面章节已介绍,这里就不再重复。下面来看下开发板上DS18B20
模块电路,如下图所示:
从上图可以看出,传感器接口的单总线管脚接至单片机 P3.7 IO
口上,在介绍单总线的时候我们说过,为了让单总线默认为高电平,通常会在单总线上接上拉电阻,在图中并没有看到有上拉电阻,这是因为单片机 IO
都外接了 10K
上拉电阻,当单片机 IO
口连接到传感器的总线管脚时即相当于它们外接上拉电阻,所以此处可以省去。
三、软件设计
本节所要实现的功能是:插上 DS18B20
温度传感器,数码管显示检测的温度值。
程序框架如下:
-
编写公共文件
打开public.c
,代码如下:
#include "public.h"
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for (i=ms;i>0;i--)
{
for (j=110;j>0;j--);
}
}
其头文件public.h
为:
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned char u8;
typedef unsigned int u16;
void delay_10us(u16 ten_us);
void delay_ms(u16 ms);
#endif
-
编写数码管显示功能
-
编写 DS18B20
读取温度功能
-
编写主函数
1.数码管显示函数
本实验要显示温度值,就存在小数和符号位,所以不能直接使用前面的数码管显示函数,需要做一点点小改动,打开smg.c
,代码如下:
#include "smg.h"
u8 gsmg_code[17] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void smg_display(u8 dat[],u8 pos)
{
u8 i = 0;
u8 pos_temp = pos - 1;
for (i=pos_temp;i<8;i++)
{
switch (i)
{
case 0 : LSC = 1;LSB = 1;LSA = 1;break;
case 1 : LSC = 1;LSB = 1;LSA = 0;break;
case 2 : LSC = 1;LSB = 0;LSA = 1;break;
case 3 : LSC = 1;LSB = 0;LSA = 0;break;
case 4 : LSC = 0;LSB = 1;LSA = 1;break;
case 5 : LSC = 0;LSB = 1;LSA = 0;break;
case 6 : LSC = 0;LSB = 0;LSA = 1;break;
case 7 : LSC = 0;LSB = 0;LSA = 0;break;
}
SMG_A_DP_PORT = dat[i-pos_temp];
delay_10us(100);
SMG_A_DP_PORT =0x00;
}
}
可以看到此处没有直接使用数码管段码数据,而是通过外部提供处理好的数码管段码数据传入进来,这样可使数码管显示更加灵活,后面实验我们都将采用这种方式。因为要让外部提前处理好数码管段码数据,所以段码数组 gsmg_code
就要定义为全局变量,需在 smg.h
头文件中声明下即可:extern u8 gsmg_code[17];
。
其头文件smg.h
为
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SMG_A_DP_PORT P0
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[],u8 pos);
#endif
2.DS18B20 初始化函数及温度读取函数
打开ds18b20c.c
,代码如下:
#include "ds18b20.h"
#include "intrins.h"
void ds18b20_reset(void)
{
DS18B20_PORT = 0;
delay_10us(75);
DS18B20_PORT = 1;
delay_10us(2);
}
u8 ds18b20_check(void)
{
u8 time_temp = 0;
while (DS18B20_PORT && time_temp < 20)
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)
return 1;
else
time_temp=0;
while((!DS18B20_PORT)&&time_temp<20)
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)
return 1;
return 0;
}
u8 ds18b20_init(void)
{
ds18b20_reset();
return ds18b20_check();
}
void ds18b20_write_byte(u8 dat)
{
u8 i = 0;
u8 temp = 0;
for (i=0;i<8;i++)
{
temp = dat & 0x01;
dat >>= 1;
if (temp)
{
DS18B20_PORT = 0;
_nop_();_nop_();
DS18B20_PORT = 1;
delay_10us(6);
}
else
{
DS18B20_PORT = 0;
delay_10us(6);
DS18B20_PORT = 1;
_nop_();_nop_();
}
}
}
u8 ds18b20_read_bit(void)
{
u8 dat = 0;
DS18B20_PORT = 0;
_nop_();_nop_();
DS18B20_PORT=1;
_nop_();_nop_();
if (DS18B20_PORT)dat = 1;
else dat = 0;
delay_10us(5);
return dat;
}
u8 ds18b20_read_byte(void)
{
u8 i = 0;
u8 temp = 0;
u8 dat = 0;
for (i=0;i<8;i++)
{
temp = ds18b20_read_bit();
dat >>= 1;
dat |= (temp << 7);
}
return dat;
}
void ds18b20_start(void)
{
ds18b20_reset();
ds18b20_check();
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0x44);
}
float ds18b20_read_temperture(void)
{
float temp;
u8 dath=0;
u8 datl=0;
u16 value=0;
ds18b20_start();
ds18b20_reset();
ds18b20_check();
ds18b20_write_byte(0xcc);
ds18b20_write_byte(0xbe);
datl=ds18b20_read_byte();
dath=ds18b20_read_byte();
value=(dath<<8)+datl;
if((value&0xf800)==0xf800)
{
value=(~value)+1;
temp=value*(-0.0625);
}
else
{
temp=value*0.0625;
}
return temp;
}
可以看到:在初始化 DS18B20
之后,我们就可以按照前面介绍的 DS18B20
的典型温度读取过程来编写温度读取函数,最终将 2 个字节的温度数据读取出来,判断最高字节的高 5 位是否为 0,如果为 0 表明读取的温度值为正温度,直接乘以 0.0625 即可,否则为负温度,需取反后加 1 再乘以 0.0625。
其头文件ds18b20.h
为
#ifndef _ds18b20_H
#define _ds18b20_H
#include "public.h"
sbit DS18B20_PORT = P3^7;
void ds18b20_reset(void);
u8 ds18b20_check(void);
u8 ds18b20_init(void);
void ds18b20_write_byte(u8 dat);
u8 ds18b20_read_bit(void);
u8 ds18b20_read_byte(void);
void ds18b20_start(void);
float ds18b20_read_temperture(void);
#endif
4.主函数
编写好 DS18B20
初始化和温度读取函数后,接下来就可以编写主函数了,代码如下:
#include "public.h"
#include "smg.h"
#include "ds18b20.h"
void main(void)
{
u8 i=0;
int temp_value;
u8 temp_buf[5];
ds18b20_init();
while (1)
{
i++;
if(i%50==0)
temp_value=ds18b20_read_temperture()*10;
if(temp_value<0)
{
temp_value=-temp_value;
temp_buf[0]=0x40;
}
else
{
temp_buf[0]=0x00;
}
temp_buf[1]=gsmg_code[temp_value/1000];
temp_buf[2]=gsmg_code[temp_value%1000/100];
temp_buf[3]=gsmg_code[temp_value%1000%100/10]|0x80;
temp_buf[4]=gsmg_code[temp_value%1000%100%10];
smg_display(temp_buf,4);
}
}
主函数代码很简单,首先调用所需外设头文件,然后初始化 DS18B20
,在 while
循环中间隔一定时间读取温度数据,并将温度值保留小数点后一位,然后将读取的温度数据转换为数码管可显示的段码,最后调用数码管显示函数。
四、实验现象
使用 USB
线将开发板和电脑连接成功后(电脑能识别开发板上 CH340
串口),把编译后产生的.hex
文件烧入到芯片内,实现现象如下:插上 DS18B20
温度传感器,数码管显示检测的温度值。
实验说明:DS18B20
默认起始温度是 85
度,之后就会进入到正常温度检测,如果条件允许可以在零下温度下检测看看。
注意:一定要注意温度传感器的方向,在接口处我们已经用丝印画了一个凸起,所以只需要将温度传感器对应插入即可。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)