参考教程:[13-1] DS18B20温度传感器_哔哩哔哩_bilibili
一、开发板上的传感器模块
1、DS18B20简介
(1)DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点。
(2)DS18B20的测温范围:-55°C 到 +125°C。
(3)DS18B20的通信接口:1-Wire(单总线)。
(4)DS18B20可形成总线结构、内置温度报警功能、可寄生供电。
2、引脚及其应用电路
引脚 | 功能 |
VCC | 电源(3.0V~5.5V) |
GND | 电源地 |
I/O(DQ) | 单总线接口 |
3、内部结构框图
(1)64-BIT ROM:作为器件地址,用于总线通信的寻址。
(2)SCRATCHPAD(暂存器):用于总线的数据交互。
(3)EEPROM:用于保存温度触发阈值和配置参数。
二、1-Wire BUS
1、概述
单总线(1-Wire BUS)是由Dallas公司开发的一种通用数据总线,它只需要一根通信线DQ(异步、半双工)即可实现数据的双向传输,当采用寄生供电时,还可以省去设备的VDD线路,此时,供电加通信只需要DQ和GND两根线。
2、单总线电路规范
(1)设备的DQ均要配置成开漏输出模式。
(2)DQ添加一个上拉电阻,阻值一般为4.7KΩ左右。
(3)若此总线的从机采取寄生供电,则主机还应配一个强上拉输出电路。
3、单总线时序结构
(1)初始化:主机将总线拉低至少480us,然后释放总线,等待15~60us后,存在的从机会拉低总线60~240us以响应主机,之后从机将释放总线。
(2)发送一位:主机将总线拉低60~120us,然后释放总线,表示发送0;主机将总线拉低1~15us,然后释放总线,表示发送1。从机将在总线拉低30us后(典型值)读取电平,整个时间片应大于60us。
(3)接收一位:主机将总线拉低1~15us,然后释放总线,并在拉低后15us内读取总线电平(尽量贴近15us的末尾),读取为低电平则为接收0,读取为高电平则为接收1 ,整个时间片应大于60us。
(4)发送一个字节:连续调用8次发送一位的时序,依次发送一个字节的8位(低位在前)。
(5)接收一个字节:连续调用8次接收一位的时序,依次接收一个字节的8位(低位在前)。
三、DS18B20的使用相关事宜
1、DS18B20操作流程
(1)初始化:从机复位,主机判断从机是否响应。
(2)ROM操作:ROM指令+本指令需要的读写操作。
(3)功能操作:功能指令+本指令需要的读写操作。
ROM指令 | 功能指令 |
SEARCH ROM [F0h] | CONVERT T [44h] |
READ ROM [33h] | WRITE SCRATCHPAD [4Eh] |
MATCH ROM [55h] | READ SCRATCHPAD [BEh] |
SKIP ROM [CCh] | COPY SCRATCHPAD [48h] |
ALARM SEARCH [ECh] | RECALL E2 [B8h] |
READ POWER SUPPLY [B4h] |
2、DS18B20数据帧
(1)温度变换:初始化→跳过ROM→开始温度变换。
(2)温度读取:初始化→跳过ROM→读暂存器→连续的读操作。(本例只要读SCRATCHPAD的前两个字节即可)
注:什么是“跳过ROM”?见上面的内部结构框图,SCRATCHPAD前接了一个ROM,跳过ROM就是直接来到SCRATCHPAD(在本例中只有SCRATCHPAD一个从机,要是后面还有其它从机就不能这样简单跳过)。
3、温度存储格式
S代表温度的正负(温度为负数,S为1),剩下的全都表示温度的数值(按二进制补码形式存储,存储在SCRATCHPAD的Byte0和Byte1,也就是前两个字节)。
四、温度传感器应用实验
1、DS18B20温度读取实验
(1)项目包含的文件:其中需要重写的都会在下面给出,未给出的沿用旧例出现过的即可(本例需要液晶屏模块的代码文件以及延时函数的代码)。
(2)补充缺失的代码文件,进行编译。(OneWire.c文件中有很多不同的延时代码,一定要结合时序图决定延迟多长时间,代码可以使用STC-ISP生成,在误差范围内延迟时间不一定要非常精确)
①OneWire.h文件:
#ifndef __OneWire_H__
#define __OneWire_H__
unsigned char OneWire_Init();
void OneWire_SendBit(bit Bit);
bit OneWire_ReceiveBit();
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte();
#endif
②OneWire.c文件:
#include <REGX52.H>
sbit OneWire_DQ = P3^7;
unsigned char OneWire_Init()
{
unsigned char i, AckBit;
OneWire_DQ = 1;
OneWire_DQ = 0; //拉低总线至少480us
i = 227;
while (--i); //Delay 500us
OneWire_DQ = 1; //主机释放总线,等待60us,从机会拉低总线60~240us
i = 29;
while (--i); //Delay 70us
AckBit = OneWire_DQ; //读取从机应答
i = 227;
while (--i); //Delay 500us(等从机释放总线)
return AckBit;
}
void OneWire_SendBit(bit Bit)
{
unsigned char i;
OneWire_DQ = 0; //主机先将总线拉低15us
i = 3;
while (--i); //Delay 10us
OneWire_DQ = Bit; //如果发生的Bit为1,释放总线,否则继续拉低总线,从机会在这50us内读走数据
i = 22;
while (--i); //Delay 50us
OneWire_DQ = 1; //主机释放总线
}
bit OneWire_ReceiveBit()
{
unsigned char i;
bit Bit;
OneWire_DQ = 0; //主机先将总线拉低1-15us
i = 1;
while (--i); //Delay 5us
OneWire_DQ = 1; //主机释放总线
i = 1;
while (--i); //Delay 5us
Bit = OneWire_DQ; //主机拉低总线后的15us内要读取数据
i = 22;
while (--i); //Delay 50us(时间片要大于60us)
return Bit;
}
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
unsigned char OneWire_ReceiveByte()
{
unsigned char i,Byte = 0x00;
for(i = 0; i < 8; i++)
{
if(OneWire_ReceiveBit())
{
Byte |= (0x01<<i);
}
}
return Byte;
}
③DS18B20.h文件:
#ifndef __DS18B20_H__
#define __DS18B20_H__
void DS18B20_ConvertT();
float DS18B20_ReadT();
#endif
④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()
{
OneWire_Init(); //初始化
OneWire_SendByte(DS18B20_SKIP_ROM); //跳过ROM
OneWire_SendByte(DS18B20_Convert_T); //发送指令:开始温度变换(测量当前温度)
}
float DS18B20_ReadT()
{
unsigned char TLSB,TMSB;
int Temp;
float T;
OneWire_Init(); //初始化
OneWire_SendByte(DS18B20_SKIP_ROM); //跳过ROM
OneWire_SendByte(DS18B20_READ_SCRATCHPAD); //发送指令:读暂存器中的温度数据
TLSB = OneWire_ReceiveByte(); //读取LSB
TMSB = OneWire_ReceiveByte(); //读取MSB(具体见内部结构框图的SCRATCHPAD)
Temp = (TMSB<<8)|TLSB; //将LSB和MSB拼接在一起得到完整的温度数据(二进制补码,有对应的十进制数字)
T = Temp/16.0; //测量值转成实际温度(16.0是经过多次测试而得的)
return T;
}
⑤main.c文件:
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"
float T;
void main()
{
DS18B20_ConvertT(); //先转换一次温度,这样就不会读到SCRATCHPAD中的默认温度值
Delay(1000);
LCD_Init();
LCD_ShowString(1,1,"Temperature");
while(1)
{
DS18B20_ConvertT(); //测当前温度
T = DS18B20_ReadT(); //获取温度值
if(T<0) //判断温度的正负
{
LCD_ShowChar(2,1,'-');
T = -T;
}
else
{
LCD_ShowChar(2,1,'+');
}
LCD_ShowNum(2,2,T,3);
LCD_ShowChar(2,5,'.');
LCD_ShowNum(2,6,(unsigned long)(T*10000)%10000,4); //显示温度T的小数部分(显示到小数点后4位)
}
}
(3)将生成的.hex文件下载到开发板中,观察液晶屏的现象。
2、DS18B20温度报警器实验
(1)项目包含的文件:其中需要重写的都会在下面给出,未给出的沿用旧例出现过的即可(本例需要液晶屏模块、独立按键模块(定时器扫描版)、存储器模块、I2C模块、定时器0模块、温度传感器模块的代码文件以及延时函数的代码)。
(2)补充缺失的代码文件,进行编译。
①OneWire.c文件:(主要是加入了屏蔽中断,防止时序被打乱)
#include <REGX52.H>
sbit OneWire_DQ = P3^7;
unsigned char OneWire_Init()
{
unsigned char i, AckBit;
EA = 0; //暂时屏蔽所有中断,防止延时函数被打乱(慎用!对定时器影响很大)
OneWire_DQ = 1;
OneWire_DQ = 0; //拉低总线至少480us
i = 227;
while (--i); //Delay 500us
OneWire_DQ = 1; //主机释放总线,等待60us,从机会拉低总线60~240us
i = 29;
while (--i); //Delay 70us
AckBit = OneWire_DQ; //读取从机应答
i = 227;
while (--i); //Delay 500us(等从机释放总线)
EA = 1; //恢复中断
return AckBit;
}
void OneWire_SendBit(bit Bit)
{
unsigned char i;
EA = 0; //暂时屏蔽所有中断,防止延时函数被打乱
OneWire_DQ = 0; //主机先将总线拉低15us
i = 3;
while (--i); //Delay 10us
OneWire_DQ = Bit; //如果发生的Bit为1,释放总线,否则继续拉低总线,从机会在这50us内读走数据
i = 22;
while (--i); //Delay 50us
EA = 1; //恢复中断
OneWire_DQ = 1; //主机释放总线
}
bit OneWire_ReceiveBit()
{
unsigned char i;
bit Bit;
EA = 0; //暂时屏蔽所有中断,防止延时函数被打乱
OneWire_DQ = 0; //主机先将总线拉低1-15us
i = 1;
while (--i); //Delay 5us
OneWire_DQ = 1; //主机释放总线
i = 1;
while (--i); //Delay 5us
Bit = OneWire_DQ; //主机拉低总线后的15us内要读取数据
i = 22;
while (--i); //Delay 50us(时间片要大于60us)
EA = 1; //恢复中断
return Bit;
}
void OneWire_SendByte(unsigned char Byte)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
OneWire_SendBit(Byte&(0x01<<i));
}
}
unsigned char OneWire_ReceiveByte()
{
unsigned char i,Byte = 0x00;
for(i = 0; i < 8; i++)
{
if(OneWire_ReceiveBit())
{
Byte |= (0x01<<i);
}
}
return Byte;
}
②main.c文件:
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "DS18B20.h"
#include "AT24C02.h"
#include "Key.h"
#include "Timer0.h"
float T,TShow;
char TLow, THigh;
unsigned char KeyNum;
void main()
{
THigh = AT24C02_ReadByte(0); //将上限值从存储器中读出
TLow = AT24C02_ReadByte(1); //将下限值从存储器中读出
if(THigh > 125|| TLow < -55 || THigh <= TLow) //如果存储器中的值非法,就赋予默认值
{
THigh = 20;
TLow = 15;
}
DS18B20_ConvertT(); //先转换一次温度,这样就不会读到SCRATCHPAD中的默认值
Delay(1000);
LCD_Init();
LCD_ShowString(1,1,"T:");
LCD_ShowString(2,1,"TH:");
LCD_ShowString(2,9,"TL:");
Timer0_Init();
while(1)
{
/*温度读取及显示*/
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); //显示温度T的小数部分(显示到小数点后2位)
/*阈值判断及显示*/
KeyNum = Key();
if(KeyNum) //如果有按键按下
{
if(KeyNum == 1) //按下按键1,上限值+1
{
THigh++;
if(TLow > 125) //温度传感器最高测到125摄氏度
TLow = 125;
}
if(KeyNum == 2) //按下按键2,上限值-1
{
THigh--;
if(TLow >= THigh) //下限不能大于等于下限
THigh++;
}
if(KeyNum == 3) //按下按键3,下限值+1
{
TLow++;
if(TLow >= THigh) //下限不能大于等于下限
TLow--;
}
if(KeyNum == 4) //按下按键4,下限值-1
{
TLow--;
if(TLow < -55) //温度传感器最低测到-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,13,"OV:H"); //超出上限警告
}
else if(T < TLow)
{
LCD_ShowString(1,13,"OV:L"); //超出下限警告
}
else
{
LCD_ShowString(1,13," ");
}
}
}
void Timer0_Routine() interrupt 1 //CPU响应中断后执行的函数
{
static unsigned int T0Count = 0; //定义计数器
T0Count++;
if(T0Count >= 20) //每20个中断信号(20ms)执行一次下面的代码段(20ms正好是按键消抖需要的时间)
{
Key_Loop();
T0Count = 0;
}
//每次中断结束都要重置计数单元
TH0 = 0xFC; //定时器0的计数单元高8位
TL0 = 0x66; //定时器0的计数单元低8位
}
(3)将生成的.hex文件下载到开发板中,根据main.c文件中的注释进行调试。