硬件开发1-51单片机4-DS18B20

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

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

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

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

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

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

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

cs 复制代码
#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 的完整测温流程

五、单总线通信协议的软件实现(DS**18B20**)

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 类型的温度值(单位:℃)

六、使用方法

  1. 确保硬件连接正确(DQ 引脚接 P3.7,电源和地正常连接)

  2. 初始化延时函数(Delay10us()Delay1ms()

  3. 直接调用get_temp()函数即可获取当前温度,例如:

    复制代码
    float temp;
    temp = get_temp(); // 读取温度值

七、注意事项

  1. 通信时序严格依赖延时函数,若延时不准确会导致通信失败
  2. get_temp()函数中包含 1000ms 的转换等待时间,若需优化响应速度,可根据 DS18B20 的转换速率调整延时(最高精度下转换时间约 750ms)
  3. 若总线上有多个 DS18B20,需修改代码(替换0xCC指令)以支持 ROM 匹配,当前代码默认单传感器场景(跳过 ROM)
相关推荐
kebidaixu2 小时前
FreeRTOS 移植到 STM32F407VETX 记录(一)
stm32·单片机·嵌入式硬件
CSDN官方博客2 小时前
「谁说嵌入式只是调包和焊板子?」—— 2026嵌入式全栈技术征锋令
嵌入式硬件·物联网·embedding
点灯小铭3 小时前
基于单片机的数码管定时插座设计与定时开关功能实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
云栖梦泽3 小时前
玩转RK3506SDK
linux·嵌入式硬件
数智工坊5 小时前
机器人四大主控板系统分层选型指南:树莓派、ESP32、STM32与Arduino的能力边界与实战定位
stm32·嵌入式硬件·机器人
进击的小头5 小时前
第8篇:IGBT 从零到精通:核心原理、关键参数、选型指南与工业级应用要点
经验分享·嵌入式硬件·学习
点灯小铭5 小时前
基于单片机的多模式智能洗衣机设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
项目題供诗5 小时前
STM32-AD单通道&AD多通道(十九)
stm32·单片机·嵌入式硬件
南岸的水6 小时前
BMS国标充电解析
单片机·嵌入式硬件·mcu
清风6666666 小时前
基于单片机的可调数控电源设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业