江协科技51单片机学习- p29 DS18B20温度传感器

   🚀write in front🚀  
🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​ 

💬本系列哔哩哔哩江科大51单片机的视频为主以及自己的总结梳理📚 

前言:

本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记,在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。

引用:

51单片机入门教程-2020版 程序全程纯手打 从零开始入门_哔哩哔哩_bilibili

​​​​​c51语言变量语句意思,C51中循环语句-CSDN博客

正文:

0. 🌿概述

在淘宝上购买了江协科技51单片机开发板套件(普中科技STC51单片机A2型号),就上在上一篇博文里说的自己计划学习下江协科技51单片机开发教程,通过STC51单片机这种MCU这种贴近于裸机的开发来增加对于系统硬件层面知识的了解和掌握。

术语和缩略语

缩写全称说明
1-WireOne-Wire Bus单总线
ScratchpadScratchpad暂存器

1. 🚀 DS18B20温度传感器介绍

DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点

  • 测温范围:-55°C 到 +125°C
  • 通信接口:1-Wire(单总线)
  • 其它特征:可形成总线结构、内置温度报警功能、可寄生供电

DS18B20是数字温度传感器,其输入输出是数字量,相对于模拟温度传感器,功能强大,使用简单。如果使用模拟温度传感器,例如热敏电阻,那么需要将一个电阻和热敏电阻串联起来,热敏电阻的阻值随着温度而变化,热敏电阻的电压分值也就随着温度而变化,使用AD(模式数字转换器)转换器来采集热敏电阻的电压分值,然后将电压分值转换为温度阻值。可以发现使用模拟温度传感器,需要AD模拟数字转换器,电路复杂。

2. 🚀引脚及应用电路

引脚说明
GND电源地
DQ单总线接口
VDD电源(3.0V ~ 5.5V)

DS18B20支持寄生供电,使用寄生供电可以减少引脚接线的使用,直接使用DQ作为寄生供电

 3. 🚀单总线通信协议

  • 单总线是由Daslas达拉斯公司开发的一种异步通用数据总线
  • 一根通信线:DQ
  • 异步,半双工
  • 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生共供电时,还可以省去设备的VDD线路,此时,供电通信只需要DQ和GND两根线。

单总线使用的时候有一些弊端,其使用和应用范围远没有I2C的使用广泛。几乎是DS18B20托起了单总线协议的一片天。

单总线的电路规范

  • 设备的DQ均要配置为开漏输出模式
  • DQ添加一个上拉电阻,阻值一般为4.7K欧姆左右
  • 若此总线的冲击采用寄生供电,则主机还需要配置一个强上拉输出电路。

单总线时序结构

初始化:当主机将总线拉低至少480us,然后释放总线,等待15-60us后,存在的从机会拉低总线60-240us以响应主机,然后从机将释放总线。

问题:这里的时间都是一个范围,不好估计要等待多长时间,最后程序里都使用一个中间值。

单总线发送一位

发送一位:主机将总线拉低60-120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1.从机将在主机法蒂30us后(典型值)读取电平,整个时间应大于60us。

 

单总线读取一位

接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1,整个时间拍你应该大于60us。

发送一个字节

发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位,低位在前。

接收一个字节:连续调用8次接收一位的时序,依次接受一个字节的8位,低位在前

 4.🚀DS18B20操作流程

每次对DS18B20进行操作都必须按照如下的步骤,每次对DS18B20操作之前都必须进行初始化

  • 初始化:从机复位,主机判断从机是否响应
  • ROM操作:ROM指令+本指令需要的读写操作
  • 功能操作:功能指令+本指令需要的读写操作

ROM操作就是对DS18B20寻址比较,功能操作就是RAM操作, 

 

执行"CONVERT T"功能指令,就会将温度传感器里的转换一次并放到RAM暂存器(ScratchPad)里。

 功能指令:

  • CONVERT T:温度传感器转换一次温度,并更新到ScratchPad暂存器里,即更新一次温度。
  • WRITE SCRATCHPAD :写暂存器
  • READ SCRATCHPAD:读暂存器
  • COPY SCRATCHPAD:将暂存器里的值写入到E2PROM
  • RECALl E2:将E2PROM里的值调到暂存器

5. 🚀DS18B20数据帧

  DS18B20数据帧,读取温度需要进行的操作:

温度转换:初始化->跳过ROM->开始温度转换

温度读取:初始化->跳过ROM->读暂存器->连续的读操作

6. 🚀DS18B20温度存储格式

DS18B20温度传感器中的温度存储格式,实际上是一种二进制的补码格式的形式,第4位的bit[3:0]表示小数,2^(-4)表示最小为0.0625的分辨率。

7. 🚀单片机电路原理图

在我使用的普中科技的51单片机开发板上 DS18B20 的DQ引脚接在 P3_7 引脚。

8. 🚀编写单总线函数读取/发送单总线数据

定义单总线的DQ引脚

sbit OneWire_DQ=P3^7;

单总线初始化函数

根据DS18B20手册中初始化时序规定,在初始化序列期间,总线控制器拉低总线并保持 480us 以发出(TX)一个复位脉冲,然后释放总线,进入接收状态(RX)。单总线由 5K 上拉电阻拉到高电平。当DS18B20 探测到 I/O 引脚上的上升沿后,等待 15-60us,然后发出一个由 60-240us低电平信号构成的存在脉冲。

单总线初始化时序:

  • 主机拉低DQ总线至少480us,然后主机释放DQ总线主机进入Rx状态,总线由上拉电阻拉高到高电平。
  • 从机(DS18B20)在检测到上升沿后,从机等待15~60us后,从机拉低DQ总线60-240us表示总线上有从机出现。
  • 主机Tx状态的时间至少为480us,主机Rx状态的时序至少为480us,这两个时间主要决定了我们在源码函数中主机应该延时的时间长短。

sbit OneWire_DQ=P3^7;

/**
  * @brief  OneWire单总线初始化
  * @param  无
  * @retval OneWire单总线从机应答位,返回0表示有从机应答,返回1表示无从机应答
  */
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	
	OneWire_DQ = 1;			//OneWire总线拉高
	OneWire_DQ = 0;			//OneWire总线拉低
	
	//Delay480us();			//OneWire总线拉低480us
	_nop_();i = 227;while (--i);	//Delay 500us
	
	OneWire_DQ = 1;			//OneWire释放总线
	
	
	
	_nop_();i = 29;while (--i);		//延时70us
	AckBit = OneWire_DQ;
	_nop_();i = 227;while (--i);	//Delay 500us
	
	return AckBit;
}

单总线主机发送一个Bit数据(写时序)

DS18B20 的数据读写是通过时序处理位来确认信息交换的。

写时序
由两种写时序:写 1 时序和写 0 时序。总线控制器通过写 1 时序写逻辑 1 到DS18B20,写 0 时序写逻辑 0 到 DS18B20。所有写时序必须最少持续 60us,包括两个写周期之间至少 1us 的恢复时间。当总线控制器把数据线从逻辑高电平拉到低电平的时候,写时序开始。

总线控制器要生产一个写时序,必须把数据线拉到低电平然后释放,在写时序开始后的 15us 释放总线。当总线被释放的时候,5K 的上拉电阻将拉高总线。总控制器要生成一个写 0 时序,必须把数据线拉到低电平并持续保持。
 

线控制器初始化写时序后,DS18B20 在一个 15us 到 60us 的窗口内对 I/O 线采样。如果线上是高电平,就是写 1。如果线上是低电平,就是写 0。

按照DS18B20手册中写时序的规定,当主机发起写时序时将DQ总线拉低然后再开始后的15us内释放总线,DQ总线从高电平拉低为低电平表示写时序的开始。写时序的长度需要至少为60us,但小于120us,写时序的时间不能太长因为写时序的时间太长的话就和Reset复位时序的长度接近了可能会造成混淆。

当主机向总线上写0时,主机拉低总线,并保持60us。当主机向总线上写1时,主机拉低总线,然后再15us内释放总线(总线拉低的时间需要大于1us),总线将会有上拉电阻拉高到高电平。 从机在 15us典型值的时间进行采样,如果此时总线为高电平则表示主机写1,总线为低电平表示主机写0。写时序的时间需要至少为60us,但小于120us。

/**
  * @brief  OneWire总线发送1个Bit数据
  * @param  要发送的Bit位数据
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	
	OneWire_DQ = 0;					//OneWire总线拉低
	_nop_();i = 2;while (--i);		//Delay 10us
	
	OneWire_DQ = Bit;
	
	i = 29;while (--i);				//Delay 70us
	
	
	OneWire_DQ = 1;					//OneWire总线释放
}

 单总线主机接收一个Bit数据(读时序)

读时序

总线控制器发起读时序时,DS18B20 仅被用来传输数据给控制器。因此,总线控制器在发出读暂存器指令[BEh]或读电源模式指令[B4H]后必须立刻开始读时序,DS18B20可以提供请求信息。除此之外,总线控制器在发出发送温度转换指令[44h]或召回 EEPROM 指令[B8h]之后读时序,详见 DS18B20 功能指令节。(源自DS18B20手册)
所有读时序必须最少 60us,包括两个读周期间至少 1us 的恢复时间。当总线控制器把数据线从高电平拉到低电平时,读时序开始,数据线必须至少保持 1us,然后总线被释放(见图 14)。在总线控制器发出读时序后,DS18B20 通过拉高或拉低总线上来传输 1 或 0。当传输逻辑 0 结束后,总线将被释放,通过上拉电阻回到上升沿状态。从 DS18B20 输出的数据在读时序的下降沿出现后 15us 内有效。因此,总线控制器在读时序开始后必须停止把 I/O 脚驱动为低电平 15us,以读取I/O 脚状态。
 

根据DS18B20手册的时序规定,主机读取DS18B20一个Bit数据的单总线时序函数如下

/**
  * @brief  OneWire总线接收一位数据
  * @param  无
  * @retval OneWire总线接收到的一位Bit数据
  */
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char Bit;
	unsigned char i;
	
	OneWire_DQ = 0;
	_nop_();_nop_();_nop_();_nop_();	//Delay 5us
	
	OneWire_DQ = 1;
	_nop_();_nop_();_nop_();_nop_();	//Delay 5us,然后采样
	Bit = OneWire_DQ;
	
	i = 22;while (--i);					//Delay 50us 
	
	return Bit;
}

 9. 实验1-读取DS18B20温度传感器

读取DS18B20温度传感器实验1-程序源码

OneWire.c 

#include <REGX52.H>
#include <INTRINS.H>
#include "onewire.h"

sbit OneWire_DQ=P3^7;

/**
  * @brief  OneWire单总线初始化
  * @param  无
  * @retval OneWire单总线从机应答位,返回0表示有从机应答,返回1表示无从机应答
  */
unsigned char OneWire_Init(void)
{
	unsigned char i;
	unsigned char AckBit;
	
	OneWire_DQ = 1;			//OneWire总线拉高
	OneWire_DQ = 0;			//OneWire总线拉低
	
	//Delay480us();			//OneWire总线拉低480us
	_nop_();i = 227;while (--i);	//Delay 500us
	
	OneWire_DQ = 1;			//OneWire释放总线
	
	
	
	_nop_();i = 29;while (--i);		//延时70us
	AckBit = OneWire_DQ;
	_nop_();i = 227;while (--i);	//Delay 500us
	
	return AckBit;
}

/**
  * @brief  OneWire总线发送1个Bit数据
  * @param  要发送的Bit位数据
  * @retval 无
  */
void OneWire_SendBit(unsigned char Bit)
{
	unsigned char i;
	
	OneWire_DQ = 0;					//OneWire总线拉低
	_nop_();i = 2;while (--i);		//Delay 10us
	
	OneWire_DQ = Bit;
	
	i = 29;while (--i);				//Delay 70us
	
	
	OneWire_DQ = 1;					//OneWire总线释放
}

/**
  * @brief  OneWire总线接收一位数据
  * @param  无
  * @retval OneWire总线接收到的一位Bit数据
  */
unsigned char OneWire_ReceiveBit(void)
{
	unsigned char Bit;
	unsigned char i;
	
	OneWire_DQ = 0;
	_nop_();_nop_();_nop_();_nop_();	//Delay 5us
	
	OneWire_DQ = 1;
	_nop_();_nop_();_nop_();_nop_();	//Delay 5us,然后采样
	Bit = OneWire_DQ;
	
	i = 22;while (--i);					//Delay 50us 
	//_nop_();i = 29;while (--i);		//Delay 70us
	
	return Bit;
}

/**
  * @brief  OneWire总线发送一个字节数据
  * @param  要发送的一个字节数据
  * @retval 无
  */
void OneWire_SendByte(unsigned char Byte)
{
	unsigned char i;
	
	for(i=0; i<8; i++)
	{
		OneWire_SendBit(Byte & (0x01<<i));		//OneWire总线先发送低位然后发送高位
	}
}

/**
  * @brief  OneWire总线接收一个字节数据
  * @param  无
  * @retval 接收到的一个字节的数据
  */
unsigned char OneWire_ReceiveByte(void)
{
	unsigned char i;
	unsigned char Byte = 0;
	
	for(i=0; i<8; i++)
	{
		if(OneWire_ReceiveBit())
			Byte |= (0x01 << i);
	}
	
	return Byte;
}

OneWire.h

#ifndef __ONE_WIRE_H__
#define __ONE_WIRE_H__

unsigned char OneWire_Init(void);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit(void);
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);
#endif

Ds18b20.c

#include <REGX52.H>
#include <INTRINS.H>
#include "OneWire.h"
#include "UART.h"
#include "LCD1602.h"

#define DS18B20_SKIPROM				0xCC
#define DS18B20_CONVERT_T			0x44
#define DS18B20_READ_SCRATCHPAD		0xBE

/**
  * @brief  发送命令字通知DS18B20采集温度传感器数据并存放在暂存器中以便接下来读取
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(void)
{
	OneWire_Init();
	
	OneWire_SendByte(DS18B20_SKIPROM);		//0xCC  1100 1100
	OneWire_SendByte(DS18B20_CONVERT_T);	//0x44	0100 0100
}

/**
  * @brief  发送命令字读取DS18B20中暂存器中的温度传感器的数据
  * @param  无
  * @retval 浮点数温度值
  */
float DS18B20_ReadTemperature(void)
{
	float T;
	int tempert;
	unsigned char TLSB;
	unsigned char TMSB;
	
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIPROM);				//0xCC  1100 1100
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);		//
	TLSB = OneWire_ReceiveByte();
	TMSB = OneWire_ReceiveByte();
	
	tempert = (TMSB << 8) | TLSB;		//TLSB和TMSB组合为int型,因为温度的低4位为小数,所以相当于左移了4位
	T = tempert/16.0;					//除以16.0得到小数
	
	return T;
}

/**
  * @brief  发送命令字读取DS18B20中暂存器中的温度传感器的数据
  * @param  无
  * @retval 温度传感器的高8位和低8位组成的原始数据,调用者需要处理分析符号位和低4位的小数部分
  */
int DS18B20_ReadT(void)
{
	int tempert;
	unsigned char TLSB;
	unsigned char TMSB;
	
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIPROM);				//0xCC  1100 1100
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);		//
	TLSB = OneWire_ReceiveByte();
	TMSB = OneWire_ReceiveByte();
	
	tempert = (TMSB << 8) | TLSB;		//TLSB和TMSB组合为int型,因为温度的低4位为小数,所以相当于左移了4位
	
	return tempert;
}

Ds18b20.h

#ifndef __DS18B20_H__
#define __DS18B20_H__


float DS18B20_ReadTemperature(void);
int DS18B20_ReadT(void);
void DS18B20_ConvertT(void);

#endif

main.c

#include <REGX52.H>
#include <INTRINS.H>
#include "LCD1602.h"
#include "onewire.h"
#include "delay.h"
#include "UART.h"

float T;
unsigned int intVal;

void main()
{
	unsigned char AckBit = 0;
	
	LCD_Init();
	
	LCD_ShowString(1,1,"Temperature");
	AckBit = OneWire_Init();
	LCD_ShowString(2,1,"Ack:");
	LCD_ShowNum(2, 5, AckBit, 1);
	
	while(1)
	{
		DS18B20_ConvertT();
		Delay(1);
		T = DS18B20_ReadT();
		
		if(T < 0){
			LCD_ShowChar(2,1,'-');
			T = -T;					//负数转换为正数
		}
		else{
			LCD_ShowChar(2,1,'+');
		}
		
		LCD_ShowNum(2, 2, T/16, 3);
		LCD_ShowChar(2, 5, '.');
		LCD_ShowNum(2, 6,(((unsigned long)T)*10000)/16%10000, 4);	//处理显示温度的小数部分
	}
}

实验1测试结果

使用淘宝购买的24MHz逻辑分析仪抓取到的One-Wire单总线读取DS18B20温度传感器值时的总线时序逻辑图。

详细的看这个发送 SkipROM 命令字 0xCC的时序。

分析这个命令字时序:

  • One-Wire单总线时序,LSB低位优先发送。
  • 主机拉低DQ总线表示写时序开始,如果要写0,主机拉低DQ总线并保持至少60us。
  • 主机拉低DQ总线表示写时序开始,如果要写1,主机在15us内释放DQ总线,然后总线由上拉电阻拉高为高电平。
  • 从机(DS18B20)在典型值30us时对DQ总线进行采样,当采样到DQ为高电平表示写1,采样到低电平表示写0.
  • 主机写0或写1的时序至少为60us,单小于120us。

逻辑分析仪抓取到的读取DS18B20从机数据的时序,如下:

详细看下这个主机读取温度值LSB低8位的时序

分析这个主机读取DS18B20的时序:

  • 主机拉低DQ总线,表示读时序开始。
  • 主机拉低DQ总线时间大于1us,并且小于15us内主机释放DQ总线。
  • 从机检测到读时序开始后,通过拉低或拉高DQ总线来发送0或1。
  • 从机在读时序开始后,贴近15us的末尾对DQ总线进行采样,DQ总线采样到低电平表示读取到0,DQ总线采样到高电平表示读取到1.
  • 读时序的时间至少要为60us,如果读时序的时间小于60us,单总线读取操作会异常。

10. 实验2-读取DS18B20温度传感器并设置温度告警

DS18B20温度传感器第二个实验增加了温度传感器读取出来的问题的告警功能,增加了温度过高的告警阈值和温度过低的告警阈值,当从DS18B20读取到的温度超过温度过高告警阈值的时候在LCD1602屏幕上显示温度过高告警,当从DS18B20上读取到的问题小于温度低告警阈值的时候显示温度低告警,同时通过独立按键KEY1,KEY2,KEY3,KEY4来调整温度告警阈值。

源码如下,这里只给出了 main.c 的源码,因为其它的源码模块在之前的实验里都已经写好了,可以参考哔站江协科技51单片机系列教程中的之前可成讲解获取到前置的其它功能模块文件。

main.c

#include <REGX52.H>
#include <INTRINS.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"
#include "Key.h"
#include "AT24C02.h"

char T_High = 28;	//带符号char类型
char T_Low = 19;	//带符号char类型

void main()
{
	int T;
	int temT;
	float temper;
	unsigned char KeyNum = 0;
	unsigned char flashFlag = 0;
	
	LCD_Init();
	LCD_ShowString(1,1,"T:");
	
	
	T_High = AT24c02_ReadByte(0);
	T_Low =  AT24c02_ReadByte(1);
	
	//第一次读取判断AT24C02 E2PROM中值是否合法
	if(T_High > 125 || T_Low < -55 || T_High < T_Low){
		T_High = 28;
		T_Low = 19;
	}
	
	while(1)
	{
		/* 温度读取及显示 */
		DS18B20_ConvertT();				//DS18B20采集温度
		Delay(1);						//延时1ms,等待温度转换完成
		T = DS18B20_ReadT();			//DS18B20读取问题
		
		if(T < 0)				
		{
			LCD_ShowChar(1,3,'-');		//如果温度小于0,显示'-'号
			temT=-T;						//将温度变为正数
		}
		else						
		{
			LCD_ShowChar(1,3,'+');		//显示正号
			temT = T;
		}
		LCD_ShowNum(1,4,temT/16,3);		//显示温度整数部分
		LCD_ShowChar(1,7,'.');			//显示小数点
		LCD_ShowNum(1,8,((unsigned long)temT)*100/16%100,2);	//显示温度小数部分
		
		
		/* 阈值判断及显示 */
		LCD_ShowString(2,1,"H:");
		LCD_ShowNum(2,3,T_High,2);
		LCD_ShowString(2,5," L:");
		LCD_ShowNum(2,8,T_Low,2);
		
		
		//判断温度过高告警,和温度过低告警
		temper = T/16.0;
		if(temper > T_High)
		{
			//flashFlag != flashFlag;
			//if(flashFlag)
				LCD_ShowString(2, 11, "HO");	//告警信息闪烁
			//else
			//	LCD_ShowString(2, 11, "  ");
		}
		else if(temper < T_Low)
		{
			//flashFlag != flashFlag;
			//if(flashFlag)
				LCD_ShowString(2, 11, "LO");	//告警信息闪烁
			//else
			//	LCD_ShowString(2, 11, "  ");
		}
		else
		{
			LCD_ShowString(2, 11, "  ");
		}
		
		
		//独立按键部分,
		//KEY1增加温度高门限值
		//KEY2降低温度高门限值
		//KEY3增加温度低门限值
		//KEY4降低温度低门限值
		KeyNum = Key();
		if(KeyNum)
		{
			if(KeyNum == 1)
			{
				T_High++;
			}
			else if(KeyNum == 2)
			{
				T_High--;
				if(T_High <= T_Low)
					T_High = T_Low + 1;
			}
			else if(KeyNum == 3)
			{
				T_Low++;
				if(T_Low >= T_High)
					T_Low = T_High - 1;
			}
			else if(KeyNum == 4)
			{
				T_Low--;
			}
			
			/* 温度阈值存到AT24C02 E2PROM */
			AT24c02_WriteByte(0, T_High);
			Delay(5);
			AT24c02_WriteByte(1, T_Low);
			Delay(5);
		}
	}
}

实验2温度告警实验实际效果:

11. 实验2-温度告警实验改进使用定时器中断按键扫描

实验2-温度告警实验改进,使用定时器中断按键扫描配合DS18B20单总线时序。在哔站江协科技51单片机的本节视频教程里讲到,定时器中断按键扫描打断DS18B20的单总线时序可能会遇到问题。

😨😨😨使用了定时器中断按键扫描,当定时器超时时定时器中断到来,定时器中断打断正在进行的DS18B20单总线时序,因为定时器中断处理函数的执行需要时间,这样进入到定时器中断到离开定时器中断处理函数后,DS18B20单总线的读取/写入时序时间要求就有可能超过规范定义的时间了,造成DS18B20单总线通讯温度读取异常。

实验2-改进使用定时器中断扫描的独立按键的方法来扫描按键,配合DS18B20单总线温度读取/写入的实际实验结果就是,LCD1602显示屏上显示的读取到问题不停跳变读取到错误的温度值😨。

🧃这个问题的原因就是上面分析的,单总线协议对时间的延时非常敏感,单总线的时间规定是us(微秒)级别的,而ST89C52RC执行一条指令的时间就是大约1个us,当定时器中断打断单总线的操作时序进入中断处理函数执行并离开中断处理函数的时候单总线的操作时序可能已经超时了。

🧃解决这个问题的方法之一是,在操作读取DS18B20温度传感器之前通过寄存器 'EA=0' 关闭掉STC89C52单片机的全局中断,在读取DS18B20温度传感器之后通过寄存器 'EA=1' 打开掉STC89C52单片机的全局中断。

🧃通过关闭和代开51单片机全局中断的方法虽然可以解决单总线读取/写入时序的问题,但是通过关闭全局中断的方式并不是一个好的解决方案,因为当在关闭51单片机系统全局中断的时候,如果此时有中断需要处理,该中断就不能得到及时的处理。本实验中我们使用的定时器中断按键扫描对时间并不敏感所以没有问题。

🧃这就是单总线鸡肋的地方,使用单总线的时候,必须十分的注意单总线的时序要求。

在DS18B20.C中通过 'EA=0, EA=1'带读取DS18B20之前关闭51单片机全局中断和打开单片机全局中断的源码如下:

#include <REGX52.H>
#include <INTRINS.H>
#include "OneWire.h"
#include "UART.h"
#include "LCD1602.h"

#define DS18B20_SKIPROM				0xCC
#define DS18B20_CONVERT_T			0x44
#define DS18B20_READ_SCRATCHPAD		0xBE

/**
  * @brief  发送命令字通知DS18B20采集温度传感器数据并存放在暂存器中以便接下来读取
  * @param  无
  * @retval 无
  */
void DS18B20_ConvertT(void)
{
	EA = 0;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
	
	OneWire_Init();
	
	OneWire_SendByte(DS18B20_SKIPROM);		//0xCC  1100 1100
	OneWire_SendByte(DS18B20_CONVERT_T);	//0x44	0100 0100
	
	EA = 1;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
}

/**
  * @brief  发送命令字读取DS18B20中暂存器中的温度传感器的数据
  * @param  无
  * @retval 浮点数温度值
  */
float DS18B20_ReadTemperature(void)
{
	float T;
	int tempert;
	unsigned char TLSB;
	unsigned char TMSB;
	
	EA = 0;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
	
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIPROM);				//0xCC  1100 1100
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);		//
	TLSB = OneWire_ReceiveByte();
	TMSB = OneWire_ReceiveByte();
	
	tempert = (TMSB << 8) | TLSB;		//TLSB和TMSB组合为int型,因为温度的低4位为小数,所以相当于左移了4位
	T = tempert/16.0;					//除以16.0得到小数
	
	EA = 1;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
	
	return T;
}

/**
  * @brief  发送命令字读取DS18B20中暂存器中的温度传感器的数据
  * @param  无
  * @retval 温度传感器的高8位和低8位组成的原始数据,调用者需要处理分析符号位和低4位的小数部分
  */
int DS18B20_ReadT(void)
{
	int tempert;
	unsigned char TLSB;
	unsigned char TMSB;
	
	EA = 0;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
	
	OneWire_Init();
	OneWire_SendByte(DS18B20_SKIPROM);				//0xCC  1100 1100
	OneWire_SendByte(DS18B20_READ_SCRATCHPAD);		//
	TLSB = OneWire_ReceiveByte();
	TMSB = OneWire_ReceiveByte();
	
	EA = 1;		//读取DS18B20的时候关闭STC52RC单片机的全局所有中断
	
	
	tempert = (TMSB << 8) | TLSB;		//TLSB和TMSB组合为int型,因为温度的低4位为小数,所以相当于左移了4位
	
	return tempert;
}

11.总结

11.1 使用Logic2配合逻辑分析仪抓取分析单总线(1-wire)时序时注意如下配置

使用Logic2配合逻辑分析仪抓取分析单总线(1-wire)时序时注意如下配置:

分析单总线时序时去掉这个勾选框“Overdirve only mode”,否则会Logic2上位机软件分析单总线通信时序异常,不能正确接续抓取到的时序数据。

11.2 单总线的读取时序时间应该大于60us

 单总线协议规范规定,单总线的读取时序时间应该大于60us。在做实验的时候通过逻辑分析仪抓取主机读取DS18B20从机的读时序发现,程序中我写出了一个Bug:就是通过单总线发起读时序读取DS18B20的时候,读时序的长度小于了60us,这样就会发现DS18B20从机就工作异常了不能正确的读取到DS18B20发送的数据。

总结:

  • 应该注意单总线的主机读取时序时间应该至少为60us。
  • 应该注意单总线的主机写时序应该至少为60us,但小于120us。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值