51单片机-04-DS18B20 数字温度传感器
一、多定时器分工架构
系统同时需要驱动蜂鸣器、数码管、串口,三件事各需独立计时,用三个定时器分别负责:
|
定时器 |
负责功能 |
配置要点 |
|
Timer0 |
驱动蜂鸣器(PWM方波) |
模式1,16位;中断中翻转 P2.5;初值 g_i 可变控制频率 |
|
Timer1 |
UART 波特率发生器 |
模式2,8位自动重装;TH1=TL1=232(2400bps);不触发中断 |
|
Timer2 |
数码管动态刷新(1ms) |
初值 64613;中断中调用 num_to_buff() + digiter_show() |
|
注意 Timer2 |
STC89C52 才有 Timer2;标准 8051 只有 Timer0/Timer1。Timer1 被 UART 占用后,蜂鸣器必须用 Timer0。 |
二、DS18B20 传感器规格
|
参数 |
规格 |
|
量程 |
−55°C ~ +125°C |
|
精度(误差) |
±0.5°C |
|
分辨率(可配置) |
9位=0.5°C / 10位=0.25°C / 11位=0.125°C / 12位=0.0625°C(默认12位) |
|
接口类型 |
GPIO 单总线(1-Wire),DQ 一根线完成双向通信 |
|
工作电压 |
3V ~ 5.5V |
三、单总线(1-Wire)协议原理
3.1 硬件连接
- DQ 线上接 4.7kΩ 上拉电阻到 VCC,空闲时总线保持高电平
- 51 单片机是主机,DS18B20 是从机,主机发起所有通信
- 同一条 DQ 线可挂多个 DS18B20(靠 ROM 地址区分)
3.2 线与特性(Open-Drain 开漏)
|
A 操作 |
B 操作 |
总线结果 |
说明 |
|
拉低 |
释放 |
低电平 |
A 把总线拉低,B 不阻止 |
|
释放 |
拉低 |
低电平 |
B 把总线拉低 |
|
拉低 |
拉低 |
低电平 |
两个都拉低 |
|
释放 |
释放 |
高电平 |
都释放,上拉电阻把总线拉高 |
|
关键 释放总线 |
作为数据接收方时,必须先释放总线(DQ_HIGH),让上拉电阻把总线拉高,再去检测从机发来的电平变化。否则主机一直拉低,永远读不到从机数据。 |
四、DS18B20 通信时序
4.1 宏定义(P3.7 接 DQ)
#define DQ_HIGH (P3 |= (1 << 7)) // 释放总线(高电平/高阻态)
#define DQ_DOWN (P3 &= ~(1 << 7)) // 拉低总线
#define DQ_CHECK ((P3 & (1 << 7)) != 0) // 读总线:非0=高,0=低
4.2 复位时序

|
复位流程 ds18b20_reset() |
|
1. 主机将 DQ 拉低 480~960us → 发出「复位脉冲」 |
|
2. 主机释放 DQ,上拉电阻将总线拉高 |
|
3. DS18B20 检测到上升沿后,等待 15~60us |
|
4. DS18B20 将 DQ 拉低 60~240us → 发出「存在脉冲」(我在!) |
|
5. DS18B20 释放 DQ,总线恢复高电平 |
|
6. 主机在释放后 60~240us 内检测到低电平 → 复位成功,返回 1 |
int ds18b20_reset(void)
{
int time = 0;
// 发复位脉冲:拉低 700us(超过最小480us)
DQ_DOWN; Delay10us(70);
DQ_HIGH; Delay10us(6); // 释放,等60us让总线稳定
// 等待 DS18B20 把总线拉低(存在脉冲)
while (DQ_CHECK && time < 30) { Delay10us(1); time++; }
if (time >= 30) return -1; // 超时:无设备
// 等待 DS18B20 释放总线(恢复高)
time = 0;
while (!DQ_CHECK && time < 30) { Delay10us(1); time++; }
if (time >= 30) return -2; // 超时:通信异常
return 1; // 复位成功
}
4.3 写时序(51 向 DS18B20 发送1字节)

|
写的bit |
主机动作 |
DS18B20动作 |
时间要求 |
|
写 1 |
拉低 >1us 后立即释放 |
45us内采样,检测到高 = 收到1 |
总时隙 >60us |
|
写 0 |
拉低 60~120us 后释放 |
60us内采样,检测到低 = 收到0 |
总时隙 > |
void write_ds18b20(unsigned char dat)
{
int i;
for (i = 0; i < 8; i++)
{
if (dat & 1)
{ // 当前bit=1(低位先行)
DQ_DOWN; _nop_(); _nop_(); // 拉低约2us
DQ_HIGH; Delay10us(5); // 释放,等50us
}
else
{ // 当前bit=0
DQ_DOWN; Delay10us(6); // 拉低60us
DQ_HIGH; Delay10us(6); // 释放,等60us
}
dat >>= 1; // 移到下一位
}
}
4.4 读时序(DS18B20 向 51 发送1字节)

|
读时隙(每个bit执行一次) |
|
1. 主机将 DQ 拉低 >1us(触发读时隙) |
|
2. 主机立即释放 DQ |
|
3. DS18B20 控制总线:发送0则拉低,发送1则保持高 |
|
4. 主机在释放后 15us 内采样 DQ:高=1,低=0 |
|
5. 等待约 60us,完成此bit,进入下一位 |
unsigned char read_ds18b20(void)
{
int i;
unsigned char dat = 0;
for (i = 0; i < 8; i++)
{
DQ_DOWN; _nop_(); _nop_(); // 拉低约2us,触发读时隙
DQ_HIGH; _nop_(); _nop_(); _nop_(); // 释放,等约3us
if (DQ_CHECK) // 采样:高=1
dat |= (1 << i); // 低位先收,逐位存入
Delay10us(6); // 等60us,完成此bit
}
return dat;
}
五、DS18B20 温度采集完整流程
|
get_temp() 完整步骤 |
|
1. ds18b20_reset() 复位,确认设备在线 |
|
2. write_ds18b20(0xCC) Skip ROM,跳过64位ROM匹配(总线只有一个设备时使用) |
|
3. write_ds18b20(0x44) Convert T,命令DS18B20开始ADC温度转换 |
|
4. Delay1ms(1000) 等待1s,12位精度转换最长需要750ms |
|
5. ds18b20_reset() 再次复位,开始读数据 |
|
6. write_ds18b20(0xCC) 再次 Skip ROM |
|
7. write_ds18b20(0xBE) Read Scratchpad,读取暂存器 |
|
8. temp_low = read_ds18b20() 读取温度低字节 |
|
9. temp_high = read_ds18b20() 读取温度高字节 |
|
10. temp = (temp_high<<8)|temp_low,return temp * 0.0625 换算为摄氏度 |
5.1 温度数据16位格式
|
位 |
权重 |
说明 |
|
bit15~bit11(5位) |
符号位 S |
全0=正温度;全1=负温度(补码) |
|
bit10~bit4(7位) |
整数部分 |
温度的整数位 |
|
bit3(1位) |
0.5°C |
小数第1位 |
|
bit2(1位) |
0.25°C |
小数第2位 |
|
bit1(1位) |
0.125°C |
小数第3位 |
|
bit0(1位) |
0.0625°C |
小数第4位(12位精度最小单位) |
换算公式:摄氏度 = (short)原始16位值 × 0.0625
例:原始值 = 0x01AC = 428(十进制),428 × 0.0625 = 26.75°C
|
负温度 short 类型 |
使用有符号 short(而非 unsigned short),是因为 DS18B20 负温度时高字节符号位为1,short 能正确处理补码,乘以 0.0625 后自动得到负值。 |
5.2 get_temp() 完整代码
float get_temp(void)
{
unsigned char temp_low = 0, temp_high = 0;
short temp = 0;
// 第一阶段:触发温度转换
ds18b20_reset();
write_ds18b20(0xCC); // Skip ROM
write_ds18b20(0x44); // 开始转换
Delay1ms(1000); // 等待转换完成
// 第二阶段:读取温度数据
ds18b20_reset();
write_ds18b20(0xCC); // Skip ROM
write_ds18b20(0xBE); // Read Scratchpad
temp_low = read_ds18b20(); // 先读低字节
temp_high = read_ds18b20(); // 再读高字节
// 第三阶段:拼合 & 换算
temp = temp_high << 8; // 高字节移到高8位
temp |= temp_low; // 低字节填入低8位
return temp * 0.0625; // 乘以分辨率 = 摄氏度
}
六、延时函数 delay.c
|
函数 |
用途 |
说明 |
|
delay(n) |
粗略软件延时 |
空循环n次,不精确,用于非时序场合 |
|
Delay10us(n) |
精确延时 10us×n |
用 _nop_() 填充,1-Wire时序必用 |
|
Delay1ms(n) |
精确延时 1ms×n |
调用 Delay10us(100) 共n次,等待温度转换用 |
|
知识点 _nop_() |
来自 intrins.h,执行一个空操作(NOP指令),占一个机器周期(约1.085us @11.0592MHz)。多个 _nop_() 叠加可精确控制微秒级延时,是1-Wire时序的关键手段。 |
七、main.c 温度采集与串口上报
#include <stdio.h> // 提供 sprintf
int main(void)
{
float t = 0;
char ds_temp[32]; // 存放格式化后的字符串
uart_init(); // 初始化串口(2400bps)
while (1) {
t = get_temp(); // 读温度(约需1秒)
sprintf(ds_temp, "temp:%.2f\r\n", t); // 格式化为字符串
uart_sendstr(ds_temp); // 串口发送给上位机
}
}
|
sprintf 格式化字符串 |
sprintf(buf, "temp:%.2f\r\n", t) 把浮点数 t 格式化为 "temp:26.75\r\n" 存入 buf。%.2f 表示保留2位小数。\r\n 是回车换行,串口助手正确换行必须。 |
八、DS18B20 命令速查
|
命令码 |
名称 |
用途 |
|
0xCC |
Skip ROM |
总线只有一个设备时使用,跳过64位ROM匹配,直接操作 |
|
0x44 |
Convert T |
触发ADC开始采样,12位精度最长需等待750ms |
|
0xBE |
Read Scratchpad |
读取暂存器,先低字节后高字节(共9字节,一般只读前2字节) |
|
0x4E |
Write Scratchpad |
配置分辨率(写入 TH/TL报警值 + 配置寄存器) |
|
0x55 |
Match ROM |
总线多设备时,指定某个设备的64位ROM地址通信 |
|
0xF0 |
Search ROM |
枚举总线上所有DS18B20的ROM地址 |
更多推荐


所有评论(0)