一、DS18B20
直接讲解这个板子上的使用方法(BS18B20以下简称BS):
DS的温度数据为16位两个字节(字节0,字节1),高四位(SSSS)表示的是温度正负(0正1负),低四位是小数部分。其余是整数。读取的时候是一串16位数据,要获得小数部分就需要除16.0.
DS的存储图:
那如何与DS通讯呢?DS有自己的一套通讯流程和控制指令,下面讲解一下通讯流程和这里用到的指令:
也就是分为下面三步,一定不要少步骤:
1.初始化
2.ROM控制指令
3.DS的功能指令
初始化的时序逻辑:
看看就行,赛点给了驱动,下面讲的DHT11没有,那就得自己写了
ROM指令
下面是DS的ROM指令:
Read ROM [33h]:
- 指令:0x33
- 功能: 主机发送此指令后,DS18B20将其唯一的64位ROM码(8字节)返回给主机。用于标识唯一的DS18B20传感器。
Match ROM [55h]:
- 指令:0x55
- 功能: 主机通过发送0x55指令,然后传递64位的ROM码,与总线上的DS18B20传感器进行匹配。只有匹配的传感器才会响应后续的指令。
Search ROM [F0h]:
- 指令:0xF0
- 功能: 主机通过发送0xF0指令,可以在总线上搜索连接的所有DS18B20传感器的ROM码。用于在多个传感器连接到同一总线时识别每个传感器。
Skip ROM [CCh]:
- 指令:0xCC
- 功能: 主机通过发送0xCC指令,可以跳过对单个传感器的ROM码的匹配,直接与总线上的所有DS18B20通信。适用于只连接一个DS18B20传感器的情况。
Alarm Search [ECh]:
- 指令:0xECh
- 功能: 主机发送此指令后,DS18B20会在总线上搜索具有温度报警状态的传感器。用于检索出发生温度报警的传感器。
Resume [ACh]:
- 指令:0xACh
- 功能: 主机发送此指令后,DS18B20从温度转换暂停状态恢复到正常工作状态。用于取消暂停状态。
Recall E2 [B8h]:
- 指令:0xB8h
- 功能: 主机发送此指令后,DS18B20将从其内部的EEPROM中恢复出厂默认设置,包括温度报警的上下限值和分辨率等设置。
Read Power Supply [B4h]:
- 指令:0xB4h
- 功能: 主机发送此指令后,DS18B20返回其供电方式的信息。用于检测DS18B20的供电状态,如使用外部电源还是从总线中提取电能。
Convert T [44h]:
- 指令:0x44
- 功能: 主机发送此指令后,DS18B20启动温度转换。用于触发DS18B20进行温度测量。
Write Scratchpad [4Eh]:
- 指令:0x4E
- 功能: 主机发送此指令后,DS18B20启动对Scratchpad的写入操作。在此之后,主机可以通过发送写入数据指令来写入Scratchpad中的配置信息,如温度阈值等。
Read Scratchpad [BEh]:
- 指令:0xBE
- 功能: 主机发送此指令后,DS18B20返回其Scratchpad中的数据,包括温度值等信息。用于读取DS18B20的当前状态和测量结果。
功能指令:
Convert T (温度转换):
- 指令:0x44
- 解释: 主机发送此指令后,DS18B20启动温度转换。DS18B20将开始测量温度并将结果存储在其内部的Scratchpad寄存器中。
Read Scratchpad (读取Scratchpad):
- 指令:0xBE
- 解释: 主机发送此指令后,DS18B20将其Scratchpad中的数据传输给主机。这包括温度数据以及相关配置信息。
Write Scratchpad (写入Scratchpad):
- 指令:0x4E
- 解释: 主机发送此指令后,DS18B20将准备好接收来自主机的3个字节的数据,用于写入Scratchpad的字节2、3和4中(即TH、TL和配置寄存器)。
Copy Scratchpad (复制Scratchpad):
- 指令:0x48
- 解释: 主机发送此指令后,DS18B20将其Scratchpad中的TH、TL和配置寄存器的数据复制到EEPROM中,以便长期存储。
Recall E2 (回忆E2):
- 指令:0xB8
- 解释: 主机发送此指令后,DS18B20从其内部的EEPROM中检索出TH、TL和配置寄存器的数据,并将其复制到Scratchpad中。
Read Power Supply (读取供电状态):
- 指令:0xB4
- 解释: 主机发送此指令后,DS18B20将其供电方式的信息传输给主机。这用于指示DS18B20是通过外部电源还是通过总线供电。
代码:
uint16_t ds18b20_read()
{
uint8_t low_temp, high_temp;
ow_reset(); //步骤一,初始化
ow_byte_wr(0xCC); //步骤二,ROM指令
ow_byte_wr(0x44); //步骤三,功能控制指令
delay_us(750000);
ow_reset();
ow_byte_wr(0xCC);
ow_byte_wr(0xBE);
low_temp = ow_byte_rd();//读取
high_temp = ow_byte_rd();
return ((high_temp<<8)|low_temp);//合成结果
}
为什么延时750ms呢?如下
12位分辨率最大转换时间为750ms。
二、DHT11
这个传感器就要靠我们自己来写了,也不难。
MCU和DHT11传感器之间的通信协议采用单总线数据格式。每个通信过程大约需要4毫秒。数据由整数部分和小数部分组成。完整的数据传输为40位,传感器先发送高位数据位。数据格式为:8位整数RH(湿度)数据 + 8位小数RH数据 + 8位整数T(温度)数据 + 8位小数T数据 + 8位校验和。如果数据传输正确,则校验和应该是“8位整数RH数据 + 8位小数RH数据 + 8位整数T数据 + 8位小数T数据”的最后8位。
MCU的起始信号与DHT11的应答
数据单母线自由状态处于高压电平。当单片机与DHT11的通信开始时,单片机程序将数据单总线电压水平从高到低,该过程必须至少需要18 ms以确保DHT检测单片机的信号,然后单片机将提高电压,等待20-40我们DHT的响应。
一旦DHT检测到启动信号,它将发出一个持续80us的低压电平响应信号。然后DHT程序将数据单总线电压水平从低到高,并保持80us,为DHT发送数据做准备。当DHT向单片机发送数据时,每一位数据都以50us低压电平开始,以下高压电平信号的长度决定了数据位是“0”还是“1”
所以在开始通讯前要先进行上面这样一个MCU发出起始信号,DHT11给出回应:
代码如下:
#include "main.h"
#define DHT11_PIN_PORT GPIOA
#define DHT11_PIN GPIO_PIN_7
#define DHT11_PIN_CLOCK __HAL_RCC_GPIOA_CLK_ENABLE()
#define DHT11_PIN_OUT_H HAL_GPIO_WritePin(DHT11_PIN_PORT, DHT11_PIN, GPIO_PIN_SET)
#define DHT11_PIN_OUT_L HAL_GPIO_WritePin(DHT11_PIN_PORT, DHT11_PIN, GPIO_PIN_RESET)
#define DHT11_PIN_IN HAL_GPIO_ReadPin(DHT11_PIN_PORT, DHT11_PIN)
extern float DH_Humi;
extern float DH_Temp;
void DHT11_GetValue(void);
void DHT11_Start()
{
DHT11_OUT();//PA7对外输出
DHT11_PIN_OUT_L;//拉低电平
HAL_Delay(20);//按照通讯协议等20us
DHT11_PIN_OUT_H;//拉高
DelayDH_us(60);//等20~40,可以多等一会
}
uint8_t DHT11_Cheak()
{
DHT11_IN();//PA7输入
uint8_t tim;
while(DHT11_PIN_IN&&tim<100)//按照通讯协议DHT11这里会先拉低,在这里等待拉低
{
tim++;
DelayDH_us(1);
}
if(tim>=100) return 0;//超时返回0代表未联系上DHT11
tim=0;
while(!DHT11_PIN_IN&&tim<100)//等DHT11拉高
{
tim++;
DelayDH_us(1);
}
if(tim>=100) return 0;//超时
return 1;//联系成功,这之后DHT11会发送数据
}
数据的时序
数据0的时序
数据1的时序
从上面可以看出来DHT11区别0/1是根据拉低50us后再拉高后高电平的持续时间。
那我们就可以等大于30us的(可以更多)如果是低电平那就是0否则是1.
uint8_t DHT11_Read_Bit(void)
{
uint8_t re = 0;
while(DHT11_PIN_IN && re < 110) //等待变为低电平
{
re++;
DelayDH_us(1);
}
re = 0;
while(!DHT11_PIN_IN && re < 110) //等待变高电平
{
re++;
DelayDH_us(1);
}
DelayDH_us(60);//等待60us
if(DHT11_PIN_IN)return 1;
else return 0;
}
之后就可以调用这个函数读取一个字节:
//从DHT11读取一个字节
uint8_t DHT11_Read_Byte(void)
{
uint8_t i, dat;
dat = 0;
for (i = 0; i < 8; i++)
{
dat <<= 1;
dat |= DHT11_Read_Bit();
}
return dat;
}
再封装一下,DHT11读取数据函数:
uint8_t buf[5];
uint8_t DHT11_Read_Data(float *temp, float *humi)
{
uint8_t i;
DHT11_Start();
if(DHT11_Cheak() == 1)
{
for(i = 0; i < 5; i++)
{
buf[i] = DHT11_Read_Byte();
}
if((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
{
*humi = buf[0]+buf[1]/8.0;
*temp = buf[2]+buf[3]/8.0;
}
}
else return 0;
return 1;
}
温湿度的变量我设置为了全局,方便调试
void DHT11_GetValue()
{
DHT11_Read_Data(&DH_Temp,&DH_Humi);
}