单总线one-Wire

单总线one-Wire

概述

One-Wire总线是DALLAS公司研制开发的一种协议

特点:

它是由一个总线主节点,一个或多个从节点组成系统,通过一根信号线对从芯片进行数据的读取

每一个符合One-Wire协议的从芯片都有一个唯一的地址,包括48位的序列号8位的家族代码和8位的CRC代码。主芯片对各个从芯片的寻址依据这64位的不同来进行。

One-Wire总线利用一根线实现双向通信(异步半双工)。因此其协议对时序的要求较严格,如应答等时序都有明确的时间要求。

工作原理

由一根数据线,系统中的数据交换,控制都由这跟线完成。
单总线通常要求外接一个约为4.7k的上拉电阻,这样,当总线闲置时,其状态为高电平1
由于它们是主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问One-Wire器件都必须严格遵循单总线命令序列,即初始化、ROM命令、功能命令。
如果出现序列混乱,One-Wire器件将不响应主机(搜索ROM命令、报警搜索命令除外)。

信号方式

复位和应答

在这里插入图片描述

  • 将DQ主线拉低持续500us DQ=0
  • 释放DQ主线的控制权 DQ=1
  • 持续20us判断DQ状态
    – 1.250us没有处于高电平 复位异常
    – 2.250us没有处于低电平 复位异常

one-Wire读写原理图

在这里插入图片描述

代码实现

#include "one_wire.h"  
#include "intrins.h"//内核用来进行低延时的头文件_nop_();

static u8 _ReadBit(void);//定义一个个读取字节的函数

void OneWire_Init(void)//初始化 因为有一个上拉电阻所以默认是高电平初步初始化无所谓
{
	DQ = 1;
}
u8 OneWire_Reset(void)//返回0:成功,返回1:失败
{
	u8 u8timeout = 0;
	//主机低位复位
	DQ = 0;
	delay_10us(50);//主机发送复位信号500us
	DQ = 1;	//主机释放DQ线,等待从机回应
	delay_10us(2);//主机等待20us后读取DQ状态
	//两个250us处于高低电平来判断是否复位成功
	//主机判断DQ是否被从机拉低250us
	while(DQ)
	{
		u8timeout++;
		if(u8timeout>25)return 1;//复位异常
		delay_10us(1);
	}

	//主机判断从机有没有释放DQ线
	u8timeout = 0;
	while(!DQ)
	{
		u8timeout++;
		if(u8timeout>25)return 1;//复位异常
		delay_10us(1);
	}
	return 0;//复位成功
}
//低位先读
void OneWire_WriteByte(u8 u8data)//LSB
{
	u8 i = 0;
	u8 u8temp = 0;//当前读取的字节
	
	for(i=0;i<8;i++)
	{
		u8temp = u8data & 0x01;//取低位
		u8data>>=1;//读取低位
		
		if(u8temp)//写1当前位写1的话  拉低电平持续2us,在拉高电平持续60us
		{
			DQ = 0;
			_nop_();_nop_();
			DQ = 1;
			delay_10us(6);
		}
		else//写0当前位写0的话  拉低电平持续60us,在拉高电平持续2us
		{
			DQ = 0;
			delay_10us(6);
			DQ = 1;
			_nop_();_nop_();
		}
	}
}
//读取字节
u8	OneWire_ReadByte(void)//LSB
{
		u8 i;
		u8 u8val = 0,u8temp = 0;
		for(i=0;i<8;i++)
		{
				//因为是一个个字节读取
				u8temp = _ReadBit();	//每次读取一个bit
				u8val += (u8temp<<i);//总数据
			
		}
	return u8val;
}

//单个比特读取
static u8 _ReadBit(void)
{
	u8 u8val = 0;

	DQ = 0;
	_nop_();_nop_();
	DQ = 1;
	_nop_();_nop_();

	if(DQ)
	{
		u8val = 1;
  }
	else
	{
		u8val = 0;
	}

	delay_10us(6);
	return u8val;
}

ROM命令

在主机检测到应答脉冲后,就可以发出 ROM 命令。这些命令与各个从机设备的唯一64位ROM代码相关,允许主机在单总线上连接多个从机设备时,指定操作某个从机设备。
在这里插入图片描述
常走是一对1也就是一号线

搜索ROM[F0h]

当系统初始上电时,主机必须找出总线上所有从机设备的ROM代码,这样主机就能够判断出从机的数目和类型。主机通过重复执行搜索ROM 循环(搜索ROM命令跟随着位数据交换),以找出总线上所有的从机设备。如果总线只有一个从机设备则可以采用读ROM命令来替代搜索ROM命令。在每次执行完搜索ROM循环后,主机必须返回至命令序列的第一步(初始化)

读ROM[33h](仅适合于单节点)

该命令仅适用于总线上只有一个从机设备。它允许主机直接读出从机的64位ROM 代码,而无须执行搜索ROM过程。
如果该命令用于多节点系统,则必然发生数据冲突,因为每个从机设备都会响应该命令。

匹配ROM[55h]

匹配ROM命令跟随64位ROM 码,从而允许主机访问多节点系统中某个指定的从机设备。仅当从机完全匹配64位ROM代码时,才会响应主机随后发出的功能命令;其它设备将处于等待复位脉冲状态。

跳越ROM[CCh] (仅适合于单节点)

主机能够采用该命令同时访问总线上的所有从机设备,而无须发出任何ROM代码信息。例如,主机通过在发出跳越ROM命令后跟随转换温度命令[44h],就可以同时命令总线上所有的DS18B20 开始转换温度,这样大大节省了主机的时间。值得注意,如果跳越ROM命令跟随的是读暂存器[BEh]的命令(包括其它读操作命令),则该命令只能应用于单节点系统,否则将由于多个节点都响应该命令而引起数据冲突。

报警搜索[ECh](仅少数1-wire 器件支持)

除那些设置了报警标志的从机响应外,该命令的工作方式完全等同于搜索ROM命令。该命令允许主机设备判断那些从机设备发生了报警(如最近的测量温度过高或过低等)。同搜索ROM命令

DS18B20功能命令

DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、 适用电压宽、与微处理器接口简单的数字化温度传感器

命令描述命令代码响应消息注释
转换温度启动温度转换0x441
读暂存器读全部的暂存器内容,包括CRC字节0xBEDS18B20传输至多9个字节2
写暂存器写暂存器第2、3和4个字节的数据(即TH、TL和配置寄存器)0x4E主机传输3个字节数据3
复制暂存器将暂存器中的TH、TL和配置字节复制到EEPROM中0x481
回读EEPROM将TH、TL和配置字节从EEPROM回读至暂存器中0xB8DS18B20传送回读状态至主机
读取供电方式0xB41bit:0 = 寄生电源,1 = 提供外部电源

注释:

  1. 在温度转换和复制暂存器数据至EEPROM期间,主机必须在单总线上允许强上拉。并且在此期间,总线上不能进行其它数据传输;
  2. 通过发出复位脉冲,主机能够在任何时候中断数据传输;
  3. 在复位脉冲发出前,必须写入全部的三个字节。

在这里插入图片描述

DS18B20特点

  • 适应电压范围更宽,电压范围3-5.5v
  • 独特的单线接口方式
  • DS18B20支持多点组网功能
  • DS18B20在使用中不需要任何外围元件
  • 温范围 -55℃ ~ 125℃,在10~85时的精度为±0.5
  • 可编程的分辨率在9~12位,
  • 在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换成数字,速度更快
  • 负压特性,不会因为发热而烧毁,只是不能正常工作

DS18B20的引脚

只需要 1 个数据传输引脚 (DQ) 。是 1-Wire 总线模式。
单总线模式没有时钟信号是怎么传输数据的呢????
单总线模式,即可传输时钟又可传输数据!

从 DS18B20 外观图可以看到,当我们正对传感器切面(传感器型号字符那 一面)时,传感器的管脚顺序是从左到右排列。管脚 1 为 GND,管脚 2 为数据 DQ,管脚 3 为 VDD。

内部结构

在这里插入图片描述

ROM

DS18B20温度传感器的内部存储器包括一个高速的暂存器RAM和一个非易失性的可电擦除的EEPROM,后者存放高温度和低温度触发器TH,TL和配置寄存器

可电擦除的EEPROM

配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下
在这里插入图片描述

低五位一直都是“1”,TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式,R1和R0是用来设置DS18B20的精度,可设置为9,10,11,12位,对应分辨率温度是0.5,0.25,0.125,0.0625℃

在这里插入图片描述
在初始状态下默认的精度是 12 位,即 R0=1、R1=1。

高速的暂存器RAM

在这里插入图片描述

发出温度转换命令44H发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第一字节

温度为正数时: 如果测得的温度大于 0,这 5 位为‘ 0’,只要将测到的数值乘以 0.0625 (默认精度是 12 位)即可得到实际温度;

温度为负数时:如果温度小于 0,这 5 位为‘ 1’, 测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。

在这里插入图片描述

DS18B20 时序包括如下几种:初始化时序、写(0 和 1)时序、 读(0 和 1)时序。 DS18B20 发送所有的命令和数据都是字节的低位在前(LSB)。

初始化时序

单总线上的所有通信都是以初始化序列开始。
a. 主机输出低电平,保持低电平时间至少480us(该时间的时间范围可以从480到960微妙),以产生复位脉冲。
b. 接着主机释放总线,外部的上拉电阻将单总线拉高,延时15~60us,并进入接收模式。
c. 接着 DS18B20拉低总线60~240us,以产生低电平应答脉冲,若为低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要 480 微妙

在这里插入图片描述

写时序

写时序包括写 0 时序和写 1 时序。
所有写时序至少需要 60us,且在 2 次 独立的写时序之间至少需要 1us 的恢复时间,两种写时序均起始于主机拉低总线。
写 0 时序:主机输出低电平,延时 60us,然后释放总线,延时 2us。
写 1 时序:主机输出低电平,延时 2us,然后释放总线,延时 60us。
在这里插入图片描述

读时序

单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要 60us,且在 2 次独立的读时序之间至少需要 1us 的恢复时间。
每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us 之内采样总线状态。
在这里插入图片描述
读 0 时序:主机输出低电平,延时 1us,然后释放总线,在15us内读取DQ的状态,18B20这时输出是0
读 1 时序:主机输出低电平,延时 至少1us,然后释放总线,在15us内读取DQ的状态,18B20这时输出是1

典型的温度读取过程为:

主机输出低电平延时 2us,然后主机转入输入模式延 时12us,然后读取单总线当前的电平,然后延时 50us。
在了解了单总线时序之后,我们来看看 DS18B20 的典型温度读取过程, DS18B20 的典型温度读取过程为:
复位→发 SKIP ROM 命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送 SKIP ROM 命令(0XCC)→发读存储器命令 (0XBE)→连续读出两个字节数据(即温度)→结束。

代码实现

实现的功能是:插上 DS18B20 温度传感器,数码管显示检测的温度值。
程序框架如下:
(1)编写数码管显示功能
(2)编写 DS18B20 读取温度功能
(3)编写主函数

//one-wire.h
#ifndef __ONE_WIRE_H
#define	__ONE_WIRE_H

#include "public.h"

sbit DQ = P3^7;			//定义单总线IO

void OneWire_Init(void);
u8 OneWire_Reset(void);			//返回0:成功,返回1:失败
void OneWire_WriteByte(u8 u8data);
u8	OneWire_ReadByte(void);

#endif


//wire.c
//功能:将DS18B20监测到的温度显示在数码管上,数值精确到小数点后一位。

#include "one_wire.h"
#include "intrins.h"

static u8 _ReadBit(void);

void OneWire_Init(void)
{
	DQ = 1;
}
u8 OneWire_Reset(void)//返回0:成功,返回1:失败
{
	u8 u8timeout = 0;
	//主机低位复位
	DQ = 0;
	delay_10us(50);//主机发送复位信号500us
	DQ = 1;	//主机释放DQ线,等待从机回应
	delay_10us(2);//主机等待20us后读取DQ状态

	//主机判断DQ是否被从机拉低250us
	while(DQ)
	{
		u8timeout++;
		if(u8timeout>25)return 1;//复位异常
		delay_10us(1);
	}

	//主机判断从机有没有释放DQ线
	u8timeout = 0;
	while(!DQ)
	{
		u8timeout++;
		if(u8timeout>25)return 1;//复位异常
		delay_10us(1);
	}
	return 0;//复位成功
}

void OneWire_WriteByte(u8 u8data)//LSB
{
	u8 i = 0;
	u8 u8temp = 0;
	
	for(i=0;i<8;i++)
	{
		u8temp = u8data & 0x01;
		u8data>>=1;
		
		if(u8temp)//写1
		{
			DQ = 0;
			_nop_();_nop_();
			DQ = 1;
			delay_10us(6);
		}
		else//写0
		{
			DQ = 0;
			delay_10us(6);
			DQ = 1;
			_nop_();_nop_();
		}
	}
}

u8	OneWire_ReadByte(void)//LSB
{
		u8 i;
		u8 u8val = 0,u8temp = 0;
		for(i=0;i<8;i++)
		{
				u8temp = _ReadBit();	//每次读取一个bit
				u8val += (u8temp<<i);
			
		}
	return u8val;
}


static u8 _ReadBit(void)
{
	u8 u8val = 0;

	DQ = 0;
	_nop_();_nop_();
	DQ = 1;
	_nop_();_nop_();

	if(DQ)
	{
		u8val = 1;
  }
	else
	{
		u8val = 0;
	}

	delay_10us(6);
	return u8val;
}

//ds18b20驱动
#ifndef __DS18B20_H
#define	__DS18B20_H

#include "public.h"
#include "one_wire.h"

void DS18B20_Init(void);
u16 DS18B20_GetTemperture(void);

#endif

//ds18b20.c
#include "ds18b20.h"

void DS18B20_Init(void)
{
	//todo
	OneWire_Init();
}
u16 DS18B20_GetTemperture(void)
{
	u16 u16val = 0;
	u8 u8DL = 0,u8DH = 0;

	
	if(OneWire_Reset()!=0)return u16val;

	OneWire_WriteByte(0xCC);//SKIP ROM命令
	OneWire_WriteByte(0x44);//发送温度转换命令
	delay_10us(1);

	if(OneWire_Reset()!=0)return u16val;

	OneWire_WriteByte(0xCC);//SKIP ROM命令
	OneWire_WriteByte(0xBE);//读取存储器命令

	//连续读出两个字节数据
	u8DL = OneWire_ReadByte();
	u8DH = OneWire_ReadByte();

	u16val = (u16)((u8DH<<8) | u8DL);

	return u16val;
}

//main.c
/*
功能要求:
将DS18B20监测的温度实时显示在数码管上,数值精确到小数点后一位。
*/

#include "public.h"
#include "smg.h"
#include "ds18b20.h"
#include "uart.h"


void main(void)
{
	u8 u8code[8] = {0};
	u8 i = 0;//计时器
	u16 u16data = 0;
	float tempter = 0.0;

	SMG_Init();
	UART_Init();
	DS18B20_Init();
	
	while(1)
	{
			//温度模块
			i++;
			if(i == 100)//每秒读一次温度
			{
					i = 0;
					u16data = DS18B20_GetTemperture();
					if(u16data & 0xf800)//零下温度
					{
						u8code[0] = 0x40;//温度符号
						u16data = (~u16data) +1;
					}
					else//零上温度
					{
						u8code[0] = 0;
					}
					tempter = u16data * 0.0625;//DS18B20温度计算
					tempter*=10;//把温度放大十倍
					u16data = (u16)tempter;
			}

			//数码管模块
			u8code[1] = smg_code[u16data/100];	//温度十位
			u8code[2] = smg_code[u16data/10%10];//温度个位
			u8code[2] |= 0x80;//小数点
			u8code[3] = smg_code[u16data%10];	//温度的小数位
			u8code[4] = 0x39;//C
			SMG_Display(u8code);
	}
}

void Uart_Isp(void)	interrupt 4
{
	u8	u8RecData = 0;

	if(RI)	//检测串口接收完成中断
	{
		UART_RecvData();
		RI = 0;		//用户清除接收数据完成标志
		
	}
}

void Int0(void)	interrupt 0
{

}

void Int1(void) interrupt 2
{

}

void Time0(void)	interrupt 1
{

	
}

void Time1(void) interrupt 3
{

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我斌哥哥

给打赏的我会单独一对一讲解

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值