DAYS41 UART 串口通信 + DS18B20 单总线温度传感器

前言

最近学习 51 单片机外设,串口 UART 和 DS18B20 温度传感器是嵌入式入门必学模块。串口实现单片机和电脑 PC 数据交互;DS18B20 是经典单总线数字温度传感器,只需要一根 IO 口就可以完成双向通信,不需要 AD 采样,直接输出数字温度,非常适合做温度采集项目。

本文结合学习笔记,把硬件接线、核心原理、时序、命令、坑点全部梳理一遍,适合刚学 51 的同学。

一、UART 串口通信原理

1. 什么是 UART

UART:通用异步收发器

  • 作用:单片机 ↔ PC、单片机 ↔ 单片机之间数据通信
  • 核心引脚:
    • TXD:单片机发送数据
    • RXD:单片机接收数据
    • GND:必须共地!通信基准电压,不共地会乱码、接收不到数据。

硬件接线:单片机 TXD 接 CH340 模块 RXD;单片机 RXD 接 CH340 模块 TXD,GND 直连。电脑 USB 插 CH340,使用串口助手调试。

2. 关键概念:波特率

波特率:串口传输速率,单位 bps,代表每秒传输多少 bit。 常用波特率:2400、4800、9600、115200

重要:单片机波特率必须和串口助手波特率完全一致,否则乱码

51 单片机模式 1/3 波特率计算公式:

复制代码
波特率 = (2^SMOD / 32) × 定时器1溢出率
  • SMOD:PCON 寄存器最高位,0=不加倍,1=波特率加倍
  • 12T 模式:定时器 1 溢出率 = SYSclk / 12 / (256‑TH1)
  • 6T 模式:定时器 1 溢出率 = SYSclk / 6 / (256‑TH1)

实际项目推荐晶振选用11.0592M,可以精准得到 9600、115200 波特率,避免波特率误差带来乱码。

3. 串口帧格式(模式 1,8 位数据,无奇偶校验)

1 位起始位 + 8 位数据位 + 停止位,没有校验位,串口助手一般选择 8‑N‑1

4. 串口工作流程

发送流程
  1. 设置串口为模式 1
  2. 设置 SMOD 波特率加倍位
  3. 定时器 1 设置 8 位自动重装模式(模式 2),装载 TH1、TL1 初值
  4. 开启定时器 TR1
  5. 写数据到 SBUF 寄存器,硬件自动发送
  6. 发送完成后 TI 硬件置 1,软件必须手动清零 TI
接收流程
  1. 配置模式 1,REN 置 1(允许接收)
  2. 定时器 1 配置同上
  3. 等待 RI 接收标志置 1,读取 SBUF
  4. 软件手动清零 RI

坑点:TI、RI 硬件置 1,不会自动清零,代码必须手动写 TI=0; RI=0;,忘记清零串口彻底卡死。

二、DS18B20 单总线温度传感器

1. DS18B20 硬件参数

  • 供电:3.0V‑5.5V
  • 测温范围:‑55℃ ~ +125℃
  • 分辨率:9‑12bit 可配置,默认 12bit,一次温度转换最大耗时 750ms
  • 引脚定义:
    • VCC:电源
    • GND:地
    • DQ:单总线双向数据 IO(接单片机普通 IO,例如 P3^7)

DQ 引脚建议外接 4.7K 上拉电阻,单总线是开漏输出,必须上拉才能输出高电平。

接线: 单片机 IO → DQ;GND 接 GND;VCC 接 3.3V/5V。

2. 单总线核心时序(重中之重,时序错就读不出温度)

所有通信由单片机(主机)主动发起,DS18B20(从机)被动应答。

① 复位时序
  1. 总线空闲高电平
  2. 主机拉低 DQ,至少480us复位脉冲
  3. 释放 DQ 总线
  4. 等待 DS18B20 发出存在脉冲(低电平,60‑240us),读到存在脉冲代表传感器正常。

如果读不到存在脉冲:硬件接线、上拉电阻、延时不对,传感器损坏。

② 写时隙(主机写数据给 DS18B20)
  • 写 0:主机拉低总线保持 60‑120us
  • 写 1:主机拉至少 1us,15us 内释放拉高总线

两个 bit 间隔必须大于 1us。

③ 读时隙(主机读取 DS18B20 数据)
  1. 主机拉低 DQ 大于 1us,触发读时隙
  2. 立刻释放总线
  3. 必须在 15us 以内采样 DQ 电平!
  4. 之后等待 60us 完成这一位读取。

坑点:读时序采样时间窗口非常窄,延时不准直接读到错误温度。

3. DS18B20 核心命令

表格

命令码 功能说明
0xCC 跳过 ROM 命令,总线上只有 1 颗 DS18B20 时使用
0x44 启动温度转换,12bit 下最长等待 750ms
0xBE 读取暂存器,读取 9 字节,前两个字节是温度 LSB、MSB

4. 读取温度完整流程

  1. DS18B20 复位
  2. 发送 0xCC 跳过 ROM
  3. 发送 0x44 启动温度转换
  4. 等待 750ms 以上,等待转换完成
  5. 再次复位
  6. 发送 0xCC
  7. 发送 0xBE 读取暂存器
  8. 读取 LSB(低字节)、MSB(高字节)
  9. 合并 16 位温度数值,12bit 模式:实际温度 = 原始数值 × 0.0625 ℃

暂存器一共 9 字节,第 9 字节 CRC 校验,简单项目可以不用 CRC 校验。

5. 温度数据格式

16 位原始数据:

  • 高 5bit:符号位;全 1 代表负温度
  • 接下来 7bit 整数温度
  • 低 4bit:小数位,最小分度 0.0625℃

三、完整示例代码(Keil C51)

环境:STC89C52,晶振 11.0592MHz;DS18B20 DQ 接 P3^7;串口波特率 9600;串口助手输出温度。

复制代码
#include <reg52.h>
#include <intrins.h>
#include <stdio.h>

sbit DQ = P3^7;

//====================延时函数====================
void DelayUs(unsigned int t)
{
    while(t--)
    {
        _nop_();_nop_();_nop_();_nop_();
    }
}

void DelayMs(unsigned int ms)
{
    unsigned int i,j;
    for(i=ms;i>0;i--)
        for(j=110;j>0;j--);
}

//====================串口UART初始化 9600@11.0592M====================
void UART_Init(void)
{
    TMOD |= 0x20;   //定时器1模式2,8位自动重装
    TH1 = 0xFD;
    TL1 = 0xFD;
    TR1 = 1;        //启动定时器1
    SCON = 0x50;    //模式1,REN=1允许接收
    PCON &= 0x7F;   //SMOD=0不加倍
}

void UART_SendByte(unsigned char dat)
{
    SBUF = dat;
    while(!TI);
    TI = 0;
}

void UART_SendStr(char *str)
{
    while(*str != '\0')
    {
        UART_SendByte(*str++);
    }
}

//====================DS18B20驱动====================
bit DS18B20_Init(void)
{
    bit ack;
    DQ = 1;
    DelayUs(10);
    DQ = 0;
    DelayMs(1);     //拉低>=480us
    DQ = 1;
    DelayUs(60);
    ack = DQ;       //读取应答脉冲
    DelayMs(1);
    return (!ack);  //返回1代表初始化成功
}

void DS18B20_WriteByte(unsigned char dat)
{
    unsigned char i;
    for(i=0;i<8;i++)
    {
        DQ = 0;
        DelayUs(2);
        DQ = dat & 0x01;
        DelayUs(60);
        DQ = 1;
        dat >>= 1;
    }
}

unsigned char DS18B20_ReadByte(void)
{
    unsigned char i,dat=0;
    for(i=0;i<8;i++)
    {
        DQ = 0;
        DelayUs(2);
        DQ = 1;
        DelayUs(8);     //15us以内采样
        if(DQ) dat |= 0x80 >> i;
        DelayUs(50);
    }
    return dat;
}

float DS18B20_GetTemp(void)
{
    unsigned char LSB,MSB;
    unsigned int temp_raw;
    if(!DS18B20_Init()) return 999; //传感器异常返回999
    
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0x44); //启动转换
    DelayMs(800);             //等待转换完成
    
    DS18B20_Init();
    DS18B20_WriteByte(0xCC);
    DS18B20_WriteByte(0xBE);
    
    LSB = DS18B20_ReadByte();
    MSB = DS18B20_ReadByte();
    temp_raw = (MSB << 8) | LSB;
    return temp_raw * 0.0625f;
}

//====================主函数====================
void main(void)
{
    float temp;
    char buf[32];
    UART_Init();
    UART_SendStr("DS18B20 Temperature Test\r\n");
    
    while(1)
    {
        temp = DS18B20_GetTemp();
        if(temp > 900)
        {
            UART_SendStr("DS18B20 ERROR!\r\n");
        }
        else
        {
            sprintf(buf,"Temp = %.2f ℃\r\n",temp);
            UART_SendStr(buf);
        }
        DelayMs(1000);
    }
}

四、常见踩坑总结

📌串口常见问题

  1. 串口助手乱码:晶振不匹配、波特率设置错误;TH1/TL1 初值错误。优先使用 11.0592M 晶振。
  2. 收不到数据:忘记共 GND;TI 标志没有软件清零;REN 没有置 1 打开接收。
  3. 只收到部分字符:发送字符串末尾缺少\r\n换行。

📌DS18B20 常见问题

  1. 一直读 999 / 错误值:时序延时不对!DS18B20 对时序非常严格,晶振改变必须重写延时函数
  2. 温度固定 85℃:上电默认值,没有执行 0x44 温度转换命令。
  3. 温度跳变:电源不稳,DQ 上拉电阻漏接;可以软件多次采样做滤波。
  4. 读取太快:发送0x44之后必须等待 750ms,没等转换完成就读取,数据无效。

五、拓展思考

  1. 多个 DS18B20 挂载同一根 DQ 总线:不能使用 0xCC 跳过 ROM,需要使用 ROM 匹配命令,匹配每个传感器唯一 64 位 ROM 序列号。
  2. DS18B20 可以设置 9/10/11bit 分辨率,减少转换时间,牺牲精度。
  3. 串口除了发送,也可以做接收,单片机接收电脑下发指令,控制继电器、LED
相关推荐
充哥单片机设计1 小时前
【STM32开源项目】智能窗户控制器
stm32·单片机·嵌入式硬件·毕业设计·智能窗户
Jlzn88881 小时前
钢带折弯±0.1°怎么来?伺服闭环补偿回弹拆解
大数据·嵌入式硬件·制造
威嵌神州1 小时前
便携式加固产品方案:加固平板、手持终端、巡检仪、管理柜
嵌入式硬件·智能手机·电脑
安河桥。1 小时前
PCIe 是什么?为什么这么快?从差分线到 Gen6 的硬核解析与场景全览
嵌入式硬件·车载系统
祖力551 小时前
DS18B20数字温度传感器
51单片机·ds18b20·数字温度传感器
糖糖单片机设计14 小时前
基于 STM32 的公交车定位与报站系统
stm32·单片机·嵌入式硬件
祖力5514 小时前
快速学会51单片机基础
单片机·嵌入式硬件·51单片机
Doraemomo17 小时前
IMX6ULL裸机开发——GPIO的使用
单片机·嵌入式硬件
笨笨饿18 小时前
#138_解决Codex要五次回复的问题
linux·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库