前言
这一次来介绍开发板上板载的温度传感器DS18B20,所使用的封装为左边这款三线的(两根电源线一根信号线)资料基于芯片手册以及普中51单片机A2开发板攻略
OneWire
硬件构成
单总线系统只有一条信号线,因此每一个总线上的器件必须是漏极开路或者三态输出(0、1、高阻态)
漏极&三态
首先先来复习一下模电----MOS管
漏极开漏电路如下
这是一个集成了二极管的MOS管,源极连接二极管
当二极管导通时,MOS管漏极接地
此时若MOS管导通,则输出口被拉至低电平0,当MOS管断开,输出口被上拉电阻拉至高电平1
当二极管断开时,MOS管漏极不接,此时输出口不受输入电路控制,呈现高阻态
OneWire电路
单总线的空闲状态为高电平,总线如果需要暂停某一执行过程时,如果想要恢复执行,总线必须停留在空闲状态,单总线处在高电平下时间可以无线长,但当总线停留在低电平超过480us时,总线上所有的器件都将被复位
时序逻辑
OneWire单总线大致包括以下几种时序:初始化时序、写(0 和 1)时序、 读(0 和 1)时序, DS18B20 发送所有的命令和数据都是字节的低位在前,下面我们来依次介绍一下
初始化时序
总线上的所有通讯均从初始化序列开始,和前面提到的一样,主机持续输出低电平使总线保持低电平在(480us-960us),这样可以产生一个复位脉冲
之后主机释放总线,总线被拉高置高电平15-60us以进入接收模式接收应答
从机拉低总线60-240us以产生低电平应答脉冲
写时序
写时序时总线被主机拉低15us左右然后释放总线,总线被拉至高电平
写时序有写1和写0,写时序至少要60us,相邻的两次写时序直接要间隔2us的恢复时间
两种写时序均起始于主机拉低低电平
在发出写时序15us后,主机发送要传输的数据,从机会在总线上进行采样
读时序
所有读时序最少60us,当主机把数据线从高电平拉到低电平时产生读时序
读时序产生后主机要保持1us后释放总线,此时从机通过控制数据线高低来输出0&1
主机在读时序后的15us内采样总线状态
当传输逻辑0结束后(45us后),总线将被释放,通过上拉电阻产生一个上升沿脉冲
单总线器件要在主机发出了读时序以后才能传输数据,因此在主机发出读数据命令时要再产生读时序才能命令从机传输数据
代码部分
#include <REGX52.H>
//引脚定义
sbit OneWire_DQ=P3^7;
/**
* @brief 单总线初始化
* @param 无
* @retval 从机响应位,0为响应,1为未响应
*/
unsigned char OneWire_Init(void)
{
unsigned char i;
unsigned char AckBit;
OneWire_DQ=1; //先置高再置低,复位
OneWire_DQ=0;
i = 247;while (--i); //保持低电平为500us,总线复位
OneWire_DQ=1; //释放总线
i = 32;while (--i); //延时70us,主机进入接收模式
AckBit=OneWire_DQ; //接收从机发来的应答位
i = 247;while (--i); //延时500us,等待从机释放总线
return AckBit;
}
/**
* @brief 单总线发送一位
* @param Bit 要发送的位
* @retval 无
*/
void OneWire_SendBit(unsigned char Bit)
{
unsigned char i;
OneWire_DQ=0; //拉低总线
i = 4;while (--i); //等待10us
OneWire_DQ=Bit; //将数据发送出去
i = 24;while (--i); //延时50us,从机此时开始采样
OneWire_DQ=1; //拉高总线释放
}
/**
* @brief 单总线接收一位
* @param 无
* @retval 读取的位
*/
unsigned char OneWire_ReceiveBit(void)
{
unsigned char i;
unsigned char Bit;
OneWire_DQ=0;
i = 2;while (--i); //主机发送一个下降沿脉冲,读时序产生,延时5us
OneWire_DQ=1; //主机释放总线
i = 2;while (--i); //延时5us
Bit=OneWire_DQ; //主机采样
i = 24;while (--i); //延时50us给主机采样
return Bit;
}
/**
* @brief 单总线发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
OneWire_SendBit(Byte&(0x01<<i)); //低位在前,因此先发送低位
}
}
/**
* @brief 单总线接收一个字节
* @param 无
* @retval 接收的一个字节
*/
unsigned char OneWire_ReceiveByte(void)
{
unsigned char i;
unsigned char Byte=0x00;
for(i=0;i<8;i++)
{
if(OneWire_ReceiveBit()){Byte|=(0x01<<i);} //低位在前,因此先接收低位
}
return Byte;
}
DS18B20
器件介绍
这是一款由DALLAS公司推出的单总线接口的温度传感器,其具有体积小,适用电压宽,且接口简单等优点,是一款良好的数字化温度传感器
该器件能够适应3.0-5.5V的电压,可通过IO口供电,通过一根信号线即可完成通信,且支持组网多点测温
测温范围为-55℃ - +125℃,可编程位有9-12位,精度最高可到0.0625℃,9位分辨率是最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度转换为数字
测量结果输出即为数字温度信号,同时带有CRC校验位,拥有良好的抗干扰能力
内部逻辑
我们一步步来介绍一下这些存储器的功能
64位ROM
这个存储器中存放了64位序列号,在出厂前就被光刻好,前8位为产品类型标号,接着48位为每一个器件自身的序列号,最后8位是前面56位的循环冗余CRC校验码
配置寄存器
这个寄存器有8位,其作用是用于配置传感器模块的分辨率模式,默认为12位模式
TM模式可以控制其为测试模式(1)和工作模式(0),产品出厂前会默认设为0,R1和R0为可编程位,用于设定分辨率模式,分别有9位、10位、11位、12位
正如图中写的,高速暂存寄存器中可以写入9个字节
其中,CRC码由ROM的前56位计算得到,而CRC字节由存储在存储器中的数据计算得到,当一个数据传输过来时,EEPROM中的CRC字节会和ROM中的CRC码进行比较以确保数据能够正常传输
对应的指令功能如下
这里要注意的几点是:
1 当DS18B20处于IO口供电模式下时,在温度转换和拷贝数据到EEPROM期间,必须给单总线一个强上拉,在这段时间内总线无法执行其他操作
2 总线控制器在任何时候都可以发出复位信号以中止数据传输
3 TH,TL,配置寄存器这三个字节的写入要在复位信号之前
执行逻辑
当从机接收到44H的数据时,经转换所得到的实际温度值会以2字节补码的形式存放在高速暂存存储器的第0个和第1个字节处,高字节的前五位为符号位,为1时代表负数,为0时代表正数,读取时低位在前,高位在后
在我们接收到数据后,需要进行一定的数学计算(在12位模式下的计算),计算流程如下:
如果温度>0,则这5位为'0',测到的数据要乘上分辨率0.0625,反之为'1',测到的数据要取反加1再乘以0.0625即可得到实际温度
不同温度区间对应的传输数据如下
举个例子,当传感器发送了07D0H的数据回来时,低位数据为D0,高位数据为07,高五位为0,代表是正数,此时经过下面的计算可以得到此时的温度为+125℃
整体流程
由于我们只有一个从机,因此我们可以使用SKIP ROM指令来忽略ROM指令,其功能为允许主机不用提供64位ROM编码就使用从机的功能
如果在多从机系统下就不能忽略ROM指令,否则会发生数据冲突,而且忽略ROM指令后只能操作读取寄存器指令
因此对于我们的单机系统来说,流程大致为:
复位 -> 发送SKIP ROM指令(0xCC) -> 发送开始转换指令(0x44)-> 延时复位 -> 发送SKIP ROM指令(0xCC)-> 发送读取存储器指令(0xBE)-> 读出温度数据 -> 结束
代码部分
#include <REGX52.H>
#include "OneWire.h"
//DS18B20指令
#define DS18B20_SKIP_ROM 0xCC
#define DS18B20_CONVERT_T 0x44
#define DS18B20_READ_SCRATCHPAD 0xBE
/**
* @brief DS18B20开始温度变换
* @param 无
* @retval 无
*/
void DS18B20_ConvertT(void)
{
OneWire_Init(); //总线初始化时序
OneWire_SendByte(DS18B20_SKIP_ROM); //发送忽略ROM指令
OneWire_SendByte(DS18B20_CONVERT_T); //发送转换指令
}
/**
* @brief DS18B20读取温度
* @param 无
* @retval 温度数值
*/
float DS18B20_ReadT(void)
{
unsigned char TLSB,TMSB; //开辟温度值存储空间
int Temp; //开辟2个字节存储总温度
float T;
OneWire_Init(); //总线初始化时序
OneWire_SendByte(DS18B20_SKIP_ROM); //发送忽略ROM指令
OneWire_SendByte(DS18B20_READ_SCRATCHPAD); //发送读取存储器指令
TLSB=OneWire_ReceiveByte(); //第一个字节为低位
TMSB=OneWire_ReceiveByte(); //第二个字节为高位
Temp=(TMSB<<8)|TLSB; //整合数据
T=Temp/16.0; //计算最终温度
return T;
}