单片机控制单总线协议温度传感器DS18B20之综合实验

这里将我编写的单片机控制单总线协议温度传感器DS18B20的程序共享一下,如有不足,敬请指出!

【实验视频】             

单片机控制单总线协议DS18B20之综合实验【实验5·趣味检测实验】

目录

               实验一   总线悬挂一个DS18B20之实物实验

               实验二   总线悬挂一个DS18B20之仿真实验

               实验三   总线悬挂两个DS18B20之实物实验

               实验四   总线悬挂两个DS18B20之仿真实验

               实验五   总线悬挂两个DS18B20之实物趣味检测实验

               启发思考

               预留思考


【说明】DS18B20为单总线协议通信,对时序要求较高,在不同单片机之间移植需要部分调整延时,本程序支持以下单片机(在DS18B20.h中修改TEST值即可)

         

               实验一   总线悬挂一个DS18B20之实物实验

【实验图片】正常效果

【实验图片】拔掉DS18B20

【实验图片】插上另一个DS18B20,同时单片机复位一下(程序中只是上电执行程序时读取一次ROM编码)

代码结构图:

程序下载链接:实验1 总线悬挂一个DS18B20之实物实验

测试程序:DS18B20测试.c

#include <intrins.h>
#include "common.h"
#include "lcd2004.h"
#include "ds18b20.h"

//显示DS18B20的64Bit 的ROM编码、温度符号、温度值
void display(DS18B20_Struct temp) ;

void main(void)
{
	DS18B20_Struct temp ;	
    
	LCD2004_Init() ;
	DS18B20_Acquire64BitCode(&temp);//获取DS18B2064Bit的光刻ROM编号	
	while(1)
	{
		DS18B20_ReadTemperature(&temp) ;
		display(temp) ;
	}
}

//显示DS18B20的64Bit 的ROM编码、温度符号、温度值
void display(DS18B20_Struct temp)
{
	unsigned char lsb,msb,i ;

	if(temp.ExistFlag == DS18B20_EXISTENCE)/*器件存在*/
	{
		LCD2004_AddressWriteString(LCD2004_ROW0,0,"NO.") ;
		LCD2004_AddressWriteString(LCD2004_ROW1,0,"Value:") ;
		LCD2004_AddressWriteString(LCD2004_ROW2,0,"Resolution:") ;
		LCD2004_AddressWriteString(LCD2004_ROW3,0,"QQ:279729201") ;
		//显示DS18B2064Bit的光刻ROM编号
	    for(i=0 ; i<8 ; i++)
	    {
	        msb = temp.serial_number[i] >>4  ;
	        lsb = temp.serial_number[i]& 0x0f  ;
	        if(msb <= 9)
				LCD2004_AddressWriteByte(LCD2004_ROW0,2*i+3,msb+'0');
	        else
				LCD2004_AddressWriteByte(LCD2004_ROW0,2*i+3,msb-10+'A');
	   
	        if(lsb <= 9)
				LCD2004_AddressWriteByte(LCD2004_ROW0,2*i+1+3,lsb+'0');
	        else
				LCD2004_AddressWriteByte(LCD2004_ROW0,2*i+1+3,lsb-10+'A');
	    }
		
		//显示温度符号、温度值
		if(temp.Temperature_sign == DS18B20_TEMPERATURE_MINUS) //温度为负
		  	LCD2004_AddressWriteByte(LCD2004_ROW1,6,'-');
		else if((temp.Temperature_sign == DS18B20_TEMPERATURE_PLUS))//温度为正
		   LCD2004_AddressWriteByte(LCD2004_ROW1,6,'+');
		LCD2004_AddressWriteByte(LCD2004_ROW1,7,temp.temperature_value/100 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_ROW1,8,(unsigned long)temp.temperature_value%100/10 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_ROW1,9,(unsigned long)temp.temperature_value%10 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_ROW1,10,'.') ;
		LCD2004_AddressWriteByte(LCD2004_ROW1,11,(unsigned long)(temp.temperature_value*10+0.5)%10 +'0') ;//四舍五入
		//显示  ℃
		LCD2004_AddressWriteByte(LCD2004_ROW1,12,0xdf) ;
		LCD2004_AddressWriteByte(LCD2004_ROW1,13,'C') ;
		
		//显示温度值分辨率:默认12,如果用户修改,这里会显示最新值
		LCD2004_AddressWriteByte(LCD2004_ROW2,11,temp.Configure_Resolution/10 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_ROW2,12,temp.Configure_Resolution%10 +'0') ;
	}
	else 
	{
		LCD2004_AddressWriteString(LCD2004_ROW0,0,"                    ") ;
		LCD2004_AddressWriteString(LCD2004_ROW1,0,"                    ") ;
		LCD2004_AddressWriteString(LCD2004_ROW2,0,"                    ") ;
		LCD2004_AddressWriteString(LCD2004_ROW3,0,"Error               ") ;
	}
}

/*################DS18B20.h  ################*/  

#ifndef __DS18B20_H__
#define __DS18B20_H__

sbit DS18B20_IO_Bit	= P3^7 ;/*根据硬件选择*/
#define  	 TEST  1
/*当TEST等于1时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
			   【备注】STC-Y1指令集包括:
						STC89Cxx/STC89LExx
						STC90Cxx/STC90LExx
						
			   当TEST等于2时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
			   【备注】STC-Y3指令集包括:
						STC12Cxx/STC12LExx
						STC11Fxx/STC11Lxx/STC10Fxx/STC10Lxx
						STC15F104E/STC15L104E(A版)
						STC15F204EA/STC15L204EA(A版)		
*/

typedef struct {
	unsigned char ExistFlag ;			//DS18B20存在标志位,值为DS18B20_NOT_EXISTENCE(不存在)或DS18B20_EXISTENCE(存在)
	unsigned char serial_number[8];		//激光ROM编号
	unsigned char Temperature_H ;		//温度值高位
	unsigned char Temperature_L ;		//温度值低位
	unsigned char High_Trigger ;		//触发警报的温度上限值
	unsigned char Low_Trigger ;			//触发警报的温度下限值
	unsigned char Configure_Resolution ;//分辨率:9/10/11/12位
	unsigned char Temperature_sign ;	//温度符号,值为DS18B20_TEMPERATURE_PLUS(温度值为正)或DS18B20_TEMPERATURE_MINUS(温度值为负)
	float temperature_value ;			//温度值计算
}DS18B20_Struct;

//DS18B20存在标志位ExistFlag的值,两种可能性:
#define DS18B20_NOT_EXISTENCE			0//不存在DS18B20,DS18B20连接异常或已损坏
#define DS18B20_EXISTENCE				1//存在DS18B20

//DS18B20检测到温度的正负标志位Temperature_sign的值,两种可能性:
#define DS18B20_TEMPERATURE_PLUS  	0	//温度为正
#define DS18B20_TEMPERATURE_MINUS 	1	//温度为负

//DS18B20内部ROM命令集
#define DS18B20_SEARCH_ROM				0xF0 //搜索ROM命令	
#define DS18B20_READ_ROM				0x33 //读取ROM命令	
#define DS18B20_MATCH_ROM				0x55 //匹配ROM命令	
#define DS18B20_SKIP_ROM				0xCC //跳过ROM命令	
#define DS18B20_ALARM_SEARCH			0xEC

//DS18B20功能命令                                          
#define DS18B20_CONVERT_TEMPERATURE		0x44 //转换温度命令
#define DS18B20_WRITE_SCRATCHPAD		0x4e //写暂存器命令
#define DS18B20_READ_SCRATCHPAD			0xBE //读暂存器命令
#define DS18B20_COPY_SCRATCHPAD			0x48 //拷贝暂存器命令,复制地址2、3、4地址数据TH、TL、configuration registers(配置寄存器)数据到DS18B20内部EEPROM中
#define DS18B20_RECALL_E2				0xB8 //召回EEPROM命令,复制DS18B20内部EEPROM的数据到地址2、3、4地址数据TH、TL、configuration registers(配置寄存器)处
#define DS18B20_READ_POWER_SUPPLY		0xB4 //读取供电方式命令

/*****************外部接口函数******************/
//读温度值(包含初始化,所以不需再调用初始化函数)
extern void  DS18B20_ReadTemperature(DS18B20_Struct *temp);

//读取DS18B20的64bit光刻ROM编号
extern void DS18B20_Acquire64BitCode(DS18B20_Struct *temp) ;
/**********************************************/

#endif /*__DS18B20_H__*/

/*################ DS18B20.c################*/ 

/***************************************************************************
模    块:DS18B20.c
说    明:DS18B20驱动程序
版    本:Version3.0		2021/01/01 06:00
编译环境:Keil_C51  V9.55
主控芯片:STC12C5A60S2  	@11.0592MHZ
		 STC89C52RC     @11.0592MHZ
		 AT89C51		@11.0592MHZ	@Proteus8.9仿真
作    者:杨瑞
联系方式:【 QQ 】279729201
		 【邮箱】279729201@qq.com            
		         yangrui90s@163.com
		 【电话】13630279531

修改记录:
=================
	2021/01/01 06:00
	记录 :
		1.完善延时函数delay720usForDs18b20()。
		2.修改函数名称模式,由"模块名称"(大写)+"_"+功能函数名称构成,例如ds18b20WriteByte函数名称
		修改为DS18B20_WriteByte函数。
		3.用DS18B20_Reset()代替函数ds18b20Init()【修改原因:便于和其他模块的XXXX_Init()函数区分开,其他模块的
		XXXX_Init()必须先调用,对其进行初步设置】。另外,将此函数修改为内部函数,取消之前的外部函数特性。
		4.DS18B20复位函数内部,将:
			if(!ds18b20_io_bit)
			{
				existenceFlag = DS18B20_EXISTENCE;
			}
			else
			{
				existenceFlag = DS18B20_NOT_EXISTENCE;			   
			}

			while((!ds18b20_io_bit) && (i<250))
			{
				i++;
			}
			修改为如下代码:
			if(!DS18B20_IO_Bit)
			{=
				ExistenceFlag = DS18B20_EXISTENCE;
				while((!DS18B20_IO_Bit) && (i++<70));
			}
			else
			{
				ExistenceFlag = DS18B20_NOT_EXISTENCE;			   
			}
			这里修改了while的位置,并完善辅助的i的值。(i是为了防止异常情况下程序”卡死”)。
		5.将读、写一个Bit数据单独封装成函数DS18B20_ReadSlot()、DS18B20_WriteSlot(unsigned char DataValue),
		  这里是为了和datasheet的“READ/WRITE TIME SLOT TIMING ”相对应,便于理解。
		6.删除函数delay750ms(),当单片机发送功能命令CONVERT T [44h] 后,DS18B20开始转换温度,同时将总线拉低,当
		  DS18B20转换温度完成后,DS18B20才释放总线为高电平,转换时间和DS18B20当前温度分辨率有关,最长时间为750ms
		  (即12Bit温度分辨率模式下),温度分辨率不同转换时间也不同,为了灵活,这里不通过延时固定的750ms,而是通过
		  判断总线电平,单片机一直等待,直到总线成高电平为止(注意这里“等待”是在DS18B20回复单片机“应答脉冲”的前
		  提下,所以程序不会因为DS18B20安装异常或器件损害而“卡死”)。
		7.将函数名ds18b20Acquire(……)修改为DS18B20_Acquire64BitCode(……)。
		8.对结构体进行完善,首先将结构体类型DS18B20_info改名为DS18B20_Struct,其次将其中元素进行完善,将ExistFlag放在第一项,
		  修改宏定义名称,DS18B20_TEMPERATURE_PLUS、DS18B20_TEMPERATURE_MINUS代替DS18B20_TEMPERATURE_POSITIVE、
		  DS18B20_TEMPERATURE_NEGATIVE。
=================
=================
	2015/05/02 11:39
	记录:
		1.修改结构体
		typedef struct {
			UB8 Temperature_sign ;
			UB8 Temperature_H ;
			UB8 Temperature_L ;
			float temperature_value ;
			UB8 serial_number[8];			 
		}DS18B20_info ;
		
		为新的结构体:
		
		typedef struct {
			UB8 serial_number[8];	//激光ROM编号
			UB8 Temperature_H ;		//温度值高位
			UB8 Temperature_L ;		//温度值低位
			UB8 High_Trigger ;		//触发警报的温度上限值
			UB8 Low_Trigger ;		//触发警报的温度下限值
			UB8 Configure_Resolution ;//分辨率:9/10/11/12位,默认精度为12位
		

			UB8 Temperature_sign ;   //温度符号,值为DS18B20_TEMPERATURE_POSITIVE(正)或者DS18B20_TEMPERATURE_NEGATIVE(负)
			float temperature_value ;//温度值计算
			UB8   ExistFlag ;        //DS18B20存在标志位
		}DS18B20_info ;
		
		2.在函数ds18b20ReadTemperature(DS18B20_info *temp)中添加读取触发警报的温度上限值、
		触发警报的温度下限值、读取分辨率的功能。
		(这里对于触发警报的温度上限值、触发警报的温度下限值没有使用,暂时保留,以作后续开
		发,对于分辨率的读取较为重要,虽然默认为12位分辨率,但是部分人员使用出现以下情况:
		在液晶屏显示DS18B20的工作模式时,写一个固定的"12"上去,这样做不是很好,因为有特殊需
		要,可能会设置DS18B20的分辨率为9/10/11位,这样就可能导致了DS18B20的真实分辨率和我
		们在液晶屏"写死"的数据12不对应,较好的做法是直接从DS18B20的内部读取分辨率,这样即
		使修改了DS18B20的分辨率,结构体DS18B20_info内部成员Configure_Resolution也可以及时
		更新回来)。

		3.修改
		(温度为负)
		temp->temperature_value = (~(((temp->Temperature_H )<<8 | temp->Temperature_L)-1))*0.0625 ;
		(温度为正)
		temp->temperature_value = ((temp->Temperature_H & (~(0xf8)))<<8 | temp->Temperature_L)*0.0625 ;

		为:
		(温度为负)
		temp->temperature_value = (~(((temp->Temperature_H )<<8 | temp->Temperature_L)-1))*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;
		(温度为正)
		temp->temperature_value = ((temp->Temperature_H & (~(0xf8)))<<8 | temp->Temperature_L)*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;

		变化主要为后面的乘数,也就是分辨率由0.0625修改为0.0625*(0x01<<(12-temp->Configure_Resolution)),意义在于最初乘以0.0625是因为DS18B20的默认
		分辨率是12位,但是因为特殊需要可能会修改DS18B20的分辨率,然后用此代码运行,发现有所偏差,为了更加统一使用,做此修改。
		

=================

=================
	2014/04/13 11:39
	记录:
		1.用结构体
			typedef struct {
				UB8 Temperature_sign ;
				UB8 Temperature_H ;
				UB8 Temperature_L ;
				float temperature_value ;
				UB8 serial_number[8];			 
			}DS18B20_info ;
		代替之前的结构,使得数据结构更加紧凑。
		
		2.添加临时测试函数void ds18b20Acquire(DS18B20_info *temp),但要注意正确的
		使用步骤。

=================

=================
	2014/02/24 23:44
	记录:
		1.本程序还适用于proteus仿真
		,但注意在仿真时修改delay15usForDs18b20()和delay720usForDs18b20()
		和函数ds18b20ReadTemperature的预编译命令即可。

		为了简化修改过程,只需要修改程序中的TEST值即可!!

		2.该程序默认使用12位转换精度.(精确到0.0625摄氏度)

=================



***************************************************************************/

#include <intrins.h>
#include "common.h" 
#include "ds18b20.h"


/*外部函数接口在ds18b20.h中声明*/
/*****************内部函数******************/
void delay15usForDs18b20(void) ;
void delay720usForDs18b20(void) ;
char DS18B20_Reset(void);
void DS18B20_WriteSlot(unsigned char DataValue);
void DS18B20_WriteByte(unsigned char DataValue);
unsigned char DS18B20_ReadSlot(void);
unsigned char DS18B20_ReadByte(void);
/**********************************************/

/******************************************************************
 - 函数名称:delay15usForDs18b20
 - 功能描述:延时15微秒
 - 函数属性:内部函数
 - 参数说明:无		 
 - 返回说明:无
 - 注	   :1.此函数支持硬件电路、proteus软件仿真。
			 2.此函数由软件STC-ISP V6.88D自带的“软件延时计算器”产生。
			   当TEST等于1时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
			   【备注】STC-Y1指令集包括:
						STC89Cxx/STC89LExx
						STC90Cxx/STC90LExx
						
			   当TEST等于2时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
			   【备注】STC-Y3指令集包括:
						STC12Cxx/STC12LExx
						STC11Fxx/STC11Lxx/STC10Fxx/STC10Lxx
						STC15F104E/STC15L104E(A版)
						STC15F204EA/STC15L204EA(A版)		
			 3.单总线对时序要求较高,当移植至其他CPU时,需要适当修改。
 ******************************************************************/
void delay15usForDs18b20(void)	//11.0592MZH
{
	#if  (TEST == 1) 	//适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
		unsigned char i;
		i = 4;
		while (--i);
	#elif (TEST ==  2)	 //适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
		unsigned char i;
		_nop_();_nop_();
		i = 38;
		while (--i);
	#endif
}

/******************************************************************
 - 函数名称:delay720usForDs18b20
 - 功能描述:延时720微秒
 - 函数属性:内部函数
 - 参数说明:无		 
 - 返回说明:无
 - 注	   :1.此函数支持硬件电路、proteus软件仿真。
			 2.此函数由软件STC-ISP V6.88D自带的“软件延时计算器”产生。
			   当TEST等于1时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
			   【备注】STC-Y1指令集包括:
						STC89Cxx/STC89LExx
						STC90Cxx/STC90LExx
						
			   当TEST等于2时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
			   【备注】STC-Y3指令集包括:
						STC12Cxx/STC12LExx
						STC11Fxx/STC11Lxx/STC10Fxx/STC10Lxx
						STC15F104E/STC15L104E(A版)
						STC15F204EA/STC15L204EA(A版)	
			 3.单总线对时序要求较高,当移植至其他CPU时,需要适当修改。
 ******************************************************************/
void delay720usForDs18b20(void)	//11.0592MZH
{
#if  (TEST == 1)	 //适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
	unsigned char i, j;
	_nop_();
	i = 2;
	j = 70;
	do
	{
		while (--j);
	} while (--i);
#elif (TEST == 2)	//适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
	unsigned char i, j;
	_nop_();
	i = 8;
	j = 187;
	do
	{
		while (--j);
	} while (--i);
#endif 
}

/******************************************************************
 - 函数名称:DS18B20_Reset
 - 功能描述:DS18B20初始化序列操作
 - 函数属性:内部函数
 - 参数说明:无
 - 返回说明:DS18B20_EXISTENCE,表示DS18B20复位成功,其产生“应答脉冲信号”
			DS18B20_NOT_EXISTENCE,表示DS18B20复位不成功,其未产生“应答脉冲信号”,可能是连接异常或器件损坏
 - 注	   :1.初始化过程包括:复位和“应答脉冲”,主机(单片机)产生至少480us的低电平,
			   然后主机释放数据线,进入接收模式。当DS18B20检测到上升沿,它等待15us至60us
			   ,然后发射一个60us至240us的“应答脉冲信号”。
			 2.DS18B20的所有通信,都是以初始化序列(包括单片机发出的复位脉冲和DS18B20的
			  “应答脉冲”)开始的。
			 3.初始化是正确操作DS18B20是重要一步,必须复位成功,只有这里返回值为DS18B20_EXISTENCE,
			   后续的操作才有意义,具体使用中可以通过检测返回值检测是否成功。
			 4.DS18B20对时间的要求较为严格,因此将此代码移植到其它CPU或场合时要注意延时函数。
			 5.经测试STC89C52RC  @11.0592MHZ时i的值为2,STC12C5A60S2 @11.0592MHZ时i的值为7,这里
			   设置i的最大值为70,时间够长了。
 ******************************************************************/
char DS18B20_Reset(void)
{
	char ExistenceFlag ;
	unsigned long i=0;
	
	DS18B20_IO_Bit = 1 ;
	_nop_() ; _nop_() ;	
	DS18B20_IO_Bit = 0 ;
	delay720usForDs18b20();//复位脉冲最少480us,这里采用1.5倍时间,即720us延时
	DS18B20_IO_Bit = 1 ;
	/*以上是单片机发送“复位”脉冲*/

	//等待15us至60us,如果DS18B20存在,DS18B20会把数据线拉低,发送一个60us至240us的“应答脉冲”
	delay15usForDs18b20();delay15usForDs18b20(); 
	delay15usForDs18b20();delay15usForDs18b20();

	if(!DS18B20_IO_Bit)
	{//如果主机(单片机)检测到数据线被DS18B20拉低,表示DS18B20复位成功
		ExistenceFlag = DS18B20_EXISTENCE;
		while((!DS18B20_IO_Bit) && (i++<70));
		//delay720usForDs18b20();
	}
	else
	{//否则DS18B20连接异常(不存在或者已损坏)
		ExistenceFlag = DS18B20_NOT_EXISTENCE;			   
	}

	return ExistenceFlag ;
}

/******************************************************************
 - 函数名称:DS18B20_WriteSlot
 - 功能描述:向DS18B20写入一个位(DS18B20的写时间片)
 - 函数属性:内部函数
 - 参数说明:要写入的位数据
 - 返回说明:无
 - 注	   :1.DS18B20的写时间片对时间的要求较为严格。因此将此代码移植到其它
			   CPU或场合时要注意延时函数
 ******************************************************************/
void DS18B20_WriteSlot(unsigned char DataValue)
{
	DS18B20_IO_Bit = 0 ;
	_nop_();_nop_();_nop_();_nop_();
	DS18B20_IO_Bit = DataValue;
	delay15usForDs18b20();delay15usForDs18b20() ;delay15usForDs18b20() ;delay15usForDs18b20() ;
	DS18B20_IO_Bit = 1 ;
	_nop_();_nop_();_nop_();//注意:两个写时间片最低间隔1us
}

/******************************************************************
 - 函数名称:DS18B20_WriteByte
 - 功能描述:向DS18B20写入一个字节数据
 - 函数属性:内部函数
 - 参数说明:dataCode,需要写入的数据
 - 返回说明:无
 - 注	   :1.此函数调用写时间片来实现字节的写入。
 ******************************************************************/
void DS18B20_WriteByte(unsigned char DataValue)
{
	unsigned char i ;

	for(i=0 ; i<8 ; i++)
	{
		DS18B20_WriteSlot(DataValue &(1<<i) );
	}
}

/******************************************************************
 - 函数名称:DS18B20_ReadSlot
 - 功能描述:从DS18B20读取一个位(DS18B20的读时间片)
 - 函数属性:内部函数
 - 参数说明:无		 
 - 返回说明:读取到的“位”数据
 - 注	   :1.DS18B20的读时间片对时间的要求较为严格。因此将此代码移植到其它
			   CPU或场合时要注意延时函数
 ******************************************************************/
unsigned char DS18B20_ReadSlot(void)
{
	unsigned char dataValue;

	DS18B20_IO_Bit = 0 ;
	_nop_();_nop_();_nop_();_nop_();
	DS18B20_IO_Bit = 1 ;
	_nop_();_nop_();	
	_nop_();_nop_();
	dataValue = DS18B20_IO_Bit;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	
	
	return dataValue;
}

/******************************************************************
 - 函数名称:DS18B20_ReadByte
 - 功能描述:从DS18B20读取一个字节
 - 函数属性:内部函数
 - 参数说明:无
 - 返回说明:读取到的字节数据
 - 注	   :1.注意此函数算法中对DataValue必须赋值为0,因为涉及到位或运算,必须注意。
 ******************************************************************/
unsigned char DS18B20_ReadByte(void)
{	
	unsigned char i,DataValue=0;//注意这里DataValue一定要赋初值为0,否则可能出现意外情况
	
	 for(i=0;i<8;i++)
	 {
	  DataValue |= (DS18B20_ReadSlot()<<i);
	 }
	 
	 return(DataValue);
}

/******************************************************************
 - 函数名称:DS18B20_ReadTemperature
 - 功能描述:读取DS18B20温度值
 - 函数属性:外部函数,供用户调用
 - 参数说明:*temp为DS18B20_Information类型结构体指针		 
 - 返回说明:temp指针对应的信息
 - 注	   :1.经测试STC89C52RC  @11.0592MHZ时i的值为4245,STC12C5A60S2 @11.0592MHZ时i的值为7992,这里
			   设置i的最大值为83620,时间够长了。
 ******************************************************************/
void  DS18B20_ReadTemperature(DS18B20_Struct *temp)
{
	unsigned long i=0;
	
	temp->ExistFlag = DS18B20_Reset() ;
	if(temp->ExistFlag == DS18B20_EXISTENCE)
	{//DS18B20存在
		DS18B20_WriteByte(DS18B20_SKIP_ROM) ;			//跳过ROM操作
		DS18B20_WriteByte(DS18B20_CONVERT_TEMPERATURE) ;//转换温度命令
		while((!DS18B20_ReadSlot()) && (i++<79920));//等待转换完成,注意如果无此等待,上电瞬间温度显示+85℃(详见datasheet)
		//delay750ms();
		
		DS18B20_Reset() ;
		DS18B20_WriteByte(DS18B20_SKIP_ROM) ;
		DS18B20_WriteByte(DS18B20_READ_SCRATCHPAD) ;
		
		temp->Temperature_L= DS18B20_ReadByte() ;/*温度值低位*/
		temp->Temperature_H= DS18B20_ReadByte() ;/*温度值高位*/
		temp->High_Trigger = DS18B20_ReadByte() ;/*触发警报的温度上限值,代码中暂未实际使用*/
		temp->Low_Trigger = DS18B20_ReadByte() ; /*触发警报的温度下限值,代码中暂未实际使用*/

		temp->Configure_Resolution= (DS18B20_ReadByte()>>5) + 9 ;//分辨率:9/10/11/12位
		
		if((temp->Temperature_H) & (0xf8))
		{//温度为负数
			temp->Temperature_sign = DS18B20_TEMPERATURE_MINUS ;
			temp->temperature_value = (~(((temp->Temperature_H )<<8 | temp->Temperature_L)-1))*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;
		}
		else
		{//温度为正数
			temp->Temperature_sign =DS18B20_TEMPERATURE_PLUS ;
			temp->temperature_value = ((temp->Temperature_H & (~(0xf8)))<<8 | temp->Temperature_L)*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;
		}
	}	

}

/******************************************************************
 - 函数名称:DS18B20_Acquire64BitCode
 - 功能描述:读取DS18B20的64Bit光刻ROM编号
 - 函数属性:外部函数,供用户调用
 - 参数说明:DS18B20_Information类型指针
 - 返回说明:无
 - 注	   :1.测试单个DS18B20的64位光刻ROM编号,8bit的CRC+48bit的串行编号+8bit的DS18B20家族码:0x28(固定)	
 ******************************************************************/
void DS18B20_Acquire64BitCode(DS18B20_Struct *temp)
{
	unsigned char i ;
		
	temp->ExistFlag = DS18B20_Reset() ;

	if(temp->ExistFlag == DS18B20_EXISTENCE)
	{
		
		DS18B20_WriteByte(DS18B20_READ_ROM);
		for(i=0 ; i<8 ; i++)
		{
			temp->serial_number[7-i] = DS18B20_ReadByte();//先读出的是低字节,所以反着存储
		}
	}
}

对于LCD2004部分,请参考我的博客文章:单片机控制LCD2004液晶屏之模块化编程

【备注】DS18B20内部有64Bit的激光ROM编码,每一个DS18B20都有唯一的编码,编码的最低8Bit为DS18B20的family code,值为28H,即只要是DS18B20,它的最低8Bit值都是28H,具体参考datasheet,如下图:

【注意】经过上面的实验,得到两个DS18B20的64Bit激光ROM编码分别是8F000006F3A83328和F1000006F18C7628,这里备忘,后面实验用得到。

               实验二   总线悬挂一个DS18B20之仿真实验

程序下载链接:实验2 总线悬挂一个DS18B20之仿真实验【程序】

Proteus仿真电路下载链接:实验2 总线悬挂一个DS18B20之仿真实验【Proteus仿真电路图】

步骤1:设置第一个DS18B20参数及硬件电路

步骤2: 第一个DS18B20实验效果截图

步骤3:设置第二个DS18B20参数及硬件设置

步骤4:第二个DS18B20实验效果截图


测试程序: 直接用“实验一”程序即可(通用)

               实验三   总线悬挂两个DS18B20之实物实验

程序下载链接:实验3 总线悬挂两个DS18B20之实物实验

程序也可由实验一程序修改而来,具体步骤如下:

步骤1:修改DS18B20.C文件,在最前面增加两个变量的定义:

unsigned char Machine = 0;//器件编号
unsigned char Machine_Serial_Number[2][8]={
											{0x8F,0x00,0x00,0x06,0xF3,0xA8,0x33,0x28},
											{0xF1,0x00,0x00,0x06,0xF1,0x8C,0x76,0x28}
											};//两个DS18B20的激光ROM编码,来源于实验一

(这里数组Machine_Serial_Number[2][8]里面保存的是两个DS18B20的光刻ROM编号,数据来自于实验一
 

步骤2:此时因为一条总线悬挂了两个DS18B20,不能再通过跳过ROM搜索命令去操作特定的DS18B20,这时将函数DS18B20_ReadTemperature由

void  DS18B20_ReadTemperature(DS18B20_Struct *temp)
{
	unsigned long i=0;
	
	temp->ExistFlag = DS18B20_Reset() ;
	if(temp->ExistFlag == DS18B20_EXISTENCE)
	{//DS18B20存在
		DS18B20_WriteByte(DS18B20_SKIP_ROM) ;			//跳过ROM操作
		DS18B20_WriteByte(DS18B20_CONVERT_TEMPERATURE) ;//转换温度命令
		while((!DS18B20_ReadSlot()) && (i++<79920));//等待转换完成,注意如果无此等待,上电瞬间温度显示+85℃(详见datasheet)
		//delay750ms();
		
		DS18B20_Reset() ;
		DS18B20_WriteByte(DS18B20_SKIP_ROM) ;
		DS18B20_WriteByte(DS18B20_READ_SCRATCHPAD) ;
		
		temp->Temperature_L= DS18B20_ReadByte() ;/*温度值低位*/
		temp->Temperature_H= DS18B20_ReadByte() ;/*温度值高位*/
		temp->High_Trigger = DS18B20_ReadByte() ;/*触发警报的温度上限值,代码中暂未实际使用*/
		temp->Low_Trigger = DS18B20_ReadByte() ; /*触发警报的温度下限值,代码中暂未实际使用*/

		temp->Configure_Resolution= (DS18B20_ReadByte()>>5) + 9 ;//分辨率:9/10/11/12位
		
		if((temp->Temperature_H) & (0xf8))
		{//温度为负数
			temp->Temperature_sign = DS18B20_TEMPERATURE_MINUS ;
			temp->temperature_value = (~(((temp->Temperature_H )<<8 | temp->Temperature_L)-1))*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;
		}
		else
		{//温度为正数
			temp->Temperature_sign =DS18B20_TEMPERATURE_PLUS ;
			temp->temperature_value = ((temp->Temperature_H & (~(0xf8)))<<8 | temp->Temperature_L)*0.0625*(0x01<<(12-temp->Configure_Resolution)) ;
		}
	}	

}

修改为:


步骤3:修改DS18B20.h文件,增加两个变量的声明

extern unsigned char  Machine;/*DS18B20编号*/
extern unsigned char Machine_Serial_Number[2][8];/*两个DS18B20激光ROM编号*/


步骤4:修改主函数,原本只有一个DS18B20_Struct结构体的变量temp:

	DS18B20_Struct temp ;

这里同一条单总线连接两个DS18B20,所以需要两个变量,修改为:

	DS18B20_Struct temp ,temp2;	

这里不再需要读取ROM编码,所以删除或屏蔽掉:

DS18B20_Acquire64BitCode(&temp);//获取DS18B2064Bit的光刻ROM编号	

通过:

在while(1)大循环中,通过:

        Machine = 0;
		DS18B20_ReadTemperature(&temp) ;
		
		Machine=1;
		DS18B20_ReadTemperature(&temp2) ;

即可将1号DS18B20和2号DS18B20的温度数据保存到temp 和temp2中,此时mian函数如下:

现在就只需要显示出来具体数据即可,将显示函数:

void display(DS18B20_Struct temp)
{
    ……………………
    ……………………
}

修改为(注意同时修改对次函数的声明):

void display(DS18B20_Struct temp,unsigned char machine,unsigned char LCD2004_Row)
{
	unsigned char lsb,msb,i ;

	if(temp.ExistFlag == DS18B20_EXISTENCE)/*器件存在*/
	{
		LCD2004_AddressWriteString(LCD2004_Row,0,"NO.") ;
		LCD2004_AddressWriteString(LCD2004_Row+1,0,"Value:") ;
		//显示DS18B2064Bit的光刻ROM编号
	    for(i=0 ; i<8 ; i++)
	    {
	        msb = Machine_Serial_Number[machine][i] >>4  ;
	        lsb = Machine_Serial_Number[machine][i]& 0x0f  ;
	        if(msb <= 9)
				LCD2004_AddressWriteByte(LCD2004_Row,2*i+3,msb+'0');
	        else
				LCD2004_AddressWriteByte(LCD2004_Row,2*i+3,msb-10+'A');
	   
	        if(lsb <= 9)
				LCD2004_AddressWriteByte(LCD2004_Row,2*i+1+3,lsb+'0');
	        else
				LCD2004_AddressWriteByte(LCD2004_Row,2*i+1+3,lsb-10+'A');
	    }
		
		//显示温度符号、温度值
		if(temp.Temperature_sign == DS18B20_TEMPERATURE_MINUS) //温度为负
		  	LCD2004_AddressWriteByte(LCD2004_Row+1,6,'-');
		else if((temp.Temperature_sign == DS18B20_TEMPERATURE_PLUS))//温度为正
		   LCD2004_AddressWriteByte(LCD2004_Row+1,6,'+');
		LCD2004_AddressWriteByte(LCD2004_Row+1,7,temp.temperature_value/100 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_Row+1,8,(unsigned long)temp.temperature_value%100/10 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_Row+1,9,(unsigned long)temp.temperature_value%10 +'0') ;
		LCD2004_AddressWriteByte(LCD2004_Row+1,10,'.') ;
		LCD2004_AddressWriteByte(LCD2004_Row+1,11,(unsigned long)(temp.temperature_value*10+0.5)%10 +'0') ;//四舍五入
		//显示  ℃
		LCD2004_AddressWriteByte(LCD2004_Row+1,12,0xdf) ;
		LCD2004_AddressWriteByte(LCD2004_Row+1,13,'C') ;
	}
	else 
	{
		LCD2004_AddressWriteString(LCD2004_Row,0,"                    ") ;
		LCD2004_AddressWriteString(LCD2004_Row+1,0,"Error               ") ;
	}
}

主函数为:

void main(void)
{
	DS18B20_Struct temp ,temp2;	
    
	LCD2004_Init() ;
	while(1)
	{
		Machine = 0;
		DS18B20_ReadTemperature(&temp) ;//存储1号DS18B20信息
		
		Machine=1;
		DS18B20_ReadTemperature(&temp2) ;//存储2号DS18B20信息
		
		display(temp,0,LCD2004_ROW0);//显示1号DS18B20信息
		display(temp2,1,LCD2004_ROW2);//显示2号DS18B20信息
	}
}

实验即可成功。

               实验四   总线悬挂两个DS18B20之仿真实验

实验四和实验三代码几乎相同,只需要修改对ROM的定义部分,即将:

unsigned char Machine_Serial_Number[2][8]={
											{0x8F,0x00,0x00,0x06,0xF3,0xA8,0x33,0x28},
											{0xF1,0x00,0x00,0x06,0xF1,0x8C,0x76,0x28}
											};//两个DS18B20的激光ROM编码,来源于实验一

修改软件仿真程序中DS18B20的ROM编码(具体参考实验二得到的显示结果)即看到效果,这里不再详述。

               实验五   总线悬挂两个DS18B20之实物趣味检测实验

经过以上实验,DS18B20的基本操作已搞定了,这里增加一个思考,在试验一中,我分别连接了两个DS18B20,得到了他们的ROM编码是:8F000006F3A83328和F1000006F18C7628,这是分别连接两个器件得到的结果,那么如果我同时连接两个DS18B20会读取到ROM编码是多少呢?

经过试验,得到结果是81000006F1883228,注意这里有趣了,这里1号ROM编码和2号ROM编码   &  操作,得到的结果就是81000006F1883228,这实际上也是两个DS18B20对总线抢夺资源,“线与” 得到的结果。

所以这里可以利用这个特性,通过DS18B20_Acquire64BitCode函数,通过检测读取到的ROM编码,知道现在两个DS18B20的连接状况(连接状况包括:两个都连接正常、1号连接正常且2号连接异常、2号连接正常且1号连接异常、两个都连接异常),也就达到了测试视频中  “实时监测”  连接情况的效果,当然这在实际应用中不一定适用,但是可以增加趣味性,也可以增加我们对DS18B20的理解。

有兴趣的可以可下载程序(根据DS18B20ROM编码,代码中修改编码即可),不再详述,欢迎讨论。

程序下载链接: 实验5 总线悬挂两个DS18B20之实物实验【趣味检测器件运行】

               启发思考

以上五个实验基本上可以掌握DS18B20的操作,这里共同讨论DS18B20的代码是如何一步一步完成的,在实际应用中DS18B20得程序还是不好调试的,很多人写了一堆代码,然后测试,结果显示的不是想要的结果就不知道如何处理了。这里作者把自己思考过程分享出来,也是以点概面讲述如何“一点一滴”写代码。【以下是个人理解,仅供参考】

【步骤1】书写延时函数。因为DS18B20是单总线协议,对时序要求严格,而DS18B20的操作中需要部分延时,例如:

发现这里有几种延时,首先有15us延时、60us延时、480us延时,这里复位信号至少480us,所以为了安全,复位信号维持1.5倍的480us即720us,所以至少需要15us延时、720us延时,以15us延时为例,作者的延时函数为:

/******************************************************************
 - 函数名称:delay15usForDs18b20
 - 功能描述:延时15微秒
 - 函数属性:内部函数
 - 参数说明:无		 
 - 返回说明:无
 - 注	   :1.此函数支持硬件电路、proteus软件仿真。
			 2.此函数由软件STC-ISP V6.88D自带的“软件延时计算器”产生。
			   当TEST等于1时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
			   【备注】STC-Y1指令集包括:
						STC89Cxx/STC89LExx
						STC90Cxx/STC90LExx
						
			   当TEST等于2时候,适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
			   【备注】STC-Y3指令集包括:
						STC12Cxx/STC12LExx
						STC11Fxx/STC11Lxx/STC10Fxx/STC10Lxx
						STC15F104E/STC15L104E(A版)
						STC15F204EA/STC15L204EA(A版)		
			 3.单总线对时序要求较高,当移植至其他CPU时,需要适当修改。
 ******************************************************************/
void delay15usForDs18b20(void)	//11.0592MZH
{
	#if  (TEST == 1) 	//适用于晶振11.0592MHZ 、8051指令集为STC-Y1硬件电路和Proteus8.9软件仿真
		unsigned char i;
		i = 4;
		while (--i);
	#elif (TEST ==  2)	 //适用于晶振11.0592MHZ 、8051指令集为STC-Y3硬件电路
		unsigned char i;
		_nop_();_nop_();
		i = 38;
		while (--i);
	#endif
}

我这里用了预编码#if,是因为作者常用的单片机包括STC89C52RC和STC12C5A60S2,而STC12C5A60S2反应较快,所以它的延时和STC89C52RC不一样,如果用户平常只用一种单片机,那么就没有必要写这么复杂。然后介绍作者的延时程序是如何来的?如下图是STC-ISP下载软件,

所以作者的延时程序是从这里复制粘贴来的,DS18B20对时序要求较高,所以虽然我们使用的是不精准延时函数,但是应该尽量无限接近我们需要的结果,这里利用官方软件自带生成的代码得到结果,这里我通过逻辑分析仪测试(有兴趣买一个,对代码调试有帮助),延时还是很靠谱的,这时成功的第一步,相应的完成延时函数delay720usForDs18b20()。

【步骤2】书写复位函数。这里作者最初的复位函数是这样的(具体过程请参考datasheet,不再详述):

char DS18B20_Reset(void)
{
	char ExistenceFlag ;
	
	DS18B20_IO_Bit = 1 ;
	_nop_() ; _nop_() ;	
	DS18B20_IO_Bit = 0 ;
	delay720usForDs18b20();//复位脉冲最少480us,这里采用1.5倍时间,即720us延时
	DS18B20_IO_Bit = 1 ;
	/*以上是单片机发送“复位”脉冲*/

	//等待15us至60us,如果DS18B20存在,DS18B20会把数据线拉低,发送一个60us至240us的“应答脉冲”
	delay15usForDs18b20();delay15usForDs18b20(); 
	delay15usForDs18b20();delay15usForDs18b20();

	if(!DS18B20_IO_Bit)
	{//如果主机(单片机)检测到数据线被DS18B20拉低,表示DS18B20复位成功
		ExistenceFlag = DS18B20_EXISTENCE;
		while(!DS18B20_IO_Bit) ;
		//delay720usForDs18b20();
	}
	else
	{//否则DS18B20连接异常(不存在或者已损坏)
		ExistenceFlag = DS18B20_NOT_EXISTENCE;			   
	}

	return ExistenceFlag ;
}

【步骤3】验证复位函数成功与否。写一个测试程序,就是对DS18B20进行复位,看看返回值是否正常,这里的正常有两层含义:第一,正常连接DS18B20时,复位函数回复的值应该是DS18B20_EXISTENCE(在DS18B20.h中,表示DS18B20连接正常,值为1);第二,故意设置异常情况——拔掉DS18B20,这时候复位函数返回值应该是DS18B20_NOT_EXISTENCE(在DS18B20.h中,表示DS18B20连接异常(比如器件损坏或未连接),值为0),只有两个情况都得到验证,才表示复位函数书写成功。

这里,注意while(!DS18B20_IO_Bit);这一句代码,意思是单片机等待DS18B20的“存在脉冲”(低电平)发送完,这里实际上在一个复杂的工程中,是不安全的,例如有信号干扰导致数据线一直是低电平,那么一个复杂的工程中可能就“卡死”在这里了,不利于调试,不建议进行“卡死”等待,作者这里借用了一个辅助i:

/******************************************************************
 - 函数名称:DS18B20_Reset
 - 功能描述:DS18B20初始化序列操作
 - 函数属性:内部函数
 - 参数说明:无
 - 返回说明:DS18B20_EXISTENCE,表示DS18B20复位成功,其产生“应答脉冲信号”
			DS18B20_NOT_EXISTENCE,表示DS18B20复位不成功,其未产生“应答脉冲信号”,可能是连接异常或器件损坏
 - 注	   :1.初始化过程包括:复位和“应答脉冲”,主机(单片机)产生至少480us的低电平,
			   然后主机释放数据线,进入接收模式。当DS18B20检测到上升沿,它等待15us至60us
			   ,然后发射一个60us至240us的“应答脉冲信号”。
			 2.DS18B20的所有通信,都是以初始化序列(包括单片机发出的复位脉冲和DS18B20的
			  “应答脉冲”)开始的。
			 3.初始化是正确操作DS18B20是重要一步,必须复位成功,只有这里返回值为DS18B20_EXISTENCE,
			   后续的操作才有意义,具体使用中可以通过检测返回值检测是否成功。
			 4.DS18B20对时间的要求较为严格,因此将此代码移植到其它CPU或场合时要注意延时函数。
			 5.经测试STC89C52RC  @11.0592MHZ时i的值为2,STC12C5A60S2 @11.0592MHZ时i的值为7,这里
			   设置i的最大值为70,时间够长了。
 ******************************************************************/
char DS18B20_Reset(void)
{
	char ExistenceFlag ;
	unsigned long i=0;
	
	DS18B20_IO_Bit = 1 ;
	_nop_() ; _nop_() ;	
	DS18B20_IO_Bit = 0 ;
	delay720usForDs18b20();//复位脉冲最少480us,这里采用1.5倍时间,即720us延时
	DS18B20_IO_Bit = 1 ;
	/*以上是单片机发送“复位”脉冲*/

	//等待15us至60us,如果DS18B20存在,DS18B20会把数据线拉低,发送一个60us至240us的“应答脉冲”
	delay15usForDs18b20();delay15usForDs18b20(); 
	delay15usForDs18b20();delay15usForDs18b20();

	if(!DS18B20_IO_Bit)
	{//如果主机(单片机)检测到数据线被DS18B20拉低,表示DS18B20复位成功
		ExistenceFlag = DS18B20_EXISTENCE;
		while((!DS18B20_IO_Bit) && (i++<70));
		//delay720usForDs18b20();
	}
	else
	{//否则DS18B20连接异常(不存在或者已损坏)
		ExistenceFlag = DS18B20_NOT_EXISTENCE;			   
	}

	return ExistenceFlag ;
}

这里的i++<70  ,70不是随便写的,而是经过测试的,具体看程序注释。

【步骤4】书写“写函数”和“读函数”,这里作者的函数如下:

/******************************************************************
 - 函数名称:DS18B20_WriteSlot
 - 功能描述:向DS18B20写入一个位(DS18B20的写时间片)
 - 函数属性:内部函数
 - 参数说明:要写入的位数据
 - 返回说明:无
 - 注	   :1.DS18B20的写时间片对时间的要求较为严格。因此将此代码移植到其它
			   CPU或场合时要注意延时函数
 ******************************************************************/
void DS18B20_WriteSlot(unsigned char DataValue)
{
	DS18B20_IO_Bit = 0 ;
	_nop_();_nop_();_nop_();_nop_();
	DS18B20_IO_Bit = DataValue;
	delay15usForDs18b20();delay15usForDs18b20() ;delay15usForDs18b20() ;delay15usForDs18b20() ;
	DS18B20_IO_Bit = 1 ;
	_nop_();_nop_();_nop_();//注意:两个写时间片最低间隔1us
}

/******************************************************************
 - 函数名称:DS18B20_WriteByte
 - 功能描述:向DS18B20写入一个字节数据
 - 函数属性:内部函数
 - 参数说明:dataCode,需要写入的数据
 - 返回说明:无
 - 注	   :1.此函数调用写时间片来实现字节的写入。
 ******************************************************************/
void DS18B20_WriteByte(unsigned char DataValue)
{
	unsigned char i ;

	for(i=0 ; i<8 ; i++)
	{
		DS18B20_WriteSlot(DataValue &(1<<i) );
	}
}

/******************************************************************
 - 函数名称:DS18B20_ReadSlot
 - 功能描述:从DS18B20读取一个位(DS18B20的读时间片)
 - 函数属性:内部函数
 - 参数说明:无		 
 - 返回说明:读取到的“位”数据
 - 注	   :1.DS18B20的读时间片对时间的要求较为严格。因此将此代码移植到其它
			   CPU或场合时要注意延时函数
 ******************************************************************/
unsigned char DS18B20_ReadSlot(void)
{
	unsigned char dataValue;

	DS18B20_IO_Bit = 0 ;
	_nop_();_nop_();_nop_();_nop_();
	DS18B20_IO_Bit = 1 ;
	_nop_();_nop_();	
	_nop_();_nop_();
	dataValue = DS18B20_IO_Bit;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	delay15usForDs18b20() ;
	
	
	return dataValue;
}

/******************************************************************
 - 函数名称:DS18B20_ReadByte
 - 功能描述:从DS18B20读取一个字节
 - 函数属性:内部函数
 - 参数说明:无
 - 返回说明:读取到的字节数据
 - 注	   :1.注意此函数算法中对DataValue必须赋值为0,因为涉及到位或运算,必须注意。
 ******************************************************************/
unsigned char DS18B20_ReadByte(void)
{	
	unsigned char i,DataValue=0;//注意这里DataValue一定要赋初值为0,否则可能出现意外情况
	
	 for(i=0;i<8;i++)
	 {
	  DataValue |= (DS18B20_ReadSlot()<<i);
	 }
	 
	 return(DataValue);
}

【步骤5】验证“写函数”和“读函数”。完成以上程序后,不要着急让DS18B20完成读温度等复杂的操作,先验证“读函数”和“写函数”是否有效,验证方法是,通过读取DS18B20的64Bit激光ROM编码,验证其是否正确,通过阅读DS18B20可知,64Bit的ROM编码中,最低8Bit为DS18B20的family code,即只要是DS18B20器件,最低8Bit的ROM编码都是28h,具体参考:

作者利用这个特性,读取编码,看看是否是28h,如果是就表示“读函数”和“写函数”是成功的。

/******************************************************************
 - 函数名称:DS18B20_Acquire64BitCode
 - 功能描述:读取DS18B20的64Bit光刻ROM编号
 - 函数属性:外部函数,供用户调用
 - 参数说明:DS18B20_Information类型指针
 - 返回说明:无
 - 注	   :1.测试单个DS18B20的64位光刻ROM编号,8bit的CRC+48bit的串行编号+8bit的DS18B20家族码:0x28(固定)	
 ******************************************************************/
void DS18B20_Acquire64BitCode(DS18B20_Struct *temp)
{
	unsigned char i ;
		
	temp->ExistFlag = DS18B20_Reset() ;

	if(temp->ExistFlag == DS18B20_EXISTENCE)
	{
		
		DS18B20_WriteByte(DS18B20_READ_ROM);
		for(i=0 ; i<8 ; i++)
		{
			temp->serial_number[7-i] = DS18B20_ReadByte();//先读出的是低字节,所以反着存储
		}
	}
}

然后写一个测试程序,显示出读取到的ROM值,如果最低8Bit数据是28H,就表示“写函数”“读函数”代码正确,再继续编写下一步代码,否则反复调试。

【6】书写其他功能函数(比如获取温度函数),这里就不再详述。

总之,写程序要写一点测试一点,这样一步步测试,结果一定是预期效果,方法对其他芯片同样适用。

               预留思考

在一个复杂工程中,DS18B20也许只是一部分代码,如果在单片机与DS18B20通信关键时候,发生一个中断,待中断执行完后,再返回来可能通信中已经丢失了重要信息,所以建议大家可以充分运用CRC校验,对通信得到的数据进行校对,如果发现信有异常信息,就表示信息是不对的,就舍弃掉而不加以运用(显示或其他操作),datasheet中也对此部分进行了介绍,这里作者没有这部分操作,这里作为一个预留思考,大家可以评论留下想法。

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刺客阿瑞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值