51单片机—单总线和DS18B20
单总线是美国DALLAS公司推出的外围串行扩展总线技术。与SPI、IIC串行数据通信方式不同.它采用单根信号线,既传输时钟又传输数据,而且数据传输是双向的,具有节省I/O口线、资源结构简单、成本低廉、便于总线扩展和维护等诸多优点。单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线一根通信线:DQ异步、半双工单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以
文章目录
一、单总线
1.1.单总线介绍
单总线是美国DALLAS公司推出的外围串行扩展总线技术。与SPI、IIC串行数据通信方式不同.它采用单根信号线,既传输时钟又传输数据,而且数据传输是双向的,具有节省I/O口线、资源结构简单、成本低廉、便于总线扩展和维护等诸多优点。
- 单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线
- 一根通信线:DQ
- 异步、半双工
- 单总线只需要一根通信线即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线
1.2.单总线的原理
单总线器件内部设置有寄生供电电路(Parasite Power Circuit)。当单总线处于高电平时,一方面通过二极管VD向芯片供电,另方面对内部电容C(约800pF)充电;当单总线处于低电平时,二极管截止,内部电容c向芯片供电。由于电容c的容量有限,因此要求单总线能间隔地提供高电平以能不断地向内部电容C充电、维持器件的正常工作。这就是通过网络线路“窃取”电能的“寄生电源”的工作原理。要注意的是,为了确保总线上的某些器件在工作时(如温度传感器进行温度转换、E2PROM写入数据时)有足够的电流供给,除了上拉电阻之外,还需要在总线上使用MOSFET(场效应晶体管)提供强上拉供电。
1.3.单总线的结构
单总线主机或从机设备通过一个漏极开路或三态端口连接至该数据线,这样允许设备在不发送数据时释放数据总线,以允许设备在不发送数据时能够释放总线,而让其他设备使用总线,其内部等效电路。
单总线要求外接一个约 5k 的上拉电阻.这样,当单总线在闲置时,状态为高电平。如果传输过程需要暂时挂起,且要求传输过程还能够继续,则总线必须处于空闲状态。
传输之间的恢复时间没有限制,只要总线在恢复期间处于空闲状态(高电平)。如果总线保持低电平超过480 us,总线上的所有器件将复位。另外,在寄生方式供电时,为了保证单总线器件在某些工作状态下(如:温度转换器件、EEPROM写入等)具有足够的电源电流,必须在总线上提供强上拉。下图是单总线的电路规范:
1.4.单总线的命令序列和时序结构
1一wire协议定义了复位脉冲、应答脉冲、写0、读0和读1时序等几种信号类型。所有的单总线命令序列(初始化ROM命令,功能命令)都是由这些基本的信号类型组成。在这些信号中,除了应答脉冲外,其他均由主机发出同步信号、命令和数据,都是字节的低位在前。典型的单总线命令序列如下:
- 初始化。
- ROM命令,跟随需要交换的数据。
- 功能命令,跟随需要交换的数据。

每次访问单总线器件.都必须遵守这个命令序列.如果序列出现混乱,则单总线器件不会响应主机。但是这个准则对于搜索ROM命令和报警搜索命令例外,在执行两者中任何一条命令后,主机不能执行其他功能命令,必须返回至第一步。
1.4.1.初始化
主机将总线拉低至少480us,然后释放总线,等待15 ~ 60us后,存在的从机会拉低总线60 ~ 240us以响应主机,之后从机将释放总线。
1.4.2.发送一比特(bit)数据
主机将总线拉低60 ~ 120us,然后释放总线,表示发送0;主机将总线拉低1 ~ 15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us。
1.4.3.接收一比特(bit)数据
主机将总线拉低1 ~ 15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us。
1.4.4.发送一个字节和接收一个字节
- 发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)。

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

1.5.单总线的软件设计
下面是OneWire.c代码:
#include <REGX52.H>
sbit OneWire_DQ=P3^7;
//单总线的初始化
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char Ackbit;
OneWire_DQ=1;
OneWire_DQ=0; //主机先把电平拉高,再拉低
i = 227;while (--i); //延时500us
OneWire_DQ=1; //主机再把电平拉高,接下来就是把总线操控权交给从机
i = 29;while (--i); //延时70us
Ackbit=OneWire_DQ; //接收从机发来的信号
i = 227;while (--i); //延时500us
return Ackbit; //将从机发来的信号返回出去
}
//主机发送一个比特,主机先把总线拉低,经过10微秒延迟,再根据主机想发送的数据来变化,从机在50微妙之内采集数据,所以主机发送0或者1都要延迟50微妙,最后主机拉高总线电平
void OneWire_SentBit(unsigned char Bit)
{
unsigned char i;
EA=0;
OneWire_DQ=0;
i = 3;while (--i); //延时10us
OneWire_DQ=Bit;
i = 22;while (--i); //延时50us
OneWire_DQ=1;
EA=1;
}
//接收一个比特,主机先把总线拉到低电平,延迟5微秒,再拉高电瓶,再延迟5微秒(为了防止总线还没到达高电平,就被从机拉低至低电平,所以延迟5微秒),然后主机用Bit变量来读取从机发来的数据,再延迟50微秒。
unsigned char OneWire_ReceiveBit()
{
unsigned char i;
unsigned char Bit;
EA=0;
OneWire_DQ=0;
i = 1;while (--i); //延时5us
OneWire_DQ=1;
i = 1;while (--i); //延时5us
Bit=OneWire_DQ;
i = 22;while (--i); //延时50us
EA=1;
return Bit;
}
void OneWire_SentByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SentBit(Byte&(0x01<<i)); //主机发送数据,按位与发送
}
}
unsigned char OneWire_ReceiveByte(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);} //主机接收数据,用Byte变量承接
}
return Byte;
}
下面是OneWire.h代码:
#ifndef __ONEWIRE_H__
#define __ONEWIRE_H__
unsigned char OneWire_Init(void);
void OneWire_SentBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit();
void OneWire_SentByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);
#endif
二、DS18B20温度传感器
2.1.DS18B20温度传感器介绍
DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。DS18B20温度传感器具有以下几个特点:
- 适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电。
- 独特的单线接口方式,DS18B20 在与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。
- 温度范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃,通常用来测量日常温度。
- 可编程的分辨率为 9~12 位,对应的可分辨温度分别为 0.5℃、0.25℃、0.125℃ 和 0.0625℃,可实现高精度测温。
2.2.引脚及应用电路
2.2.1.引脚图

2.2.2.应用电路图
从 DS18B20 外观图可以看到,传感器的管脚顺序是从左到右排列。管脚 1 为 GND,管脚 2 为数据DQ,管脚 3 为 VDD。如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向,通常开发板上都会标出传感器的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可。
2.3.内部结构框图

ROM 中的 64 位序列号是出厂前被光刻好的,它可以看作是该 DS18B20 的地址序列号。64 位光刻 ROM 的排列是:开始 8 位(28H)是产品类型标号,接着的 48 位是该 DS18B20 自身的序列号,最后 8 位是前面 56 位的循环冗余校验码。光刻 ROM 的作用是使每一个 DS18B20 都各不相同,这样就可以实现一根总线上挂接多个 DS18B20 的目的。
- 64-BIT ROM:作为器件地址,用于总线通信的寻址
- SCRATCHPAD(暂存器):用于总线的数据交互
- EEPROM:用于保存温度触发阈值和配置参数
2.4.存储器结构
第0和1字节分别是温度值低位和高位;第2字节是高温限值(TH);第3字节是低温限值(TL);第4字节是配置寄存器;第5、6、7字节保留;第8字节是CRC校验值。
当**温度转换命令(44H)**发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第 0 和第 1 个字节。存储的两个字节,高字节的前 5 位是符号位 S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式如下:
如果测得的温度大于 0,这 5 位为 ‘0’,只要将测到的数值乘以 0.0625(默认精度是 12 位)即可得到实际温度;如果温度小于 0,这 5 位为 ‘1’,测到的数值需要取反加 1 再乘以 0.0625 即可得到实际温度。温度与数据对应关系如下:
2.5.软件设计
下面是DS18B20.c的代码:
#include <REGX52.H>
#include "OneWire.h"
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
void DS18B20_ConvertT(void)
{
OneWire_Init();
OneWire_SentByte(DS18B20_SKIP_ROM);
OneWire_SentByte(DS18B20_CONVERT_T);
}
float DS18B20_ReadT(void)
{
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init();
OneWire_SentByte(DS18B20_SKIP_ROM);
OneWire_SentByte(DS18B20_READ_SCRATCHPAD);
TLSB=OneWire_ReceiveByte();
TMSB=OneWire_ReceiveByte();
Temp=(TMSB<<8)|TLSB;
T=Temp/16.0;
return T;
}
下面是DS18B20.h的代码:
#ifndef __DS18B20_H__
#define __DS18B20_H__
void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);
#endif
下面是实现如温度超过上下阈值,而引发蜂鸣器报警的现象,下面是main主函数代码:
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"
#include "Key.h"
#include "AT24C02.h"
float T=0,TShow;
char THigh,TLow;
unsigned char KeyNum;
void main()
{
Timer0_Init ();
DS18B20_ConvertT();
Delay(1000);
THigh=AT24C02_ReadByte(0);
TLow=AT24C02_ReadByte(1);
if(THigh>125 || TLow<-55 || THigh<=TLow)
{
THigh=20;
TLow=15;
}
LCD_Init();
LCD_ShowString(1,1,"T:");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
LCD_ShowNum(2,4,THigh,3);
LCD_ShowNum(2,12,TLow,3);
/*读取温度及显示*/
while(1)
{
KeyNum=Key();
DS18B20_ConvertT();
T=DS18B20_ReadT();
if(T<0)
{
LCD_ShowChar(1,3,'-');
TShow=-T;
}
else
{
LCD_ShowChar(1,3,'+');
TShow=T;
}
LCD_ShowNum(1,4,TShow,3);
LCD_ShowChar(1,7,'.');
LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);
/*阈值判断及显示*/
if(KeyNum)
{
if(KeyNum==1)
{
THigh++;
if(THigh>125){THigh=125;}
}
if(KeyNum==2)
{
THigh--;
if(THigh<=TLow){THigh++;}
}
if(KeyNum==3)
{
TLow++;
if(THigh<=TLow){TLow--;}
}
if(KeyNum==4)
{
TLow--;
if(TLow<-55){TLow=-55;}
}
LCD_ShowSignedNum(2,4,THigh,3);
LCD_ShowSignedNum(2,12,TLow,3);
AT24C02_WriteByte(0,THigh);
Delay(5);
AT24C02_WriteByte(1,TLow);
Delay(5);
}
if(T>THigh)
{
LCD_ShowString(1,12,"OV:H");
}else if(T<TLow)
{
LCD_ShowString(1,12,"OV:L");
}
else
{
LCD_ShowString(1,12," ");
}
}
}
void Timer0() interrupt 1
{
static unsigned int Tcount;
TL0 = 0x18;
TH0 = 0xFC;
Tcount++;
if(Tcount>=20)
{
Tcount=0;
Key_loop();
}
}
更多推荐



所有评论(0)