STM32-单总线类传感器-ds18b20温度传感器

DS18B20温度传感器工作原理

DS18B20技术性能特征

①、独特的单总线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯。大大提高了系统的抗干扰性。

② 、测温范围 -55℃~+125℃,精度为±0.5℃。

③、支持多点组网功能,多个DS18B20可以并联在唯一的三线上,最多只能并联8个,实现多点测温,如果数量过多,会使供电电源电压过低,从而造成信号传输的不稳定。

④、工作电源: 3.0~5.5V/DC(可以数据线寄生电源)。

⑤ 、在使用中不需要任何外围元件。

⑥、测量结果以9~12位数字量方式串行传送。

DS18B20硬件连接

DS18B20通信类型

单总线是一种半双工通信方式

DS18B20共有6种信号类型:复位脉冲、应答脉冲、写0、写1、读0和读1。

所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。

复位脉冲

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

写时序

写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线。

写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。

写1时序:主机输出低电平,延时2us,然后释放总线,延时60us。

读时序

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态。

典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us。

温度读取过程

DS18B20的典型温度读取过程为:

①复位

②发SKIPROM命令(0XCC)

③发开始转换命令(0X44)

④复位

⑤发送SKIPROM命令(0XCC)

⑥发读存储器命令(0XBE)

⑦连续读出两个字节数据(即温度)

⑧结束。

温度数据的存储

转化后得到的11位数据,存储在18B20的两个8比特的RAM中, MSB的前面5位是符号位,如果测得的温度大于0,这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反再乘于0.0625即可得到实际温度。

例如+125℃的数字输出为07D0H,,-25.0625℃的数字输出为FE6FH

实例分析

测量温度,并串口打印

cubemx配置

只需要将连接传感器的信号线的管脚配置为输出模式即可

代码实操

ds18b20.h

#ifndef __DS18B20_H
#define __DS18B20_H

#include "stm32f4xx_hal.h"


//IO方向设置
#define DS18B20_IO_IN()  {GPIOG->MODER&=~(3<<(6*2));GPIOG->MODER|=0<<(6*2);}	//PG6输入模式
#define DS18B20_IO_OUT() {GPIOG->MODER&=~(3<<(6*2));GPIOG->MODER|=1<<(6*2);} 	//PG6输出模式
 
IO操作函数											   
#define	DS18B20_OUT_LOW   HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6,  GPIO_PIN_RESET)   //数据端口	PG6
#define	DS18B20_OUT_HIGH  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6,  GPIO_PIN_SET)   //数据端口	PG6
#define	DS18B20_DQ_IN     HAL_GPIO_ReadPin(GPIOG,  GPIO_PIN_6)  //数据端口	PG6 



uint8_t DS18B20_Init(void);					//初始化DS18B20
short DS18B20_Get_Temp(void);				//获取温度
void DS18B20_Start(void);						//开始温度转换
void DS18B20_Write_Byte(uint8_t dat);//写入一个字节
uint8_t DS18B20_Read_Byte(void);		//读出一个字节
uint8_t DS18B20_Read_Bit(void);			//读出一个位
uint8_t DS18B20_Check(void);				//检测是否存在DS18B20
void DS18B20_Reset(void);							//复位DS18B20 
#endif

ds18b20.c

#include "ds18b20.h"
uint32_t usctick = 0;
uint32_t time_delay = 0;
extern TIM_HandleTypeDef htim6;

//延时nus
//nus为要延时的us数.	
//nus:0~190887435(最大值即2^32/fac_us@fac_us=168)
static uint8_t fac_us = 168;    //这里主时钟为168M, 所以在1us内ticks会减168次
 
void delay_us(uint32_t nus)
{		
	uint32_t ticks;
	uint32_t told,tnow,tcnt=0;
	uint32_t reload=SysTick->LOAD;				//LOAD的值	    	 
	ticks=nus*fac_us; 						         //1us需要的节拍数 
	told=SysTick->VAL;        				//刚进入时的计数器值
	while(1)
	{
		tnow=SysTick->VAL;	
		if(tnow!=told)
		{	    
			if(tnow<told)tcnt+=told-tnow;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else tcnt+=reload-tnow+told;	    
			told=tnow;
			
			if(tcnt>=ticks)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	};
}

//复位DS18B20
void DS18B20_Reset(void)	   
{                 
	DS18B20_IO_OUT();   //设置为输出
	DS18B20_OUT_LOW ; 	  //拉低DQ
	delay_us(650);      //拉低650us
	DS18B20_OUT_HIGH ; 	//拉高DQ 
	delay_us(20);       //20US
}

//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
uint8_t DS18B20_Check(void) 	   
{   
	uint8_t retry=0;
	DS18B20_IO_IN();    //设置为输入
	
	 //等待DS18B20拉低总线回应,如果超过200us未拉低,则认为未回应
  while ((DS18B20_DQ_IN == 1) && (retry<200))   
	{
		retry++;
		delay_us(1);
	};	 
	if(retry>=200)return 1;        //DS18B20超时未拉低总线
	else retry=0;                  //DS18B20拉低总线
	 
  while ( (DS18B20_DQ_IN == 0 ) && ( retry < 240) )    //测试拉低总线的时间是否在240us内
	{
		retry++;
		delay_us(1);
	};
	if(retry>=240)return 1;	      //超过240us错误   
	
	return 0;                     //正确回应
}

//从DS18B20读取一个位
//返回值:1/0
uint8_t DS18B20_Read_Bit(void) 
{
	uint8_t data;
	DS18B20_IO_OUT();   //设置为输出
	DS18B20_OUT_LOW ; 	  //拉低DQ
	delay_us(3);
	DS18B20_OUT_HIGH ; 	//拉高DQ 
	DS18B20_IO_IN();    //设置为输入
	delay_us(12);
	
	if(DS18B20_DQ_IN)  data=1;
	else data=0;
	
	delay_us(50);  
	
	return data;
}

//从DS18B20读取一个字节
//返回值:读到的数据,先读数据的低位
uint8_t DS18B20_Read_Byte(void)   
{        
	uint8_t i,j,dat;
	dat=0;
	for (i=0;i<8;i++) 
	{
        j=DS18B20_Read_Bit();
        dat=(j<<i)|dat;
    }						    
	return dat;
}

//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(uint8_t dat)     
 {             
    uint8_t j;
    uint8_t testb;
    DS18B20_IO_OUT();     //设置为输出
    for (j=1;j<=8;j++) 
	{
        testb=dat&0x01;
        dat=dat>>1;
        if(testb)       // 写1
        {
            DS18B20_OUT_LOW ; 	  //拉低DQ
            delay_us(2);                            
            DS18B20_OUT_HIGH ; 	  //拉高DQ 
            delay_us(60);             
        }
        else            //写0
        {
            DS18B20_OUT_LOW ; 	  //拉低DQ
            delay_us(60);             
            DS18B20_OUT_HIGH ; 	  //拉高DQ 
            delay_us(2);                          
        }
    }
}
 

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

//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
uint8_t DS18B20_Init(void)
{
	DS18B20_Reset();
	return DS18B20_Check();
}

//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{
    uint8_t temp;
    uint8_t TL,TH;
    short tem;
	
    DS18B20_Start ();           //开始转换读取
	    
    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=(double)tem*0.625;//转换  获得不带符号位的11位温度值   
		
	if(temp)return tem; //返回温度值
	else return -tem;    
}

main函数

	printf("this is DS18B20 test
");
	
	
	if(!DS18B20_Init())
	{
		printf(" DS18B20  is here
");

	}else
	{
		printf(" DS18B20  is  not here
");
	}
	
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

		  temperature = DS18B20_Get_Temp();	
		
			if(temperature<0)
			{
				printf("-");											//显示负号
				temperature=-temperature;					//转为正数
			}
			printf("temperature = %d.%d
",temperature/10,temperature%10);		   
	
		
		HAL_Delay(1000);
}