51单片机-温度传感器
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 校验码,具有极强的抗干扰纠错能力。
- 负压特性:
电源极性接反时,芯片不会因发热而烧毁,但不能正常工作。
传感器的管脚顺序是从左到右排列。管脚 1 为 GND, 管脚 2 为数据DQ, 管脚 3 为 VDD。如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向,通常我们在开发板上都会标出传感器的凸起处,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可
配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:
低五位一直都是"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;
高字节的前 5 位是符号位 S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后
如果测得的温度大于 0,这 5 位为‘ 0’,只要将 测到的数值乘以 0.0625(默认精度是 12 位)即可 得到实际温度;如果温度小于 0,<这 5 位为‘ 1’, 测到的数值需要取反加 1 再乘以 0.0625 即可 得到实际温度。
如何读取温度数据?
由于 DS18B20是单总线器件,所有的单总线器件都要求采用严格的 信号时序,以保证 数据的完整性。DS18B20 时序包括如下几种:初始化时序、写(0 和 1)时序、 读(0和 1)时序(DS18B20 发送所有的命令和数据都是字节的 低位在前 )
初始化时序
//分析
● 首先实粗线是我们的单片机 IO 口拉低这个引脚,虚粗线是 DS18B20 拉低这个引脚,细线是单片机和 DS18B20 释放总线后,依靠上拉电阻的作用把 IO 口引脚拉上去,51单片机释放总线就是给 `高电平`
● 首先单片机要拉低这个引脚,持续大概 480us 到 960us 之间的时间即可,我们的程序中持续了 500us。然后,单片机释放总线,就是给高电平,DS18B20 等待大概 15 到 60us 后,会主动拉低这个引脚大概是 60 到 240us(就是说不需要我们编程拉低自动拉的),而后 DS18B20 会主动释放总线,这样 IO 口会被上拉电阻自动拉高
相关寄存器
- ROM 操作指令;Skip ROM(跳过 ROM):
0xCC。当总线上只有一个器件的时候,可以跳过 ROM,不进行 ROM 检测 - Read Scratchpad(读暂存寄存器):0xBE ,DS18B20 的温度数据是
2个字节,我们读取数据的时候,先读取到的是低字节的低位,读完了第一个字节后,再读高字节的低位,直到两个字节全部读取完毕 - Convert Temperature(启动温度转换):0x44;当我们发送一个启动温度转换的指令后,DS18B20 开始进行转换。从转换开始到获取温度,DS18B20 是需要时间的,而这个时间长短取决于 DS18B20 的精度
写时序
写时序包括写 0 时序和写 1 时序。所有写时序至少需要 60us,且在 2 次独立的写时序之间 至少需要 1us 的恢复时间,两种写时序均起始于主机拉低总线。写 1 时序: 主机输出低电平,延时 2us,然后释放总线,延时 60us。写 0时序: 主机输出低电平,延时 60us,然后释放总线,延时 2us。
读时序
所有读时序 至少需要 60us,且在 2 次独立的读时序之间 至少需要 1us 的恢复时间。每个读时序都由主机发起,至少拉低总线 1us。主机在读时序期间必须释放总线, 并且在时序起始后的 15us 之内采样总线状态。
典型的读时序过程为: 主机输出低电平延时 2us,然后主机转入输入模式延时 12us,然后读取单总线当前的电平,然后延时 50us
DS18B20 的典型温度读取过程
复位 → 发 SKIP ROM 命令(0XCC) → 发开始转换命令(0X44) → 延时 → 复位 → 发送 SKIP ROM 命令(0XCC) → 发读存储器命令(0XBE) → 连续读出两个字节数据(即温度) → 结束。
软件编程
main.c
/*
实验名称:DS18B20温度传感器实验
接线说明:
实验现象:下载程序后,插上DS18B20温度传感器,数码管显示检测的温度值
注意事项:注意温度传感器的方向,在接口处我们已经用丝印画了一个凸起,
所以只需要将温度传感器对应插入即可
*/
# include "public.h"
# include "smg.h"
# include "ds18b20.h"
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 i=0;
int temp_value;
u8 temp_buf[5];
ds18b20_init();//初始化DS18B20
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);
}
}
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
public.c
# include "public.h"
void delay_10us(u16 ten_us)//当传入 Ten_us=1时,大约延时10us
{
while(ten_us--);
}
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
{
for(j=110;j>0;j--);
}
}
ds18b20.h
# ifndef _ds18b20_H
# define _ds18b20_H
# include "public.h"
sbit DS18B20_PORT=P3^7;
void ds18b20_reset();
u8 ds18b20_check();
u8 ds18b20_init();
void ds18b20_write_byte(u8 dat);
u8 ds18b20_read_bit();
u8 ds18b20_read_byte();
void ds18b20_start();
float ds18b20_read_temperture();
# endif
ds18b20.c
# include "ds18b20.h"
# include "intrins.h"
/**************初始化时序*****************/
void ds18b20_reset()
{
DS18B20_PORT=0;//拉低总线
delay_10us(75);//延时480~960 us
DS18B20_PORT=1;//拉高总线
delay_10us(2);//延时15~60 us
}
u8 ds18b20_check()
{
u8 time_temp=0;
while(DS18B20_PORT&&time_temp<20)//等待 DQ 为低电平
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)
return 1;//如果超时则强制返回 1
else
time_temp=0;
while((!DS18B20_PORT)&&time_temp<20)//等待 DQ 为高电平
{
time_temp++;
delay_10us(1);
}
if(time_temp>=20)
return 1;//如果超时则强制返回 1
return 0;//如果上面都没执行表示都检测到返回0
}
/********************************************************************
***********
* 函 数 名 : ds18b20
_init
* 函数功能 : 初始化 DS18B20 的 IO 口 DQ 同时检测 DS 的存在
* 输 入 : 无
* 输 出 : 1:不存在,0:存在
*********************************************************************
**********/
u8 ds18b20_init()
{
ds18b20_reset();
return ds18b20_check();
}
/***************写时序****************/
void ds18b20_write_byte(u8 dat)
{
u8 i=0;
u8 temp=0;
for(i=0;i<8;i++)//循环8次,每次写一位,且先写低位再写高位
{
temp=dat&0x01;//通过&得到最低位
dat>>=1;//将次高位移到低位
if(temp)
{
DS18B20_PORT=0;
_nop_(); _nop_();//一个代表1us
DS18B20_PORT=1;
delay_10us(6);
}
else
{
DS18B20_PORT=0;
delay_10us(6);
DS18B20_PORT=1;
_nop_(); _nop_();//一个代表1us
}
}
}
u8 ds18b20_read_bit()
{
u8 dat =0;
DS18B20_PORT=0;
_nop_(); _nop_();
DS18B20_PORT=1;
_nop_();_nop_(); //该段时间不能过长,必须在15us内读取数据
if(DS18B20_PORT)
dat=1;
else
dat=0;
delay_10us(5);
return dat;
}
u8 ds18b20_read_byte()
{
u8 i=0;
u8 temp=0;
u8 dat=0;
for(i=0;i<8;i++)
{
temp=ds18b20_read_bit();
dat>>=1;//先右移一位
dat|=(temp<<7);//记得|上不然下次就为0了
}
return dat;
}
void ds18b20_start()
{
ds18b20_reset();//复位
ds18b20_check();//检查 DS18B20
ds18b20_write_byte(0xcc);//SKIP ROM
ds18b20_write_byte(0x44);//转换命令
}
float ds18b20_read_temperture()
{
u8 dath=0;//读取高字节
u8 datl=0;//读取低字节
u16 value=0;
float temp;
ds18b20_start();//开始转换
ds18b20_reset();//复位
ds18b20_check();//检查 DS18B20
ds18b20_write_byte(0xCC);//SKIP ROM
ds18b20_write_byte(0xBE);//读存储器
datl=ds18b20_read_byte();//低字节
dath=ds18b20_read_byte();//高字节
value=(dath<<8)+datl;//合并为 16 位数据
if((value&0xf800)==0xf800)//0xf800==1111 1000 判断符号位,负温度
{
value=(~value)+1;//取反再加1
temp=value*(-0.0625);//乘以精度
}
else//正温度
{
temp=value*0.0625;
}
return temp;
}
smg.h
# ifndef _smg_H
# define _smg_H
# include "public.h"
sbit A0=P2^2;
sbit A1=P2^3;
sbit A2=P2^4;
# define SMG_A0_F_PORT P0//宏定义数码管P0端口
extern u8 gsmg_code[17]; //注意要加extern
void smg_display(u8 dat[],u8 pos);
# endif
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:A0=1;A1=1;A2=1;break;//Y7//板子从左边数第一个数码管,下面以此类推
case 1:A0=0;A1=1;A2=1;break;//Y6
case 2:A0=1;A1=0;A2=1;break;//Y5
case 3:A0=0;A1=0;A2=1;break;//Y4
case 4:A0=1;A1=1;A2=0;break;//Y3
case 5:A0=0;A1=1;A2=0;break;//Y2
case 6:A0=1;A1=0;A2=0;break;//Y1
case 7:A0=0;A1=0;A2=0;break;//Y0
}
SMG_A0_F_PORT=dat[i-pos_temp];//传送段选数据
delay_10us(100);//延时1毫秒左右
SMG_A0_F_PORT=0x00;//消影
}
}










