基于蓝桥杯的单片机模块练习——DS18B20温度传感器
相关知识点
1.概况
特点:
1.单总线通讯
2.每个设备都有一个存储在板载只读存储器中的唯一64位串行代码,所以一条通讯总线可挂多个DS18B20
3.测量范围是-55℃至+125℃,从-10℃到+85℃有±0.5℃的精度
4.温度计分辨率可由用户从9到12位选择
5.最慢在750毫秒内将温度转换为12位数字值
2.测量温度的操作
温度传感器的分辨率可由用户配置为9、10、11或12位,分别对应于0.5℃、0.25℃、0.125℃和0.0625℃的增量。上电时的默认分辨率为12位。
要启动温度测量和模数转换,主机必须发出转换[44h]命令。转换后,产生的热数据存储在暂存存储器的2字节温度寄存器中,DS18B20返回空闲状态。
温度数据在温度寄存器中存储为16位符号扩展二进制补码。如下图:
符号位表示温度是正还是负:对于正数,S = 0;对于负数,S = 1。
如果DS18B20配置为12位分辨率,温度寄存器中的所有位都将包含有效数据。对于11位分辨率,位0未定义。对于10位分辨率,位1和0未定义,对于9位分辨率,位2、1和0未定义。
注意:
1)我们从18B20里面读出的数据全是温度的补码形式,所以如果是正温度则不需要特殊处理,如果是负温度,那么需要把补码做一下转换。
【思考:-10.125 的补码如何写呢???】
2)温度寄存器的上电复位值为+85℃。也就是:0000 0101 0101 0000
3.温度计分辨率配置
将数据写入暂存字节2、3(TH、TL和配置寄存器)和4 :【4Eh】
该命令允许主机向DS18B20的暂存区写入3字节的数据。第一个数据字节写入TH寄存器(暂存区的字节2),第二个字节写入TL寄存器(字节3),第三个字节写入配置寄存器(字节4)。数据必须首先以最低有效位传输。主机发出复位之前,必须写入所有三个字节,否则数据可能会损坏。
void Config_18B20()
{
init_ds18b20();
Write_DS18B20(0xcc);//跳过ROM
Write_DS18B20(0x4e);//写暂存器指令4E
Write_DS18B20(0x7d);//写高速缓存器TH高温限值125度
Write_DS18B20(0x00);//写高速缓存器TL低温限值0度
Write_DS18B20(0x1f);//写配置寄存器4
//0x1f : 0.5000°C 转换时间93.75ms
//0x3f : 0.2000°C 转换时间187.5ms
//0x5f : 0.1250°C 转换时间375ms
//0x7f : 0.0625°C 转换时间750ms
init_ds18b20();
}
note:上电默认值为R0 = 1,R1 = 1 (12位分辨率)
4.访问DS18B20的顺序
注意:每次访问DS18B20时都要遵循这个顺序,这一点非常重要,因为如果序列中的任何步骤丢失或顺序错误,DS18B20将不会响应。
step1.初始化
单线总线上的所有事务都以初始化序列开始。初始化序列由总线主机发送的复位脉冲和从机发送的存在脉冲组成。存在脉冲让总线主设备知道从设备(如DS18B20)在总线上,并准备运行。
step2.发送ROM指令
总线主控器检测到存在脉冲后,可以发出只读存储器命令。这些命令对每个从设备的唯一64位只读存储器代码进行操作,如果单线总线上有多个从设备,允许主设备选择一个特定的设备。这些命令还允许主机确定总线上有多少设备和什么类型的设备,或者是否有任何设备出现报警情况。ROM命令有5个,每个命令8位长。在发出DS18B20功能命令之前,主设备必须发出适当的只读存储器命令。
step3.跳过ROM【CCh】
主机可以使用该命令同时寻址总线上的所有设备,而无需发送任何只读存储器代码信息。例如,主机可以通过发出跳过只读存储器命令,然后发出转换T [44h]命令,使总线上的所有DS18B20s同时执行温度转换。
step4.开始温度转换【44h】
该命令启动单次温度转换。转换后,产生的热数据存储在暂存存储器的2字节温度寄存器中,DS18B20返回低功耗空闲状态。
step5.读取温度【BEh】
该命令允许主机读取温度数据。数据传输从字节0的最低有效位开始,相继通过暂存区,直到读取完所有字节数据。如果只需要部分数据,主机可以随时发出复位以终止读取。
注意,只有当总线上只有一个从设备时,读暂存[BEh]命令才能跟随跳过只读存储器命令。在这种情况下,通过允许主机从从机读取而不发送设备的64位只读存储器代码,可以节省时间。如果有一个以上的从机,跟随读暂存命令的跳过只读存储器命令将导致总线上的数据冲突,因为多个设备将试图同时传输数据。
5.单总线通讯相关时序图及如何用程序模拟
1.初始化
与DS18B20的所有通信都从初始化序列开始,该序列由来自主机的复位脉冲和来自DS18B20的存在脉冲组成。当DS18B20响应复位发送存在脉冲时,它向主机指示它在总线上,准备工作。
在初始化序列期间,总线主设备通过将单线总线拉低至少480秒来发送复位脉冲。然后,总线主设备释放总线并进入接收模式。当总线释放时,5k的上拉电阻将单线总线拉高。当DS18B20检测到该上升沿时,它会等待15秒至60秒,然后通过将单线总线拉低60秒至240秒来发送存在脉冲。
//-----------------------------------------------------
//DS18B20设备初始化
bit init_ds18b20(void)
{
bit initflag = 0;//作为返回数值
// EA = 0;防止中断打扰时序
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);//480~960 us
DQ = 1;//释放总线
Delay_OneWire(10); //15~60 DS18B20会来拉低总线
initflag = DQ; //读取18B20的复位应答信号
Delay_OneWire(5);//等待60~240us总线释放
//EA = 1;
return initflag;//应答信号为低电平,表示复位成功
}
2.写入时序
所有写入时隙的持续时间必须至少为60us,各个写入时隙之间的恢复时间至少为1us。两种类型的写入时隙都是由主机拉低单线总线来启动的。为了产生写1时隙,在将单线总线拉低后,总线主设备必须在15us内释放单线总线。当总线被释放时,5k的上拉电阻将把总线拉高。要生成写0时隙,在将单线总线拉低后,总线主设备必须在时隙期间(至少60us)继续保持总线低电平。
DS18B20在主机启动写入时隙后持续15us至60us的时间内对单线总线进行采样。如果在采样窗口期间总线为高电平,则向DS18B20写入1。如果线路为低电平,则向DS18B20写入0。
//===============================================
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
//EA = 0;
for(i=0;i<8;i++)
{
DQ = 0;//迅速拉低总线10~15us
DQ = dat&0x01;//要写入的这一位是1,DQ就=1,要写入的这位是0,DQ就等于0
Delay_OneWire(5);//延时一会20~45us,等待DS18来读
DQ = 1;//释放总线
dat >>= 1;//开始准备写下一位
}
Delay_OneWire(5);
//EA = 1;
}
//===============================================
3.读出时序
所有读取时隙的持续时间必须至少为60us,时隙之间的恢复时间至少为1us。通过主设备将单线总线拉低至少1us,然后释放总线,启动读取时隙。在主机启动读取时隙后,DS18B20将开始在总线上传输1或0。DS18B20通过保持总线高电平传输1,通过拉低总线传输0。当传输0时,DS18B20将在时隙结束时释放总线,总线将被上拉电阻拉回到其高空闲状态。
DS18B20的数据在启动读取时隙的下降沿后15us内有效。因此,主机必须释放总线,然后在从插槽开始的15us内对总线状态进行采样。
//===============================================
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;//储存读取到的数据
//EA = 0;
for(i=0;i<8;i++)
{
DQ = 0;//迅速拉低
dat >>= 1;//这是从最高位写的,所以要相佑方移动到第一位
DQ = 1;//把这个置低,才能让DS18来决定他便成1还是0
if(DQ)//如果读到1,则把最高位写1,其他位不变
{
dat |= 0x80;
}
Delay_OneWire(5);//延时45us左右,在读取下一位
}
// EA = 1;
return dat;
}
//-----------------------------------------------------
6.比赛时需要改写onewire驱动
#include "onewire.h"
#include <intrins.h>
unsigned int t_data = 0x0000;
//单总线延时函数
void Delay_OneWire(unsigned int t) //STC89C52RC
{
t *= 12;//需要把延时扩大为原来的12倍
while(t--);
}
//===============================================
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
//EA = 0;
for(i=0;i<8;i++)
{
DQ = 0;//迅速拉低总线10~15us
DQ = dat&0x01;//要写入的这一位是1,DQ就=1,要写入的这位是0,DQ就等于0
Delay_OneWire(5);//延时一会20~45us,等待DS18来读
DQ = 1;//释放总线
dat >>= 1;//开始准备写下一位
}
Delay_OneWire(5);
//EA = 1;
}
//===============================================
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;//储存读取到的数据
//EA = 0;
for(i=0;i<8;i++)
{
DQ = 0;//迅速拉低
//_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
dat >>= 1;//这是从最高位写的,所以要相佑方移动到第一位
DQ = 1;//把这个置高,才能让DS18来决定他便成1还是0
if(DQ)//如果读到1,则把最高位写1,其他位不变
{
dat |= 0x80;
}
Delay_OneWire(5);//延时45us左右,在读取下一位
}
// EA = 1;
return dat;
}
//-----------------------------------------------------
//DS18B20设备初始化
bit init_ds18b20(void)
{
bit initflag = 0;//作为返回数值
// EA = 0;防止中断打扰时序
// DQ = 1;
// Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80);//480~960 us
DQ = 1;//释放总线
Delay_OneWire(10); //15~60 DS18B20会来拉低总线
initflag = DQ; //读取18B20的复位应答信号
Delay_OneWire(5);//等待60~240us总线释放
//EA = 1;
return initflag;//应答信号为低电平,表示复位成功
}
void rd_temperature()
{
//unsigned char i;
unsigned char LSB = 0x00, MSB = 0x00;
init_ds18b20();
Write_DS18B20(0xcc);//跳过ROM
Write_DS18B20(0x44);//开始温度转换
// for(i = 125; i > 0; i--)//延时750ms
// {
// Display();
// }
init_ds18b20();
Write_DS18B20(0xcc);
Write_DS18B20(0xbe);//读取温度
LSB = Read_DS18B20();
MSB = Read_DS18B20();
init_ds18b20();
t_data = MSB;
t_data <<= 8;
t_data = t_data | LSB;
if((t_data & 0xf800) == 0x0000)
{
t_data >>= 4;
t_data *= 10;
t_data = t_data + (LSB & 0x0f) * 0.625;
}
}
头文件onewire.h
#ifndef __ONEWIRE_H
#define __ONEWIRE_H
#include "stc15f2k60s2.h"
#include "SEG.h"
sbit DQ = P1^4; //单总线接口
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);
void rd_temperature();
#endif