总线上挂多个DS18B20器件读取方法

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

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

实现方法:(以总线上挂2个器件为例)
1.先单个读取每个器件的64位序列号
2.把序列号用数组存放起来
3.读取温度时发不同的序列号匹配对应器件

下面以总线上挂2个器件为例贴出代码
ds18b20.c文件的代码

#include "ds18b20.h"

unsigned char wendubuf1[2];//wendubuf[0]存扩大10倍的温度高8位   wendubuf[0]存扩大10倍的温度低8位   比如326 为32.6
unsigned char wendubuf2[2];//wendubuf[0]存扩大10倍的温度高8位   wendubuf[0]存扩大10倍的温度低8位   比如326 为32.6
unsigned char rom1[8]={0x28,0xCB,0x0C,0x96,0xF0,0x01,0x3C,0xE4};//28 CB 0C 96 F0 01 3C E4 
unsigned char rom2[8]={0x28,0x63,0xC6,0x48,0xF6,0xB7,0x3C,0xEB};//28 63 C6 48 F6 B7 3C EB 


void DS18B20Init()
{
	SetGpioMode(GPIOP3,PIN_3,GPIO_Mode_Out_IN);//设置GPIO模式   GPIO_Mode_Out_IN准双向 GPIO_Mode_Out_PP推挽 GPIO_Mode_AIN高阻GPIO_Mode_Out_OD开漏

	DS_OUT=1;    //输出1
}





/* 复位总线,获取存在脉冲,以启动一次读写操作 */
unsigned char Get18B20Ack()
{
    u8 retry=0;
    

    DS_OUT = 0;     //产生500us复位脉冲
    delay_us(750);
    DS_OUT = 1;
    delay_us(15);    //延时15us

    while (DS_OUT&&retry<200)//等待被拉低
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=200)return 1;
	else retry=0;
    while (!DS_OUT&&retry<240)
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	  
  
	return 0;
}

//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 			 // read one bit
{
  u8 Data;

  DS_OUT=0; 
	delay_us(2);
  DS_OUT=1; 

	delay_us(12);
	if(DS_OUT)Data=1;
  else Data=0;	 
  delay_us(50);           
  return Data;
}
//从DS18B20读取一个字节
//返回值:读到的数据

//写一个字节到DS18B20
//dat:要写入的字节


/* 向DS18B20写入一个字节,dat-待写入字节 */
void Write18B20(unsigned char dat)
{
    u8 j;
    u8 testb;




    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) 
        {
            DS_OUT=0;// Write 1
            delay_us(2);                            
            DS_OUT=1;
            delay_us(60);             
        }
        else 
        {
            DS_OUT=0;// Write 0
            delay_us(60);             
            DS_OUT=1;
            delay_us(2);                          
        }
    }
}


/* 从DS18B20读取一个字节,返回值-读到的字节 */
unsigned char Read18B20()
{
    u8 i,j,dat;
    dat=0;
	for (i=1;i<=8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}


/* 启动一次18B20温度转换,返回值-表示是否启动成功 */
void Start18B20()
{
    unsigned char ack=1;
    
    ack = Get18B20Ack();   //执行总线复位,并获取18B20应答
    if (ack == 0)          //如18B20正确应答,则启动一次转换
    {
       Write18B20(0xCC);  //跳过ROM操作
       Write18B20(0x44);  //启动一次温度转换
    }
    
}
void ReadRom(void)//读ROM
{
	unsigned char index;
	
    bit ack;
    
    ack = Get18B20Ack();   //执行总线复位,并获取18B20应答
    if (ack == 0)          //如18B20正确应答,则启动一次转换
    {
        Write18B20(0x33);  //读ROM
    }
	
	for (index = 0;index < 8;index++)
	{
		rom1[index] = Read18B20();
	}

}
void CheckRom(unsigned char a) //匹配ROM命令
{
	unsigned char j;  //用于循环
	Get18B20Ack();   //执行总线复位,并获取18B20应答
	Write18B20(0x55);	 //发送匹配ROM命令
	if(a==1)
	{
		for(j=0;j<8;j++)
		{
			 Write18B20(rom1[j]);//发送18B20的序列号,先发送低字节
		}
	}else if(a==2)
	{
		for(j=0;j<8;j++)
		{
			 Write18B20(rom2[j]);//发送18B20的序列号,先发送低字节
		}
	}


}

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



void Get_Temp1()//获取温度
{
	unsigned char res=1;
	int temp;        //读取到的当前温度值
	int intT, decT;  //温度值的整数和小数部分
	//unsigned char wenduzs,wenduxs;//整数  小数

	res = Get18B20Temp1(&temp);  //读取当前温度

	if (res==0)                    //读取成功时,刷新当前温度显示
	{
		if(temp<0)//把负数变正数
		{
			temp=~(temp-1);//把负数变为正数
			intT = temp >> 4;             //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
			
		}
		else
		{
			intT = temp >> 4;             //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
		}
		decT = temp & 0x000F;            //分离出温度值小数部分
        decT = (decT*10) / 16;        //1/16=0.0625,除以16等于乘以0.0625,乘以10就保留了1位小数了。二进制的小数部分转换为1位十进制位
		//wenduzs=(unsigned char)intT;//整除
		//wenduxs=(unsigned char)decT;//小数
		//wendubuf[0]=wenduzs;//把整数存入暂存数组0
		//wendubuf[1]=wenduxs;//把小数存入暂存数组1  注意  小数是*10后的值
		wendubuf1[0]=(intT*10+decT)>>8;//高8位
		wendubuf1[1]=(unsigned char)(intT*10+decT);//低8位
  }

  Start18B20();               //重新启动下一次转换

}

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



void Get_Temp2()//获取温度
{
	unsigned char res=1;
	int temp;        //读取到的当前温度值
	int intT, decT;  //温度值的整数和小数部分
	//unsigned char wenduzs,wenduxs;//整数  小数

	res = Get18B20Temp2(&temp);  //读取当前温度

	if (res==0)                    //读取成功时,刷新当前温度显示
	{
		if(temp<0)//把负数变正数
		{
			temp=~(temp-1);//把负数变为正数
			intT = temp >> 4;             //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
			
		}
		else
		{
			intT = temp >> 4;             //右移后16进制转换为十进制值刚好等于温度整数值,分离出温度值整数部分0.0625
		}
		decT = temp & 0x000F;            //分离出温度值小数部分
        decT = (decT*10) / 16;        //1/16=0.0625,除以16等于乘以0.0625,乘以10就保留了1位小数了。二进制的小数部分转换为1位十进制位
		//wenduzs=(unsigned char)intT;//整除
		//wenduxs=(unsigned char)decT;//小数
		//wendubuf[0]=wenduzs;//把整数存入暂存数组0
		//wendubuf[1]=wenduxs;//把小数存入暂存数组1  注意  小数是*10后的值
		wendubuf2[0]=(intT*10+decT)>>8;//高8位
		wendubuf2[1]=(unsigned char)(intT*10+decT);//低8位
  }

  Start18B20();               //重新启动下一次转换

}
ds18b20.h文件的代码

```c
#ifndef _DS18B20_H
#define _DS18B20_H
#include "config.h"

sbit DS_OUT=P3^3;

extern unsigned char wendubuf1[2];//wendubuf[0]存扩大10倍的温度高8位   wendubuf[1]存扩大10倍的温度低8位   比如326 为32.6
extern unsigned char wendubuf2[2];//wendubuf[0]存扩大10倍的温度高8位   wendubuf[1]存扩大10倍的温度低8位   比如326 为32.6
extern unsigned char rom1[8];
extern unsigned char rom2[8];


void Start18B20(void);// 启动一次18B20温度转换,返回值-表示是否启动成功 */  初始化值调用1次
void Get_Temp1(void);//温度处理函数,包括把温度值存入数组    获取温度值调用
void Get_Temp2(void);//温度处理函数,包括把温度值存入数组    获取温度值调用
void Ds18B20_Gpio_Init(void);// IO口初始化
void DS18B20Init();//温度传感器
void ReadRom(void);//读ROM
#endif

main.c文件代码

#include "ds18b20.h"






void main()
{

	DS18B20Init();//温度传感器

	


	while(1)
	{

		if(bitVar.flag1S==1)	//1s标志
		{
			bitVar.flag1S=0;
			if(bitVar.change==0)
			{
				Get_Temp1();
				bitVar.change=1;
			}else
			{
				Get_Temp2();
				bitVar.change=0;
			}
			wenDu.wenDu1=( (wendubuf1[0]<<8)+wendubuf1[1] );
			wenDu.wenDu2=( (wendubuf2[0]<<8)+wendubuf2[1] );
			
			//ReadRom();//读ROM时用串口把序列号输出来
		//	UartWrite(rom1, 8);      //串口输出
		
		}	

	
		

	}
}

你可能感兴趣的:(51,51单片机,c语言)