2016-03-18 08:55:33 a1314521531 阅读数 1378
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2182 人正在学习 去看看 朱有鹏

单片机——DS18B20


宗旨:技术的学习是有限的,分享的精神是无限的。


        DS18B20——温度传感器,单片机可以通过 1-Wire 和 DS18B20 进行通 信,最终将温度读出。1-Wire 总线的硬件接口很简单,只需要把 18B20 的数据引脚和单片 机的一个 IO 口接上就可以通信。最高12为的温度存储值,补码形式存储。

2字节,LSB低字节,MSB高字节,-55~125

 

1、初始化

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

 

2ROM操作指令

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

 

3RAM存储器操作指令

       Read Scratchpad(读暂存寄存器):0xBE—— DS18B20 的温度数据是 2 个字节,我们读取数据的时候,先 读取到的是低字节的低位,读完了第一个字节后,再读高字节的低位,一直到两个字节全部 读取完毕。

Convert Temperature(启动温度转换):0x44—— 12位最大的转换时间是 750ms

 

4DS18B20的位写时序

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

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

 

5DS18B20的位读时序

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

#include<reg52.h>
#include<intrins.h>

typedef unsigned char uchar;

sbit IO_18B20 = P3 ^ 2; //DS18B20通信引脚

/* 软件延时函数,延时时间(t*10)us */
void DelayX10us(uchar t)
{
  do
  {
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
  }
  while (--t);
}
/* 复位总线,获取存在脉冲,以启动一次读写操作*/
bit Get18B20Ack()
{
  bit ack;

  EA = 0;  //禁止总中断
  IO_18B20 = 0;     //产生500us复位脉冲
  DelayX10us(50);
  IO_18B20 = 1;
  DelayX10us(6);    //延时60us
  ack = IO_18B20;   //读取存在脉冲
  while(!IO_18B20); //等待存在脉冲结束
  EA = 1;  //重新使能总中断

  return ack;
}
/* 向DS18B20写入一个字节,dat-待写入字节 */
void Write18B20(uchar dat)
{
  uchar mask;

  EA = 0;  //禁止总中断
  for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,依次移出8个bit
  {
    IO_18B20 = 0;         //产生2us低电平脉冲
    _nop_();
    _nop_();
    if ((mask & dat) == 0) //输出该bit值
    {
      IO_18B20 = 0;
    }
    else
    {
      IO_18B20 = 1;
    }
    DelayX10us(6);        //延时60us
    IO_18B20 = 1;         //拉高通信引脚
  }
  EA = 1;  //重新使能总中断
}
/* 从DS18B20读取一个字节,返回值-读到的字节 */
uchar Read18B20()
{
  uchar dat;
  uchar mask;

  EA = 0;  //禁止总中断
  for (mask = 0x01; mask != 0; mask <<= 1) //低位在先,依次采集8个bit
  {
    IO_18B20 = 0;         //产生2us低电平脉冲
    _nop_();
    _nop_();
    IO_18B20 = 1;         //结束低电平脉冲,等待18B20输出数据
    _nop_();              //延时2us
    _nop_();
    if (!IO_18B20)        //读取通信引脚上的值
    {
      dat &= ~mask;
    }
    else
    {
      dat |= mask;
    }
    DelayX10us(6);        //再延时60us
  }
  EA = 1;  //重新使能总中断

  return dat;
}
/* 启动一次18B20温度转换,返回值-表示是否启动成功 */
bit Start18B20()
{
  bit ack;

  ack = Get18B20Ack();   //执行总线复位,并获取18B20应答
  if (ack == 0)          //如18B20正确应答,则启动一次转换
  {
    Write18B20(0xCC);  //跳过ROM操作
    Write18B20(0x44);  //启动一次温度转换
  }
  return ~ack;   //ack==0表示操作成功,所以返回值对其取反
}
/* 读取DS18B20转换的温度值,返回值-表示是否读取成功 */
bit Get18B20Temp(int *temp)
{
  bit ack;
  uchar LSB, MSB; //16bit温度值的低字节和高字节

  ack = Get18B20Ack();    //执行总线复位,并获取18B20应答
  if (ack == 0)           //如18B20正确应答,则读取温度值
  {
    Write18B20(0xCC);   //跳过ROM操作
    Write18B20(0xBE);   //发送读命令
    LSB = Read18B20();  //读温度值的低字节
    MSB = Read18B20();  //读温度值的高字节
    *temp = ((int)MSB << 8) + LSB; //合成为16bit整型数
  }
  return ~ack;  //ack==0表示操作应答,所以返回值为其取反值
}

2019-02-27 18:06:09 Xiaomo_haa 阅读数 461
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2182 人正在学习 去看看 朱有鹏

 

 

温度传感器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();					//重新启动下一次转换

 

 

 

2019-06-03 15:54:22 threehecarim 阅读数 869
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2182 人正在学习 去看看 朱有鹏

ds18b20对时序要求很高,所以一定要注意延时

下面是32单片机的程序

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
	DS18B20_IO_OUT(); 	//SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 	//拉低DQ
    delay_us(750);    	//延时750us
    DS18B20_DQ_OUT=1; 	//DQ=1 
	delay_us(15);     	//15US
}

//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void) 	   
{   
	u8 retry=0;
	DS18B20_IO_IN();	//SET PG11 INPUT	 
    while (DS18B20_DQ_IN&&retry<200)
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=200)return 1;
	else retry=0;
    while (!DS18B20_DQ_IN&&retry<240)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	    
	return 0;
}

//从DS18B20读取一位
//返回1/0
u8 DS18B20_Read_Bit(void) 	 
{
    u8 data;
	DS18B20_IO_OUT();	//SET PG11 OUTPUT
    DS18B20_DQ_OUT=0; 
	delay_us(2);
    DS18B20_DQ_OUT=1; 
	DS18B20_IO_IN();	//SET PG11 INPUT
	delay_us(12);
	if(DS18B20_DQ_IN)data=1;
    else data=0;	 
    delay_us(50);           
    return data;
}

//从DS18B20读取一个字节
u8 DS18B20_Read_Byte(void)     
{        
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}

//写一个字节到DS18B20
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;
    u8 testb;
	DS18B20_IO_OUT();	//SET PG11 OUTPUT;
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
            DS18B20_DQ_OUT=0;	// Write 1
            delay_us(2);                            
            DS18B20_DQ_OUT=1;
            delay_us(60);             
        }
        else 
        {
            DS18B20_DQ_OUT=0;	// Write 0
            delay_us(60);             
            DS18B20_DQ_OUT=1;
            delay_us(2);                          
        }
    }
}

//开始温度转换
void DS18B20_Start(void) 
{   						               
    DS18B20_Rst();	   
	DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);	// skip rom
    DS18B20_Write_Byte(0x44);	// convert
} 

//初始化DS18B20的DQ 同时检测DS18B20的存在
//返回1:不存在
//返回0:存在   	 
u8 DS18B20_Init(void)
{
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
	
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;		
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  
 	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);

 	GPIO_SetBits(GPIOC,GPIO_Pin_10);    //拉高

	DS18B20_Rst();

	return DS18B20_Check();
}  

//从ds18b20得到温度值
//精度0.1C
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL,TH;
	short tem;
    DS18B20_Start ();  			// ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();	 
    DS18B20_Write_Byte(0xcc);	// skip rom
    DS18B20_Write_Byte(0xbe);	// convert	    
    TL=DS18B20_Read_Byte(); 	// LSB 
    TH=DS18B20_Read_Byte(); 	// MSB  
		USART_SendData(USART1, TL);
		delay_ms(10);
		USART_SendData(USART1, TH);
	   	  
    if(TH>7)
    {
        TH=~TH;
        TL=~TL; 
        temp=0;					//温度为负
    }else temp=1;				//温度为正  	  
    tem=TH; 					
    tem<<=8;    
    tem+=TL;					
    tem=(float)tem*0.625;		
	if(temp)return tem; 	
	else return -tem;    
}

然后这里是51单片机的程序

这里我用的是12M晶振,延时程序如下

/*us延时,参数为1时约延时7us
			  10时约延时50us
			 100时约延时205us
			 250时约延时480us	
*/
void delay(uchar us) {
	while(us--);
}

然后下面就是对DQ信号线操作的程序

//复位ds18b20
void Init_ds18b20(void) {
	DQ = 1; 
	delay(1); 
	DQ = 0; 
	delay(250); 
	DQ = 1; 
	delay(100);
}

//读一个字节
uchar ReadoneByte() {
	uchar i;
	uchar dat = 0;
	for(i=8; i>0; i--) {
		DQ = 0;
		_nop_();
		dat >>= 1;
		DQ = 1;
		delay(1);
		if(DQ) dat |= 0x80;
		delay(10);
	}
	return dat;
}

//写一个字节
void WriteoneByte(uchar dat) {
	uchar i;
	for(i=8; i>0; i--) {
		DQ = 0;
		_nop_();
		DQ = dat & 0x01;
		delay(10);
		DQ = 1;
		dat >>= 1;
		delay(1);
	}
}

//获取温度值
int GetTempe() {
	int temp;
	uchar teml, temh;
	Init_ds18b20();
	WriteoneByte(0xcc);
	WriteoneByte(0x44);
	Init_ds18b20();
	WriteoneByte(0xcc);
	WriteoneByte(0xbe);
	teml = ReadoneByte();//先读低四位
	temh = ReadoneByte();//再读高四位
	
	temp = teml/16+temh*16;//这里只保留了整数,去掉了小数
	//temp <<= 8;
	//temp += teml;
	//t = temp * 0.625;
	return temp;
}

2018-03-16 15:08:36 qq_33656619 阅读数 3904
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2182 人正在学习 去看看 朱有鹏

依旧是接着蓝桥杯的机会来学习一下DS18B20和单总线技术,平台依旧是IAP15F2K61S2单片机

DS18B20单线数字温度计以9位数字量的形式反映器件的温度值,DS18B20通过一个单线接口发送或者接受信息,因此在中央微处理器和DS18B20之间只需要一根连线。下图为其引脚功能说明:


器件框图如下所示:


从图中可以看出,DS18B20主要由三个数字部件构成,分别是64位激光ROM,温度传感器,非易失性温度报警触发器TH和TL。

DS18B20所使用的总线技术为单总线技术,目前单片机与外设之间进行数据传输的串行总线主要由SCI,I2C和SPI总线,其中SCI总线总是以异步方式进行通信(一条数据输入线,一条数据输出线),而I2C总线以同步串行二线方式进行通信(一条时钟线,一条数据线),SPI总线则以同步串行三线方式进行通信(一条数据输入线,一条数据输出线)。这些总线都需要至少两条或两条以上的信号线,而DS18B20使用的单总线技术与上述总线不同,它采用单条信号线,既可传输时钟,有可传输数据,而且数据传输是双向的,因而这种单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等优点。单总线适用于单主机系统,能够控制一个或多个从机设备。

还是老方法,先对DS18B20进行初始化,然后对DS18B20进行读写操作。

首先我们来看看控制DS18B20的指令。

33H-----读ROM:读DS18B20温度传感器ROM中的编码。

55H-----匹配ROM。发出此命令之后,接着发出64位ROM的编码,访问单总线上与该编码相对应的DS18B20并使之做出响应,为下一步对该DS18B20的读/写做准备。

F0H-----搜索ROM。用于确定挂接在同一总线上DS18B20的个数,识别64位ROM地址,为操作各器件做好准备。

CCH-----跳过ROM。忽略64位ROM地址,直接向DS18B20发温度变换命令,适用于一个从机工作。

ECH-----告警搜索命令。执行后只有温度超过设定值上限或下限芯片才会做出反应。

以上指令是针对总线上挂接多个DS18B20的情况,但如果总线上只挂接了一个DS18B20,就不需要以上ROM指令,直接进行如下的温度转换和读取操作。

44H----温度转换。启动DS18B20进行温度转换,12位转换时长最长为750ms,9位为93.75ms,结果存入内部9字节的RAM中。

BEH----读暂存器。读内部ROM中9字节的温度数据。

4EH----写暂存器。发出向内部RAM的第二三字节写上下线温度数据的命令,紧跟该命令之后,是传送两字节的数据。

48H----复制暂存器。将RAM中第2,3字节的内容复制到EEPROM中。

B8H----重调EEPROM。将EEPROM中内容恢复到RAM中的第三四字节。

B4H----读供电方式。读DS18B20的供电模式。寄生供电时,DS18B20发送0;外接电源供电时,DS18B20发送1。

和DS18B20的任何通信都需要以初始化序列开始,初始化序列见下图:


上图含义如下:

1.先将数据线置高电平1

2.延时(时间要求不严格,但尽可能短些)

3.数据线拉到低电平0

4.延时750us(该时间范围可以在480~960us)

5.数据线拉到高电平1

6.延时等待。如果初始化成功,则在15~60us内产生一个由DS18B20返回的低电平0,据该状态可以确定它的存在,但是应注意,不能无限地等待,不然会使程序进入死循环,所以要进行超时判断。

7.若CPU读到数据线上的低电平0后,还有进行延时,其延时的时间从发出高电平算起至少要480us。

8.将数据线再次拉到高电平1后结束。

DS18B20写数据,时序如下图所示:


其含义如下:

1.数据线先置低电平0

2.延时确定的时间为15us

3.按从低位到高位的顺序发送数据(一次只发送一位)

4.延时时间为45us

5.将数据线拉到高电平1

6.重复1~5步骤,直到发送完整个字节

7.最后将数据线拉高到1

DS18B20读数据,时序图如下:


含义如下:

1.将数据线拉高到1

2.延时2us

3.将数据线拉低到0

4.延时6us

5.将数据线拉高到1

6.延时4us

7.读数据线的状态得到一个状态位,并进行数据处理

8.延时30us

9.重复1~7步骤,直到读取完一个字节

下面我们一起分析一下蓝桥杯官方给出的代码,如果鄙生有理解错的地方,还望各位大牛指点。

首先是初始化函数

bit init_ds18b20(void)
{
  bit initflag = 0;
  DQ = 1;      //首先将数据线置高
  Delay_OneWire(12);  //短暂延时
  DQ = 0;      //将数据线拉到低电平
  Delay_OneWire(80);    //延时在480~960US之间
  DQ = 1;                     //数据线拉回高电平
  Delay_OneWire(10);  // 延时等待
  initflag = DQ;     // initflag等于1初始化失败
  Delay_OneWire(5);     //最后一个延时
  return initflag;

}

其中的Delay_OneWire()函数如下所示:注意这是一段延时,但是延时t值需要根据你单片机的一个机器周期时长来确定,否则时序会出错,即无法读出温度值!!!

void Delay_OneWire(unsigned int t)
{
int i;
        while(t--)
{
      for(i=0;i<12;i++);
}
}

//向DS18B20写一个字节

void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)    //重复直到发送完整个字节
{
DQ = 0;  //数据线首先拉低
DQ = dat&0x01;
Delay_OneWire(5);  //延时45us
DQ = 1;    //将数据线拉回高电平1
dat >>= 1;
}
Delay_OneWire(5);

}

//从DS18B20读取一个字节

unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat; 
for(i=0;i<8;i++)    //重复接收直到读完整个字节
{
DQ = 0;   //数据线拉低
dat >>= 1;
DQ = 1;   //数据线拉高
if(DQ)
{
dat |= 0x80;
}    
Delay_OneWire(5);
}
return dat;
}

以上就是蓝桥杯官方给出的代码,下面我给出我根据单总线库写出的温度读取函数:

void Read_tem(void)
{
  unsigned char low,high;
        char temp;
  Init_DS18B20();       
  Write_DS18B20(0xCC);  //忽略64位ROM地址,直接向DS18B20发送温度变换命令,适用于一个从机工作
  Write_DS18B20(0x44);   //温度转换
  Delay_OneWire(200);


  Init_DS18B20();
  Write_DS18B20(0xCC);   
  Write_DS18B20(0xBE);   //读取温度数据


  low = Read_DS18B20();    //低八位
  high = Read_DS18B20();   //高八位
  
  temp = high<<4;           //整合成一个字节
  temp |= (low>>4);
  
  return temp;
}

至此,DS18B20部分就全部介绍完了,如果有错误,希望各位大牛指正。

2019-08-06 16:03:26 hltx666 阅读数 688
  • 温度传感器DS18B20-第2季第1部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第2季第1个课程,主要讲解单片机系统中常用的温度传感器DS18B20。本课程的目标是让大家进一步掌握时序的分析和编程实现,学会移植和调试DS18B20的程序,能够读取温度。

    2182 人正在学习 去看看 朱有鹏

    本文是武汉市海联天下物联网有限公司技术团队内部学习笔记,将详细讲解DS18B20温度显示以及51单片机如何利用单总线与DS18B20通信,DS18B20重要时序掌握。——技术部 张傲

现象描述

    使用DS18B20测量温度,并在数码管中显示出来。

DS18B20简介

    DS18B20 单线数字温度传感器,即“一线器件”,其具有独特的优点:
    (1)采用单总线的接口方式 与微处理器连接时 仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。 单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络。
    (2)测量温度范围宽,测量精度高 。DS18B20 的测量范围为 -55 ℃ ~+ 125 ℃ ; 在 -10~+ 85°C 范围内,精度为 ± 0.5°C 。
    (3)多点组网功能 多个 DS18B20 并联在惟一的单线上,实现多点测温。

单线总线特点

    单总线即只有一根数据线,系统中的数据交换,控制都由这根线完成。单总线通常要求外接一个约为 4.7K—10K 的上拉电阻,这样,当总线闲置时其状态为高电平。
温度测量电路

DS18B20的初始化

    主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
    作为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
DS18B20初始化时序图

sbit DS  = P2^2;      //单片机P22引脚与通信总线连接着
void dsreset(void)               
{
   uint i;
   DS=0;              //主机发出一个480-960微秒的低电平脉冲
   i=103;              //   480-960us延时
   while(i>0)i--;
   DS=1;              //释放总线变为高电平
   i=4;
   while(i>0)i--;           //15-60us延时
}   

    主机发出各种操作命令都是向DS18B20写0和写1组成的命令字节,接收数据时也是从DS18B20读取0或1的过程。因此首先要搞清主机是如何进行写0、写1、读0和读1的。
    写周期最少为60微秒,最长不超过120微秒。
    写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则将总线置为低电平,若主机想写1,则将总线置为高电平,持续时间最少60微秒直至写周期结束,然后 释放总线为高电平至少1微秒给总线恢复 。而DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
DS18B20读写控制时序图

void tmpwritebyte(uchar dat)   		  
{  
 uint i; uchar j; bit testb;      //bit定义位变量,分配内存地址
 for(j=1;j<=8;j++)           //因为按位接收,所以每次只存储1位,每字节要8位
 {testb=dat&0x01;           //从最低位开始赋值,dat是8位要写的内容
  dat=dat>>1;              //将第7位右移,再赋值给testb
  if(testb)               
  { DS=0; i++;i++;             //写 1的时候总线先拉低1us	
    DS=1; i=8;while(i>0)i--;     //释放总线,写周期至少为60us
   }
   else
  { DS=0;i=8; while(i>0)i--;         //写 0的时候总线先拉低1us,写周期至少60us	 
    DS=1; i++;i++;                //写0完成的时候释放总线
   }
  }
 }

    对于读数据操作时序也分为读0时序和读1时序两个过程。
    读周期是从主机把单总线拉低1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。
    作为从机DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。
    主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60微秒才能完成。

bit tmpreadbit(void)                  //这里用到写一位,是把函数作为下一个函数的参{                                   数来使用,因为函数做参数使用,所以有返回值
 uint i;
 bit dat;                    //局部变量只在函数内部有用,与写函数中的dat不冲突
 DS = 0;i++;                  //读1字节的位数,总线拉低1us             
 DS = 1;i++;i++;               //读1 时,总线释放为高电平
 dat= DS;                    //将DS18b20读到的数据赋值给dat
 i=8;while(i>0)i--;               //读与写相同的点就是时序的周期都是60us
 return (dat);                  //函数有返回值,可以将函数作参数使用
}
uchar tmpread(void)           //读一个字节 
{ uchar i,j,dat;         //同样的dat变量名重复,但是局部变量只应用于函数内部
  dat=0;
  for(i=1;i<=8;i++)      //一个字节8位,将读一位的函数循环8次,得到一个字节
  {j=tmpreadbit();     //这里将读一位的函数返回值赋值给变量j
   dat=(j<<7)|(dat>>1);  //读出的数据最低位在最前面,这样刚好一个字节在DAT里
 }return(dat);
}      

    经过单线接口访问DS18B20的协议(protocol)如下:
    (1) 初始化
    (2) ROM操作命令
    (3) 存储器操作指令
    (4) 处理数据
    DS18B20的存储器由一个高速暂存RAM和一个非易失性、电可擦除(E2)RAM组成:
DS18B20存储器
配置寄存器:
寄存器配置
选择寄存器分辨率
    出厂设置默认R0、R1为11。就是12位分辨率,也即是1位代表0.0625摄氏度。

ROM操作指令介绍

ROM操作ROM操作指令
    DS18B20经过转换所得的温度值以二进制补码形式存放在高速暂存存储器的第0和第1个字节。所以当我们只想简单的读取温度值的时候,只需要读取暂存器中的第0和第1个字节就可以了。
简单的读取温度值的步骤:
    1:跳过ROM操作
    2:发送温度转换命令
    3:跳过ROM操作
    4:发送读取温度命令
    5:读取温度值

void tmpchange(void) 	    //温度转换函数
{
 dsreset();               //初始化
 delay(1);
 tmpwritebyte(0xcc);	    //跳过ROM
 tmpwritebyte(0x44); 	    //温度变换
}
uint tmp()          //获取温度值的函数  
{ float tt;           //定义单精度浮点数类型,一般用来表示带有小数部分的实数
  uchar a,b;
  dsreset();          //初始化 
  delay(1);
  tmpwritebyte(0xcc);   //跳过ROM
  tmpwritebyte(0xbe);   //读暂存器
  a=tmpread();          //低八位赋值给a
  b=tmpread();          //高八位赋值给b
  temp=b;
  temp<<=8;      //temp=temp<<8,把高8位数据左移8位
  temp=temp|a;   //用左移8位的值或上低8位的值,得到的就是温度的值
  tt=temp*0.0625;   //因为出厂设置分辨率是12,每一位是0.0625摄氏度
  temp=tt*10+0.5;  //tt是实际温度,但是是浮点数表示,*10转变为整数,+0.5为补偿
  return temp;     //返回值到主函数中
}
void display(uint temp)	      //显示函数
{
    uchar A1, A2, A2t, A3;
    A1 = temp / 100;           //百位
    A2t = temp % 100;          //十位,%为取余运算符
    A2 = A2t / 10;              //个位
    A3 = A2t % 10;              //十分位

    P0 = table[A1];		         //P0是数码管控制脚,显示十位
    P3 = 0xef;
    delay(3);

    P0 = table1[A2];		//显示个位
    P3 = 0xdf;
    delay(3);

    P0 = table[A3];		//显示十分位
    P3 = 0xbf;
    delay(1);
}

附件:Keil C51工程源码

DS18B20使用详解

阅读数 204

DS18B20单总线协议

阅读数 3437

51单片机ds18b20

阅读数 777

没有更多推荐了,返回首页