硬件开发1-51单片机4-DS18B20
本文介绍了DS18B20温度传感器的驱动代码实现方法。DS18B20是一款单总线数字温度传感器,具有测量范围-55℃~125℃、精度±0.5℃的特点,支持寄生供电。文章详细阐述了其硬件连接方式(P3.7引脚需接上拉电阻)和软件实现,包括复位、读写操作的核心函数(ds18b20_Reset、write_ds18b20、read_ds18b20)以及温度获取函数get_tmp的实现原理。通过UART串
DS18B20 温度传感器驱动代码用法总结
一、DS18B20硬件介绍
1、DS18B20:
在无干扰时,DS18B20 指示值近似室温;有自身发热或外部热源时,指示值偏向器件温度
2、通信方式:半双工
因此,读、写和完成温度变换所需的电源可以由数据线本身提供,而不需要外部电源
能够完成:温度转换→数据存储→与主机通信
<主从关系图>
3、优点:
DS1820 靠 “唯一序列号” 实现了 “一根线连多个传感器且能区分彼此”,让 “多位置同时测温度” 变得更简单,因此能用于各种需要多点温度管理的场景
4、特点:
独特的单线接口,只需1个接口引脚即可通信
多点(multidrop)能力使分布式温度检测应用得以简化
不需要外部元件
可用数据线供电
不需备份电源
以9 位数字值方式读出温度
在1秒(典型值)内把温度变换为数字
用户可定义的,非易失性的温度告警设置
告警搜索命令识别和寻址温度在编定的极限之外的器件 (温度告警情况)
应用范围包括恒温控制,工业系统,消费类产品,温度计或任何热敏系统
5、测量范围:
-55℃-125℃
6、测量精度:
±0.5℃
7、引脚排列
引 脚(8 脚 SOIC) | 引脚 PR35 | 符号 | 说明 |
---|---|---|---|
5 | 1 | GND | 地 |
4 | 2 | DQ |
单线运用的数据输入 / 输出引脚:漏极开路见 “寄生电源” 一节 (51单片机连接P37引脚) |
3 | 3 | V₍DD₎ | 可选 V₍DD₎引脚。有关连接的细节见 “寄生电源” 一节 |
▲ 51单片机的P37引脚:外接上拉电阻->保证空闲状态是高电平
满足DS18B20单总线 “空闲高电平” 的通信要求
二、代码实现
1、main.c
#include <reg51.h>
#include <stdio.h>
#include <intrins.h>
#include "ds18b20.h"
#include "uart.h"
int main(void)
{
float ret = 0;
xdata char s[20];
Uart_Init();
while (1)
{
ret = get_tmp();
sprintf(s,"%f",ret);
Uart_SendStr(s);
}
return 0;
}
2、ds18b20.c
#include <reg51.h>
#include <stdio.h>
#include <intrins.h>
#include "delay.h"
#include "uart.h"
#define DQ_HIGH ((P3 |= (1<<7))) //将与DQ相连接的P37号引脚制1
#define DQ_DOWN ((P3 &= ~(1<<7))) //将与DQ相连接的P37号引脚制0
#define DQ_CHECK ((P3 & (1<<7)) != 0) //检测DQ引脚电平 p37为1时 为 1
// DQ_CHECK为1是高电平
//ds18b20 复位函数
//过程满足:主机发送复位脉冲 → 传感器回应存在脉冲(有两个阶段) → 主机确认存在脉冲
int ds18b20_Reset(void)
{
int t = 0;
//*初始化 —> 复位和存在脉冲
//ds18b20 的p3 连接着一个上拉电阻 所以空闲时是高电平
DQ_DOWN; //将DQ引脚拉低
Delay10us(70); // 延时700us
DQ_HIGH; //将DQ引脚拉高
Delay10us(5); // 延时50us
//*检测传感器是否存在脉冲的 第一阶段 (现高电平)
//在300us内检测DQ引脚是否被ds18b20拉低,被拉低代表ds18b20回复了一个存在脉冲信号
while (DQ_CHECK && t < 30) //高电平进入循环等待,低电平跳出循环(说明回复了一个存在脉冲信号)
{
Delay10us(1);
t++;
}
if (t >= 30) //如果300us内没恢复低电平,返回失败
{
return 0;
}
t = 0;
// *检测传感器是否存在脉冲的 第二阶段 (现低电平)
// 在300us内检测DQ引脚是否被ds18b20拉高
while (!DQ_CHECK && t < 30)
{
Delay10us(1);
t++;
}
if (t > 30) //如果300us内没恢复高电平,返回失败
{
return 0;
}
//两个阶段都检测成功,返回 1 表示传感器存在且正常
return 1;
}
// 向ds18b20发送一个字节数据
void write_ds18b20(unsigned char dat)
{
int i = 0;
//* 循环判断要发送的数据中的8bit是1还是0
// 写 1 时:只需极短拉低(≥1μs)
//写 0 时:需要长拉低(≥60μs)
for (i = 0; i < 8; i++)
{
if (dat & 1) //bit -> 1
{
DQ_DOWN;
_nop_();
_nop_();
DQ_HIGH;
Delay10us(5);
}
else //bit -> 0
{
DQ_DOWN;
Delay10us(5);
DQ_HIGH;
}
dat >>= 1;
}
}
// 从ds18b20读取一个字节数据
unsigned char read_ds18b20(void)
{
int i = 0;
unsigned char ret = 0;
for (i = 0; i < 8 ; i++)
{
DQ_DOWN;
_nop_();
_nop_();
DQ_HIGH;
_nop_();
_nop_();
_nop_();
if (DQ_CHECK) // 如果DQ是高位,ds18b20发送一个bit1
{
ret |= (1 << i);
}
Delay10us(5);
}
return ret;
}
// 获取ds18b20采集到的温度
float get_tmp(void)
{
short ret = 0;
unsigned char tl = 0;
unsigned char th = 0;
ds18b20_Reset();
write_ds18b20(0xCC);
write_ds18b20(0x44);
Delay1ms(1000);
ds18b20_Reset();
write_ds18b20(0xCC);
write_ds18b20(0xBE);
tl = read_ds18b20();
th = read_ds18b20();
ret = th << 8;
ret |= tl;
return ret * 0.0625;
}
3、ds18b20.h
#ifndef DS18B20_H__
#define DS18B20_H__
extern int ds18b20_Reset(void);
extern void write_ds18b20(unsigned char dat);
extern unsigned char read_ds18b20(void);
extern float get_tmp(void);
#endif
4、uart.c
#include <reg51.h>
xdata char recv_buffer[32];
unsigned int pos = 0;
// 串口接收服务
void uart_RecvHandler(void) interrupt 4
{
if ((SCON & (1 << 0)) == 1)
{
if (pos < 32)
{
recv_buffer[pos++] = SBUF;
recv_buffer[pos] = 0;
}
SCON &= ~(1 << 0);
}
}
//串口初始化
void Uart_Init(void)
{
//将scon寄存器中的bit6和bit7清0
SCON &= ~(3 << 6);
//串口工作模式选择:SMO:0 SM1:1 代表串口工作在8位UART模式
SCON |= (1 << 6);
// 允许串口接收数据
SCON |= (1 << 4);
// 串口波特率加倍
PCON &= ~(1 << 6);
PCON |= (1 << 7);
// TMOD寄存器高四位清0
// 定时器1工作在8位自动重装模式
TMOD &= ~(0x0F << 4);
TMOD |= (1 << 5);
// 2 ^ 8 - 2 ^ smod * focs/32/bps/12 bps:2400
TL1 = 230;
TH1 = 230;
// 允许定时器1开始计数
TCON |= (1 << 6);
// 允许CPU响应中断 + 允许串口产生中断
IE |= (1 << 7) | (1 << 4);
}
void Uart_SendChar(unsigned char ch)
{
SBUF = ch;
while ((SCON & (1 << 1)) == 0);
SCON &= ~(1 << 1);
}
void Uart_SendStr(const char *p)
{
while (*p)
{
Uart_SendChar(*p++);
}
}
void Uart_SendBuffer(const char *p,int len)
{
while(len--)
{
Uart_SendChar(*p++);
}
}
5、uart.h
#ifndef UART_H__
#define UART_H__
extern void Uart_Init(void);
extern void Uart_SendChar(unsigned char ch);
extern void Uart_SendStr(const char *p);
extern void Uart_SendBuffer(const char *p,int len);
extern xdata char recv_buffer[32];
extern unsigned int pos;
#endif
6、delay.c
#include <intrins.h>
//粗略进行延时
void delay(unsigned int n)
{
while(n--);
}
//12MHz晶振版本 更加精准控制时间
void Delay10us(unsigned int n) //@12.000MHz
{
unsigned char data i;
_nop_();
_nop_();
_nop_();
i = 2 * n;
while (--i)
{
_nop_();
}
}
void Delay1ms(unsigned int n)
{
while(n--)
{
Delay10us(100);
}
}
7、delay.h
#ifndef DELAY_H__
#define DELAY_H__
extern void delay(unsigned int n);
extern void Delay10us(unsigned int n);
extern void Delay1ms(unsigned int n);
#endif
三、核心函数功能说明
文件 | 功能 | 核心作用 |
---|---|---|
main.c |
主程序逻辑 | 循环获取温度并通过串口发送 |
ds18b20.c |
DS18B20 驱动(复位 / 读写 / 测温) | 实现单总线通信协议,获取温度值 |
uart.c |
串口初始化与数据发送 | 将温度数据转换为字符串输出 |
delay.c |
延时函数 | 提供单总线通信所需的精确时序(μs 级) |
各.h 文件 |
函数 / 变量声明 | 实现跨文件调用,模块解耦 |
四、核心流程详解(main)
1、初始化阶段:
Uart_Init(); // 初始化串口,设置波特率、工作模式
2、2. 循环测温与发送:
main()内的
while(1)
ret = get_tmp(); // 获取温度值(float类型)
sprintf(s,"%f",ret); // 转换为字符串
Uart_SendStr(s); // 串口发送字符串
核心是get_tmp()
函数,它封装了 DS18B20 的完整测温流程
五、单总线通信协议的软件实现(DS18B20
)
1、复位函数:ds18b20_Reset(void)


实现的原理:DS18B20 的通信依赖严格的时序,根据时序图电平的起伏以及所需的时间进行的结合精确的延时函数,模拟时序图中 “电平起伏的时间规律”,从而实现让传感器能识别命令、传输数据(复位、写指令、读温度等操作)
功能:向 DS18B20 发送复位信号,并检测传感器是否响应
流程:拉低 DQ 引脚 700us(复位信号)→ 释放引脚 50us → 检测 300us 内是否收到传感器的应答信号(低电平)→ 检测 300us 内应答信号是否结束(恢复高电平)
返回值:1
表示传感器响应正常;0
表示无响应(通信失败)
2、写数据函数:write_ds18b20(unsigned char dat)

功能:向 DS18B20 写入 1 字节数据(8 位)
原理:按位发送数据,每位时序如下:
发送1
:拉低 DQ 引脚 2 个时钟周期 → 释放引脚 → 延时 50us
发送0
:拉低 DQ 引脚 50us → 释放引脚
3、读数据函数:read_ds18b20(void)

功能:从 DS18B20 读取 1 字节数据(8 位)
原理:按位读取数据,每位时序如下:
拉低 DQ 引脚 2 个时钟周期 → 释放引脚 → 延迟 3 个时钟周期后检测 DQ 引脚电平 → 高电平表示读入1
,低电平表示读入0
→ 延时 50us
返回值:读取到的 1 字节数据
4、温度读取函数:get_temp(void)
功能:触发温度转换并读取转换后的温度值
流程:
复位传感器 → 发送跳过 ROM 指令(0xCC
)→ 发送温度转换指令(0x44
)→ 延时 1000ms 等待转换完成
再次复位 → 发送跳过 ROM 指令(0xCC
)→ 发送读取暂存器指令(0xBE
)
读取温度低 8 位(tl
)和高 8 位(th
)→ 组合为 16 位数据(ret = th << 8 | tl
)。
转换为温度值:ret * 0.0625
(DS18B20 的温度分辨率为 0.0625℃)
返回值:float 类型的温度值(单位:℃)
六、使用方法
- 确保硬件连接正确(DQ 引脚接 P3.7,电源和地正常连接)
- 初始化延时函数(
Delay10us()
和Delay1ms()
) - 直接调用
get_temp()
函数即可获取当前温度,例如:float temp; temp = get_temp(); // 读取温度值
七、注意事项
- 通信时序严格依赖延时函数,若延时不准确会导致通信失败
get_temp()
函数中包含 1000ms 的转换等待时间,若需优化响应速度,可根据 DS18B20 的转换速率调整延时(最高精度下转换时间约 750ms)- 若总线上有多个 DS18B20,需修改代码(替换
0xCC
指令)以支持 ROM 匹配,当前代码默认单传感器场景(跳过 ROM)
更多推荐
所有评论(0)