stm32外设笔记-单总线设备(DS18B20|DHT11)

1、DS18B20驱动

一般常见的DS18B20如下所示,当然也有那种金属头防水的,本质上用的芯片还是这个,这里我们不对他进行单独介绍。
在这里插入图片描述

根据官方的数据手册,关于DS18B20的描述,这里翻译如下:

官方的数据手册我也放到我的gitee了,有需要的可以直接下载: DS18B20

DS18B20 数字温度计提供 9 至 12 位(可配置)温度读数,用于指示设备的温度。信息通过 1-Wire 接口发送到 DS18B20 或从 DS18B20 发送,因此只有一根线(和地线)需要从中央微处理器连接到 DS18B20。读取、写入和执行温度转换的电源可以来自数据线本身,无需外部电源。因为每个 DS18B20 都包含一个唯一的芯片序列号,所以多个 DS18B20 可以存在于同一条 1-Wire 总线上。这允许将温度传感器放置在许多不同的地方。此功能有用的应用包括 HVAC 环境控制、建筑物、设备或机械内部的温度感应以及过程监控。

总结一下就是:

  • 单总线,并且每个设备出厂唯一地址,所以理论上可以不断扩展设备
  • 精度9-12位
  • 最少仅仅接两根线(信号和地即可完成控制)

一般我们常用的原理图如下所示:
在这里插入图片描述

关于DS18B20的数据读取,官方手册描述如下:

与 DS18B20 的通信是通过 1-Wire 端口进行的。对于 1-Wire 端口,在 ROM 功能协议建立之前,存储器和控制功能将不可用。主机必须首先提供五个 ROM 功能命令之一:

  • (1)读取 ROM
  • (2)匹配 ROM
  • (3)搜索 ROM
  • (4)跳过 ROM
  • (5) 报警搜索

这些命令在每个器件的 64 位激光 ROM 部分上运行,如果 1-Wire 线路上存在许多器件,则可以挑选出特定器件,并向总线主机指示存在器件的数量和类型。在成功执行 ROM 功能序列后,可以访问内存和控制功能,然后主机可以提供六个内存和控制功能命令中的任何一个。

一个控制功能命令指示 DS18B20 执行温度测量。该测量结果将被放入 DS18B20 的暂存器存储器中,并且可以通过发出读取暂存器存储器内容的存储器功能命令来读取。温度报警触发器 TH 和 TL 各由 1 个字节的 EEPROM 组成。如果报警搜索命令未应用于 DS18B20,这些寄存器可用作通用用户存储器。暂存器还包含一个配置字节,用于设置温度到数字转换的所需分辨率。写入 TH、TL 和配置字节是使用存储器功能命令完成的。对这些寄存器的读取访问是通过暂存器进行的。所有数据都先读取和写入最低有效位。

2、DS18B20驱动实战

从数据手册中可以获知DS18B20数据读写时序图

  • 复位信号
    在这里插入图片描述
    对应代码编写如下:
    在这里插入图片描述
  • 数据读写时序图
    在这里插入图片描述
    对应代码如下
    在这里插入图片描述
  • 之后就是整合数据了,具体如下所示

在这里插入图片描述

完整代码如下所示

ds18b20.c

#include "ds18b20.h"

#define DS18B20_HIGH HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, GPIO_PIN_SET)
#define DS18B20_LOW HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, GPIO_PIN_RESET)


#define fac_us 72   //时钟频率,单位MHZ

/*微秒级延时函数*/
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; 						//需要的节拍数
	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;			//时间超过/等于要延迟的时间,则退出.
		}
	}
}


void Ds18b20_Write_Out_Input(uint8_t cmd) //这里和iic那个部分差不多
{
	GPIO_InitTypeDef GPIO_InitStruct;

	if(cmd) //为1的时候是输出模式
	{
		  GPIO_InitStruct.Pin = DS18B20_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
		  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		  HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);
	}
	else // 为0的时候上拉输入
	{
		  GPIO_InitStruct.Pin = DS18B20_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
		  HAL_GPIO_Init(DS18B20_GPIO_Port, &GPIO_InitStruct);
	}
}

uint8_t Ds18b20_Reset(void) //可用此函数检测是否存在ds18b20
{
	uint8_t data;

	Ds18b20_Write_Out_Input(1);
	DS18B20_LOW;
	delay_us(480);
	DS18B20_HIGH;
	delay_us(60);
	Ds18b20_Write_Out_Input(0);
	data = HAL_GPIO_ReadPin(DS18B20_GPIO_Port, DS18B20_Pin);
	delay_us(420);

	return data;
}

void Ds18b20_Write_Byte(uint8_t data)
{
	for(uint8_t i = 0;i<8;i++)
	{
		Ds18b20_Write_Out_Input(1);
		DS18B20_LOW;
		delay_us(2);
		if(data&0x01) //写1拉高,否则拉低
			DS18B20_HIGH;
		else
			DS18B20_LOW;
		delay_us(60);
		DS18B20_HIGH;

		data >>= 1; //写完一位之后数据移位
	}
}

uint8_t Ds18b20_Read_Byte(void)
{
	uint8_t data = 0;
	for(uint8_t i = 0;i<8;i++)
	{
		data >>= 1;
		Ds18b20_Write_Out_Input(1);
		DS18B20_LOW;
		delay_us(2);
		DS18B20_HIGH;
		Ds18b20_Write_Out_Input(0);
		if(HAL_GPIO_ReadPin(DS18B20_GPIO_Port, DS18B20_Pin) == 1)
		{
			data |= 0x80; // >>1
		}
		delay_us(60);
	}
	return data;
}

float Ds18b20_Read_Temp(void)
{
	uint8_t MSB = 0,MSL = 0;
	uint16_t Temp = 0;
	float temp = 0;

	if(Ds18b20_Reset() == RESET)
	{
		Ds18b20_Reset();
		Ds18b20_Write_Byte(0XCC);
		Ds18b20_Write_Byte(0X44);
		HAL_Delay(750);
		Ds18b20_Reset();
		Ds18b20_Write_Byte(0XCC);
		Ds18b20_Write_Byte(0XBE);

		MSL = Ds18b20_Read_Byte();
		MSB = Ds18b20_Read_Byte();
		Temp = 	MSB;
		Temp = Temp << 8|MSL;

		if((Temp&0XF800) == 0XF800)//如果结果是负数
		{
			temp = (((~Temp)+0x10)*-0.0625);
		}
		else
		{
			temp = Temp*0.0625; //这个就是浮点数参数了
//			Temp = temp*10+0.5;
		}
		return temp;
	}
	else
		return 0;//没有检测到传感器
}

ds18b20.h

#include "main.h"

uint8_t Ds18b20_Reset(void);
float Ds18b20_Read_Temp(void);
void delay_us(uint32_t nus);

在主函数中周期打印结果如下:
在这里插入图片描述

读取结果如下所示,可以看到数据基本正常
在这里插入图片描述

3、多个DS18B20一起驱动

先看下最终的效果:

在这里插入图片描述
因为在最开始就提到这个传感器每个地址都不一样嘛,这样就可以进行配置了,具体命令见原理图如下所示:
在这里插入图片描述

然后是代码部分,相比上面的代码部分,增加了读传感器ROM的相关操作

void DS18B20_Read_ROM(void)
{

	uint8_t morid[8];

	Ds18b20_Reset();
	Ds18b20_Write_Byte(0x33);

	for(uint8_t i=0;i<8;i++){

		morid[i] = Ds18b20_Read_Byte();
		printf("%#X,",morid[i]);
	}
	printf("\r\n");
}
void DS18B20_Write_ROM(uint8_t *id)
{

	Ds18b20_Reset();
	Ds18b20_Write_Byte(0x55);

	for(uint8_t i=0;i<8;i++){

		Ds18b20_Write_Byte(*id++);
	}
}

读数据的函数也做了相关变化如下所示:
在这里插入图片描述
实际代码如下所示

float Ds18b20_Read_Temp(uint8_t *id)
{
	uint8_t MSB = 0,MSL = 0;
	uint16_t Temp = 0;
	float temp = 0;

	if(Ds18b20_Reset() == RESET)
	{
		DS18B20_Write_ROM(id);
		Ds18b20_Write_Byte(0X44);
		HAL_Delay(750);
		DS18B20_Write_ROM(id);
		Ds18b20_Write_Byte(0XBE);

		MSL = Ds18b20_Read_Byte();
		MSB = Ds18b20_Read_Byte();
		Temp = 	MSB;
		Temp = Temp << 8|MSL;

		if((Temp&0XF800) == 0XF800)//如果结果是负数
		{
			temp = (((~Temp)+0x10)*-0.0625);
		}
		else
		{
			temp = Temp*0.0625; //这个就是浮点数参数了
//			Temp = temp*10+0.5;
		}
		return temp;
	}
	else
		return 0;//没有检测到传感器
}

之后首先在主函数初始化部分读取下我们连上的设备id
在这里插入图片描述
之后将获取到的id存到我们事先准备好的数组中去
在这里插入图片描述
下面开始测试功能是否正常
在这里插入图片描述

3、DHT11驱动

一般我们用的如下所示
在这里插入图片描述
参数详情
在这里插入图片描述
连接图
在这里插入图片描述
通信时序:

官方数据手册描述如下:

当 MCU 发出启动信号时,DHT11 从低功耗模式切换到运行模式,等待 MCU 完成启动信号。完成后,DHT11 会向 MCU 发送一个包含相对湿度和温度信息的 40 位数据的响应信号。用户可以选择收集(读取)一些数据。没有来自 MCU 的启动信号,DHT11 不会给 MCU 响应信号。数据采集​​完毕后,DHT11 将切换到低功耗模式,直到再次收到来自 MCU 的启动信号。
在这里插入图片描述

  • 起始信号

在这里插入图片描述

  • 数据0
    在这里插入图片描述
  • 数据1

在这里插入图片描述
详情见下面的代码编辑部分!!!

4、DHT11驱动实战

完整代码如下所示:

dht11.c

#include "dht11.h"
#include "ds18b20.h"

uint16_t time = 0;

#define DHT11_HIGH HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_SET)
#define DHT11_LOW HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)

void DHT11_Write_Out_Input(uint8_t cmd) //这里和iic那个部分差不多
{
	GPIO_InitTypeDef GPIO_InitStruct;

	if(cmd) //为1的时候是输出模式
	{
		  GPIO_InitStruct.Pin = DHT11_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
		  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
		  HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
	}
	else // 为0的时候上拉输入
	{
		  GPIO_InitStruct.Pin = DHT11_Pin;
		  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
		  HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
	}
}
uint8_t DHT11_Read_Byte(void)
{
	uint8_t data = 0;
	DHT11_Write_Out_Input(0);
	for(uint8_t i = 0;i<8;i++)
	{
		while((HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_RESET)&&(++time<1000));
		time = 0;

		data <<= 1;
		delay_us(40);
		if(HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET)
		{
			data |= 0x01;
			while((HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET)&&(++time<1000));
		}
	}
	return data;
}

uint8_t DHT11_Read_Humi_Temp(uint8_t *humi_h,uint8_t *humi_l,uint8_t *temp_h,uint8_t *temp_l)
{
	uint8_t data[5]; //四十位数据

	DHT11_Write_Out_Input(1);
	DHT11_LOW;
	HAL_Delay(20);
	DHT11_HIGH;
	delay_us(30);

	DHT11_Write_Out_Input(0);
	while((HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_RESET) && (++time<1000));
	time = 0;
	while((HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin) == GPIO_PIN_SET) && (++time<1000));
	time = 0;

	for(uint8_t i=0;i<5;i++)
	{
		data[i] = DHT11_Read_Byte();
	}
	HAL_Delay(1);
	if(data[0]+data[1]+data[2]+data[3] == data[4])
	{
		*humi_h = data[0];
		*humi_l = data[1];
		*temp_h = data[2];
		*temp_l = data[3];
	}
	else
		return 1;
	return 0;
}

dht11.h

uint8_t DHT11_Read_Humi_Temp(uint8_t *humi_h,uint8_t *humi_l,uint8_t *temp_h,uint8_t *temp_l);

在主函数中编写温度读取函数
在这里插入图片描述

效果如下所示:
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桃成蹊2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值