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 |

cs 复制代码
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 | 总时隙 > |

cs 复制代码
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,进入下一位 |

cs 复制代码
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() 完整代码

cs 复制代码
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 温度采集与串口上报

cs 复制代码
#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地址 |

相关推荐
至为芯2 小时前
PY32F003至为芯支持32位ARM内核的低成本MCU微控制器
单片机·集成电路·芯片
zjxtxdy2 小时前
STM32开发板简介
stm32·单片机·嵌入式硬件
【 STM32开发 】2 小时前
【STM32 + CubeMX 教程】RTC 实时时钟 之 闹钟 -- F407篇
stm32·单片机·嵌入式硬件
weiyvyy2 小时前
接口开发的完整流程:从需求到验证
驱动开发·嵌入式硬件·硬件架构·硬件工程
MC_J2 小时前
STM32+FMC驱动W9825G6 SDRAM程序以及遇到的问题讲解
stm32·单片机
少年潜行2 小时前
【开源】STM32驱动BH1750(附开源代码)
单片机
0南城逆流03 小时前
【STM32】知识点介绍八:UART/USART串口功能
stm32·单片机·嵌入式硬件
国家一级保护废物...3 小时前
51单片机day1
单片机·嵌入式硬件·51单片机
小白学电子_3 小时前
STM32常用HAL常见库函数快速运用和讲解
stm32·单片机·嵌入式硬件