本帖最后由 尹小舟 于 2024-6-4 10:58 编辑
1 . DS18B20
是Dallas Semiconductor(现在属于Maxim Integrated Products)制造的一种常用的1-Wire接口的数字温度计。
主要特性:
- 1-Wire接口:只需一个数据线(加上地线)即可与微控制器通信,简化了硬件设计。
- 数字输出:直接输出数字温度值,无需额外的A/D转换电路。
- 测量范围:通常为-55°C到+125°C(不同型号可能有所不同)。
- 分辨率:可编程为9位到12位,对应于0.5°C到0.0625°C的分辨率。
- 多点测温:可以在同一数据线上连接多个DS18B20传感器,通过ROM命令识别每个传感器。
- 低功耗:在待机模式下功耗极低,适合电池供电的应用。
- 封装:常见封装为TO-92(晶体管封装)或小型表面贴装封装(如SOT-23)。
可应用于环境控制、设备或过程控制、消费类测温电子产品等。
2 . DS18B20注意千万不要接反
DS18B20通常有三根引脚:VCC(电源正极)、GND(地线)和DQ(数字信号线)。在连接时,需要确保:
- VCC:连接到微控制器或电源的正极(通常是+5V或+3.3V,取决于你的系统和DS18B20的型号)。
- GND:连接到微控制器或电源的负极(地线)。
- DQ:连接到微控制器的数据线上。这根线用于与DS18B20进行1-Wire通信。
其他元件,一般VCC和GND不是对称的,接反不会烧毁,DS18B20接反的话会直接短路。
DS18B20的体积很小,用它组成的温度测量系统线路非常简单,只需要一根数据线即可实现通信,并且可通过数据线寄生供电。单总线系统只有一根数据线,主机或从机设备通过一个漏极开路(或3态端口连接至该数据线,从而使得主机或从机设备在不发送数据时可释放数据总线,以便总线可被其他设备使用。由DS18B20的DO开漏等效电路图可知,其单总线要求外接一个约5kΩ的上拉电阻,以保证总线闲置时为高电平。
3 . 内部结构
DS18B20的温度测量范围在-55~+125℃之间,数字温度计的分辨率可以从9~12位选择(默认为12位),内部可设置非易失报警温度上、下限(TH、TL),每个器件均有唯一的8字节(64位)光刻码(LaseredROMCode),分别为1字节CRC检验码、6字节序列号和1字节家族代码(FamilyCode:0x28),这使得多个DS18B20传感器可以共用总线构成多点测温网络。利用ROM 搜索命令可通过排除法获取总线上挂载的所有器件的光刻码,利用报警搜索命令可识别并标识出总线上所有超过限定温度的器件。
4 . DS18B20 的时序
之前我使用Arduino的库驱动失败了,要自己实现DS18B20驱动了
单总线器件要求遵守严格的通信协议以保证数据的完整性,该协议定义了几种信号类型,包括复位脉冲、应答脉冲(在线脉冲或称存在脉冲)、写0/1、读0/1,除了应答脉冲以外,其余信号均由主机发出同步信号,并且发送的所有命令和数据都是字节的低位在前。时序如下图
5 . delayMicroseconds()
在Arduino 中 delayMicroseconds()与delay()函数都可用于延迟程序运行。不同的是,delayMicroseconds()的参数单位是微秒(1毫秒=1000微秒)。
6.程序代码
#include "Arduino.h"
#include "esp_system.h"
/* 引脚定义 */
#define DS18B20_DQ_PIN SCL
/* 宏函数 */
#define DS18B20_DQ_OUT(x) digitalWrite(DS18B20_DQ_PIN, x)
#define DS18B20_DQ_IN digitalRead(DS18B20_DQ_PIN)
#define DS18B20_MODE_IN pinMode(DS18B20_DQ_PIN, INPUT)
#define DS18B20_MODE_OUT pinMode(DS18B20_DQ_PIN, OUTPUT)
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] 复位DS18B20
* @param 无
* @retval 无
*/
static void ds18b20_reset(void)
{
DS18B20_MODE_OUT;
DS18B20_DQ_OUT(0); /* 拉低DQ,复位 */
delayMicroseconds(750); /* 拉低750us */
DS18B20_DQ_OUT(1); /* DQ=1, 释放复位 */
delayMicroseconds(15); /* 延迟15US */
}
/**
* @brief 等待DS18B20的回应
* @param 无
* @retval 0, DS18B20正常
* 1, DS18B20异常/不存在
*/
uint8_t ds18b20_check(void)
{
uint8_t retry = 0;
uint8_t rval = 0;
DS18B20_MODE_IN;
while (DS18B20_DQ_IN && retry < 200) /* 等待DQ变低, 等待200us */
{
retry++;
delayMicroseconds(1);
}
if (retry >= 240)
{
rval = 1;
}
else
{
retry = 0;
while (!DS18B20_DQ_IN && retry < 240) /* 等待DQ变高, 等待240us */
{
retry++;
delayMicroseconds(1);
}
if (retry >= 240)
{
rval = 1;
}
}
delayMicroseconds(400);
return rval;
}
/**
* @brief 从DS18B20读取一个位
* @param 无
* @retval 读取到的位值: 0 / 1
*/
static uint8_t ds18b20_read_bit(void)
{
uint8_t data = 0;
DS18B20_MODE_OUT;
DS18B20_DQ_OUT(0);
delayMicroseconds(2);
DS18B20_DQ_OUT(1);
delayMicroseconds(16);
DS18B20_MODE_IN;
if (DS18B20_DQ_IN)
{
data = 1;
}
delayMicroseconds(50);
return data;
}
/**
* @brief 从DS18B20读取一个字节
* @param 无
* @retval 读到的数据
*/
static uint8_t ds18b20_read_byte(void)
{
uint8_t i, b, data = 0;
for (i = 0; i < 8; i++)
{
b = ds18b20_read_bit(); /* DS18B20先输出低位数据 ,高位数据后输出 */
data |= b << i; /* 填充data的每一位 */
}
return data;
}
/**
* @brief 写一个字节到DS18B20
* @param data: 要写入的字节
* @retval 无
*/
static void ds18b20_write_byte(uint8_t data)
{
uint8_t j;
DS18B20_MODE_OUT;
for (j = 1; j <= 8; j++)
{
if (data & 0x01)
{
DS18B20_DQ_OUT(0);
delayMicroseconds(2);
DS18B20_DQ_OUT(1);
delayMicroseconds(60);
}
else
{
DS18B20_DQ_OUT(0);
delayMicroseconds(60);
DS18B20_DQ_OUT(1);
delayMicroseconds(2);
}
data >>= 1;
}
}
/**
* @brief 开始温度转换
* @param 无
* @retval 无
*/
static void ds18b20_start(void)
{
ds18b20_reset();
if(ds18b20_check())
{
Serial.printf("没有找到DS18B20\r\n");
}
ds18b20_write_byte(0xcc); /* skip rom */
ds18b20_write_byte(0x44); /* convert */
delayMicroseconds(970);
}
/**
* @brief 初始化DS18B20
* @param 无
* @retval 0:正常,1:不存在/不正常
*/
uint8_t ds18b20_init(void)
{
ds18b20_reset();
return ds18b20_check();
}
/**
* @brief 从ds18b20得到温度值(精度:0.1C)
* @param 无
* @retval 温度值 (-550~1250)
* @note 返回的温度值放大了10倍.
* 实际使用的时候,要除以10才是实际温度.
*/
short ds18b20_get_temperature(void)
{
uint8_t flag = 1; /* 默认温度为正数 */
uint8_t TL = 0, TH = 0;
short temp;
ds18b20_start(); /* ds1820 start convert */
ds18b20_reset();
ds18b20_check();
ds18b20_write_byte(0xcc); /* skip rom */
ds18b20_write_byte(0xbe); /* convert */
TL = ds18b20_read_byte(); /* LSB */
TH = ds18b20_read_byte(); /* MSB */
Serial.printf("DS18B20 Data TL = %d TH = %d\r\n",TL,TH);
if (TH > 7)
{ /* 温度为负,查看DS18B20的温度表示法与计算机存储正负数据的原理一致:
正数补码为寄存器存储的数据自身,负数补码为寄存器存储值按位取反后+1
所以我们直接取它实际的负数部分,但负数的补码为取反后加一,但考虑到低位可能+1后有进位和代码冗余,
我们这里先暂时没有作+1的处理,这里需要留意 */
TH = ~TH;
TL = ~TL;
flag = 0;
}
temp = TH; /* 获得高八位 */
temp <<= 8;
temp += TL; /* 获得底八位 */
/* 转换成实际温度 */
if (flag == 0)
{ /* 将温度转换成负温度,这里的+1参考前面的说明 */
temp = (double)(temp + 1) * 0.625;
temp = -temp;
}
else
{
temp = (double)temp * 0.625;
}
return temp;
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if(ds18b20_init())
{
Serial.printf("没有找到DS18B20\r\n");
}
}
void loop() {
short c_data = 0;
// put your main code here, to run repeatedly:
c_data = ds18b20_get_temperature();
Serial.printf("DS18B20 Data = %d\r\n",c_data);
delay(100);
}