硬件开发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)
相关推荐
吠品3 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery11404 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白7 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米8 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip
尼喃9 小时前
662K/65Z5三脚稳压芯片规格书—PW6566B33/B30引脚功能
嵌入式硬件
创界工坊工作室9 小时前
63-基于STC89C52霍尔传感器的智能化电能表设计
stm32·单片机·嵌入式硬件·51单片机·proteus
智购科技自动售货机厂家10 小时前
让AI经营自动售货机:从Project Vend看工具权限与业务规则~YH
大数据·服务器·单片机·嵌入式硬件·深度学习
Zwawa11 小时前
从 printf 到曲线:把数据送上 VOFA+
c语言·单片机·嵌入式
意法半导体STM3212 小时前
【官方原创】LAT6048 如何扩大MP2的A35核和M33核共享内存
stm32·单片机·嵌入式硬件·内存映射·stm32mp2
en.en..12 小时前
STM32(F429)启动模式与两种烧录方式
stm32·单片机·嵌入式硬件