51单片机基础-DS18B20温度传感器

第十六章 DS18B20温度传感器

1. 导入

在前面章节中,我们学习了EEPROM、I²C通信等数据存储技术。本章将引入一种重要的传感器------DS18B20数字温度传感器 ,它采用单总线(1-Wire)协议 ,仅需一根I/O线即可完成供电与通信(支持寄生供电),具有测温精度高、抗干扰能力强、支持多点组网等特点。

DS18B20测温范围为 -55°C ~ +125°C ,精度可达 ±0.5°C,广泛应用于环境监测、恒温控制、智能家电等领域。

本章目标:

  • 理解DS18B20的单总线通信原理;
  • 掌握复位、读写时序;
  • 实现温度的读取与解析;
  • 将温度值通过数码管或串口显示;
  • 为后续构建温控系统、数据采集网络打下基础。

2. 硬件设计

2.1 DS18B20引脚说明(TO-92封装)

引脚 名称 连接方式
1 GND 接地
2 DQ 数据线(接单片机I/O)
3 VDD 接VCC(推荐外部供电)

有两种供电方式:

  • 外部供电:VDD接VCC,DQ接I/O;
  • 寄生供电:VDD接地,靠总线供电(对时序要求高)。
    本章采用外部供电方式,更稳定。

2.2 电路连接

DS18B20 连接
GND 单片机GND
DQ P1.7(单总线数据线)
VDD VCC(+5V)
  • 必须在DQ与VCC之间接一个4.7kΩ上拉电阻,确保空闲时为高电平。

单总线为开漏结构,需上拉。


3. 软件设计

3.1 单总线通信原理

DS18B20使用单线双向通信,所有操作都由主机(单片机)发起。通信流程:

  1. 复位:主机发送复位脉冲,DS18B20回应存在脉冲;
  2. 发送命令:如启动测温(0x44)、读取温度(0xBE);
  3. 读取温度数据:16位有符号数,包含整数与小数部分。

3.2 基本时序函数

c 复制代码
#include <reg52.h>

sbit DQ = P1^7;

void delay_us(unsigned int us) {
    while(us--) _nop_();
}

// DS18B20复位
unsigned char ds18b20_reset() {
    unsigned char presence;
    DQ = 0;
    delay_us(480);     // 至少480μs
    DQ = 1;
    delay_us(60);      // 主机释放总线
    presence = DQ;     // 从机拉低表示存在
    delay_us(420);     // 等待周期结束
    return presence;   // 0: 存在,1: 无设备
}

3.3 单总线读写函数

c 复制代码
// 写一个字节
void ds18b20_write_byte(unsigned char dat) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        DQ = dat & 0x01;   // 先写低位
        delay_us(60);
        DQ = 1;
        dat >>= 1;
    }
}

// 读一个字节
unsigned char ds18b20_read_byte() {
    unsigned char i, dat = 0;
    for (i = 0; i < 8; i++) {
        DQ = 0;
        _nop_();
        DQ = 1;            // 释放总线
        delay_us(2);
        dat >>= 1;
        if (DQ) dat |= 0x80;
        delay_us(60);
    }
    return dat;
}

3.4 启动温度转换

c 复制代码
void ds18b20_start_convert() {
    ds18b20_reset();
    ds18b20_write_byte(0xCC);  // 跳过ROM命令(单设备)
    ds18b20_write_byte(0x44);  // 启动温度转换
}

3.5 读取温度值

c 复制代码
int ds18b20_read_temperature() {
    unsigned char TL, TH;
    int temp;

    ds18b20_reset();
    ds18b20_write_byte(0xCC);  // 跳过ROM
    ds18b20_write_byte(0xBE);  // 读温度寄存器

    TL = ds18b20_read_byte();  // 低8位
    TH = ds18b20_read_byte();  // 高8位

    temp = (TH << 8) | TL;     // 合成16位数据

    // 转换为实际温度(默认12位精度,0.0625°C/LSB)
    if (TH & 0x80) {           // 负数(补码)
        temp = ~temp + 1;
        return -((temp * 625) / 10000.0);  // 转为摄氏度(保留两位小数)
    }
    return (temp * 625) / 10000;           // 正数
}

温度单位为0.0625°C,所以实际温度 = 读数 × 0.0625


3.6 简化温度读取(整数部分)

若只需整数温度:

c 复制代码
unsigned char get_temp() {
    int temp = ds18b20_read_temperature();
    return temp / 1000;  // 返回整数部分(单位m°C)
}

3.7 主程序示例:读取并显示温度

c 复制代码
#include <reg52.h>

// 数码管段码表(共阴)
unsigned char code seg_code[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};

void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 125; j++);
}

void display_digit(unsigned char pos, unsigned char num) {
    P0 = seg_code[num];
    P2 = (P2 & 0xF0) | (0x0F & ~(1 << pos));
    delay_ms(1);
}

void main() {
    int temp;
    unsigned char t_display[4];

    while(1) {
        ds18b20_start_convert();
        delay_ms(750);  // 最长转换时间(12位精度)

        temp = ds18b20_read_temperature();  // 单位:0.001°C

        // 分离各位数字(假设温度0~99.9°C)
        t_display[0] = (temp / 1000) / 10;       // 十位
        t_display[1] = (temp / 1000) % 10;       // 个位
        t_display[2] = (temp % 1000) / 100;      // 小数第一位
        t_display[3] = 0x80 | seg_code[t_display[2]]; // 带小数点

        // 动态扫描显示
        unsigned char i;
        for (i = 0; i < 100; i++) {
            display_digit(0, t_display[0]);
            display_digit(1, t_display[1]);
            display_digit(2, seg_code[t_display[2]]);
            delay_ms(5);
        }
    }
}

3.8 通过串口发送温度

c 复制代码
void uart_send_byte(unsigned char dat) {
    SBUF = dat;
    while(TI == 0);
    TI = 0;
}

void send_temp_serial() {
    int temp = ds18b20_read_temperature();
    uart_send_string("Temp: ");
    if (temp < 0) {
        uart_send_byte('-');
        temp = -temp;
    }
    uart_send_byte('0' + temp / 10000);
    uart_send_byte('0' + (temp / 1000) % 10);
    uart_send_byte('.');
    uart_send_byte('0' + (temp % 1000) / 100);
    uart_send_string("°C\r\n");
}

3.9 编译与下载

  • Keil中创建工程;
  • 确保DQ接P1.7,上拉电阻为4.7kΩ;
  • 编译生成HEX;
  • 下载至单片机;
  • 观察数码管或串口是否显示合理温度。

若读数为85°C(默认值):

  • 未成功启动转换;
  • 检查复位和写命令流程。

4. 小结

本章通过驱动DS18B20温度传感器,掌握了单总线通信与传感器数据采集技术,主要内容包括:

  • 硬件连接:学会使用外部供电与上拉电阻;
  • 通信时序:掌握复位、读写、存在脉冲等单总线机制;
  • 温度解析:从16位数据提取实际温度值;
  • 应用扩展:通过数码管或串口显示温度;
  • 系统能力:为构建温控系统、环境监测设备奠定基础。

4.1 常见问题与解决

问题 原因 解决方法
读数85°C 未成功通信,返回默认值 检查复位和上拉电阻
读数异常 时序不准 优化delay_us,避免编译器优化
多设备冲突 未使用ROM匹配 单设备用0xCC跳过,多设备需读ROM
转换慢 精度设为12位 可设置为9位加快速度

4.2 下一步学习建议

  • 学习 多DS18B20组网,实现多点测温;
  • 结合 继电器 实现恒温控制;
  • 使用 LCD1602 显示更丰富信息;
  • 构建 无线温湿度监控系统(加nRF24L01)。

本章标志着你已掌握单总线传感器驱动能力,具备了数据采集系统的核心技能


相关推荐
Han.miracle3 小时前
数据结构——排序的超级详解(Java版)
java·数据结构·学习·算法·leetcode·排序算法·1024程序员节
AI棒棒牛3 小时前
论文精读系列:Retinanet——目标检测领域中的SCI对比实验算法介绍!可一键跑通的对比实验,极大节省小伙伴的时间!!!
yolo·目标检测·计算机视觉·对比实验·1024程序员节·创新·rtdter
胜天半月子3 小时前
嵌入式开发 | C语言 | 单精度浮点数解疑--为什么规格化数中指数位E不能是E=0 或 E=255?
c语言·嵌入式c·1024程序员节·单精度浮点数范围
Lethehong3 小时前
告别显卡焦虑:Wan2.1+cpolar让AI视频创作走进普通家庭
cpolar·1024程序员节
傻童:CPU3 小时前
C语言需要掌握的基础知识点之图
c语言·1024程序员节
C灿灿数模3 小时前
2025MathorCup大数据竞赛A题B题选题建议与分析,思路模型
1024程序员节
木法星人3 小时前
Ubuntu安装nvm(无需梯子自动连接github下载安装)
ubuntu·nvm·1024程序员节
草莓base3 小时前
【JUC】Future + CompletableFuture详解
java·juc·1024程序员节