蓝桥杯之单片机设计与开发(19)——DS18B20

 

 

温度传感器DS18B20

DS18B20是美信公司的一款温度传感器,单片机可以通过1-Wire协议与DS18B20进行通信,最终将温度读出。1-Wire总线的硬件接口很简单,只需要把DS18B20的数据引脚和单片机的一个IO 口接上就可以了。

DS18B20通过编程可以实现最高12位的温度存储值,在寄存器中,以补码的格式存储。

一共 2 个字节,LSB 是低字节,MSB 是高字节,其中 MSb 是字节的高位,LSb 是字节的低位。可以看出来,二进制数字,每一位代表的温度的含义,都表示出来了。其中 S表示的是符号位,低 11 位都是 2 的幂,用来表示最终的温度。DS18B20 的温度测量范围是从-55 度到+125 度,而温度数据的表现形式,有正负温度,寄存器中每个数字如同卡尺的刻度一样分布。如下图所示:

二进制数字最低位变化 1,代表温度变化 0.0625 度的映射关系。当 0 度的时候,那就是0x0000,当温度 125 度的时候,对应十六进制是 0x07D0,当温度是零下 55 度的时候,对应的数字是 0xFC90。反过来说,当数字是 0x0001 的时候,那温度就是 0.0625 度了。

DS18B20工作协议

1、初始化

和I2C的寻址类似,1-Wire总线开始也需要检测这条总线上是否存在DS18B20这个器件。如果这条总线上存在DS18B20,总线会根据时序要求返回一个低电平脉冲,如果不存在的话,也就不会返回脉冲,即总线保持为高电平,所以习惯上称之为检测存在脉冲。此外,获取存在脉冲不仅仅是检测是否存在DS18B20,还要通过这个脉冲过程通知DS18B20准备好,单片机要对它下手了。

通过上图,实粗线是单片机IO口拉低这个引脚,虚粗线是DS18B20拉低这个引脚,细线是单片机和DS18B20释放总线后,依靠上拉电阻的作用把IO口引脚拉上去。

存在脉冲检测过程:首先是单片机要拉低这个引脚,持续大概480~960us之间的时间即可,在持续里持续了500us。然后单片机释放总线,就是给高电平,DS18B20等待大概15~60us之后,会主动拉低这个引脚大概是60~240us,而后DS18B20会主动释放总线,这样IO口会被上拉电阻自动拉高。

根据下面的程序:由于 DS18B20 时序要求非常严格,所以在操作时序的时候,为了防止中断干扰总线时序,先关闭总中断。然后第一步,拉低 DS18B20 这个引脚,持续 500us;第二步,IO释放总线,延时 60us;第三步,读取存在脉冲,并且等待存在脉冲结束。

2、ROM操作指令

总线上可以挂多个器件,通过不同的器件地址来访问不同的器件。同样1-Wire总线也可以挂多个器件,但是它只有一条线,如何区分不同的器件。

在每个DS18B20内部都有一个唯一的64位长的序列号,这个序列号值就存在DS18B20内部的ROM中。开始的8位是产品类型编码(DS18B20是0x10),接着的 48 位是每个器件唯一的序号,最后的 8 位是 CRC 校验码。DS18B20 可以引出去很长的线,最长可以到几十米,测不同位置的温度。单片机可以通过和DS18B20 之间的通信,获取每个传感器所采集到的温度信息,也可以同时给所有的 DS18B20 发送一些指令。

Skip ROM(跳过ROM):0xCC

当总线上只有一个器件的时候,可以跳过 ROM,不进行 ROM 检测。

3、RAM存储器操作指令

RAM读取指令,常用的有两条。

Read Scratchpad(读暂存寄存器):0xBE

这里要注意,DS18B20的温度数据是两个字节,读取数据的时候,先读取到的是低字节的低位,读完了第一个字节后,再读高字节的低位,直到两个字节全部读取完毕。

Convert Temperature(启动温度转换):0x44

当发送一个启动温度转换的指令后,DS18B20开始进行转换。从转换开始到获取温度,DS18B20是需要时间的,而这个时间的长短取决于DS18B20的精度。DS18B20最高可以用12位来存储温度,但是也可以使用11位、10位和9位一共四种格式位数越高,精度越高,9位模式最低位变化1个数字,温度就变化0.5°C,同时转换速度也要相应快一些。

其中寄存器R1和R0决定了转换的位数,出厂默认值就是11,也就是12位表示温度,最大转换时间是750ms。当启动转换之后,至少要再等750ms之后才能读取温度,否则读到的温度有可能是错误的值。

如果读取到的值为85°C,这个值要么是没有启动转换,要么是启动转换了,但还没有等待一次转换彻底完成,,读到的是一个错误的数据。

4、DS18B20的位读写时序

写时序

当要给 DS18B20 写入 0 的时候,单片机直接将引脚拉低,持续时间大于 60us 小于 120us就可以了。图上显示的意思是,单片机先拉低 15us 之后,DS18B20 会在从 15us 到 60us 之间的时间来读取这一位,DS18B20 最早会在 15us 的时刻读取,典型值是在 30us 的时刻读取,最多不会超过 60us,DS18B20 必然读取完毕,所以持续时间超过 60us 即可。

当要给 DS18B20 写入 1 的时候,单片机先将这个引脚拉低,拉低时间大于 1us,然后马上释放总线,即拉高引脚,并且持续时间也要大于 60us。和写 0 类似的是,DS18B20 会在15us 到 60us 之间来读取这个 1。

DS18B20的时序比较严格,,写的过程中最好不要有中断打断。但是在两个“位”之间的间隔,是大于1小于无穷的,在这个时间段,是可以开中断来处理其他程序的。


读时序

当要读取 DS18B20 的数据的时候,我们的单片机首先要拉低这个引脚,并且至少保持1us 的时间,然后释放引脚,释放完毕后要尽快读取。从拉低这个引脚到读取引脚状态,不能超过 15us。从上图可以看出来,主机采样时间,也就是 MASTER SAMPLES,是在 15us 之内必须完成的。

DS18B20所表示的温度值中,有小数和整数两部分。常用的带小数的数据处理方法有两种,一种是定义成浮点型直接处理,第二种是定义成整型,然后把小数和整数部分分离出来,在合适的位置点上小数点即可。

DS18B20封装程序

/*******************************************************************************
* 函数名	:Delayus
* 输入值	:unsigned int us
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月17日
* 功能描述:1T单片机延时指定us
* 备注		:最大形参65535,即最大延时65ms
*******************************************************************************/
void Delayus(unsigned int us)
{
	do{
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
	}while(--us);
}

/*******************************************************************************
* 函数名	:Get18B20Ack
* 输入值	:none
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:复位总线,获取18B20存在脉冲,以启动一次读写操作
* 备注		:
*******************************************************************************/
bit Get18B20Ack(void)
{
	bit ack;
	
	EA = 0;					//禁止总中断
	DS18B20_IO = 0;			//产生500us的复位脉冲
	Delayus(500);
	DS18B20_IO = 1;			//延时60us
	Delayus(60);
	ack = DS18B20_IO;		//读取存在脉冲
	while(!DS18B20_IO);	//等待存在脉冲结束
	
	EA = 1;					//重新使能总中断
	
	return ack; 	
}

/*******************************************************************************
* 函数名	:DS18B20Write
* 输入值	:unsigned char dat
* 返回值	:none
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:向18B20写入一个字节
* 备注		:dat为待写入字节
*******************************************************************************/
void DS18B20Write(unsigned char dat)
{
 	unsigned char mask;

	EA = 0;						//禁止总中断						
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在先,依次移出8个bit
	{
		DS18B20_IO = 0;			//产生2us低电平脉冲
		Delayus(2);
	 	if(dat & mask)		    //输出该bit值
		 	DS18B20_IO = 1;
		else
		 	DS18B20_IO = 0;
		Delayus(60);			//延时60us
		DS18B20_IO = 1;			//拉高通信引脚
	}
	EA = 1;						//重新使能总中断
}

/*******************************************************************************
* 函数名	:DS18B20Read
* 输入值	:none
* 返回值	:unsigend char dat
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:从18B20读取一个字节
* 备注		:返回值为读取到的字节
*******************************************************************************/
unsigned char DS18B20Read(void)
{
 	unsigned char mask, dat = 0;
	
	EA = 0;						//禁止总中断
	for(mask = 0x01; mask != 0; mask <<= 1)	//低位在先,依次采集8个bit
	{
		DS18B20_IO = 0;			//产生2us低电平脉冲
		Delayus(2);
		DS18B20_IO = 1;			//结束低电平脉冲,等待18B20输出数据
		Delayus(2);				//延时2us
	 	if(DS18B20_IO)			//读取通信引脚上的值
		{
		 	dat |= mask;
		}
		Delayus(60);			//再延时60us
	}
	
	EA = 1;						//重新使能总中断
	
	return dat;	
}

/*******************************************************************************
* 函数名	:Start18B20
* 输入值	:none
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:启动一次18B20温度转换
* 备注		:返回值为是否启动成功
*******************************************************************************/
bit Start18B20()
{
 	bit ack;

	ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
	if(ack == 0)				//如18B20正确应答,则启动一次转换
	{
	 	DS18B20Write(0xCC);		//跳过ROM操作
		DS18B20Write(0x44);		//启动一次温度转换
	}

	return ~ack;				//ack == 0 表示操作成功,所以返回值对其取反
}

/*******************************************************************************
* 函数名	:Get18B20Temp
* 输入值	:int *temp
* 返回值	:bit ~ack
* 作者		:小默haa
* 时间		:2019年2月27日
* 功能描述:读取18B20转换的温度值
* 备注		:返回值为是否读取成功
*******************************************************************************/
bit Get18B20Temp(int *temp)
{
 	bit ack;
	unsigned char LSB, MSB;		//16bit温度值的低字节和高字节

	ack = Get18B20Ack();		//执行总线复位,并获取18B20应答
	if(ack == 0)				//如18B20正确应答,则读取温度值
	{
	 	DS18B20Write(0xCC);		//跳过ROM操作
		DS18B20Write(0xBE);		//发送读命令
		LSB = DS18B20Read();	//读温度值的低字节
		MSB = DS18B20Read();	//读温度值的高字节
		*temp = ((unsigned int) MSB << 8) + LSB;	//合成16bit的整数
	}

	return ~ack;				//ack == 0 表示操作应答,所以返回值为1其取反值
} 

为使显示精度也能达到0.0625°C,我们可以在程序中做如下处理。

bit res = 0;
int Temp = 0;				        //读取当前的温度值
int Temp_int = 999, Temp_dec = 999;	        //温度值的整数和小数部分

res = Get18B20Temp(&Temp);	                //读取当前温度
if(res)				                //如果读取到
{
	Temp_int = Temp >> 4;			//分离出温度值整数部分
	Temp_dec = Temp & 0xF;		        //分离出温度值小数部分
	Temp_dec = Temp_dec * (10000 / 16);	//二进制小数部分转换为4位十进制
}
Start18B20();					//重新启动下一次转换

 

 

 

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值