当前位置: 首页 > article >正文

51单片机完全学习——DS18B20温度传感器

一、DS18B20数据手册解读

首先我们知道DS18B20使用的是单总线传输,默认情况下读出来的温度是12位的,我们这里只讨论外部电源供电这种情况。

有这张图片我们知道,12位温度的最小分辨率是10^-4次方,因此就是0.0625.我们只需要将最后读到的温度,乘以0.0625即可得到我们测量到的温度。

通过单总线端口访问DS18B20的协议是:首先进行初始化、ROM操作命令、存储器操作命令、执行/数据,这4步来完成的。接下来我们一步一步的来看

(1)初始化

首先我们需要知道,所有的执行都是从一个初始化序列开始的,我们需要控制总线先产生一个复位脉冲,一个最少保持480微秒的低电平信号,然后释放总线,进入到接收状态,当DS18B20检测到I/O引脚上的上升沿之后,DS18B20等待15~60微秒,然后发送一个存在脉冲,也就是一个60~240微秒的低电平信号。具体编程实现如下:我这里加了一些返回参数,用来保证初始化的成功

uchar ds18b20_init(void)
{
	uchar i = 0;
	DS18B20_BUS = 1;    //将数据线拉高方便产生下降沿
	_nop_();
	DS18B20_BUS = 0;    //产生复位脉冲
	delay500us();       //最短为480微秒的低电平信号
	DS18B20_BUS = 1;    //释放掉总线并进入接收状态
	_nop_();
	while (DS18B20_BUS) //循环检测ds18b20发送过来的存在信号
	{
		i++;
		delay20us();      //每20微秒检测一次
		if (i > 7)
		{
			return 1;       //如果等了140微秒还没有就返回1,表示失败
		}
	}
	delay500us();       //如果没有失败,就延时至少480微秒,等待ds18b20将总线释放
	return 0;
}

(2)ROM操作命令

我们想要进行ROM指令的操作,就必须得有一定的方法,给DS18B20发送数据,和接收数据。接下来我们先实现DS18B20对于一个字节数据的发送和接收。我们首先来看发送函数,也就是给DS18B20写数据:

我们主要是根据上面的时序图来进行程序的编写,首先先对时序图做一些解释:当主机把数据线从逻辑高电平拉高到逻辑低电平的时候,写时序开始,有两种写时序,一种是写0,一种是写1.所有的些时间时序必须最少持续60微秒,两个写周期之间至少间隔1微秒。数据线上的电平变低以后,DS18B20在15~60微秒的窗口内对数据线进行采样,如果线上是高电平就写1,如果线上是低电平就写0,我这里的编程思路是:首先将数据线拉低持续1个微秒,然后赶紧释放总线,然后将我们想要发送的0或者1放在数据线上,然后持续至少60微秒的延时。最后在释放总线,具体的编程实现如下:

//给ds18b20写入数据
void ds18b20_write(uchar cmd)
{
	uchar i = 0, temp = 0;
	for (i=0; i<8; i++)
	{
		DS18B20_BUS = 0;       //首先将总线拉低,产生写时序
		_nop_();               //延时一个微秒
		DS18B20_BUS = 1;       //释放总线
		temp = (cmd & 0x01);   
		DS18B20_BUS = temp;    //发送0或者1
		delay60us();           //凑够至少60微秒的时间
		DS18B20_BUS = 1;       //释放总线
		cmd >>= 1;             //连续两个写周期间隙应该大于1us
	}
}

接下来我们就需要进行读字节时序了,还是一样先来看时序图:

读时序和写时序差不都,只不过我们需要注意主机的采样时间,当主机把数据线从高电平拉到低电平时,读时序开始,数据线必须至少保持1微秒,然后我们就要将数据线释放掉,方便DS18B20传输数据,从DS18B20输出的数据在读时序开始后的15微秒内有效,也就是说我们必须在这个时间间隔内完成采样。所有的读时序都必须最少60微秒,两个读周期至少1微秒的恢复时间。具体编程实现如下:

//从ds18b20里面读取数据
uchar ds18b20_read(void)
{
	uchar date = 0, i = 0, temp = 0;
	for (i=0; i<8; i++)
	{
		DS18B20_BUS = 0;            //拉低总线产生读时序的起始信号
		_nop_();                    //产生1us的延时
		DS18B20_BUS = 1;            //释放掉总线
		delay6us();                 //延时6us 这里也可以是其他的延时只要保证15us以内
		temp = DS18B20_BUS;         //采集ds18b20上面的数据
		date = (date | (temp << i));//采集到的数据从低位到高位一次放好
		delay60us();                //等待ds18b20释放总线后,准备下一次的就收
	}
	return date;
}

当我们有了这两个函数之后我们就可以进行ROM指令的操作了。接下来我们来看一下,我们常用的ROM指令:一般来讲我们的设备上就只有一个DS18B20,也就是说,我们只需要一条指令,跳过ROM这一条指令就可以了,也就是发送cch就可以了。具体下图:

(3)存储器操作指令

我们一般也就用到 温度转换命令、读取暂存器命令。其他的不太需要:在我们发送出去读存储器的指令之后,我们只要连续的不停的读,他就会一直读到最后一个字节,也就是说当我们发送读存储器命令之后,我们读第一次就是再读,下面存储器映射表的字节0, 读第二次就是读字节1。如果我们只是想获取读到的温度值,我们只需要,发送完读,存储器命令之后读两次就可以了。

二、DS18B20读取温度

首先我们得知道,所有的操作都包含4步,1、初始化, 2、ROM操作指令,3、暂存器操作指令 4、读数据(这一步可以都很多次都可以)

首先我们需要给DS18B20发送一个温度转换的指令,具体编程如下:我这里发送完命令之后直接给他足够的时间,让他完成温度的转换。

//发送检测温度的命令
void ds18b20_change_temperature(void)
{
	ds18b20_init();        //首先进行初始化
	ds18b20_write(0xcc);   //发送ROM指令
  ds18b20_write(0x44);   //发送温度转换指令
	delay1s();             //等待温度转换完毕
}

 温度转换完成之后就是,读温度了,DS18B20将温度转换完毕后,他不会向我们主动发送,而是将转换完后的温度,放到暂存器里面,因此我们只需要都暂存器就可以了。具体代码实现如下:在使用的时候,我们只需要连续调用这两个函数就可以啦。

//读取转换完成的温度
//注意这里温度转换过程中将温度结果放大了1000倍
uint ds18b20_read_temperatur(void)
{
	uint temp = 0;
	uchar temp1 = 0, temp2 = 0;
	ds18b20_init();          //首先进行初始化
	ds18b20_write(0xcc);     //发送ROM指令
	ds18b20_write(0xbe);     //读取暂存器
	temp1 = ds18b20_read();	 //读取暂存器第0个字节    温度的低8位   
	temp2 = ds18b20_read();  //读取暂存器第1个字节    温度的高8位
    temp = ((temp2 << 8) | temp1);	
	temp = temp * 62.5;      //将温度进行转换
	return temp;
}

我这里还专门为这个读取温度的函数,写了一个串口发送函数,实现如下:需要注意串口发送一个字节的函数需要自己实现。

//串口发送温度专用函数
void uart_send_num1(uint num)
{
	uchar a[10] = 0, i = 0;
	do
	{
		a[i] = num % 10 + '0';
		num /= 10;
		a[i+1] = '\0';
		i++;
	}while (num);
	while (i--)
	{	
		uart_send_byte(a[i]);
		if (i == 3)
		{
			uart_send_byte('.');
		}
	}
	
}

三、总线上有两个DS18B20

我们前面讨论的是,总线上只有一个DS18B20的情况,接下来我们讨论总线上有2个DS18B20的情况,具体目标是让两个DS18B20能独立地采集温度。并将温度打印在串口:

首先我们需要知道我们手中的DS18B20内置ROM的唯一64位编码,具体实现如下:需要注意的是我们先一个一个的将DS18B20放到总线上然后运行下面的程序,这样我们就可以在串口上面看到这个DS18B20的唯一64位编码了。

	ds18b20_init();
	ds18b20_write(0x33);
	temp = ds18b20_read();
    uart_send_byte(temp);	
    temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);	
	temp = ds18b20_read();
    uart_send_byte(temp);

接下来我们就可以将2个DS18B20的总线连接到一起,然后通过ROM指令里面的匹配ROM,来查找我们需要查找的DS18B20,然后再和他进行通讯,发送温度转换命令,然后读取温度即可,具体实现如下:

        ds18b20_init();
	    ds18b20_write(0x55);
		ds18b20_write(0x28);
		ds18b20_write(0xaf);
		ds18b20_write(0x0d);
		ds18b20_write(0x85);
		ds18b20_write(0x00);
		ds18b20_write(0x00);
		ds18b20_write(0x00);
		ds18b20_write(0xd2);
		
		ds18b20_write(0x44);
	    delay1s();
	    temp = ds18b20_read_temperatur1();
		uart_send_byte('a');
	    uart_send_num1(temp);

	    ds18b20_init();
		ds18b20_write(0x55);
		ds18b20_write(0x28);
		ds18b20_write(0xb4);
		ds18b20_write(0xfc);
		ds18b20_write(0x43);
		ds18b20_write(0xd4);
		ds18b20_write(0xe1);
		ds18b20_write(0x3c);
		ds18b20_write(0x01);
		
		ds18b20_write(0x44);
	    delay1s();
	    temp = ds18b20_read_temperatur2();
		uart_send_byte('b');
	    uart_send_num1(temp);

 需要注意的是,在温度读取函数里面我们也必须针对不同的DS18B20有着不同的温度读取函数,我这里写的是原始的处理方式,以上的代码也可以简化,将每个DS18B20的64位编码放到一个数据里面,我们使用循环的方式发送就可以了。这里就不做进一步修改了,感兴趣的可以自己修改。


uint ds18b20_read_temperatur1(void)
{
	uint temp = 0;
	uchar temp1 = 0, temp2 = 0;
	ds18b20_init();          //首先进行初始化
	ds18b20_write(0x55);     //发送ROM指令
	ds18b20_write(0x28);
	ds18b20_write(0xaf);
	ds18b20_write(0x0d);
	ds18b20_write(0x85);
	ds18b20_write(0x00);
	ds18b20_write(0x00);
	ds18b20_write(0x00);
	ds18b20_write(0xd2);
	ds18b20_write(0xbe);     //读取暂存器
	temp1 = ds18b20_read();	 //读取暂存器第0个字节    温度的低8位   
	temp2 = ds18b20_read();  //读取暂存器第1个字节    温度的高8位
  temp = ((temp2 << 8) | temp1);	
	temp = temp * 62.5;      //将温度进行转换
	return temp;
}

uint ds18b20_read_temperatur2(void)
{
	uint temp = 0;
	uchar temp1 = 0, temp2 = 0;
	ds18b20_init();          //首先进行初始化
	ds18b20_write(0x55);     //发送ROM指令
	ds18b20_write(0x28);
	ds18b20_write(0xb4);
	ds18b20_write(0xfc);
	ds18b20_write(0x43);
	ds18b20_write(0xd4);
	ds18b20_write(0xe1);
	ds18b20_write(0x3c);
	ds18b20_write(0x01);
	ds18b20_write(0xbe);     //读取暂存器
	temp1 = ds18b20_read();	 //读取暂存器第0个字节    温度的低8位   
	temp2 = ds18b20_read();  //读取暂存器第1个字节    温度的高8位
  temp = ((temp2 << 8) | temp1);	
	temp = temp * 62.5;      //将温度进行转换
	return temp;
}


http://www.kler.cn/a/370084.html

相关文章:

  • 澎峰科技计算软件栈与沐曦GPU完成适配和互认证
  • Flink Gauss CDC:深度剖析存量与增量同步的创新设计
  • pikachu靶场-敏感信息泄露概述
  • JavaScript笔记基础篇03——函数
  • Github 2025-01-20 开源项目周报 Top15
  • [HCTF 2018]WarmUp
  • 医院信息化与智能化系统(12)
  • 极狐GitLab 发布安全补丁版本17.5.1, 17.4.3, 17.3.6
  • TextHarmony:视觉文本理解与生成的新型多模态大模型
  • 唤醒车机时娱乐屏出现黑屏,卡顿的案例分享
  • 深度学习(五):语音处理领域的创新引擎(5/10)
  • 106. 平行光阴影计算
  • springmvc请求源码流程解析(二)
  • 优先算法——移动零(双指针)
  • LVGL移植教程(超详细)——基于GD32F303X系列MCU
  • 人脸美颜 API 对接说明
  • 批量剪辑视频软件源码搭建全解析,支持OEM
  • 【瑞吉外卖】-day01
  • 使用 three.js 渲染个blender模型
  • 特定机器学习问题的基准测试数据
  • 【数据价值化】数据资产变现及管理规划
  • SQL语句优化之Sql执行顺序
  • 记录如何在RK3588板子上跑通paddle的OCR模型
  • 从零开始:使用Spring Boot搭建网上摄影工作室
  • c++二级指针
  • 数据结构作业day03