一、DS18B20介绍
DS18B20数字温度传感器是DALLAS公司生产的单总线器件,用它来组成一个测温系统具有线路简单,体积小,在一根通信线上可以挂很多这样的数字温度传感器,十分方便。
温度传感器种类众多,应用在高精度、高可靠性的场合时DALLAS公司生产的DS18B20温度传感器当仁不让。超小的体积,超低的硬件开销,抗干扰能力强,精度高,附加功能强,使得DS18B20更受欢迎。DS18B20的优势更是我们学习单片机技术和开发温度相关小产品的不二选择。了解工作原理和应用可以拓宽您对单片机开发的思路。
二、DS18B20特点
三、DS18B20在实际应用中的典型接法
1、工作在寄生电源下的典型接法
2、 外部供电下的典型接法
四、单总线时序
DS18B20采用1-wire Bus所有数据都在一条线上传输,因此单总线协议对时序要求非常严格以确保数据的完整性。
单总线信号类型:复位脉冲、存在脉冲、写0、写1、读0、读1。所有这些信号除存在脉冲由DS18B20发出的以外其他信号都由总线控制器发出。
数据传输总是从最低有效位开始。
1、初始化时序
初始化时序里面包含了复位DS18B20和接收DS18B20返回的存在信号。
主机和DS18B20做任何通讯前都需要对其初始化。初始化期间,总线控制器拉低总线并保持480us以上挂在总线上的器件将被复位,然后释放总线,等到15-60us,此时18B20将返回一个60-240us之间的低电平存在信号。
复位脉冲和存在脉冲时序图:
2、写时序
写时序分为写0时序和写1时序。
总线控制器通过控制单总线高低电平持续时间从而把逻辑1或0写DS18B20中。
总线控制器要产生一个写时序,必须将总线拉低最少1us,产生写0时序时总线必须保持低电平60~120us之间,然后释放总线,产生写1时序时在总线产生写时序后的15us内允许把总线拉高。注意:2次写周期之间至少间隔1us
3、读时序
读时序分为读0时序和读1时序。
总线控制器通过读取由DS18B20控制的总线高低电平接收DS18B20数据,总线控制器要产生一个读时序,必须将总线拉低至少1us,然后释放总线,在读信号开始后15us内总线控制器采样总线数据,读一位数据至少保持在60us以上。注意:2次读周期之间至少间隔1us
读时序图:
读1详细时序图:
五、DS18B20暂存器
温度寄存器图表:
配置寄存器图表:
部分ROM指令及功能指令:
执行序列 |
通过单线总线端口访问DS18B20的协议如下: |
步骤1. 初始化 |
步骤2. ROM操作指令 |
步骤3. DS18B20功能指令 |
忽略ROM指令(CCh):
这条指令允许总线控制器不用提供64 位ROM 编码就使用功能指令。例如,总线控制器可以先发出一条忽略ROM 指令,然后发出温度转换指令[44h],从而完成温度转换操作。在单点总线情况下使用该命令,器件无需发回64 位ROM 编码,从而节省了时间。如果总线上有不止一只从机,若发出忽略ROM指令,由于多只从机同时传送信号,总线上就会发生数据冲突。
六、DS18B20功能指令
1、温度转换指令(44h)
这条命令用以启动一次温度转换。温度转换指令被执行,产生的温度转换结果数据以2个字节的形式被存储在高速暂存器中,而后DS18B20保持等待状态。
2、读暂存器指令(BEh)
这条命令读取暂存器的内容。读取将从字节0 开始,一直进行下去,直到读完暂存器所有字节,如果不想读完所有字节,控制器可以在任何时间发出复位命令来中止读取。
3、写暂存器指令(4Eh)
这条命令向DS18B20 的暂存器写入数据,开始位置在TH 寄存器(暂存器的第2个字节),接下来写入TL 寄存器(暂存器的第3 个字节),最后写入配置寄存器(暂存器的第4 个字节)
4、拷贝暂存器指令(48h)
这条命令把TH,TL 和配置寄存器(第2、3、4 字节)的内容拷贝到EEPROM 中。
七、执行序列
通过单线总线端口访问DS18B20的协议如下:
步骤1. 初始化
步骤2. ROM操作指令
步骤3. DS18B20功能指令
温度转换命令
读取暂存器命令
八、DS18B20驱动代码
1、51单片机(数码管显示)
#include <reg52.h>
#include <intrins.h>
#define MAIN_Fosc 11059200UL //宏定义主时钟HZ
/*====================================
自定义类型名
====================================*/
typedef unsigned char INT8U;
typedef unsigned char uchar;
typedef unsigned int INT16U;
typedef unsigned int uint;
/*====================================
硬件接口位声明
====================================*/
sbit DS = P2^2; //DS18B20单总线
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
/*====================================
共阴极数码管段选码
====================================*/
uchar code table[]={
//0 1 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B C D E F - . 关显示
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x40, 0x80, 0x00
};
/*====================================
数码管位选码
====================================*/
//第1位 2位 3位 4位 5位 6位 7位 8位
uchar code T_COM[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//数码管位码
/*====================================
函数:void Delay_Ms(INT16U ms)
参数:ms,毫秒延时形参
描述:12T 51单片机自适应主时钟毫秒级延时函数
====================================*/
void Delay_Ms(INT16U ms)
{
INT16U i;
do{
i = MAIN_Fosc / 96000;
while(--i); //96T per loop
}while(--ms);
}
/*us延时函数,执行一次US--所需6.5us进入一次函数需要11.95us*/
void Delay_us(uchar us)
{
while(us--);
}
/*====================================
函数:void Display(INT16U Value)
参数:Value,显示值 取值0-65535
描述:共阴极数码管显示函数可显示一个字节的数
====================================*/
void Display(INT16U Value) //注意由于需要显示的数大于一个字节所有形参需为int型
{
//------------------------------
DU = 0; //关闭段选
P0 = table[Value/100]; //数码管显示百位
DU = 1; //打开段选
DU = 0; //关闭段选
WE = 0; //关闭位选
P0 = T_COM[0]; //第一位数码管
WE = 1; //打开位选
WE = 0; //关闭位选
Delay_Ms(3);
//-------------------------------
DU = 0;
P0 = table[Value%100/10]|0x80; //显示十位
DU = 1;
DU = 0;
WE = 0;
P0 = T_COM[1]; //第二位数码管
WE = 1;
WE = 0;
Delay_Ms(3);
//-------------------------------
DU = 0;
P0 = table[Value%10]; //显示个位
DU = 1;
DU = 0;
WE = 0;
P0 = T_COM[2]; //第三位数码管
WE = 1;
WE = 0;
Delay_Ms(3);
}
/*单总线初始化时序*/
bit ds_init()
{
bit i;
DS = 1;
_nop_();
DS = 0;
Delay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
DS = 1; //释放总线
Delay_us(4); //延时37.95us 等待18B20发回存在信号
i = DS;
Delay_us(20); //141.95us
DS = 1;
_nop_();
return (i);
}
/*写一个字节*/
void write_byte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();//产生些时序
DS = dat & 0x01;
Delay_us(10);//76.95us
DS = 1; //释放总线准备下一次数据写入
_nop_();
dat >>= 1;
}
}
uchar read_byte()
{
uchar i, j, dat;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();//产生读时序
DS = 1;
_nop_();//释放总线
j = DS;
Delay_us(10);//76.95us
DS = 1;
_nop_();
dat = (j<<7)|(dat>>1);
}
return (dat);
}
void main()
{
uint i;
uchar L, M;
/* ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x4e);//写暂存器指令
write_byte(0x7f);
write_byte(0xf7);
write_byte(0x1f);//配置工作在9位模式下
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x48);*/
while(1)
{
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x44);//发送温度转换指令
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0xbe);//读取DS18B20暂存器值
L = read_byte();
M = read_byte();
i = M;
i <<= 8;
i |= L;
i = i * 0.0625 * 10 + 0.5;
Display(i);
}
}
2、51单片机(LCD1602液晶显示)
#include <reg52.H>
#include <intrins.H>
#include <math.H>
#define uchar unsigned char
#define uint unsigned int
sbit dula = P2^6;
sbit wela = P2^7;
sbit rw = P3^6;
sbit RS = P3^5;
sbit LCDEN = P3^4;
void delayUs()
{
_nop_();
}
void delayMs(uint a)
{
uint i, j;
for(i = a; i > 0; i--)
for(j = 100; j > 0; j--);
}
void writeComm(uchar comm)
{
RS = 0;
P0 = comm;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
//写数据:RS=1, RW=0;
void writeData(uchar dat)
{
RS = 1;
P0 = dat;
LCDEN = 1;
delayUs();
LCDEN = 0;
delayMs(1);
}
void init()
{
rw = 0;
dula = wela = 0;
writeComm(0x38);
writeComm(0x0c);
writeComm(0x06);
writeComm(0x01);
}
void writeString(uchar * str, uchar length)
{
uchar i;
for(i = 0; i < length; i++)
{
writeData(str[i]);
}
}
/**//*****************************DS18B20*******************************/
sbit ds = P2^2;
void dsInit()
{
unsigned int i;
ds = 0;
i = 100;
while(i>0) i--;
ds = 1;
i = 4;
while(i>0) i--;
}
void dsWait()
{
unsigned int i;
while(ds);
while(~ds);
i = 4;
while(i > 0) i--;
}
bit readBit()
{
unsigned int i;
bit b;
ds = 0;
i++;
ds = 1;
i++; i++;
b = ds;
i = 8;
while(i>0) i--;
return b;
}
unsigned char readByte()
{
unsigned int i;
unsigned char j, dat;
dat = 0;
for(i=0; i<8; i++)
{
j = readBit();
dat = (j << 7) | (dat >> 1);
}
return dat;
}
void writeByte(unsigned char dat)
{
unsigned int i;
unsigned char j;
bit b;
for(j = 0; j < 8; j++)
{
b = dat & 0x01;
dat >>= 1;
if(b)
{
ds = 0; i++; i++;
ds = 1;
i = 8; while(i>0) i--;
}
else
{
ds = 0;
i = 8; while(i>0) i--;
ds = 1;
i++; i++;
}
}
}
void sendChangeCmd()
{
dsInit();
dsWait();
delayMs(1);
writeByte(0xcc);
writeByte(0x44);
}
void sendReadCmd()
{
dsInit();
dsWait();
delayMs(1);
writeByte(0xcc);
writeByte(0xbe);
}
int getTmpValue()
{
unsigned int tmpvalue;
int value;
float t;
unsigned char low, high;
sendReadCmd();
low = readByte();
high = readByte();
tmpvalue = high;
tmpvalue <<= 8;
tmpvalue |= low;
value = tmpvalue;
\
t = value * 0.0625;
\
value = t * 100 + (value > 0 ? 0.5 : -0.5); //大于0加0.5, 小于0减0.5
return value;
}
void display(int v)
{
unsigned char count;
unsigned char datas[] = {0, 0, 0, 0, 0};
unsigned int tmp = abs(v);
datas[0] = tmp / 10000;
datas[1] = tmp % 10000 / 1000;
datas[2] = tmp % 1000 / 100;
datas[3] = tmp % 100 / 10;
datas[4] = tmp % 10;
writeComm(0xc0+3);
if(v < 0)
{
writeString("- ", 2);
}
else
{
writeString("+ ", 2);
}
if(datas[0] != 0)
{
writeData('0'+datas[0]);
}
for(count = 1; count != 5; count++)
{
writeData('0'+datas[count]);
if(count == 2)
{
writeData('.');
}
}
}
/**//*****************************DS18B20*******************************/
void main()
{
uchar table[] = " xianzaiwendu: ";
sendChangeCmd();
init();
writeComm(0x80);
writeString(table, 16);
while(1)
{
delayMs(1000); //温度转换时间需要750ms以上
writeComm(0xc0);
display(getTmpValue());
sendChangeCmd();
}
}
3、STM32
#include "stm32f4xx_hal.h"
// DS18B20引脚定义
#define DS18B20_GPIO_PORT GPIOA
#define DS18B20_GPIO_PIN GPIO_PIN_0
// 定义DS18B20相关命令
#define DS18B20_CMD_SKIP_ROM 0xCC
#define DS18B20_CMD_CONVERT_T 0x44
#define DS18B20_CMD_READ_SCRATCHPAD 0xBE
// 函数声明
void DS18B20_DelayUs(uint32_t us);
void DS18B20_Init(void);
uint8_t DS18B20_Reset(void);
void DS18B20_WriteByte(uint8_t byte);
uint8_t DS18B20_ReadByte(void);
float DS18B20_GetTemperature(void);
int main(void)
{
// 初始化HAL库
HAL_Init();
// 初始化GPIO引脚
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = DS18B20_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStruct);
while (1)
{
// 测量温度并输出结果
float temperature = DS18B20_GetTemperature();
printf("Temperature: %.2f°C\r\n", temperature);
// 延时一段时间
HAL_Delay(1000);
}
}
// 微秒级延时函数
void DS18B20_DelayUs(uint32_t us)
{
uint32_t ticks = us * (SystemCoreClock / 1000000) / 3;
while (ticks--)
{
__NOP();
}
}
// 初始化DS18B20
void DS18B20_Init(void)
{
// 复位DS18B20
DS18B20_Reset();
// 发送跳过ROM命令
DS18B20_WriteByte(DS18B20_CMD_SKIP_ROM);
}
// 复位DS18B20并检测设备存在
uint8_t DS18B20_Reset(void)
{
uint8_t presence = 0;
// 拉低总线
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
DS18B20_DelayUs(480);
// 释放总线
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
DS18B20_DelayUs(60);
// 检测DS18B20响应
presence = HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
DS18B20_DelayUs(420);
return presence;
}
// 发送一个字节给DS18B20
void DS18B20_WriteByte(uint8_t byte)
{
for (uint8_t i = 0; i < 8; i++)
{
// 发送低位
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
DS18B20_DelayUs(2);
// 发送高位,根据byte的第i位来决定
if (byte & (1 << i))
{
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
}
DS18B20_DelayUs(60);
// 释放总线
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
}
}
// 从DS18B20读取一个字节
uint8_t DS18B20_ReadByte(void)
{
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; i++)
{
// 发送低位
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_RESET);
DS18B20_DelayUs(2);
// 释放总线
HAL_GPIO_WritePin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN, GPIO_PIN_SET);
DS18B20_DelayUs(8);
// 读取高位数据
if (HAL_GPIO_ReadPin(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN))
{
byte |= (1 << i);
}
DS18B20_DelayUs(50);
}
return byte;
}
// 读取DS18B20温度
float DS18B20_GetTemperature(void)
{
DS18B20_Init();
// 发送温度转换命令
DS18B20_WriteByte(DS18B20_CMD_CONVERT_T);
// 等待转换完成
HAL_Delay(800);
// 复位DS18B20并跳过ROM
DS18B20_Init();
// 发送读取寄存器命令
DS18B20_WriteByte(DS18B20_CMD_READ_SCRATCHPAD);
// 读取温度数据
uint8_t tempLow = DS18B20_ReadByte();
uint8_t tempHigh = DS18B20_ReadByte();
// 计算温度值
int16_t temp = (tempHigh << 8) | tempLow;
float temperature = (float)temp / 16.0f;
return temperature;
}