STM32 一线协议下实现 DS18B20 温度传感器采样

文章目录

  • 1- DS18B20温度传感器简介
  • (1)简介
  • (2)特征
  • (3)引脚
  • 2- 内部功能结构
  • (1)64位光刻ROM
  • (2)温度传感器(存放数据形式)
  • (3)配置寄存器
  • (4)内部存储器
  • 4- DS18B20操作指令
  • (1)ROM操作指令
  • (2)存储器操作指令
  • 5- 信号时序
  • (1)复位脉冲和应答脉冲
  • (2)主机写时序
  • (3)主机读时序
  • 6- 代码编写
  • (1)流程
  • (2)创建ds18b20.c
  • (3)创建ds18b20.h
  • (3)编写main.c
  • 7- 结果展示

  • 1- DS18B20温度传感器简介

    (1)简介

    温度传感器是各种传感器中比较常用的一种,随着科技的进步,现代的温度传感器已经走向数字化,DS18B20就是具有代表性的一种,因体积小、抗干扰能力强、精度高、接口简单等特点,广泛应用在生产、生活等多个领域,如粮仓、储罐、空调、冷柜等测温场合。随着现代仪器的发展,微型化、集成化、数字化正成为传感器发展的一个重要方向。

    美国DALLAS公司推出的数字化温度传感器DS18B20采用单总线协议,即与单片机连接时仅需占用一个I/O端口,无需其它外围电路,直接将环境温度转化成数字信号,以数字码方式串行输出,大大简化了传感器与微处理器的接口。

    (2)特征

    DS18B20温度传感器的特性:

  • 独特的单总线接口,在与微处理器连接时仅需要一条口线即可实现双向通讯。
  • 测温范围为-55℃ ~ +125℃,具有可编程的9 ~ 12位分辨率,对应可分辨温度分别为0.5,0.25、0.125和0.0625 ℃(能够感受到这个温度变化,但是获取温度的时候达不到这么精确)。
    注意:分辨温度与精度的区别,在-10℃ ~ + 85℃范围内的精度为±0.5℃。
  • 工作电压范围宽:3.0V ~ 5.5V,在寄生电源方式下可由数据线供电。
  • 在使用中不需要任何外围电路,全部传感元件及转换电路集成在芯片内。(上拉电阻)
  • 测量结果直接以温度信号的数字量输出,采用单总线串行传送方式,同时可传送CRC校验码,具有极强的抗干扰和纠错能力。
  • 在9位分辨率时,最多在93.75ms内把温度转换为数字量;12位分辨率时,最多在750ms内把温度转换为数字量。
  • 负压特性,电源极性接反的时候,芯片不会因为发热而烧毁,只是不能正常工作。
  • (3)引脚

    比较重要的就是三个引脚:

  • GND为电源地
  • DQ为数字信号的输入输出端(我接的开发板PA5管脚)
  • VDD为外接供电电源输入端(我接的5V)

  • 我们一般采用的都是外部供电方式:
    在外部电源供电方式下,DS18B20工作电源由VCC引脚接入,不存在电源电流不足的问题,可以保证转换精度,同时在总线上理论可以挂接任意多个DS18B20传感器,组成多点测温系统。同时注意在外部供电的方式下,GND引脚不能悬空,否则不能转换温度,读取的温度总是85℃。
    这种外部电源供电方式是DS18B20最佳的工作方式,工作稳定可靠,抗干扰能力强,而且电路比较简单(只需三根线),可以开发出稳定可靠的多点温度监控系统。同时这种外接电源方式下,可以充分发挥DS18B20宽电源电压范围的优点,即使电源电压VCc降到3V,依然能够保证温度测量精度。
    当然想要了解其他的供电方式可以查资料看一下,这里就不过多阐述了。


    2- 内部功能结构

    DS18B20内部结构中与操作有关的主要有:64位光刻ROM、温度传感器、9个字节的RAM存储器、EEPROM(温度报警寄存器TH和TL、配置寄存器)。

    (1)64位光刻ROM

    光刻ROM中的64位序列号是出厂前被光刻好的,可以看作是该DS18B20的地址序列码。
    64位光刻ROM的排列是:

  • (低位)开始8位是产品类型标号
  • 接着的48位是该DS18B20自身的序列号,并且每个DS18B20的序列号都不相同,因此它可以看作是该DS18B20的地址序列码
  • 最后8位则是前面56位的循环冗余校验码。
  • 由于每一个DS18B20的ROM数据都各不相同,因此微控制器就可以通过单总线对多个DS18B20进行寻址,从而可以实现一根总线上挂接多个DS18B20进行通信。

    (2)温度传感器(存放数据形式)

    如果我们只关心采样温度值的话,则只需要读前两个字节即可。其中Byte[0]为温度值的低字节,而Byte[1]为温度值的高字节。

    这16位数据的格式如下图所示:

    其中BIT[3:0]为温度值的小数部分,而BIT[10:4]为温度值的整数部分,BIT[15:11]则为符号位,如果为0则温度为正值,如果为1则温度为负值。

    举例子我们来看一看:

    +125℃的数字输出为07D0H
    0000 0111 1101 0000
    0000 0111 1101 0000
    符号位 整数位 小数位 =>> ( +(符号)) (2 ^0 + 2 ^2 +…+ 2 ^6(整数))(0(无小数位)) = +125

    +0.5℃ 的数字输出为0008H
    0000 0000 0000 1000
    0000 0000 0000 1000
    符号位 整数位 小数位 =>> ( +(符号)) (0(整数))(2^ -1(小数位0.5)) = +0.5

    -55℃ 的数字输出为FC90H
    1111 1100 1001 0000
    1111 1100 1001 0000
    符号位 整数位 小数位
    这里计算就不能直接计算了,因为在DS18B20内部是以补码的形式存储的,整数的原码就是本身,如果为负数,是需要转换成原码再计算的,按位取反+1:

    (3)配置寄存器

    配置寄存器的低五位一直都是“1",最高位TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式。在DS18B20出厂时该位被设置为0,用户不要去改动。R1和RO用来设置分辨率。
    DS18B20出厂时被设置为12位分辨率,不同分辨率对应的温度转换时间如下图所示,在应用时应注意所采用的分辨率及其对应转换时间,确保在温度转换完成后再进行温度的读取。

    (4)内部存储器

    存储器包括9个字节的暂存存储器和掉电不丢失的EEPROM存储单元。

    经转换所得的温度值以二字节补码形式存放在暂存存储器的第0和第1个字节中。单片机可通过DQ读出该数据,读取时低位在前,高位在后,低字节在前,高字节在后。
    对应的温度计算前面已经讲过了:
    当符号位S=0时,直接将二进制位转换为十进制
    当符号位S=1时,先将补码变为原码,再计算十进制值

    DS18B20内部设有温度报警触发器TH和TL,可通过软件写入报警上下限。上电复位时,TH和TL的值从EEPROM中复制到高速暂存器中的第2和第3字节。在完成温度转换后,所测温度值将自动与储存在TH和TL内的触发值进行比较,如果测得温度高于TH或低于TL, DS18B20内部的告警标志就会被置位,表示温度超出了设定范围,报警状态将一直保持,直到再一次测温度达不到报警。


    4- DS18B20操作指令

    DS18B20的指令整体上分为两类:ROM操作指令、存储器操作指令(标黄的为常用的。)。

    (1)ROM操作指令

  • Read ROM[33H]:
    这个指令使总线控制器读到DS18B20的64位序列号。注意:只有在总线上存在单个DS1820的时候才能使用这个指令。如果总线有不止一个从机,当所有从机试图同时传送信号时就会发生数据冲突。

  • Match ROM[55H]:
    匹配ROM指令,后跟64位ROM序列号,使总线控制器在总线上多个DS18B20中选定一个。只有序列号完全匹配的DS18B20才能响应随后的存储器操作。这条指令适用于单个(没必要)或多个DS18B20。

  • Skip ROM[CCH]:
    跳过ROM指令允许总线控制器不用提供64位序列号就使用存储器操作指令,在只有单个DS18B20时可以节省时间。如果总线上不止一个从机,不能使用该指令,原因同Read ROM。

  • Search ROM[FOH]:
    当一个系统初次启动时,总线控制器需要知道总线上有多个器件以及它们的64位编码,采用搜索ROM指令使总线控制识别总线上所有从机的64位编码。

  • Alarm Search[ECH]:
    报警搜索指令与Search ROM类似,但只有最近一次测温后有报警置位的DS18B20才会响应。

  • (2)存储器操作指令

  • Write Scratchpad[4EH]:
    在该写暂存器指令后可向DS18B20的暂存器TH、TL以及配置寄存器中写入数据。

  • Read Scratchpad[BEH]:
    读暂存器指令。发送该指令后DS18B20将从第1个字节开始,依次送出9个字节的内容。如果不想读完所有字节,控制器可以在任何时间发出复位指令来中止读取或读完想要字节后不再接收数据。

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

  • Convert T[44H]:
    启动温度转换指令,温度转换完成后存放在第1和第2个字节中。如果使用寄生电源,总线控制必须在发出这条指令后的10us内启动强上拉。

  • Recall EEPROM[B8H]:
    复制EEPROM指令把TH、TL和配置寄存器里的值拷贝回暂存器。这种拷贝操作在DS18B20上电时自动执行,上电后,暂存器里就存在了有效的数据。

  • Read Power Supply[B4H]:
    读电源模式指令,发给DS18B20后,再发出读时间隙,返回电源模式:0为寄生电源,1为外部电源。


  • 5- 信号时序

    要想编程使用DS18B20的测温功能,还需要进一步知道如何进行初始化以及如何进行指令的发送与温度的接收,这就需要深入理解初始化和读与写的时序图。相对于之前的编程实践,在对DS18B20进行操作时,对时间的要求比较严格,且单总线协议理解起来相对困难,如果把握不好,往往不能正确实现测温功能。为此,需要深入掌握其时序要求。

    (1)复位脉冲和应答脉冲

    主机首先发出一个480us – 960us的低电平脉冲(复位),然后释放总线变为高电平,并在随后的480微秒时间内,对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
    作为从机的DS18B20上电后就一直检测总线上是否有480us – 960us的低电平出现,如果检测到该复位脉冲则在总线变为高电平后,等待15us – 60us,之后将总线电平拉低60us – 240us(响应存在脉冲),告诉主机本器件已做好准备。若检测不到复位脉冲则一直处于检测等待。

    我们简单来看就是:主机将DQ拉成低电平保持最少480us后释放总线,延时15 ~ 60us后的60~240us时间内检测DQ是否为低电平,再延时240us保持起始时序的完整。(从主机角度看进行初始化的过程)

    (2)主机写时序

    写周期最少为60微秒,最长不超过120微秒。写周期的开始,主机先把总线拉低大于1微秒表示写周期开始。

    若主机想写0:则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平延时2s。
    若主机想写1:在拉低总线电平1微秒后就释放总线为高电平,延时60us。

    作为从机的DS18B20在检测到总线被拉低后等待15微秒后从15us ~ 45us对总线采样(典型时间15us),在采样时间内,若检测到总线为高电平则认为主机发送了1,若检测到总线为低电平则认为主机发送了0。

    (3)主机读时序

    读周期最少为60微秒,最长不超过120微秒。读周期的开始,主机先把总线拉低大于1微秒,然后释放总线。主机释放总线后:

    若DS18B20发送0:则把总线拉低并保持至少从读周期开始的15us,然后释放总线为高电平。若DS18B20发送1:则在主机释放总线后不拉低总线(为高电平)。

    主机须在读周期开始的15us内检测总线电平的高低,若检测到总线为低则表示DS18B20发送来0,若检测到总线为高则表示DS18B20发送来1。

  • 无论是读还是写,都是以主机把总线拉低至少1us开始。

  • 无论是写0还是读0,都是以把总线释放至少1us结束。对于写0尤其需要注意(程序设计),对于读零过程由DS18B20负责拉高(其实结束前早以拉高)


  • 6- 代码编写

    (1)流程

    如果只有一个DS18B20测温,就不需要搜索ROM、读ROM以及匹配ROM等操作了,只要用跳过ROM(CCH),就可进行如下温度转换(44H)和读取操作(BEH)了。

    (2)创建ds18b20.c

    /*
     * ds18b20.c
     *
     *  Created on: 2022年6月28日
     *      Author: 28980
     */
    
    
    #include "ds18b20.h"
    #include "usart.h"
    #include "tim.h"
    #include "gpio.h"
    #include "main.h"
    
    typedef struct w1_gpio_S
    {
    	GPIO_TypeDef	*group;
    	uint16_t		pin;
    }w1_gpio_s;
    
    static w1_gpio_s W1DS =
    {
    	.group = GPIOA,
    	.pin = GPIO_PIN_5,
    };
    
    #define W1DS_Input()	\
    {\
    	GPIO_InitTypeDef GPIO_InitStruct = {0};\
     	GPIO_InitStruct.Pin = W1DS.pin;\
     	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;\
     	GPIO_InitStruct.Pull = GPIO_PULLUP;\
     	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
     	HAL_GPIO_Init(W1DS.group, &GPIO_InitStruct);\
    }
    
    
    #define W1DS_Output()	\
    {\
    	GPIO_InitTypeDef GPIO_InitStruct = {0};\
     	GPIO_InitStruct.Pin = W1DS.pin;\
     	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;\
     	GPIO_InitStruct.Pull = GPIO_NOPULL;\
     	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;\
     	HAL_GPIO_Init(W1DS.group, &GPIO_InitStruct);\
    }
    
    #define W1DS_Write(x)	HAL_GPIO_WritePin(W1DS.group, W1DS.pin,\
    		(x==1)?GPIO_PIN_SET : GPIO_PIN_RESET)
    
    #define W1DQ_Read()	HAL_GPIO_ReadPin(W1DS.group, W1DS.pin)
    
    uint8_t DS18B20_Reset(void)
    {
    	uint8_t retry = 0;
    	uint8_t rv;
    
    	W1DS_Output();
    	W1DS_Write(1);
    	delay_us(2);
    
    	W1DS_Write(0);
    	delay_us(480);
    
    	W1DS_Write(1);
    	delay_us(60);
    
    	W1DS_Input();
    	while(W1DQ_Read() && retry < 240)
    	{
    		retry++;
    		delay_us(1);
    	}
    
    	if(retry >= 240)
    	{
    		rv = 1;
    	}
    
    	delay_us(240);
    
    	W1DS_Output();
    	W1DS_Write(1);
    	delay_us(240);
    
    	return rv;
    
    }
    
    
    void DS18B20_Writte_Byte(uint8_t byte)
    {
    	uint8_t i;
    
    	W1DS_Output();
    
    	for(i = 0; i< 8; i++)
    	{
    		if(byte & 0x01)
    		{
    			W1DS_Write(0);
    			delay_us(2);
    
    			W1DS_Write(1);
    			delay_us(60);
    		}
    		else
    		{
    			W1DS_Write(0);
    			delay_us(80);
    
    			W1DS_Write(1);
    			delay_us(2);
    		}
    
    		byte >>= 1;
    		delay_us(2);
    	}
    }
    
    
    uint8_t DS18B20_Read_Bit(void)
    {
    	uint8_t rv = 0;
    
    	W1DS_Output();
    	W1DS_Write(0);
    	delay_us(2);
    	W1DS_Write(1);
    	delay_us(2);
    
    	W1DS_Input();
    
    	if(W1DQ_Read())
    	{
    		rv = 1;
    	}
    
    	delay_us(60);
    	W1DS_Output();
    	W1DS_Write(1);
    
    	return rv;
    }
    
    
    uint8_t DS18B20_Read_Byte(void)
    {
    	uint8_t bit;
    	uint8_t i, Byte = 0;
    
    	for(i = 0; i < 8; i++)
    	{
    		bit = DS18B20_Read_Bit();
    		printf("read bit:%d\r\n", bit);
    		if(bit)
    		{
    			Byte |= (1 << i);
    		}
    		delay_us(2);
    	}
    	printf("Byte:%d\r\n", Byte);
    	return Byte;
    }
    
    uint8_t DS18B20_Start_Convet(void)
    {
    	if(DS18B20_Reset())
    	{
    		printf("Initialization failed!\r\n");
    		return -1;
    	}
    
    	DS18B20_Writte_Byte(0xCC);
    
    	DS18B20_Writte_Byte(0x44);
    
    	return 0;
    }
    
    int DS18B20_SampleData(float *temperature)
    {
    	uint8_t Byte[2];
    	uint8_t sign;
    	uint16_t temp;
    
    	if(!temperature)
    		return -1;
    
    	if(DS18B20_Start_Convet())
    	{
    			printf("Convet Temperature failed!\r\n");
    			return -2;
    	}
    
    	if(DS18B20_Reset())
    	{
    		printf("Initialization failed!\r\n");
    		return -3;
    	}
    
    	DS18B20_Writte_Byte(0xCC);
    	DS18B20_Writte_Byte(0xBE);
    
    	Byte[0] = DS18B20_Read_Byte();
    	Byte[1] = DS18B20_Read_Byte();
    
    	printf("Byte[0]:%d\r\n", Byte[0]);
    	printf("Byte[1]:%d\r\n", Byte[1]);
    
    	if(Byte[1] > 7)
    	{
    		sign = 0;
    		temp = ~(Byte[1]<<8 | Byte[0]) + 1;
    	}
    	else
    	{
    		sign = 1;
    		temp = Byte[1]<<8 | Byte[0];
    	}
    
    
    	*temperature = (temp>>4) + (temp & 0x0F) * 0.0625;
    	if(!sign)
    	{
    		*temperature = - *temperature;
    	}
    
    	return 0;
    }
    
    

    (3)创建ds18b20.h

    /*
     * ds18b20.h
     *
     *  Created on: 2022年10月27日
     *      Author: Administrator
     */
    
    #ifndef INC_DS18B20_H_
    #define INC_DS18B20_H_
    
    int DS18B20_SampleData(float *temperature);
    
    #endif /* INC_DS18B20_H_ */
    
    

    (3)编写main.c

      beep_start(2, 300);//蜂鸣器实现代码,可以不要
      printf("Welcome to that DS18B20 get Temperature executor\r\n");
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  if(DS18B20_SampleData(&temperature) < 0)
    	  {
    		  printf("ERROE: DS18B20 Sample Data failure\r\n");
    	  }
    
    	  else
    	  {
    		  printf("DS18B20 Sample Temperature :%.3f\r\n", temperature);
    	  }
    
    	  HAL_Delay(3000);
      /* USER CODE END 3 */
      }
    

    7- 结果展示

    保存烧录然后运行,可以看见如果我们对着传感器哈气,温度会有所变化,说明成功。


    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 一线协议下实现 DS18B20 温度传感器采样

    发表评论