51单片机——模拟1-Wire总线与DS18B20通信(测量温度)

目录

一、功能描述

二、主要模块介绍

2.1 DS18B20介绍

 2.1.2 DS18B20内部结构

2.1.3 DS18B20的温度转换

2.1.4 DS18B20模块程序

2.2 1-Wire总线介绍

2.2.1 单总线初始化时序 

2.2.2 单总线写时序(主机发送一位数据)

2.2.3 单总线读时序(主机接收一位数据)

2.2.4 单总线发送一个字节数据和接收一个字节数据

三、测试文件

四、效果演示


一、功能描述

        51单片机模拟1-Wire总线与DS18B20通信,测量温度,并用LCD_1602显示出来。可以设置温度的上限值和下限值,并用AT24C02保存上限值和下限值,具有高温、低温报警闪烁功能。

二、主要模块介绍

2.1 DS18B20介绍

        DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、 适用电压宽、与微处理器接口简单的数字化温度传感器。

  • 测温范围:-55℃~+125℃
  • 通信接口:单总线
  • 掉电保护功能 DS18B20 内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。

  • 可寄生供电

2.1.1 DS18B20外部引脚

  •  VDD:电源3.0v~5.5v
  • GND:电源地
  • DQ:单总线接口

 2.1.2 DS18B20内部结构

        DS18B20 温度传感器的内部存储器包括一个高速的暂存器 RAM 和一个非易失性的可电擦除的 EEPROM,后者存放高温度和低温度触发器 TH、TL 和配置寄存器。

  • 64位ROM:器件的地址,用于总线通信寻址。
  • 高速暂存器RAM:用于总线的数据交互。
  • EEPROM:用于保存温度触发阈值和配置参数。 

配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下:

         低五位一直都是"1",TM 是测试模式位,用于设置 DS18B20 在工作模式还是在测试模式。在 DS18B20 出厂时该位被设置为 0,用户不需要去改动。R1 和 R0 用来设置 DS18B20 的精度(分辨率),可设置为 9,10,11 或 12 位,对 应的分辨率温度是 0.5℃,0.25℃,0.125℃和 0.0625℃。R0 和 R1 配置如下图:

在初始状态下默认的精度是 12 位,即 R0=1、R1=1。

高速暂存存储器由 9 个 字节组成,其分配如下:

        当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。存储的两个字节,高字节的前 5 位 是符号位 S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后, 数据格式如下:

2.1.3 DS18B20的温度转换

        前面已知,DS18B20的温度值是以二进制补码的形式存储在LS Byte和MS Byte,所以温度值是一个16位的数。温度分辨率是0.0625℃。如果温度>0℃,高5位是"0",把测量的16位数乘以0.0625就是实际温度值;如果温度<0℃,高5位是"1",因为负数的原码与补码不同,由原码=~(补码-1),求得原码,再把原码乘以0.0625就是实际温度值。

  1. 求测量值的原码(正数原码=补码,负数原码=~(补码-1))
  2. 实际温度=原码X0.0625。

2.1.4 DS18B20模块程序

  • 温度变换:初始化->跳过ROM->开始温度变换
  •  温度读取:初始化->跳过ROM->读暂存器中的测量值->转化成实际值

程序如下:

​
#include"OneWire.h"
#include"LCD1602.h"
#define DS18B20_Skip_ROM  0xcc
#define DS18B20_ConvertT  0x44
#define DS18B20_Read_scratchpad  0xbe
/*
     *函数名:	 DS18B20_Convert_T()
     *函数功能: 温度转换命令
     *输入:	 无
     *输出:	 无
*/
void DS18B20_Convert_T()
{
	OneWire_Init();
	OneWire_Sendbyte(DS18B20_Skip_ROM);
	OneWire_Sendbyte(DS18B20_ConvertT);
}
/*
     *函数名:	 DS18B20_ReadT()
     *函数功能: 读取DS18B20测量的温度值数据
     *输入:	 无
     *输出:	 T:温度值
*/
float DS18B20_ReadT()
{
	unsigned char TLSB,TMSB;		 
	int Temp=0;
	float T=0;
	OneWire_Init();
	OneWire_Sendbyte(DS18B20_Skip_ROM);
	OneWire_Sendbyte(DS18B20_Read_scratchpad);
	TLSB=OneWire_Receivebyte();	
	TMSB=OneWire_Receivebyte();	
	Temp=(TMSB<<8)|TLSB; 
	Temp&=0x07ff;//把高三位变为0,其他位不变
	if(Temp>0){T=Temp*0.0625;} //正数:原码=补码
	else
	{
	   	T=(~(Temp-1))*0.0625;  //负数:原码=~(补码-1)
	}
	return T;
}

​

2.2 1-Wire总线介绍

  • 单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线。
  • 一根数据线:DQ。
  • 通信方式:异步、半双工
  • 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去VDD线,DQ同时供电和传输数据,此时只需要DQ和GND两根线。

2.2.1 单总线初始化时序 

  1. 主机拉低总线480us后,释放总线。
  2. 从机等待15~60us,再拉低总线60~240us响应主机后,从机释放总线。
  3. 保证从主机释放总线到初始化结束至少480us。

程序如下:

sbit OneWire_DQ=P3^7;
/*
     *函数名:	 OneWire_Init()
     *函数功能: 单总线的初始化信号
     *输入:	 无
     *输出:	 bit:从机的响应应答
*/
bit OneWire_Init()
{	
	unsigned char i=0;
	bit ack=1;
	OneWire_DQ=1;
	OneWire_DQ=0;
	i = 227;while (--i);//500us
	OneWire_DQ=1; //释放总线
	i = 25;while (--i);//60us
	ack=OneWire_DQ;
	i = 158;while (--i);//350us
	return ack;
}

2.2.2 单总线写时序(主机发送一位数据)

写"0"时序:

  1. 主机拉低总线60~120us。
  2. 从机将在第15us~60us之间采样(从机读取数据)。
  3. 主机释放总线。

写"1"时序:

  1. 主机拉低总线1~15us,然后释放总线。
  2. 从机将在第15us~60us之间采样(从机读取数据)。
  3. 应保证整个时间片大于60us。

程序如下:

/*
     *函数名:	 OneWire_Sendbite(unsigned char bite)
     *函数功能: 主机发送一位数据
     *输入:	 bite:一位数据
     *输出:	 无
*/
void OneWire_Sendbite(unsigned char bite)
{	
	unsigned char i=0;
	if(bite==0)//发送0
	{
		OneWire_DQ=0;
		i = 30;while (--i);//70us
		OneWire_DQ=1;			
	}
	if(bite!=0)
	{
		OneWire_DQ=0;
	   	i = 2;while (--i);//10us
		OneWire_DQ=1;
		i = 25;while (--i);//60us
	}
}

2.2.3 单总线读时序(主机接收一位数据)

  1. 主机拉低总线1us~小于15us的时间,然后释放总线。
  2. 从机控制总线。
  3. 主机在第15us末尾的地方进行采样。

 程序如下:

/*
     *函数名:	  OneWire_Receivebite()
     *函数功能:  主机接收一位数据
     *输入:	  无
     *输出:	  bite:接收的一位数据
*/
unsigned char OneWire_Receivebite()
{
	unsigned char i=0;
	unsigned char  bite=0;
	OneWire_DQ=0;
	i = 2;while (--i);//5us
	OneWire_DQ=1; //释放总线
	i = 3;while (--i);//7us
	bite=OneWire_DQ;
	i = 22;while (--i);//50us
	return bite;
}

2.2.4 单总线发送一个字节数据和接收一个字节数据

  • 发送一个字节:连续调用8次写时序,依次发送一个字节的8位(低位在前)。
  • 接收一个字节:连续调用8次读时序,依次接收一个字节的8位(低位在前)。

程序如下:

/*
     *函数名:	   OneWire_Sendbyte(unsigned char byte)
     *函数功能:   主机发送一个字节数据
     *输入:	   byte:一个字节
     *输出:	   无
*/
void OneWire_Sendbyte(unsigned char byte)
{
	unsigned char i=0;
	for(i=0;i<8;i++)
	{
		OneWire_Sendbite(byte&(0x01<<i));
	}
}
/*
     *函数名:	   OneWire_Receivebyte()
     *函数功能:   主机接收一个字节数据
     *输入:	   无
     *输出:	   byte:接收的一个字节数据
*/
unsigned char OneWire_Receivebyte()
{
	unsigned char i,byte=0;
	for(i=0;i<8;i++)
	{
		if(OneWire_Receivebite()!=0)
		{
			byte|=0x01<<i;
		};  
	}
	return byte;
}

三、测试文件

#include <REGX52.H>
#include"LCD1602.h"
#include"DS18B20.h"
#include"Delay.h"
#include"Timer0_Init.h"
#include"AT24C02.h"
#include"IndependentKey.h" 
char THigh=20;
char TLow=-1;
unsigned char set_mode;
unsigned char flash;
void set_T()
{
	unsigned char keynum;
	keynum=key();
	if(keynum!=0)
	{
		if(keynum==1){set_mode=!set_mode;}
		if(keynum==2)
		{
			if(set_mode==0){THigh++;}//加温度值
			else{TLow++;}
		}
		if(keynum==3)
		{
			if(set_mode==0){THigh--;}//减温度值
			else{TLow--;}
		}
		if(keynum==4)
		{
			AT24C02_Writebyte(0x01,THigh);
			Delay(5);
			AT24C02_Writebyte(0x02,TLow);
			Delay(5);
		}
	}
		LCD_ShowNum(2,16,set_mode+1,1);
}
void show_T()
{
	LCD_ShowString(2,1,"TH:");
	LCD_ShowString(2,8,"TL:");
	if(THigh>0){LCD_ShowString(2,4,"+");LCD_ShowNum(2,5,THigh,2);}
	else{LCD_ShowString(2,4,"-");LCD_ShowNum(2,5,0-THigh,2);}
	if(TLow>0){LCD_ShowString(2,11,"+");LCD_ShowNum(2,12,TLow,2);}
	else{LCD_ShowString(2,11,"-");LCD_ShowNum(2,12,0-TLow,2);}
}
int main()
{
	float T=0;
	LCD_Init();
	LCD_ShowString(1,1,"T:");
	DS18B20_Convert_T();
	Delay(1000);
	Timer0_Init();
	AT24C02_Writebyte(0x03,THigh);
	Delay(5);
	AT24C02_Writebyte(0x04,TLow);
	Delay(5);
	THigh=AT24C02_Readbyte(0x01);
	TLow=AT24C02_Readbyte(0x02);
	while(1)
	{
		DS18B20_Convert_T();
		T=DS18B20_ReadT();
		if(T<0){LCD_ShowString(1,3,"-");}
		if(T>0){LCD_ShowString(1,3,"+");}
		LCD_ShowNum(1,4,T/1,3);
		LCD_ShowString(1,7,".");
		LCD_ShowNum(1,8,(unsigned long)(T*10000)%10000,4);
		set_T();//设置上下限温度
		show_T(); //显示上下限温度
		if(flash==0)
		{
			if(T>=THigh){LCD_ShowString(1,13,"T:H ");}	//闪烁警告
			if(T<=TLow){LCD_ShowString(1,13,"T:L ");}
		}
		if(flash!=0)
		{
		 	LCD_ShowString(1,13,"   ");
		}
		if(T>TLow&&T<THigh){LCD_ShowString(1,13,"T:OK");}
	}
}
void Timer0_ISR() interrupt 1
{	
	static int count1,count2=0;
	count1++;
	count2++;
	TH0=0xfc;
	TL0=0x18;
	if(count1==20)
	{
		count1=0;
		Key_Loop();	
	}
	if(count2==500)
	{
		 count2=0;
		 flash=!flash;
	} 
}

四、效果演示

        低温度暂时测试不了。

DS18B20温度传感器

猜你喜欢

转载自blog.csdn.net/ssssshhbh/article/details/129267499