《嵌入式-STM32开发指南》第三部分 外设篇 - 第2章 温度传感器DS18B20

Posted Bruceoxl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《嵌入式-STM32开发指南》第三部分 外设篇 - 第2章 温度传感器DS18B20相关的知识,希望对你有一定的参考价值。

2.1 理论分析

2.1.1概述

DS18B20 是 DALLAS 最新单线数字温度传感器,新的"一线器件"体积更小、适用电压更宽、更经济。Dallas 半导体公司的数字化温度传感器 DS1820 是世界上第一片支持 "一线总线"接口的温度传感器。

DS18B20采用的单总线协议,也就是只需占用主机一个I/O口,无需其他外围电路,直接将环境温度转换为数字信号,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。另外,以"一线总线"的数字方式传输,大大提高了系统的抗干扰性。适合于恶劣环境的现场温度测量,如:环境控制、设备或过程控制、测温类消费电子产品等。

DS18B20不仅接线方便,而且可根据应用场合的不同改变其封装以此适应不同的工作环境,如管道式、螺纹式、磁铁吸附式等,可适用于锅炉测温、机房测温、蔬菜大棚等多种场合。

图1 DS18B20实物图

2.1.2特性

(1) 具有独特的单总线接口,与主机主需要一个线即可实现双向通信;
(2) 测温范围为-55+125℃,在-10+85℃时,精度为±0.5℃;
(3) 可编程的分辨率为 9~12 位,对应的可分辨温度分别为 0.5℃、0.25℃、0.125℃和 0.0625℃;
(4) 在9 位分辨率时,最多在93.75ms 内把温度值转换为数字,在12 位分辨率时,最多在 750ms 内把温度值转换为数字;
(5) 负压特性,电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。
(6) 工作电压范围宽,电压范围为3.0~5.5V,还可用数据线供电;
(7) 支持多点组网,多个DS18B20可以并联实现多点组网测温。值得注意的是,如果数量过多,需要解决供电问题,否则电压过低会导致信号传输不稳定;
(8) 测量结果采用数字信号输出,同时具有CRC校验,具较强的抗干扰和纠错能力。

2.1.3外形和内部结构

DS18B20 内部结构主要由四部分组成:64 位光刻 ROM、温度传感器、非挥发的温度报警触发器 TH 和 TL、配置寄存器。DS18B20 的管脚排列如下图所示。

图2 DS18B20 管脚图

DS18B20 的内部结构图如下图所示。

图3 DS18B20 内部结构图

其中与操作有关的有:64位光刻ROM、9个字节的RAM存储器、温度传感器、EERPOM(温度报警寄存器TH和TL、配置寄存器)。

1.光刻ROM
为了区分不同的单总线器件,厂家在生产单总线器件时要刻录一个64位的二进制ROM代码,标志着单总线器件的ID号。光刻ROM的64位序列号是出厂被提前光刻好的,也就该DS18B20的地址序列码,64位光刻ROM的排序依次是:8位产品标识符、48位自身序列号、8位循环校验码。由于每个DS18B20的地址各不相同,因此可以通过寻址的方式与多个DS18B20通信。

图4 64位序列号格式

2.内存映射
DS18B20的内存映射如下图所示,包括9个字节的暂存存储器和掉电不丢失的EEPROM存储单元。

图5 DS18B20内存映射

其中Byte0和Byte1只读,存储的是温度的信息,主机可通过DQ引脚读出该数据,Byte2和Byte3是温度报警寄存器,Byte4用于控制温度值得精度,Byte5、Byte6和Byte7保留给内存使用,Byte8是CRC生成的校验寄存器。

3.温度传感器
DS18B20的测量结果是通过16位的二进制进行传输,其格式如下所示。

图6温度寄存器格式

其中LS BYTE的高4位存储的是温度的整数值,低4位存储的温度的小数值,MS BYTE的高5位存储的是符号位,低3位存储的是整数值。温度转换示例如下表所示。

表1温度转换示例

4.温度报警触发器
用户可通过配置Byte2和Byte3来配置温度报警器的TH和TL,当配置了温度报警触发寄存器,DS18B20就会将TH和TL的值写入EEPROM中,再次上电复位后就会从EEPROM中读取TH和TL的值,当转换的温度达到阈值,DS18B20的报警标志就会被处罚,直到温度小于设置的阈值。

5.配置寄存器
配置寄存器是Byte4,单只用到两位,最高位为0,低4位为1,R0和R1用来配置温度的分辨率,

图7配置寄存器

DS18B20出厂的默认分辨率是12位,不同的分辨率的转换时间如下表所示。在实际应用过程中,应注意采用的分辨率机器转换时间,确保温度在转换后再进行温度读取。

表2转换精度关系

2.1.4 DS18B20指令和时序

2.1.4.1 DS18B20的指令

DS18B20的指令整体上分为两类:ROM指令和存储器操作指令

1.ROM指令
在主机检测到应答脉冲后,就可以发出 ROM 命令。这些命令与各个从机设备的唯一64位 ROM代码相关,允许主机在单总线上连接多个从机设备时,指定操作某个从机设备。这些命令还允许主机能够检测到总线上有多少个从机设备以及其设备类型,或者有没有设备处于报警状态。

从机设备可能支持5种 ROM 命令(实际情况与具体型号有关),每种命令长度为8位。主机在发出功能命令之前,必须送出合适的 ROM 命令。ROM命令的操作流程图如下图所示。

图8 DS18B20 ROM指令流程

下面将简要地介绍各个 ROM 指令的功能,以及在何种情况下使用。

(1) READ ROM [33h](仅适合于单节点)
这条指令用于读取DS18B20的64位序列号。值得注意的是,该指令仅适用于总线上只有一个从机设备。它允许主机直接读出从机的64位 ROM 代码,无须执行搜索 ROM 过程。如果该命令用于多节点系统,则必然发生数据冲突,因为每个从机设备都会响应该命令。

(2) MATCH ROM [55h]
匹配 ROM 指令,后跟随64位 ROM序列号,从而允许主机访问多节点系统中某个指定的从机设备。仅当从机完全匹配64位 ROM 代码时,才会响应主机随后发出的功能命令;其它设备将处于等待复位脉冲状态。这条指令适用于单个或者多个DS18B20。

(3) SKIP ROM [CCh](仅适合于单节点)
主机通过执行该指令,访问总线上的从机设备,而无须发出任何 ROM 指令信息。例如,主机通过在发出跳越 ROM 指令后跟随转换温度指令 [44h] ,就可以同时命令总线上所有的 DS18B20 开始转换温度,这样大大节省了主机的时间。值得注意,如果跳越 ROM指令跟随的是读暂存器 [BEh] 的指令(包括其它读操作命令),则该命令只能应用于单节点系统,否则将由于多个节点都响应该命令而引起数据冲突。

(4) SEARCH ROM [F0h]
当系统初始上电时,主机必须找出总线上所有从机设备,即采用搜索指令来识别总线上的所有的从机64位序列号,这样主机就能够判断出从机的数目和类型。主机通过重复执行搜索 ROM 循环(搜索ROM指令跟随着位数据交换),以找出总线上所有的从机设备。如果总线只有一个从机设备,则可以采用读 ROM 指令来替代搜索 ROM 指令。在每次执行完搜索 ROM 循环后,主机必须返回至指令序列的第一步(初始化)。

(5) ALARM SEARCH ROM [ECh]
除那些设置了报警标志的从机响应外,该命令的工作方式完全等同于搜索 ROM 指令。该指令允许主机设备判断那些从机设备发生了报警(如最近的测量温度过高或过低等)。同搜索 ROM 指令一样,在完成报警搜索循环后,主机必须返回至指令序列第一步。

2.存储器操作指令
在主机发出 ROM 命令,以访问某个指定的 DS18B20,接着就可以发出 DS18B20 支持的某个功能命令。这些命令允许主机写入或读出DS18B20暂存器、启动温度转换以及判断从机的供电方式。DS18B20的功能命令流程如下图所示。

图9 DS18B20 功能指令流程

(1) CONVERT T [44h]
启动温度转换指令,温度转换后存放在Byte0和Byte1中,如果使用寄生电源,总线控制必须在发出该条指令后的10us内强制上拉,以保证DS18B20的供电充足。

(2) WRITE SCRATCHPAD [4Eh]
在写暂存器指令后可向DS18B20的暂存器TH和TL以及配置寄存器中写入数据。

(3) READ SCRATCHPAD [BEh]
读暂存器指令,发送该指令后DS18B20将从第一个字节开始,依次送出9个字节的内容,如果不想读完所有的字节,控制器可以在任意时间发出复位指令来终止读取数据。

(4) COPY SCRATCHPAD [48h]
复制暂存器指令将TH和TL与配置寄存器的中的内容拷贝到EEPROM中。如果使用寄生电源。总线控制器必须在这条指令发出后的10us内启动强上拉,并保持至少10ms的时间。

(5) RECALL EEPROM [B8h]
复制EEPROM指令把TH和TL与配置寄存器的值拷贝会暂存器中,这种拷贝操作在DS18B20上电后自动执行,上电后,暂存器中就存在有效的数据。

(6) READ POWER SUPPLY [B4h]
读电源模式指令,发给DS18B20后,再发出读时间隙,返回电源模式,0位寄生电源,1位外部电源。

2.1.4.2 DS18B20的时序

单总线协议定义了几种信号类型:复位脉冲、答应脉冲、写0、写1、读0和读1时序。下面针对DS18B20介绍其相应的时序控制图。

1.初始化序列:复位和应答脉冲
单总线上的所有通信都是以初始化序列开始,包括:主机发出的复位脉冲及从机的应答脉冲,如下图所示。

图10初始化时序图

在主机初始化过程,主机通过拉低单总线至少480us,当然在480~960us的低电平都是可以的,以产生 (Tx) 复位脉冲。接着,主机释放总线变为高电平,并进入接收模式 (Rx)在,随后的480us内,对总线进行检测,如果有低电平,则说明总线上有期间作出应答,否则说明无设备在总线上。

作为从机的DS18B20会一直检测总线上是否有480~960us的低电平信号,如果检测到复位脉冲,则延时15-60us,接着通过拉低总线60-240us,以产生应答脉冲。当从机发出响应主机的应答脉冲时,即向主机表明它处于总线上,且工作准备就绪。若检测不到复位脉冲则一直处于检测等待中。

2.主机读写时序
在写时序期间,主机向单总线器件写入数据;而在读时序期间,主机读入来自从机的数据。在每一个时序,总线只能传输一位数据。

图11读写时序图
  • 写时序

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

写0时序的过程:在主机拉低总线后,只需在整个时序期间保持低电平即可(至少60us)。

写1时序的过程:主机在拉低总线后,接着必须在15us之内释放总线,由5k上拉电阻将总线拉至高电平。一直到写周期结束。

总的来说,在写时序起始后15-60us期间,单总线器件采样总线电平状态。如果在此期间采样为高电平,则逻辑1被写入该器件:如果为低电平,则写入逻辑0

  • 读时序

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。

所有读时序至少需要60us,且在两次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。在主机发起读时序之后,单总线器件才开始在总线上发送0或1。

若从机发送1,则保持总线为高电平;若发送0,则拉低总线。当发送0时,从机在该时序结束后释放总线,由上拉电阻将总线拉回至空闲高电平状态。从机发出的数据在起始时隙之后,保持有效时间15us,因而,主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态

【注】无论是读是写,都是以主机把总线至少拉低1us开始,无论是读0还是写0,都是以总线拉高至少1us结束。

2.1.5 STM32读取 DS18B20 温度值

当主机对多个DS18B20的某一设备进行操作时,主机首先逐个与挂在总线设备挂接,使用搜索ROM(FOh)指令,读出其序列号(33H),然后发送匹配指令(55h),紧接着提供64位序列号,之后就是操作该DS18B20了。

如果只有一个DS18B20测温,就不需要搜索ROM、读ROM以及匹配ROM操作了,只需要指令跳过ROM(CCh)指令,就可指令温度转换(44h)和读取温度(BEh)操作了。

图12 DS18B20温度获取流程

总的来说,单线接口访问DS18B20的流程如下:

1.ROM操作

A.主机复位操作,即对DS18B20初始化;
B.主机对DS18B20写跳过ROM指令(CCh),单设备不需要其他操作;
C主机对DS18B20写启动温度转换(44h),启动温度转换;
D.时等待温度转换值,当然延时大小还由分辨率决定。

【注】每个指令在写的时候都是低位在前,高位在后。

2.存储器操作

A.主机复位操作;
B.主机对DS18B20写跳过ROM指令(CCh);
C.主机发出读取RAM指令(BEh),DS18B20依次发送9个字节数据,在字节在前,高字节在后。如果只想读取温度值,在读完前两个字节就不在读取后面的数据。

2.2 实验详解

2.2.1实验目的

1)通过实验掌握STM32 芯片GPIO 的配置方法

2)掌握温度传感器DS18B20的原理与使用

2.2.2实验设备

硬件:PC 机一台;STM32开发板一套; DS18B20一个
软件:Windows 10系统,Keil5集成开发环境、串口助手

2.2.3硬件连接

DS18B20有两种连接方式:外部电源供电和寄生电源供电

1.外电电源供电方式
在外部电源供电方式下,DS18B20的工作电源由VCC引脚引入,不存在电源电流不足的问题,可以保证转换的精度,在单总线上还可以挂在任意多个设备。值得注意的是,在外部供电的方式下,GND引脚不能悬空,悬空会导致温度读取失败。

采用外部电源供电是DS18B20的最佳方式,工作稳定,抗干扰强,电路简单,可以开发多点测温系统。

图13外部供电方式

2.寄生电源供电方式
在寄生电源供电方式下,DS18B20只需要一个线就可正常工作,无需外部供电,值得注意的,此种供电方式,需要使用一个MOS管将单总线强上拉以此提供充足的电流,在发出任何涉及到拷贝EEPROM存储器或启动温度转换的指令后,必须最多10us把I/O强上拉。

这种供电方式也可适用于多点测温,但需要多一根I/O线来进行强上拉切换。

图14寄生供电方式

另外,如果只有一个从机设备,也可不需要增加MOS管,但是当I/O提供的电能不够,则会导致无法转换温度或温度误差大。

2.2.4 Lib_V3.5.0库实现

在前文已经介绍了DS18B20的指令和时序,接下来通过STM32来读取温度值,STM32读取温度的流程如下。

图15 MCU 读取 DS18B20 温度值流程图

根据以上流程,接下来通过代码依次实现。

1.首先对GPIO初始化,配置为推挽输出,速度设置为高速。

/**
  * @brief  配置DS18B20用到的I/O口
  * @param  None
  * @retval None
  */
static void DS18B20_GPIO_Config(void)
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
  GPIO_InitTypeDef GPIO_InitStructure;


  /*开启DS18B20_DQ_GPIO_PORT的外设时钟*/
  DS18B20_DQ_SCK_APBxClock_FUN ( DS18B20_DQ_GPIO_CLK, ENABLE); 

  /*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/															   
  GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_GPIO_PIN;	

  /*设置引脚模式为通用推挽输出*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

  /*设置引脚速率为50MHz */   
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

  /*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
  GPIO_Init ( DS18B20_DQ_GPIO_PORT , &GPIO_InitStructure );
}

/**
  * @brief  主机给从机发送复位脉冲
  * @param  None
  * @retval None
  */
static void DS18B20_Rst(void)
{
	/* 主机设置为推挽输出 */
	DS18B20_Mode_Out_PP();
	
	DS18B20_DQ_0;
	/* 主机至少产生480us的低电平复位信号 */
	Delay_us(750);
	
	/* 主机在产生复位信号后,需将总线拉高 */
	DS18B20_DQ_1;
	
	/*从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲*/
	Delay_us(15);
}

/**
  * @brief  检测从机给主机返回的存在脉冲
  * @param  None
  * @retval 0:成功,1:失败
  */
static uint8_t DS18B20_Presence(void)
{
	uint8_t pulse_time = 0;
	
	/* 主机设置为上拉输入 */
	DS18B20_Mode_IPU();
	
	/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号 
	 * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
	 */
	while( DS18B20_DQ_IN() && pulse_time<100 )
	{
		pulse_time++;
		Delay_us(1);
	}	
	/* 经过100us后,存在脉冲都还没有到来*/
	if( pulse_time >=100 )
		return 1;
	else
		pulse_time = 0;
	
	/* 存在脉冲到来,且存在的时间不能超过240us */
	while( !DS18B20_DQ_IN() && pulse_time<240 )
	{
		pulse_time++;
		Delay_us(1);
	}	
	if( pulse_time >=240 )
		return 1;
	else
		return 0;
}

/**
  * @brief  DS18B20 初始化函数
  * @param  None
  * @retval 0:初始化成功,检测到传感器,1:初始化失败
  */
uint8_t DS18B20_Init(void)
{
	DS18B20_GPIO_Config ();
	
	DS18B20_DQ_1;
	
	DS18B20_Rst();
	
	return DS18B20_Presence ();
}

初始化部分主要配置IO口,检测是否有DS18B20设备。

对设备惊醒初始化,接下来就读取温度,对于单个设备可使用跳过匹配 ROM,单考虑到通用性,这里使用匹配ROM的方式。

2.读取ID的代码实现如下。

/**
  * @brief  在匹配 ROM 情况下获取 DS18B20 温度值 
  * @param  ds18b20_id:用于存放 DS18B20 序列号的数组的首地址
  * @retval None
  */
void DS18B20_ReadId ( uint8_t * ds18b20_id )
{
	uint8_t uc;
		
	DS18B20_WriteByte(0x33);       //读取序列号
	
	for ( uc = 0; uc < 8; uc ++ )
	  ds18b20_id [ uc ] = DS18B20_ReadByte();	
}

代码很简单,先发送读取ID的指令,然后读取ID即可。

其读写Byte的实现如下。

/**
  * @brief  从DS18B20读取一个bit
  * @param  None
  * @retval 读取到的数据
  */
static uint8_t DS18B20_ReadBit(void)
{
	uint8_t dat;
	
	/* 读0和读1的时间至少要大于60us */	
	DS18B20_Mode_Out_PP();
	/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
	DS18B20_DQ_0;
	Delay_us(10);
	
	/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
	DS18B20_Mode_IPU();
	//Delay_us(2);
	
	if( DS18B20_DQ_IN() == SET )
		dat = 1;
	else
		dat = 0;
	
	/* 这个延时参数请参考时序图 */
	Delay_us(45);
	
	return dat;
}

/**
  * @brief  从DS18B20读一个字节,低位先行
  * @param  None
  * @retval 读到的数据
  */
static uint8_t DS18B20_ReadByte(void)
{
	uint8_t i, j, dat = 0;	
	
	for(i=0; i<8; i++) 
	{
		j = DS18B20_ReadBit();		
		dat = (dat) | (j<<i);
	}
	return dat;
}

/**
  * @brief  写一个字节到DS18B20,低位先行
  * @param  dat:待写入数据
  * @retval None
  */
static void DS18B20_WriteByte(uint8_t dat)
{
	uint8_t i, testb;
	DS18B20_Mode_Out_PP();
	
	for( i=0; i<8; i++ )
	{
		testb = dat&0x01;
		dat = dat>>1;		
		/* 写0和写1的时间至少要大于60us */
		if (testb)
		{			
			DS18B20_DQ_0;
			/* 1us < 这个延时 < 15us */
			Delay_us(8);
			
			DS18B20_DQ_1;
			Delay_us(58);
		}		
		else
		{			
			DS18B20_DQ_0;
			/* 60us < Tx 0 < 120us */
			Delay_us(70);
			
			DS18B20_DQ_1;			
			/* 1us < Trec(恢复时间) < 无穷大*/
			Delay_us(2);
		}
	}
}

以上代码的实现严格对其前面的时序图,只是需要注意的是,写一个bit数据需要将I/O的输出变为输入。

3.在匹配 ROM 情况下获取 DS18B20 温度值

这里分为两步,也就是图15的大部分内容,先执行温度转换指令,然后执行温度转换值。完整过程如下。

/**
  * @brief  在匹配 ROM 情况下获取 DS18B20 温度值 
  * @param  ds18b20_id:存放 DS18B20 序列号的数组的首地址
  * @retval 温度值
  */
float DS18B20_GetTemp_MatchRom ( uint8_t * ds18b20_id )
{
	uint8_t tpmsb, tplsb, i;
	short s_tem;
	float f_tem;
	
	
	DS18B20_MatchRom ();            //匹配ROM
	
  for(i=0;i<8;i++)
		DS18B20_WriteByte ( ds18b20_id [ i ] );	
	
	DS18B20_WriteByte(0X44);				/* 开始转换 */

	
	DS18B20_MatchRom ();            //匹配ROM
	
	for(i=0;i<8;i++)
		DS18B20_WriteByte ( ds18b20_id [ i ] );	
	
	DS18B20_WriteByte(0XBE);				/* 读温度值 */
	
	tplsb = DS18B20_ReadByte();		 
	tpmsb = DS18B20_ReadByte(); 
	
	
	s_tem = tpmsb<<8;
	s_tem = s_tem | tplsb;
	
	if( s_tem < 0 )		/* 负温度 */
		f_tem = (~s_tem+1) * 0.0625;	
	else
		f_tem = s_tem * 0.0625;
	
	return f_tem; 		
}

完整代码如下所示。

/* Includes*********************************************************************/
#include "./SysTick/stm32f103_SysTick.h"
#include "./DS18B20/stm32f103_DS18B20.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

static void DS18B20_GPIO_Config(void);
static void DS18B20_Mode_IPU(void);
static void DS18B20_Mode_Out_PP(void);
static void DS18B20_Rst(void);
static uint8_t DS18B20_Presence(void);
static uint8_t DS18B20_ReadBit(void);
static uint8_t DS18B20_ReadByte(void);
static void DS18B20_WriteByte(uint8_t dat);
static void DS18B20_SkipRom(void);
static void DS18B20_MatchRom(void);

/**
  * @brief  配置DS18B20用到的I/O口
  * @param  None
  * @retval None
  */
static void DS18B20_GPIO_Config(void)
{		
	/*定义一个GPIO_InitTypeDef类型的结构体*/
  GPIO_InitTypeDef GPIO_InitStructure;


  /*开启DS18B20_DQ_GPIO_PORT的外设时钟*/
  DS18B20_DQ_SCK_APBxClock_FUN ( DS18B20_DQ_GPIO_CLK, ENABLE); 

  /*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/															   
  GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_GPIO_PIN;	

  /*设置引脚模式为通用推挽输出*/
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

  /*设置引脚速率为50MHz */   
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 

  /*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
  GPIO_Init ( DS18B20_DQ_GPIO_PORT , &GPIO_InitStructure );
}

/**
  * @brief  配置DS18B20-DATA引脚变为输入模式
  * @param  None
  * @retval None
  */
static void DS18B20_Mode_IPU(void)
{
 	  GPIO_InitTypeDef GPIO_InitStructure;

	  	/*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/	
	  GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_GPIO_PIN;

	   /*设置引脚模式为浮空输入模式*/ 
	  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	

	  /*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
	  GPIO_Init(DS18B20_DQ_GPIO_PORT, &GPIO_InitStructure);
}

/**
  * @brief  配置DS18B20-DATA引脚变为输出模式
  * @param  None
  * @retval None
  */
static void DS18B20_Mode_Out_PP(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;

	 	/*选择要控制的DS18B20_DQ_GPIO_PORT引脚*/															   
  	GPIO_InitStructure.GPIO_Pin = DS18B20_DQ_GPIO_PIN;	

	/*设置引脚模式为通用推挽输出*/
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

	/*设置引脚速率为50MHz */   
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

	/*调用库函数,初始化DS18B20_DQ_GPIO_PORT*/
  	GPIO_Init(DS18B20_DQ_GPIO_PORT, &GPIO_InitStructure);
}

/**
  * @brief  主机给从机发送复位脉冲
  * @param  None
  * @retval None
  */
static void DS18B20_Rst(void)
{
	/* 主机设置为推挽输出 */
	DS18B20_Mode_Out_PP();
	
	DS18B20_DQ_0;
	/* 主机至少产生480us的低电平复位信号 */
	Delay_us(750);
	
	/* 主机在产生复位信号后,需将总线拉高 */
	DS18B20_DQ_1;
	
	/*从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲*/
	Delay_us(15);
}

/**
  * @brief  检测从机给主机返回的存在脉冲
  * @param  None
  * @retval 0:成功,1:失败
  */
static uint8_t DS18B20_Presence(void)
{
	uint8_t pulse_time = 0;
	
	/* 主机设置为上拉输入 */
	DS18B20_Mode_IPU();
	
	/* 等待存在脉冲的到来,存在脉冲为一个60~240us的低电平信号 
	 * 如果存在脉冲没有来则做超时处理,从机接收到主机的复位信号后,会在15~60us后给主机发一个存在脉冲
	 */
	while( DS18B20_DQ_IN() && pulse_time<100 )
	{
		pulse_time++;
		Delay_us(1);
	}	
	/* 经过100us后,存在脉冲都还没有到来*/
	if( pulse_time >=100 )
		return 1;
	else
		pulse_time = 0;
	
	/* 存在脉冲到来,且存在的时间不能超过240us */
	while( !DS18B20_DQ_IN() && pulse_time<240 )
	{
		pulse_time++;
		Delay_us(1);
	}	
	if( pulse_time >=240 )
		return 1;
	else
		return 0;
}

/**
  * @brief  从DS18B20读取一个bit
  * @param  None
  * @retval 读取到的数据
  */
static uint8_t DS18B20_ReadBit(void)
{
	uint8_t dat;
	
	/* 读0和读1的时间至少要大于60us */	
	DS18B20_Mode_Out_PP();
	/* 读时间的起始:必须由主机产生 >1us <15us 的低电平信号 */
	DS18B20_DQ_0;
	Delay_us(10);
	
	/* 设置成输入,释放总线,由外部上拉电阻将总线拉高 */
	DS18B20_Mode_IPU();
	//Delay_us(2);
	
	if( DS18B20_DQ_IN() == SET )
		dat = 1;
	else
		dat = 0;
	
	/* 这个延时参数请参考时序图 */
	Delay_us(45);
	
	return dat;
}

/**
  * @brief  从DS18B20读一个字节,低位先行
  * @param  None
  * @retval 读到的数据
  */
static uint8_t DS18B20_ReadByte(void)
{
	uint8_t i, j, dat = 0;	
	
	for(i=0; i<8; i++) 
	{
		j = DS18B20_ReadBit();		
		dat = (dat) | 《嵌入式-STM32开发指南》第三部分 外设篇 - 第4章 超声波测距

《嵌入式-STM32开发指南》第三部分 外设篇 - 第4章 超声波测距

《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器

《嵌入式-STM32开发指南》第三部分 外设篇 - 第5章 光敏传感器

《嵌入式-STM32开发指南》第三部分 外设篇 - 第1章 温湿度传感器DHT11

《嵌入式-STM32开发指南》第三部分 外设篇 - 第1章 温湿度传感器DHT11