DS18B20传感器+STM32f103+Keil【原理时序+源码+中文手册+视频】

特此鸣谢:b站的up主 江协科技   有一些图是网上的,侵权删

 该篇文章是看了b站博主总结的,该视频把原理还有代码细节讲的非常非常详细。如果有时间,推荐看视频。

[13-1] DS18B20温度传感器_哔哩哔哩_bilibili

【免费】中英文手册和源码下载(好处之一:该中文手册连流程图的英文都翻译了)

https://download.youkuaiyun.com/download/qq_62975093/90165423

一、DS18B20温度传感器介绍

DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点

图片第一张是防水的可以测水温的,第二种不防水。但原理都一样

8c95515971a14b62ab15a34977456388.png

特点:

测温范围为-55℃到+125℃,在-10℃到+85℃范围内误差为±0.4°

通信接口:1-Wire(单总线),即使用单线进行数据的发送和接收

返回16位二进制温度数值,温度计分辨率可设置为9~12位(默认12位)

可形成总线结构(将任意多的DS18b20挂载到一根总线上,通过ROM搜索读取相应DS18B20的温度值)

独立供电或可寄生供电(不需要再外部供电下即可工作,只接DQ和GND。当总线高电平时能量由单线上拉电阻经过DQ引脚获得。高电平同时充电一个内部电容,当总线低电平时由此电容供应能量。这种供电方法被称为“寄生电源”。)

二、原理图与接线

0e83d73f32a1419b9943c5ea6fc4ac6b.png

 

DQ引脚单总线上必须有一个上拉电阻(R1)以实现单总线闲置时,其处于高电平状态,也可配置为开漏输出模式

原因:

【常用传感器】DS18B20温度传感器原理详解及例程代码-优快云博客

本项目使用的是外部电源供电

216b59b2b6f64c96820f69ce604b3192.png

860d235d16894abf9c60fd0d2b256c57.png

若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路(像读EEPROM等耗电操作仅仅靠弱上拉是不行的)

给它一个强上拉就相当于给它一个非常高的电源正极,以达到内部供电的稳定。

三、内部框图

c83a3c6c5a184375a7876ad7d199352c.png

Temperature Sensor温度测量传感器。它相当于内部的一个模拟温度传感器,它自动把这些温度转换。当我们发出指令让它开始温度转换的时候,这个温度传感器就工作,然后把它的数据放在我们的暂存器RAM里面,然后再进行数据交互,就可以把温度值拿出来了。

1、如果是寄生供电

4db42a353d4047bb8846d1f50783ba2d.png

那个电源就是从DQ进来的,向内部的控制器供电。

当总线高电平时能量由单线上拉电阻经过DQ引脚获得。高电平同时充电一个内部电容,当总线低电平时由此电容cpp供应能量给内部。

7936cf40d07946759397949b79466637.png

这次我们的程序将不会用到寄生供电,因为寄生供电还需要一个强上拉,而我们的电路板没有强上拉的电路。我们直接使用这个外部VDD供电,就不用考虑寄生供电的部分了

2、暂存器

高速暂存器由9个字节组成

    字节0~1 是温度存储器,用来存储转换好的温度。第0个字节存储温度低8位,第一个字节存储温度高8位。有一个默认值,如果上电直接读的话,默认就是85摄氏度
    字节2~3 是用户用来设置最高报警和最低报警值(TH和TL)。
    字节4 是配置寄存器,用来配置转换精度,可以设置为9~12 位。默认12位。
    字节5~7 保留位。芯片内部使用
    字节8  8位CRC生成器(CRC,循环冗余码,一种校验码)

44493f581c0444c7b505727b20a1f00b.png

c5e4e0700976424896d3385c49fee07a.png
 

暂存器的字节2~字节4和EEPROM上的字节一样,为什么呢?往暂存器里写东西的时候,我们要发送一条指令把暂存器里的东西复制到EEPROM里面。当然我们也可以发送指令将EEPROM里面的东西回调到暂存器里面。即不能直接读写EEPROM,需要先将数据写入暂存器,然后通过通信,将数据在写入EEPROM,读出数据同理。

掉电后,EEPROM数据还在,暂存器数据不保存。

(1)温度数据寄存器LSB/MSB格式

当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。

7ca3022ea40f48d1b8f9841d961a518d.png

高字节的五个S为符号位,温度为正值时S=1,温度为负值时S=0

①当五个符号位S=0时,温度为正值,直接将后面的11位二进制转换为十进制,再乘以分辨率对应的最小刻度,就可以得到温度值;

②当五个符号位S=1时,温度为负值,先全部取反后加1,再计算十进制值。再乘以分辨率对应的最小刻度,在乘负数,就可以得到温度值;

分辨率最小刻度
9位0.5℃
10位0.25℃
11位0.125℃
12位0.0625℃
默认12位分辨率(2^-4=0.0625,4位小数位)

e56cc3a5a2294e9183eb2c2efe0c259f.png

3、指令

只需关注三个指令

①CC:忽略ROM指令。因为只连接了一个DS18B20传感器,不需要ROM搜索

②44:温度转换

③BE:读取温度

a3c13582db964374955f78eed2a00878.png

此图非原创

四、时序

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

1、初始化

复位
1.总线上的主设备通过拉低 1-Wire 总线超过 480us 来发送(TX)复位脉冲。
2.主设备释放总线而进入接收模式(RX)。当总线释放后,5kΩ左右的上拉电阻将 1-Wire 总线
拉至高电平。
 
响应:
3.当 DS18B20 检测到该上升边沿信号后,其等待 15us 至 60us 后
4.通过将 1-Wire 总线拉低 60us 至 240us 来实现发送一个存在脉冲。
5.主机在拉低 60us 至 240us期间 读取总线电平,判断是否存在传感器

3bbdf3c89228472091be189a5a220539.png

​

/**************************************************************************************
 * 描  述 : 主机给从机发送复位脉冲
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Rst(void)
{
	DS18B20_Output_Input(DS18B20_OUTPUT);     //主机设置为推挽输出 
	
	DS18B20_DATA_OUT(LOW);     //主机至少产生480us的低电平复位信号
	delay_us(480);
	DS18B20_DATA_OUT(HIGH);   //主机在产生复位信号后,需将总线拉高
	delay_us(15);   //从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
}

 /**************************************************************************************
 * 描  述 : 检测从机给主机返回的存在脉冲
 * 入  参 : 无
 * 返回值 : 0:成功   1:失败  
 **************************************************************************************/
static u8 DS18B20_Presence(void)
{
	u8 pulse_time = 0;
	
	DS18B20_Output_Input(DS18B20_INPUT);    //主机设置为上拉输入
	
	/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号 
	 * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,若存在会在15~60us后给主机发一个存在脉冲
	 */
	while( DS18B20_DATA_IN() && pulse_time<100 )
	{
		pulse_time++;
		delay_us(1);
	}	

	if( pulse_time >=100 )  //经过100us后,存在脉冲都还没有到来
		return 1;             //读取失败
	else                 //经过100us后,存在脉冲到来
		pulse_time = 0;    //清零计时变量
	
	while( !DS18B20_DATA_IN() && pulse_time<240 )  // 存在脉冲到来,且存在的时间不能超过240us
	{
		pulse_time++;
		delay_us(1);
	}	
	if( pulse_time >=240 ) // 存在脉冲到来,且存在的时间超过了240us
		return 1;        //读取失败
	else
		return 0;
}

[点击并拖拽以移动]
​

2、写

总线控制器通过控制单总线高低电平持续时间从而把逻辑1或0写DS18B20中。每次只传输1位数据

①单片机想要给DS18B20写入一个0时,需要将单片机引脚拉低,保持低电平时间要在60~120us之间,然后释放总线
②单片机想要给DS18B20写入一个1时,需要将单片机引脚拉低,拉低时间需要大于1us,然后在15us内拉高总线.

DS18B20 将会在 15us 至 60us 的时间窗口内对总线进行采样。如果总线是高电平,则逻辑 1 被写入 DS18B20;若总线是低电平,则逻辑 0 被写入 DS18B20。
 

注意:

先写命令的低八位字节,在写高八位字节

每个写时段最小必须有 60us 的持续时间且 独立的写时段间至少有 1us 的恢复时间。

6028b47131814654bd7cfeee54445712.png

/**************************************************************************************
 * 描  述 : 写一个字节到DS18B20,低位先行
 * 入  参 : u8
 * 返回值 : 无  
 **************************************************************************************/
void DS18B20_Write_Byte(u8 dat)
{
	u8 i, testb;
	DS18B20_Output_Input(DS18B20_OUTPUT);
	
	for( i=0; i<8; i++ )
	{
		testb = dat&0x01;
		dat = dat>>1;		
		/* 写0和写1的时间至少要大于60us */
		if (testb)
		{			
			DS18B20_DATA_OUT(LOW);
			delay_us(8);   //1us < 这个延时 < 15us
			
			DS18B20_DATA_OUT(HIGH);
			delay_us(58);    //58us+8us>60us
		}		
		else
		{			
			DS18B20_DATA_OUT(LOW);  
			/* 60us < Tx 0 < 120us */
			delay_us(70);
			
			DS18B20_DATA_OUT(HIGH);			
			/* 1us < Trec(恢复时间) < 无穷大*/
			delay_us(2);
		}
	}
}

3、读

①主机将总线拉低1~15us,然后释放总线

②在总线拉低后 15us内,主机读取总线电平(尽量贴近15us的末尾,因为传感器电平稳定和传输需要时间),读取为低电平则为接收0,读取为高电平则为接收1

注意:

主机只有在发送读暂存器命令(0xBE)或读电源类型命令(0xB4)后,立即生成读时隙指令,DS18B20才能向主机传送数据。 也就是先发读取指令,再发送读时隙

读时序时是先读低字节,在读高字节

所有读时隙必须至少需要60us,且在两次独立的时隙之间至少需要1ps的恢复时间

449466be8ada4499a6e59d5932ef3df9.png

/**************************************************************************************
 * 描  述 : 从DS18B20读取一个bit
 * 入  参 : 无
 * 返回值 : u8 
 **************************************************************************************/
static u8 DS18B20_Read_Bit(void)
{
	u8 dat;
	
	/* 读0和读1的时间至少要大于60us */	
	DS18B20_Output_Input(DS18B20_OUTPUT);
	/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
	DS18B20_DATA_OUT(LOW);
	delay_us(10);
	
	/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
	DS18B20_Output_Input(DS18B20_INPUT);
	
	if( DS18B20_DATA_IN() == SET )
		dat = 1;
	else
		dat = 0;
	
	/* 这个延时参数请参考时序图 */
	delay_us(45);/*DS18B20中输出的数据在初始化读时序后仅有15us的有效时间
								每个读时段最小必须有60us的持续时间			*/
	
	return dat;
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读一个字节,低位先行
 * 入  参 : 无
 * 返回值 : u8  
 **************************************************************************************/
u8 DS18B20_Read_Byte(void)
{
	u8 i, j, dat = 0;	
	
	for(i=0; i<8; i++) 
	{
		j = DS18B20_Read_Bit();		//从DS18B20读取一个bit
		dat = (dat) | (j<<i);
	}
	
	return dat;																																																																																
}

五、代码

c7325b53ac1c44a2b2e2fa893f1b14dd.png

此图非原创

#include "ds18b20.h"
#include "stm32f10x.h"

 /**************************************************************************************
 * 描  述 : 配置DS18B20用到的I/O口   PB12
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_GPIO_Config(void)
{		
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(DS18B20_CLK, ENABLE); 
															   
  GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;	
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
  GPIO_Init(DS18B20_PORT, &GPIO_InitStructure);
	
	GPIO_SetBits(DS18B20_PORT, DS18B20_PIN);	 //DS18B20数据引脚初始化配置为高电平输出
}


 /**************************************************************************************
 * 描  述 : 配置DS18B20-DATA引脚模式
 * 入  参 : cmd控制DS18B20-DATA引脚,1为输出模式,0为输入模式
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Output_Input(DS18B20_Mode cmd){

	GPIO_InitTypeDef  GPIO_InitStructure;
	
	if(cmd==DS18B20_OUTPUT){  //cmd为1——输出模式
	
		GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
	  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		
	}else{//cmd为0——输入模式
	
		GPIO_InitStructure.GPIO_Pin = DS18B20_PIN;
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	
	}
	  GPIO_Init(DS18B20_PORT,&GPIO_InitStructure);
}



 /**************************************************************************************
 * 描  述 : 主机给从机发送复位脉冲
 * 入  参 : 无
 * 返回值 : 无
 **************************************************************************************/
static void DS18B20_Rst(void)
{
	DS18B20_Output_Input(DS18B20_OUTPUT);     //主机设置为推挽输出 
	
	DS18B20_DATA_OUT(LOW);     //主机至少产生480us的低电平复位信号
	delay_us(480);
	DS18B20_DATA_OUT(HIGH);   //主机在产生复位信号后,需将总线拉高
	delay_us(15);   //从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
}

 /**************************************************************************************
 * 描  述 : 检测从机给主机返回的存在脉冲
 * 入  参 : 无
 * 返回值 : 0:成功   1:失败  
 **************************************************************************************/
static u8 DS18B20_Presence(void)
{
	u8 pulse_time = 0;
	
	DS18B20_Output_Input(DS18B20_INPUT);    //主机设置为上拉输入
	
	/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号 
	 * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,若存在会在15~60us后给主机发一个存在脉冲
	 */
	while( DS18B20_DATA_IN() && pulse_time<100 )
	{
		pulse_time++;
		delay_us(1);
	}	

	if( pulse_time >=100 )  //经过100us后,存在脉冲都还没有到来
		return 1;             //读取失败
	else                 //经过100us后,存在脉冲到来
		pulse_time = 0;    //清零计时变量
	
	while( !DS18B20_DATA_IN() && pulse_time<240 )  // 存在脉冲到来,且存在的时间不能超过240us
	{
		pulse_time++;
		delay_us(1);
	}	
	if( pulse_time >=240 ) // 存在脉冲到来,且存在的时间超过了240us
		return 1;        //读取失败
	else
		return 0;
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读取一个bit
 * 入  参 : 无
 * 返回值 : u8 
 **************************************************************************************/
static u8 DS18B20_Read_Bit(void)
{
	u8 dat;
	
	/* 读0和读1的时间至少要大于60us */	
	DS18B20_Output_Input(DS18B20_OUTPUT);
	/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
	DS18B20_DATA_OUT(LOW);
	delay_us(10);
	
	/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
	DS18B20_Output_Input(DS18B20_INPUT);
	
	if( DS18B20_DATA_IN() == SET )
		dat = 1;
	else
		dat = 0;
	
	/* 这个延时参数请参考时序图 */
	delay_us(45);/*DS18B20中输出的数据在初始化读时序后仅有15us的有效时间
								每个读时段最小必须有60us的持续时间			*/
	
	return dat;
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读一个字节,低位先行
 * 入  参 : 无
 * 返回值 : u8  
 **************************************************************************************/
u8 DS18B20_Read_Byte(void)
{
	u8 i, j, dat = 0;	
	
	for(i=0; i<8; i++) 
	{
		j = DS18B20_Read_Bit();		//从DS18B20读取一个bit
		dat = (dat) | (j<<i);
	}
	
	return dat;																																																																																
}

 /**************************************************************************************
 * 描  述 : 写一个字节到DS18B20,低位先行
 * 入  参 : u8
 * 返回值 : 无  
 **************************************************************************************/
void DS18B20_Write_Byte(u8 dat)
{
	u8 i, testb;
	DS18B20_Output_Input(DS18B20_OUTPUT);
	
	for( i=0; i<8; i++ )
	{
		testb = dat&0x01;
		dat = dat>>1;		
		/* 写0和写1的时间至少要大于60us */
		if (testb)
		{			
			DS18B20_DATA_OUT(LOW);
			delay_us(8);   //1us < 这个延时 < 15us
			
			DS18B20_DATA_OUT(HIGH);
			delay_us(58);    //58us+8us>60us
		}		
		else
		{			
			DS18B20_DATA_OUT(LOW);  
			/* 60us < Tx 0 < 120us */
			delay_us(70);
			
			DS18B20_DATA_OUT(HIGH);			
			/* 1us < Trec(恢复时间) < 无穷大*/
			delay_us(2);
		}
	}
}


 /**************************************************************************************
 * 描  述 : DS18B20初始化函数
 * 入  参 : 无
 * 返回值 : u8
 **************************************************************************************/
u8 DS18B20_Init(void)
{
	DS18B20_GPIO_Config();   
	DS18B20_Rst();
	
	return DS18B20_Presence();
}

 /**************************************************************************************
 * 描  述 : 从DS18B20读取温度值
 * 入  参 : 无  
 * 返回值 : float 
 **************************************************************************************/
float DS18B20_Get_Temp(void)
{
	u8 tpm_MSB, tpm_LSB;
	short temp;
	float temperature;
	
	DS18B20_Rst();	   							//主机给从机发送复位脉冲
	DS18B20_Presence();	 						//检测从机给主机返回的存在脉冲
	DS18B20_Write_Byte(0XCC);				/* 跳过 ROM */
	DS18B20_Write_Byte(0X44);				/* 开始转换 */
	
	DS18B20_Rst();
  DS18B20_Presence();
	DS18B20_Write_Byte(0XCC);				/* 跳过 ROM */
  DS18B20_Write_Byte(0XBE);				/* 读温度值 */
	
	tpm_LSB = DS18B20_Read_Byte();		 
	tpm_MSB = DS18B20_Read_Byte(); 
	
	temp = tpm_MSB<<8;
	temp = temp | tpm_LSB;
	
	if( (temp & 0xF800) == 0 )	/* 判断前五位的符号位是否都为0,为0温度为正 */
		temperature = (temp * 0.625)+0.5;	  //扩大十倍,目的将小数点后第一位也转换为可显示数字
																				//加减0.5目的是四舍五入操作。
	else
		temperature = -((~temp+1) * 0.625)-0.5;	//取反加一变原码,乘以负号,是因为温度为负数
	  
	return temperature; 	
}


/*************************************END OF FILE******************************/

DS18B20.h

#ifndef __DS18B20_H
#define	__DS18B20_H

#include "stm32f10x.h"
#include "delay.h"

#define HIGH  1
#define LOW   0

#define DS18B20_CLK     RCC_APB2Periph_GPIOB
#define DS18B20_PIN     GPIO_Pin_12                  
#define DS18B20_PORT		GPIOB 

//带参宏,可以像内联函数一样使用,输出高电平或低电平
#define DS18B20_DATA_OUT(a)	if (a)	\
					GPIO_SetBits(GPIOB,GPIO_Pin_12);\
					else		\
					GPIO_ResetBits(GPIOB,GPIO_Pin_12)
 //读取引脚的电平
#define  DS18B20_DATA_IN()	   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)

typedef struct
{
	u8  humi_int;		//湿度的整数部分
	u8  humi_deci;	 	//湿度的小数部分
	u8  temp_int;	 	//温度的整数部分
	u8  temp_deci;	 	//温度的小数部分
	u8  check_sum;	 	//校验和
		                 
}DS18B20_Data_TypeDef;

typedef enum {
    DS18B20_INPUT = 0,  // 定义枚举值为0,表示输入模式
    DS18B20_OUTPUT = 1   // 定义枚举值为1,表示输出模式
} DS18B20_Mode;

u8 DS18B20_Init(void);
float DS18B20_Get_Temp(void);
#endif /* __DS18B20_H */

main.c

/************************************************************************************
 * 文件名  :main.c
 * 描述    :        
 * 硬件连接:
 * DS18B20传感器模块: VCC -> 5V; GND -> GND; DAT -> PB12;
 *
 * 功能描述:测量温度(ADC1、PA2、DMA方式读取);
             串口1接收测量所得的浊度值(波特率115200);
**********************************************************************************/
#include "stm32f10x.h"
#include "bsp_usart1.h"
#include <string.h>
#include "delay.h"
#include "ds18b20.h"


float temp_data=0.0;

void TEMP_Value_Conversion()
{
	  temp_data=DS18B20_Get_Temp();
}

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{	
	  DS18B20_Init();
    /* 配置USART1 */
    USART1_Config();

  while(1)
	{	
		TEMP_Value_Conversion();		
		printf("%f\n",temp_data);
		delay_ms(1000);	
	}	
}
/*********************************************END OF FILE**********************/

六、代码注意事项

1、刚上电,就打印或者显示默认值85℃

原因:因为稳定转换需要时间,如果是12位分辨率,则转换时间为750ms

解决:转换后,给一个充分时间的延时

085fa2db697142d6b8954db315c0572a.png

 视频0:41:46处

[13-2] DS18B20温度读取&温度报警器_哔哩哔哩_bilibili

2、单总线BUG:读取温度时,会被其他中断打断,扰乱读取

原因:单总线使用的是绝对时间延时,无时钟线

解决:写时序时,把中断屏蔽。写完时序再打开中断。不过最好是用多线程,即RTOS系统

视频1:04:25处

[13-2] DS18B20温度读取&温度报警器_哔哩哔哩_bilibili

3、主机拉低电平,传感器怎么知道是读还是写。默认是写,只有发读指令后,传感器才会发数据。而且读写有主机控制。

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值