我是靠谱客的博主 苗条刺猬,最近开发中收集的这篇文章主要介绍嵌入式单片机基础篇(四十二)之单总线协议-DS18B20原理与stm32F1以及51单片机程序单总线协议-DS18B20原理与stm32F1以及51单片机程序,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

单总线协议-DS18B20原理与stm32F1以及51单片机程序

一、DS18B20技术性能特征

①、 独特的单总线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。大大提高了系统的抗干扰性。
② 、测温范围 -55℃~+125℃,精度为±0.5℃。
③、支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只能并联8个,实现多点测温,如果数量过多,会使供电电源电压过低,从而造成信号传输的不稳定。
④、 工作电源: 3.0~5.5V/DC (可以数据线寄生电源)。
⑤ 、在使用中不需要任何外围元件。
⑥、 测量结果以9~12位数字量方式串行传送。

二、DS18B20特点:

(1)DS18B20共有6种信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1。所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。
(2)单总线是一种半双工通信方式

三、DS18B20的典型温度读取过程:

DS18B20的典型温度读取过程为:复位>>发SKIP ROM命令(0XCC)>>发开始转换命令(0X44)>>延时>>复位>>发送SKIP ROM命令(0XCC)>>发读存储器命令(0XBE)>>连续读出两个字节数据(即温度)>>结束。
1、如果总线上只有一个ds18b20,那么初始化时可以跳过ROM(CCH)
2、如果总线上只有多个ds18b20,那么初始化时需要搜索ROM(FOH)、读ROM(33H)读出64位序列号、发送匹配ROM指令(55H)以及64位序列号

四、原理与程序:

1、51单片机原理与程序:
(1)复位脉冲函数:

sbit DQ=P1^7;
unsigned char time;   //设置全局变量,专门用于严格延时
/*****************************************************
函数功能:将DS18B20传感器初始化,读取应答信号
出口参数:flag 
原理:主机将数据线DQ拉成低电平保持480us-960us后释放总线,等待15-60us后,在60us-240us内检测总线是否为
 低电平,如果为低电平说明ds18b20器件功能正常
***************************************************/
bit Init_DS18B20(void)	
{
 bit flag;         //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
 DQ = 1;           //先将数据线拉高
 for(time=0;time<2;time++); //略微延时约6微秒
 DQ = 0;           //再将数据线从高拉低,要求保持480~960us
 for(time=0;time<200;time++);  //略微延时约600微秒        //以向DS18B20发出一持续480~960us的低电平复位脉冲 
 DQ = 1;           //释放数据线(将数据线拉高) 
  for(time=0;time<10;time++);  //延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)
 flag=DQ;          //让单片机检测是否输出了存在脉冲(DQ=0表示存在)      
 for(time=0;time<200;time++); //延时足够长时间,等待存在脉冲输出完毕
     
 return (flag);    //返回检测成功标志
}

(2)写单字节函数:

/*****************************************************
函数功能:向DS18B20写入一个字节数据
入口参数:dat
原理:
(1)主机读0时序:主机先把总线拉低大于1us,如果主机发送0,主机会把总线继续拉低至少60us直至写周期结束,然后释放总线为高电平
(2)主机读1时序:主机先把总线拉低大于1us,然后释放总线直至写周期结束
(3)作为从机的ds18b20在检测到总线被拉低后(写周期开始),等待15us,在15-45us内对总线采样
(4)数据传输:多字节低字节在前,高字节在后,一字节的低位在前,高位在后
***************************************************/  
void WriteOneChar(unsigned char dat)
{
	unsigned char i=0;
	for (i=0; i<8; i++)
		 {
		  DQ =1;         // 先将数据线拉高
		  _nop_();	     //等待一个机器周期	 
		  DQ=0;          //将数据线从高拉低时即启动写时序       
		  DQ=dat&0x01;   //利用与运算取出要写的某位二进制数据,
                       //并将其送到数据线上等待DS18B20采样	
		 for(time=0;time<10;time++)	
		     ;//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样
		  DQ=1;          //释放数据线		    
		  for(time=0;time<1;time++)
			  ;//延时3us,两个写时序间至少需要1us的恢复期
		  dat>>=1;       //将dat中的各二进制位数据右移1位
		 }
	  for(time=0;time<4;time++)
	              ; //稍作延时,给硬件一点反应时间
}

(3)读单字节函数:

/*****************************************************
函数功能:从DS18B20读取一个字节数据
出口参数:dat
原理:
(1)主机读0时序:主机先把总线拉低大于1us,然后释放总线,如果ds18b20发送0,ds18b20会把总线拉低15us,然后释放总线为高电平
(2)主机读1时序:主机先把总线拉低大于1us,然后释放总线,如果ds18b20发送1,则在主机释放总线后不拉低总线,也就是为高电平
(3)主机需在读周期开始(主机先把总线拉低大于1us,然后释放总线)后的15us内检测总线电平的高低。
***************************************************/ 
unsigned char ReadOneChar(void)
 {
		unsigned char i=0;	
		unsigned char dat;  //储存读出的一个字节数据
		for (i=0;i<8;i++)
		 {
		   
		   DQ =1;       // 先将数据线拉高
		   _nop_();	    //等待一个机器周期	 
		   DQ = 0;      //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
		   _nop_();     //等待一个机器周期		
		   _nop_();     //等待一个机器周期		   
		   DQ = 1;     //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
		   for(time=0;time<2;time++);      //延时约6us,使主机在15us内采样
			 	dat>>=1;
		   if(DQ==1)
		      dat|=0x80;  //如果读到的数据是1,则将1存入dat
		   for(time=0;time<1;time++);//延时3us,两个读时序之间必须有大于1us的恢复期	
	    }	                    
	 return(dat);    //返回读出的十进制数据
}

(4)DS18B20温度转换函数:

/*****************************************************
函数功能:做好读温度的准备
***************************************************/ 
void ReadyReadTemp(void)
{
      Init_DS18B20();     //将DS18B20初始化
		WriteOneChar(0xCC); // 跳过读序号列号的操作
		WriteOneChar(0x44); // 启动温度转换	  
	   for(time=0;time<100;time++);	 //温度转换需要一点时间
		Init_DS18B20();     //将DS18B20初始化
		WriteOneChar(0xCC); //跳过读序号列号的操作
		WriteOneChar(0xBE); //读取温度寄存器,前两个分别是温度的低位和高位	
}

转化后得到的12位数据,存储在18B20的两个8比特的RAM中,二进制中的前面5位是符号位,如果测得的温度大于0, 这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反加1再乘于0.0625即可得到实际 温度。 例如+125℃的数字输出为07D0H,,-25.0625℃的数字输出为FE6FH。

(5)主机温度转换函数:

 while(1)                //不断检测并显示温度
	 {	
		ReadyReadTemp();     //读温度准备
	  TL=ReadOneChar();    //先读的是温度值低位
		TH=ReadOneChar();    //接着读的是温度值高位
		TN=TH*16+TL/16;      //实际温度值=(TH*256+TL)/16,即:TH*16+TL/16
			                  //这样得出的是温度的整数部分,小数部分被丢弃了
	   TD=(TL%16)*10/16;    //计算温度的小数部分,将余数乘以10再除以16取整,
			                  //这样得到的是温度小数部分的第一位数字(保留1位小数)
	   display_temp1(TN);    //显示温度的整数部分
	   display_temp2(TD);    //显示温度的小数部分
      delaynms(10);               
    }	

2、51单片机完整程序:

#include<reg51.h>    //包含单片机寄存器的头文件
#include<intrins.h>  //包含_nop_()函数定义的头文件
unsigned char code digit[10]={"0123456789"};     //定义字符数组显示数字
unsigned char code Str[]={"Test by DS18B20"};    //说明显示的是温度
unsigned char code Error[]={"Error!Check!"};     //说明没有检测到DS18B20
unsigned char code Temp[]={"Temp:"};             //说明显示的是温度
unsigned char code Cent[]={"Cent"};              //温度单位
/*******************************************************************************
以下是对液晶模块的操作程序
*******************************************************************************/
sbit RS=P2^0;           //寄存器选择位,将RS位定义为P2.0引脚
sbit RW=P2^1;           //读写选择位,将RW位定义为P2.1引脚
sbit E=P2^2;            //使能信号位,将E位定义为P2.2引脚
sbit BF=P0^7;           //忙碌标志位,,将BF位定义为P0.7引脚
/*****************************************************
函数功能:延时1ms
(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
***************************************************/
void delay1ms()
{
   unsigned char i,j;	
	 for(i=0;i<10;i++)
	  for(j=0;j<33;j++)
	   ;		 
 }
/*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
 void delaynms(unsigned char n)
 {
   unsigned char i;
	for(i=0;i<n;i++)
	   delay1ms();
 }
/*****************************************************
函数功能:判断液晶模块的忙碌状态
返回值:result。result=1,忙碌;result=0,不忙
***************************************************/
bit BusyTest(void)
  {
    bit result;
	  RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态
    RW=1;
    E=1;        //E=1,才允许读写
    _nop_();   //空操作
    _nop_();
    _nop_(); 
    _nop_();   //空操作四个机器周期,给硬件反应时间	
    result=BF;  //将忙碌标志电平赋给result
    E=0;         //将E恢复低电平
   return result;
  }
/*****************************************************
函数功能:将模式设置指令或显示地址写入液晶模块
入口参数:dictate
***************************************************/
void WriteInstruction (unsigned char dictate)
{   
    while(BusyTest()==1);   //如果忙就等待
	 RS=0;                  //根据规定,RS和R/W同时为低电平时,可以写入指令
	 RW=0;   
	 E=0;                   //E置低电平(根据表8-6,写指令时,E为高脉冲,
                           // 就是让E从0到1发生正跳变,所以应先置"0"
	 _nop_();
	 _nop_();               //空操作两个机器周期,给硬件反应时间
	 P0=dictate;            //将数据送入P0口,即写入指令或地址
	 _nop_();
	 _nop_();
	 _nop_();
	 _nop_();               //空操作四个机器周期,给硬件反应时间
	 E=1;                   //E置高电平
	 _nop_();
	 _nop_();
	 _nop_();
	 _nop_();               //空操作四个机器周期,给硬件反应时间
	  E=0;                  //当E由高电平跳变成低电平时,液晶模块开始执行命令
 }
/*****************************************************
函数功能:指定字符显示的实际地址
入口参数:x
***************************************************/
 void WriteAddress(unsigned char x)
 {
     WriteInstruction(x|0x80); //显示位置的确定方法规定为"80H+地址码x"
 }
/*****************************************************
函数功能:将数据(字符的标准ASCII码)写入液晶模块
入口参数:y(为字符常量)
***************************************************/
 void WriteData(unsigned char y)
 {
    while(BusyTest()==1);  
	  RS=1;           //RS为高电平,RW为低电平时,可以写入数据
	  RW=0;
	  E=0;            //E置低电平(根据表8-6,写指令时,E为高脉冲,
                     // 就是让E从0到1发生正跳变,所以应先置"0"
	  P0=y;           //将数据送入P0口,即将数据写入液晶模块
	  _nop_();
	  _nop_();
 	  _nop_();
     _nop_();       //空操作四个机器周期,给硬件反应时间
	  E=1;           //E置高电平
	  _nop_();
	  _nop_();
	  _nop_();
	 _nop_();        //空操作四个机器周期,给硬件反应时间
	 E=0;            //当E由高电平跳变成低电平时,液晶模块开始执行命令
 }
/*****************************************************
函数功能:对LCD的显示模式进行初始化设置
1602初始化原理:
延时
写指令38H
延时
写指令38H
延时
写指令38H
(每次写指令、读/写数据操作之前均需检测信号)
写指令38H:显示模式设置
写指令08H:显示关闭
写指令01H:显示清屏
写指令06H:显示光标移动设置
写指令0CH:显示开及光标设置
1.1读状态:输入:RS=L,RW=H,E=H
----输出:D0~D7=状态字
1.2写指令:输入:E=L,RS=L,RW=L,D0~D7=指令码,E=H
----输出:无
1.3读数据:输入:RS=H,RW=H,E=H
----输出:D0~D7=数据
1.4写数据:输入:E=L,RS=H,RW=L,D0~D7=数据,E=H
----输出:无
HD44780内置DDRAM、CGROM和CGRAM。
DDRAM就是显示数据RAM,用来寄存待显示的字符代码。共80个字节,地址和屏幕的对应关系如下:
显示位置	1	2	3	4	5	6	7	… …	40
第一行	00H	01H	02H	03H	04H	05H	06H	… …	27H
第二行	40H	41H	42H	43H	44H	45H	46H	… …	67H

也就是说想要在LCD1602屏幕上的第一行第一个位置显示一个“A”,就要向DDRAM的00H地址写“A”字的代码就OK了,但具体的写入是要按照LCD模块的指令格式来进行的。
但是我们发现每一行有40个地址,而我们们每行只能显示16个字符,
其实际多的位置可以实现字符的移动,我们在看大佬作品的时候可能会见到有的字符是从左面移过来,他的实现形式就用到了着些多的地址。将数据先写到未显示的地址然后使用指令进行左移就可以了。
***************************************************/
void LcdInitiate(void)
{
    delaynms(15);               //延时15ms,首次写指令时应给LCD一段较长的反应时间
    WriteInstruction(0x38);     //显示模式设置:16×2显示,5×7点阵,8位数据接口
	delaynms(5);                //延时5ms ,给硬件一点反应时间
    WriteInstruction(0x38);
	delaynms(5);               //延时5ms ,给硬件一点反应时间
	WriteInstruction(0x38);     //连续三次,确保初始化成功
	delaynms(5);               //延时5ms ,给硬件一点反应时间
	WriteInstruction(0x0c);     //显示模式设置:显示开,无光标,光标不闪烁
	delaynms(5);               //延时5ms ,给硬件一点反应时间
	WriteInstruction(0x06);     //显示模式设置:光标右移,字符不移
	delaynms(5);                //延时5ms ,给硬件一点反应时间
	WriteInstruction(0x01);     //清屏幕指令,将以前的显示内容清除
	delaynms(5);             //延时5ms ,给硬件一点反应时间

 } 
/************************************************************************
以下是DS18B20的操作程序
 ************************************************************************/ 
sbit DQ=P1^7;
unsigned char time;   //设置全局变量,专门用于严格延时
/*****************************************************
函数功能:将DS18B20传感器初始化,读取应答信号
出口参数:flag 
原理:主机将数据线DQ拉成低电平保持480us-960us后释放总线,等待15-60us后,在60us-240us内检测总线是否为
 低电平,如果为低电平说明ds18b20器件功能正常
***************************************************/
bit Init_DS18B20(void)	
{
 bit flag;         //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
 DQ = 1;           //先将数据线拉高
 for(time=0;time<2;time++); //略微延时约6微秒
 DQ = 0;           //再将数据线从高拉低,要求保持480~960us
 for(time=0;time<200;time++);  //略微延时约600微秒        //以向DS18B20发出一持续480~960us的低电平复位脉冲 
 DQ = 1;           //释放数据线(将数据线拉高) 
  for(time=0;time<10;time++);  //延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)
 flag=DQ;          //让单片机检测是否输出了存在脉冲(DQ=0表示存在)      
 for(time=0;time<200;time++); //延时足够长时间,等待存在脉冲输出完毕
     
 return (flag);    //返回检测成功标志
}
/*****************************************************
函数功能:从DS18B20读取一个字节数据
出口参数:dat
原理:
(1)主机读0时序:主机先把总线拉低大于1us,然后释放总线,如果ds18b20发送0,ds18b20会把总线拉低15us,然后释放总线为高电平
(2)主机读1时序:主机先把总线拉低大于1us,然后释放总线,如果ds18b20发送1,则在主机释放总线后不拉低总线,也就是为高电平
(3)主机需在读周期开始(主机先把总线拉低大于1us,然后释放总线)后的15us内检测总线电平的高低。
***************************************************/ 
unsigned char ReadOneChar(void)
 {
		unsigned char i=0;	
		unsigned char dat;  //储存读出的一个字节数据
		for (i=0;i<8;i++)
		 {
		   
		   DQ =1;       // 先将数据线拉高
		   _nop_();	    //等待一个机器周期	 
		   DQ = 0;      //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
		   _nop_();     //等待一个机器周期		
		   _nop_();     //等待一个机器周期		   
		   DQ = 1;     //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
		   for(time=0;time<2;time++);      //延时约6us,使主机在15us内采样
			 	dat>>=1;
		   if(DQ==1)
		      dat|=0x80;  //如果读到的数据是1,则将1存入dat
		   for(time=0;time<1;time++);//延时3us,两个读时序之间必须有大于1us的恢复期	
	    }	                    
	 return(dat);    //返回读出的十进制数据
}
/*****************************************************
函数功能:向DS18B20写入一个字节数据
入口参数:dat
原理:
(1)主机读0时序:主机先把总线拉低大于1us,如果主机发送0,主机会把总线继续拉低至少60us直至写周期结束,然后释放总线为高电平
(2)主机读1时序:主机先把总线拉低大于1us,然后释放总线直至写周期结束
(3)作为从机的ds18b20在检测到总线被拉低后(写周期开始),等待15us,在15-45us内对总线采样
(4)数据传输:多字节低字节在前,高字节在后,一字节的低位在前,高位在后
***************************************************/  
void WriteOneChar(unsigned char dat)
{
	unsigned char i=0;
	for (i=0; i<8; i++)
		 {
		  DQ =1;         // 先将数据线拉高
		  _nop_();	     //等待一个机器周期	 
		  DQ=0;          //将数据线从高拉低时即启动写时序       
		  DQ=dat&0x01;   //利用与运算取出要写的某位二进制数据,
                       //并将其送到数据线上等待DS18B20采样	
		 for(time=0;time<10;time++)	
		     ;//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样
		  DQ=1;          //释放数据线		    
		  for(time=0;time<1;time++)
			  ;//延时3us,两个写时序间至少需要1us的恢复期
		  dat>>=1;       //将dat中的各二进制位数据右移1位
		 }
	  for(time=0;time<4;time++)
	              ; //稍作延时,给硬件一点反应时间
}
/******************************************************************************
以下是与温度有关的显示设置
 ******************************************************************************/
 /*****************************************************
函数功能:显示没有检测到DS18B20
***************************************************/   
void display_error(void)
 {
       unsigned char i;
	 	     WriteAddress(0x00);    //写显示地址,将在第1行第1列开始显示
			   i = 0;                //从第一个字符开始显示
				while(Error[i] != '')  //只要没有写到结束标志,就继续写
				{						
					WriteData(Error[i]);   //将字符常量写入LCD
					i++;                 //指向下一个字符
					delaynms(100);        //延时100ms较长时间,以看清关于显示的说明
				}	
				while(1)              //进入死循环,等待查明原因
				  ;
}
/*****************************************************
函数功能:显示说明信息
***************************************************/   
void display_explain(void)
 {
       unsigned char i;
	 	     WriteAddress(0x00);    //写显示地址,将在第1行第1列开始显示
			   i = 0;                //从第一个字符开始显示
				while(Str[i] != '')  //只要没有写到结束标志,就继续写
				{						
					WriteData(Str[i]);   //将字符常量写入LCD
					i++;                 //指向下一个字符
					delaynms(100);        //延时100ms较长时间,以看清关于显示的说明
				}	
}
/*****************************************************
函数功能:显示温度符号
***************************************************/   
void display_symbol(void)
 {
       unsigned char i;
	 	     WriteAddress(0x40);    //写显示地址,将在第2行第1列开始显示
			   i = 0;                //从第一个字符开始显示
				while(Temp[i] != '')  //只要没有写到结束标志,就继续写
				{						
					WriteData(Temp[i]);   //将字符常量写入LCD
					i++;                 //指向下一个字符
					delaynms(50);        //延时1ms给硬件一点反应时间
				}	
}

/*****************************************************
函数功能:显示温度的小数点
***************************************************/   
void 	display_dot(void)
{         
	 WriteAddress(0x49);	  //写显示地址,将在第2行第10列开始显示		   
	 WriteData('.');      //将小数点的字符常量写入LCD
	 delaynms(50);         //延时1ms给硬件一点反应时间		
}
/*****************************************************
函数功能:显示温度的单位(Cent)
***************************************************/   
void 	display_cent(void)
{
           unsigned char i;    
	 	     WriteAddress(0x4c);        //写显示地址,将在第2行第13列开始显示	
			   i = 0;                    //从第一个字符开始显示 
				while(Cent[i] != '')     //只要没有写到结束标志,就继续写
				{					
					WriteData(Cent[i]);     //将字符常量写入LCD
					i++;                 //指向下一个字符
					delaynms(50);        //延时1ms给硬件一点反应时间
				}	
}
/*****************************************************
函数功能:显示温度的整数部分
入口参数:x
***************************************************/ 
void display_temp1(unsigned char x)
{
 unsigned char j,k,l;     //j,k,l分别储存温度的百位、十位和个位
	j=x/100;              //取百位
	k=(x%100)/10;    //取十位
	l=x%10;             //取个位  
	WriteAddress(0x46);    //写显示地址,将在第2行第7列开始显示
	WriteData(digit[j]);    //将百位数字的字符常量写入LCD
	WriteData(digit[k]);    //将十位数字的字符常量写入LCD
	WriteData(digit[l]);    //将个位数字的字符常量写入LCD
	delaynms(50);         //延时1ms给硬件一点反应时间     
 }
 /*****************************************************
函数功能:显示温度的小数数部分
入口参数:x
***************************************************/ 
 void display_temp2(unsigned char x)
{
 	WriteAddress(0x4a);      //写显示地址,将在第2行第11列开始显示
	WriteData(digit[x]);     //将小数部分的第一位数字字符常量写入LCD
	delaynms(50);          //延时1ms给硬件一点反应时间
}
/*****************************************************
函数功能:做好读温度的准备
***************************************************/ 
void ReadyReadTemp(void)
{
      Init_DS18B20();     //将DS18B20初始化
		WriteOneChar(0xCC); // 跳过读序号列号的操作
		WriteOneChar(0x44); // 启动温度转换	  
	   for(time=0;time<100;time++);	 //温度转换需要一点时间
		Init_DS18B20();     //将DS18B20初始化
		WriteOneChar(0xCC); //跳过读序号列号的操作
		WriteOneChar(0xBE); //读取温度寄存器,前两个分别是温度的低位和高位	
}


/*****************************************************
函数功能:主函数
***************************************************/ 

 void main(void)
 {	  
	  unsigned char TL;     //储存暂存器的温度低位
     unsigned char TH;    //储存暂存器的温度高位
     unsigned char TN;      //储存温度的整数部分
	  unsigned char TD;       //储存温度的小数部分
	  LcdInitiate();         //将液晶初始化
	   delaynms(5);        //延时5ms给硬件一点反应时间	
		if(Init_DS18B20()==1)
	    display_error();
		  display_explain();
   	  display_symbol();    //显示温度说明
      display_dot();       //显示温度的小数点
      display_cent();      //显示温度的单位
   while(1)                //不断检测并显示温度
	 {	
		ReadyReadTemp();     //读温度准备
	  TL=ReadOneChar();    //先读的是温度值低位
		TH=ReadOneChar();    //接着读的是温度值高位
		TN=TH*16+TL/16;      //实际温度值=(TH*256+TL)/16,即:TH*16+TL/16
			                  //这样得出的是温度的整数部分,小数部分被丢弃了
	   TD=(TL%16)*10/16;    //计算温度的小数部分,将余数乘以10再除以16取整,
			                  //这样得到的是温度小数部分的第一位数字(保留1位小数)
	   display_temp1(TN);    //显示温度的整数部分
	   display_temp2(TD);    //显示温度的小数部分
      delaynms(10);               
    }	
			  
}

3、proteus仿真图:
在这里插入图片描述

4、stm32F1原理与程序:
(1)复位脉冲:
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少480 us,,以产生复位脉冲。接着主机释放总线,4.7K的上拉电阻将单总线拉高,延时15~60 us,并进入接收模式(Rx)。接着DS18B20拉低总线60~240 us,以产生低电平应答脉冲。

//复位DS18B20
void DS18B20_Rst(void)	   
{                 
    DS18B20_IO_OUT(); //设置为输出模式
    DS18B20_DQ_OUT=0; //拉低DQ
    delay_us(750);    //拉低750us(至少480us)
    DS18B20_DQ_OUT=1; //DQ=1拉高释放总线 
    delay_us(15);     //15US
    //进入接受模式,等待应答信号。
}

(2)应答信号:

//等待DS18B20的回应
//返回1:未检测到DS18B20的存在    返回0:存在
u8 DS18B20_Check(void) 	   
{   
    u8 retry=0;
    DS18B20_IO_IN();//SET PA0 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;
}

(3)写单字节函数:
写时序:
写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线。
写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。
写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。

//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     
 {             
    u8 j;  u8 testb;
    DS18B20_IO_OUT();//设置PA0为输出
    for (j=1;j<=8;j++) 
   {
        testb=dat&0x01;
        dat=dat>>1;
        if (testb) //输出高
        {
            DS18B20_DQ_OUT=0;// 主机输出低电平
            delay_us(2);                  //延时2us
            DS18B20_DQ_OUT=1;//释放总线
            delay_us(60); //延时60us            
        }
        else //输出低
        {
            DS18B20_DQ_OUT=0;//主机输出低电平
            delay_us(60);               //延时60us
            DS18B20_DQ_OUT=1;//释放总线
            delay_us(2);                  //延时2us        
        }
    }
}

(4)读一个位函数:
读时序:
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。
所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。
典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。
典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。

//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 			 // read one bit
{
    u8 data;
    DS18B20_IO_OUT();//设置为输出
    DS18B20_DQ_OUT=0; //输出低电平2us
    delay_us(2);
    DS18B20_DQ_OUT=1; //拉高释放总线
   DS18B20_IO_IN();//设置为输入
   delay_us(12);//延时12us
   if(DS18B20_DQ_IN)data=1;//读取总线数据
    else data=0;	 
    delay_us(50);  //延时50us         
    return data;
}

(5)读单字节函数:

//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)    // read one byte
{        
    u8 i,j,dat;
    dat=0;
   for (i=1;i<=8;i++) 
   {
        j=DS18B20_Read_Bit();
        dat=(j<<7)|(dat>>1);
    }						    
    return dat;
}

(6)开始温度转换:

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

在这里插入图片描述
转化后得到的12位数据,存储在18B20的两个8比特的RAM中,二进制中的前面5位是符号位,如果测得的温度大于0, 这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反加1再乘于0.0625即可得到实际 温度。 例如+125℃的数字输出为07D0H,,-25.0625℃的数字输出为FE6FH。

//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
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(); 
    TH=DS18B20_Read_Byte();  	  
    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;    
} 

4、stm32F1完整程序:
(1)ds18b20.c

#include "ds18b20.h"
#include "delay.h"
//复位 DS18B20
void DS18B20_Rst(void) 
{ 
DS18B20_IO_OUT(); //SET PA0 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 PA0 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) // read one bit
{
 u8 data;
DS18B20_IO_OUT();//SET PA0 OUTPUT
 DS18B20_DQ_OUT=0; 
delay_us(2);
 DS18B20_DQ_OUT=1; 
DS18B20_IO_IN();//SET PA0 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data=1;
 else data=0;
 delay_us(50); 
 return data;
}
//从 DS18B20 读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void) // read one byte
{ 
 u8 i,j,dat;
 dat=0;
for (i=1;i<=8;i++) 
{
 j=DS18B20_Read_Bit();
 dat=(j<<7)|(dat>>1);
 } 
 return dat;
}
//写一个字节到 DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat) 
{ 
 u8 j;
 u8 testb;
DS18B20_IO_OUT();//SET PA0 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)// ds1820 start convert
{ 
 DS18B20_Rst(); 
DS18B20_Check();
 DS18B20_Write_Byte(0xcc);// skip rom
 DS18B20_Write_Byte(0x44);// convert
} 
//初始化 DS18B20 的 IO 口 DQ 同时检测 DS 的存在
//返回 1:不存在
//返回 0:存在 
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG, ENABLE); //使能 PG 口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //PORTG.11 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOG, &GPIO_InitStructure); //初始化 GPIO
GPIO_SetBits(GPIOG,GPIO_Pin_11); //输出 1
DS18B20_Rst();
return DS18B20_Check();
} //从 ds18b20 得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
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 
 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; 
}

(2)main.c

int main(void)
{
u8 t=0; 
short temperature; 
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组 2
uart_init(115200); //串口初始化为 115200
LED_Init(); //初始化与 LED 连接的硬件接口
LCD_Init(); //初始化 LCD 
POINT_COLOR=RED; //设置字体为红色
LCD_ShowString(30,50,200,16,16,"ELITE STM32");
LCD_ShowString(30,70,200,16,16,"DS18B20 TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2015/1/16"); 
while(DS18B20_Init()) //DS18B20 初始化
{
LCD_ShowString(30,130,200,16,16,"DS18B20 Error");
delay_ms(200);
LCD_Fill(30,130,239,130+16,WHITE);
delay_ms(200);
} 
LCD_ShowString(30,130,200,16,16,"DS18B20 OK");
POINT_COLOR=BLUE;//设置字体为蓝色
LCD_ShowString(30,150,200,16,16,"Temp: . C");
while(1)
{ 
if(t%10==0) //每 100ms 读取一次
{ 
temperature=DS18B20_Get_Temp();
if(temperature<0)
{
LCD_ShowChar(30+40,150,'-',16,0); //显示负号
temperature=-temperature; //转为正数
}else LCD_ShowChar(30+40,150,' ',16,0); //去掉负号
LCD_ShowNum(30+40+8,150,temperature/10,2,16); //显示正数部分 
 LCD_ShowNum(30+40+32,150,temperature%10,1,16); //显示小数部分
} 
delay_ms(10);
t++;
if(t==20)
{
t=0;
LED0=!LED0;
} } }

最后

以上就是苗条刺猬为你收集整理的嵌入式单片机基础篇(四十二)之单总线协议-DS18B20原理与stm32F1以及51单片机程序单总线协议-DS18B20原理与stm32F1以及51单片机程序的全部内容,希望文章能够帮你解决嵌入式单片机基础篇(四十二)之单总线协议-DS18B20原理与stm32F1以及51单片机程序单总线协议-DS18B20原理与stm32F1以及51单片机程序所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部