前言
最近学习 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
- 设置 SMOD 波特率加倍位
- 定时器 1 设置 8 位自动重装模式(模式 2),装载 TH1、TL1 初值
- 开启定时器 TR1
- 写数据到 SBUF 寄存器,硬件自动发送
- 发送完成后 TI 硬件置 1,软件必须手动清零 TI!
接收流程
- 配置模式 1,REN 置 1(允许接收)
- 定时器 1 配置同上
- 等待 RI 接收标志置 1,读取 SBUF
- 软件手动清零 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(从机)被动应答。
① 复位时序
- 总线空闲高电平
- 主机拉低 DQ,至少480us复位脉冲
- 释放 DQ 总线
- 等待 DS18B20 发出存在脉冲(低电平,60‑240us),读到存在脉冲代表传感器正常。
如果读不到存在脉冲:硬件接线、上拉电阻、延时不对,传感器损坏。
② 写时隙(主机写数据给 DS18B20)
- 写 0:主机拉低总线保持 60‑120us
- 写 1:主机拉至少 1us,15us 内释放拉高总线
两个 bit 间隔必须大于 1us。
③ 读时隙(主机读取 DS18B20 数据)
- 主机拉低 DQ 大于 1us,触发读时隙
- 立刻释放总线
- 必须在 15us 以内采样 DQ 电平!
- 之后等待 60us 完成这一位读取。
坑点:读时序采样时间窗口非常窄,延时不准直接读到错误温度。
3. DS18B20 核心命令
表格
| 命令码 | 功能说明 |
|---|---|
0xCC |
跳过 ROM 命令,总线上只有 1 颗 DS18B20 时使用 |
0x44 |
启动温度转换,12bit 下最长等待 750ms |
0xBE |
读取暂存器,读取 9 字节,前两个字节是温度 LSB、MSB |
4. 读取温度完整流程
- DS18B20 复位
- 发送
0xCC跳过 ROM - 发送
0x44启动温度转换 - 等待 750ms 以上,等待转换完成
- 再次复位
- 发送
0xCC - 发送
0xBE读取暂存器 - 读取 LSB(低字节)、MSB(高字节)
- 合并 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);
}
}
四、常见踩坑总结
📌串口常见问题
- 串口助手乱码:晶振不匹配、波特率设置错误;TH1/TL1 初值错误。优先使用 11.0592M 晶振。
- 收不到数据:忘记共 GND;TI 标志没有软件清零;REN 没有置 1 打开接收。
- 只收到部分字符:发送字符串末尾缺少
\r\n换行。
📌DS18B20 常见问题
- 一直读 999 / 错误值:时序延时不对!DS18B20 对时序非常严格,晶振改变必须重写延时函数。
- 温度固定 85℃:上电默认值,没有执行 0x44 温度转换命令。
- 温度跳变:电源不稳,DQ 上拉电阻漏接;可以软件多次采样做滤波。
- 读取太快:发送
0x44之后必须等待 750ms,没等转换完成就读取,数据无效。
五、拓展思考
- 多个 DS18B20 挂载同一根 DQ 总线:不能使用 0xCC 跳过 ROM,需要使用 ROM 匹配命令,匹配每个传感器唯一 64 位 ROM 序列号。
- DS18B20 可以设置 9/10/11bit 分辨率,减少转换时间,牺牲精度。
- 串口除了发送,也可以做接收,单片机接收电脑下发指令,控制继电器、LED