1. 外设温度计概述
外设温度计通常指通过单片机等主控芯片外接的温度传感器,用于实时采集环境或物体温度。在嵌入式开发中,DS18B20 是最常用的数字温度传感器之一,它采用单总线通信协议,只需一根数据线即可完成与主控的通信,接线简单、成本低廉,广泛应用于工业控制、智能家居、环境监测等场景。
2. DS18B20 传感器简介
DS18B20 是一款单总线的数字温度传感器,可以采集 9 到 12 bit 的温度数据,并支持设置温度上下限报警。它内部集成了温度采集、模数转换和通信逻辑,主控芯片只需通过单总线协议即可读取温度值。
- 测温范围:-55 ℃ 到 +125 ℃。
- 供电范围:3.0V 到 5.5V。
- 采集时间:在 12 bit 分辨率下,采集一次温度耗时约 750ms。
- 分辨率:可配置为 9 到 12 bit,默认上电为 12 bit。
3. 引脚说明
DS18B20 共有三个引脚,接线方式如下:
| 引脚 | 名称 | 说明 |
|---|---|---|
| GND | 地线 | 接单片机的 GND |
| DQ | 数据线 | 通信传输引脚,接单片机的 P37 |
| VCC | 电源 | 接单片机的 VCC 引脚 |
4. 单总线通信时序
DS18B20 采用单总线协议,所有通信都由主机发起,时序要求较为严格,主要包括复位信号、写时序和读时序三类。
4.1 复位信号
- 空闲时总线保持高电平。
- 开始通信时,主机拉低总线至少 480us,让从机复位。
- 从机会在 480us 之内发送一个 60 到 240us 的低电平脉冲,表示对主机复位信号的响应。
4.2 写时序
- 主机写 0:拉低总线 60 到 120us,表示发送 0。
- 主机写 1:先拉低总线至少 1us,必须在 15us 以内拉高总线到高电平,让 DS18B20 采样到高电平。
- 发送数据时,位与位之间的间隔要大于 1us。
4.3 读时序
- 读取数据时,主机先拉低总线至少 1us,随后 DS18B20 开始控制总线向主机发送数据。
- 主机必须在 15us 内,在 13us 左右完成对总线电平的采样。
- DS18B20 发送 0 时,主机采样到低电平;发送 1 时,主机采样到高电平。
5. 内部暂存器与指令
DS18B20 内部有暂存器用于存放温度转换结果,主机通过发送特定指令来控制传感器工作。常用指令如下:
| 指令 | 功能 |
|---|---|
| 0xCC | 跳过 ROM,直接访问传感器 |
| 0x44 | 启动温度转换,转换结果存入暂存器 |
| 0xBE | 读取暂存器中的 9 个字节数据 |
6. 温度读取整体流程
读取 DS18B20 温度数据的完整流程如下:
- 发送 DS18B20 复位信号。
- 发送 0xCC,跳过 ROM。
- 发送 0x44,启动温度转换。
- 等待约 800ms,等待转换完成。
- 再次发送复位信号。
- 发送 0xCC,跳过 ROM。
- 发送 0xBE,读取暂存器数据。
- 读取 2 个字节数据,第一个字节为 LSB,第二个字节为 MSB。
- 温度值 = 读取到的数值 × 0.0625。
7. 代码示例
下面给出基于 51 单片机读取 DS18B20 温度并通过串口输出的完整示例代码,可直接在 Keil 中编译运行。
c
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>
sbit DQ = P3^7;
volatile unsigned char RecvBuff[20] = {0};
volatile unsigned char RecvLen = 0;
volatile unsigned char IsCommandLine = 0;
void Delay800ms(void) //@11.0592MHz
{
unsigned char data i, j, k;
_nop_();
i = 6;
j = 155;
k = 91;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay480us(void) //@11.0592MHz
{
unsigned char data i;
_nop_();
i = 218;
while (--i);
}
void Delay10us(void) //@11.0592MHz
{
unsigned char data i;
i = 2;
while (--i);
}
void UartHandler(void) interrupt 4
{
if (RI)
{
RecvBuff[RecvLen] = SBUF;
RI = 0;
RecvLen++;
if ('\n' == RecvBuff[RecvLen-1])
{
IsCommandLine = 1;
}
}
return;
}
void UartInit(void)
{
//设置串口工作模式为模式1
SCON &= ~(0x3 << 6);
SCON |= (0x1 << 6);
//使能接收
REN = 1;
//波特率不加倍
PCON &= ~(0x1 << 7);
//设置定时器1为8位自动重装载
TMOD &= ~(0x3 << 4);
TMOD |= 0x1 << 5;
//设置波特率为2400,TH1初值为0xF4
TH1 = 0xF4;
//开启定时器1
TR1 = 1;
//开启串口中断
ES = 1;
EA = 1;
return;
}
void UartSendByte(unsigned char TmpByte)
{
SBUF = TmpByte;
while (0 == TI);
TI = 0;
return;
}
void UartSendString(char *pstr)
{
while (*pstr != '\0')
{
UartSendByte(*pstr);
pstr++;
}
return;
}
//DS18B20复位,拉低480us
void DQReset(void)
{
DQ = 0;
Delay480us();
DQ = 1;
return;
}
//读取DS18B20的回复
void ReadDQRespone(void)
{
Delay10us();
//等待低电平信号到来
while (DQ);
//跳过低电平
while (!DQ);
return;
}
void SendByte(unsigned char TmpByte)
{
int n = 0;
for (n = 0; n < 8; n++)
{
if (TmpByte & (0x1 << n))
{
DQ = 0;
_nop_();
_nop_();
DQ = 1;
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
}
else
{
DQ = 0;
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
DQ = 1;
_nop_();
_nop_();
}
}
return;
}
unsigned char RecvByte(void)
{
unsigned char TmpByte = 0x00;
unsigned char n = 0;
for (n = 0; n < 8; n++)
{
DQ = 0;
_nop_();
_nop_();
DQ = 1;
Delay10us();
if (DQ)
{
TmpByte |= (0x1 << n);
}
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
Delay10us();
}
return TmpByte;
}
int ReadTemp(void)
{
unsigned char LSBVal = 0;
unsigned char MSBVal = 0;
int Val = 0;
//上电1ms,等待电平平稳
DQ = 1;
Delay480us();
Delay480us();
DQReset();
ReadDQRespone();
SendByte(0xCC);
SendByte(0x44);
Delay800ms();
DQReset();
ReadDQRespone();
SendByte(0xCC);
SendByte(0xBE);
LSBVal = RecvByte();
MSBVal = RecvByte();
Val = (MSBVal << 8) | LSBVal;
Val >>= 4;
return Val;
}
int main(void)
{
int Temp = 0;
char tmpbuff[20] = {0};
UartInit();
while (1)
{
Temp = ReadTemp();
sprintf(tmpbuff, "%d", Temp);
UartSendString(tmpbuff);
Delay800ms();
}
return 0;
}
代码说明:主函数循环读取温度值,通过 sprintf 转换为字符串后经串口发送,每次发送后延时 800ms。下面按功能模块对代码进行详细分析。
7.1 延时函数
代码中提供了三个延时函数,均基于 11.0592MHz 晶振计算循环次数:Delay800ms 用于等待温度转换完成,Delay480us 用于 DS18B20 复位时拉低总线,Delay10us 用于读写时序中的位级延时。这些延时通过空循环实现,精度依赖晶振频率,实际使用时需根据主频调整循环次数。
7.2 串口初始化与收发
UartInit 将串口配置为模式 1(8 位 UART),使能接收,波特率设为 2400,并开启串口中断。串口接收中断 UartHandler 将收到的字节存入 RecvBuff,当收到换行符时置位 IsCommandLine,用于后续命令解析。发送函数 UartSendByte 和 UartSendString 分别实现单字节和字符串发送,主函数通过它们把温度值输出到上位机。
7.3 单总线底层时序
DQReset 实现复位信号,拉低总线 480us 后释放;ReadDQRespone 等待从机的应答脉冲。SendByte 按位发送数据,写 1 时先拉低再快速拉高,写 0 时保持低电平约 60us;RecvByte 按位读取数据,在拉低总线后于 15us 内采样电平,从而还原每个 bit。这两组函数共同实现了 DS18B20 单总线协议的核心时序。
7.4 温度读取与主循环
ReadTemp 严格遵循第 6 节的流程:先复位并等待应答,发送 0xCC 跳过 ROM,发送 0x44 启动转换,延时 800ms 等待完成;再次复位后发送 0xBE 读取暂存器,取出 LSB 和 MSB 两个字节,合并后右移 4 位得到整数温度值。主函数 main 初始化串口后进入死循环,不断读取温度并通过串口发送,实现周期性上报。
7. 注意事项
- 单总线通信对时序要求严格,主机在读写时要注意拉低和采样的时间窗口,避免误码。
- DS18B20 上电默认工作在 12 bit 分辨率,温度转换一次约需 750ms,读取前需要留足等待时间。
- 接线时务必保证 GND 共地,否则通信可能不稳定。
- 如果使用寄生供电方式,数据线需要外接上拉电阻,具体以实际电路设计为准。