主要参考学习资料:B站【普中官方】51单片机手把手教学视频
开发资料下载链接:http://www.prechin.cn/gongsixinwen/208.html
单片机套装:普中STC51单片机开发板A4标准版套餐7
目录
DS18B20介绍
主要特性

DS18B20是达拉斯半导体公司推出的单总线接口的传感器,与传统的热敏电阻测温元件相比,它是一种新型的体积小、工作范围宽、与微处理器接口简单的数字化温度传感器,具体特点如下:
①工作电压范围为3.0V~5.5V,除了依靠电源管脚供电,也可采用寄生电源方式(DQ数据线管脚供电)。
②采用单线接口方式,与微处理器只需要一根线即可实现数据通信。
③支持多点组网功能,多个DS18B20可以同时并联在单总线上。
④不需要外围元器件,所有传感器和转换电路都集成在DS18B20中。
⑤工作温度范围为-55℃~125℃,在-10℃~85℃的精度为±0.5℃。
⑥分辨率(测量数据的细微程度)设置范围为9~12bit,即0.5~0.0625,分辨率越高,最大转换时间越慢。DS18B20默认分辨率为12bit,最大转换时间750ms。
⑦测量结果直接以数字通过DQ串行输出给CPU,同时可传输CRC校验码,具有较强的抗干扰能力。
⑧具有负压特性,GND和VDD反接时DS18B20不会因发热而烧毁,但不能正常工作。
内部结构

ROM:刻有64位序列号,可以看作是地址序列号,含有8位产品类型标号(28H)、48位DS18B20自身序列号和8位针对前56位的CRC校验码。每个DS18B20的序列号都不相同,以便实现单总线的多点组网。
高速缓存存储器:包括一个高速暂存器RAM和一个EEPROM,后者存放高/低温触发器TH、TL和结构寄存器。
高速暂存存储器:由9个字节组成:
寄存器内容 | 字节地址 |
---|---|
温度值低位LS Byte | 0 |
温度值高位MS Byte | 1 |
高温限值TH | 2 |
低温限值TL | 3 |
配置寄存器 | 4 |
保留 | 5 |
保留 | 6 |
保留 | 7 |
CRC校验值 | 8 |
当温度转换命令44H发布后,经转换所得温度值以二进制补码形式存放在存储器MS和LS中,MS的高5位为符号位,若温度非负,则符号位均为0,将测得的数值乘以分辨率即为实际温度;温度为负时,符号位均为1,测得的数值需先取反后加一再乘以分辨率。

配置寄存器:高3位依次为TM、R1、R0,低5位均为1。TM为测试模式位,用于设置DS18B20在工作模式还是测试模式,在出厂时已被设置为1(工作模式),不宜改动。R1和R0用于设置分辨率,默认均为1,如下表所示:
R1 | R0 | 精度 | 最大转换时间 |
---|---|---|---|
0 | 0 | 9bit | 93.75ms |
0 | 1 | 10bit | 187.5ms |
1 | 0 | 11bit | 375ms |
1 | 1 | 12bit | 750ms |
控制时序
单总线器件需要严格的信号时序来保证数据的完整性,DS18B20的时序包括初始化时序、写时序、读时序。DS18B20发送命令和数据的字节均是低位在前。
初始化时序

单总线的所有通信均以初始化时序开始。①主机将总线先置高后拉低,保持480~960μs。②主机释放总线,电阻上拉,等待DS18B20在15~60μs后响应。③DS18B20拉低总线,保持60~240μs。④主机读取到低电平后继续延时,延时时间从释放总线算起至少480μs。
写时序

写时序包括写0和写1,所有写时序的过程至少需要60μs,且两次独立的写时序之间至少需要1μs的恢复时间。写时序均起始于主机拉低总线,若写0则延时60μs后拉高总线并继续延时2μs;若写1则延时大概2μs后拉高总线并继续延时60μs。
读时序

单总线器件仅在主机发出读时序后才向主机发送数据,所以在主机发出读取命令之后必须马上产生读时序。读时序的过程也至少需要60μs,两次独立的读时序之间至少需要1μs的恢复时间。读时序均由主机产生至少1μs的低电平发起,随后释放总线,并在其后的15μs之间采集总线状态,低电平则读0,高电平则读1。
DS18B20典型的温度读取过程:复位(初始化时序)→发动SKIP ROM命令(0XCC)→发送开始转换命令(0X44)→延时→复位→发送SKIP ROM命令(0XCC)→发送读存储器命令(0XBE)→连续读出两个字节的温度数据→结束。
硬件设计

本实验板载DS18B20传感器的数据总线绑定到P3.7IO口管脚。
实验15 DS18B20温度传感器
实现功能:插上DS18B20温度传感器,数码管显示检测的温度值。
DS18B20驱动
按照实验14中的多文件工程框架,在App>ds18b20中创建:
ds18b20.h
c
#ifndef _DS18B20_H
#define _DS18B20_H
#include "public.h"
//定义IO管脚
sbit DS18B20_PORT = P3^7;
//声明主函数需要的源文件函数
u8 ds18b20_init(void);
float ds18b20_read_temperature(void);
#endif
ds18b20.c
c
#include "ds18b20.h"
//通过intrins.h调用延时2μs需要用到的nop函数
#include "intrins.h"
//复位函数
void ds18b20_reset(void)
{
DS18B20_PORT = 0;
delay_10us(75);
DS18B20_PORT = 1;
delay_10us(2);
}
/*
检测DS18B20函数是否存在
若一直为高电平或低电平则不存在,返回1,否则返回0
*/
u8 ds18b20_check(void)
{
//计时器变量
u8 time_temp = 0;
//检测低电平
while(DS18B20_PORT && time_temp<20)
{
time_temp++;
delay_10us(1);
}
//若等待超时则返回1,否则重置计时器
if(time_temp >= 20)return 1;
else time_temp = 0;
//检测高电平
while((!DS18B20_PORT) && (time_temp<20))
{
time_temp++;
delay_10us(1);
}
//若等待超时则返回1,否则返回0
if(time_temp >= 20)return 1;
return 0;
}
//初始化同时检测存在
u8 ds18b20_init(void)
{
ds18b20_reset();
return ds18b20_check();
}
//写字节函数
void ds18b20_write_byte(u8 dat)
{
//8位输入循环控制变量
u8 i = 0;
//存储准备输入的位的变量
u8 temp = 0;
//从低到高按位输入
for(i=0;i<8;i++)
{
//和00000001进行与运算提取最低位
temp = dat & 0X01;
//字节右移将次低位移到最低位
dat >>= 1;
//根据temp写1或写0
if(temp)
{
//拉低总线
DS18B20_PORT = 0;
//延时2μs,nop函数延时1个机器周期,12MHz下即为1μs
_nop_();_nop_();
//写入1
DS18B20_PORT = 1;
//延时60μs
delay_10us(6);
}
else
{
//拉低总线写入0
DS18B20_PORT = 0;
//延时60μs
delay_10us(6);
//释放总线
DS18B20_PORT = 1;
//延时2μs
_nop_();_nop_();
}
}
}
//读取一个位
u8 ds18b20_read_bit(void)
{
//存储读取数据的变量
u8 dat = 0;
//拉低总线
DS18B20_PORT = 0;
//延时2μs
_nop_();_nop_();
//释放总线后读取
DS18B20_PORT = 1;
if(DS18B20_PORT)dat = 1;
else dat = 0;
delay_10us(5);
return dat;
}
//读取一字节
u8 ds18b20_read_byte(void)
{
//8位读取循环控制变量
u8 i = 0;
//存储读取到的每一位的变量
u8 temp = 0;
//将每一位存储进字节的变量
u8 dat = 0;
//从低到高按位读取
for(i=0;i<8;i++)
{
//读取一位
temp = ds18b20_read_bit();
//字节右移一位并将新位存储到最高位
dat >>= 1;
//或运算防止其他位被擦除
dat |= temp << 7;
}
return dat;
}
//温度读取函数
float ds18b20_read_temperature(void)
{
//存储高八位数值
u8 dath = 0;
//存储低八位数值
u8 datl = 0;
//存储完整十六位数值
u16 value = 0;
//存储转换得到的实际温度
float temp = 0;
//参照读时序介绍中的温度读取过程
ds18b20_reset();
ds18b20_check();
ds18b20_write_byte(0XCC);
ds18b20_write_byte(0X44);
ds18b20_reset();
ds18b20_check();
ds18b20_write_byte(0XCC);
ds18b20_write_byte(0XBE);
//先读低八位,后读高八位
datl = ds18b20_read_byte();
dath = ds18b20_read_byte();
//高八位移到十六位中的高八位再与低八位结合得到完整的十六位数值
value = (dath << 8) + datl;
//和1111100000000000与运算检测符号位
if((value&&0XF800) == 0XF800)
{
//符号位均为1则取反加一得到正值
value = (~value) + 1;
//计算实际温度
temp = value * (-0.0625);
}
else temp = value * 0.0625;
return temp;
}
数码管驱动
数码管驱动延用实验14,但为了数码管显示更加自定义,可以显示负号、小数点等符号,段码值转换功能将移到函数外部来完成,即参数dat直接传入段码值,以下是smg.c修改后的程序:
c
#include "smg.h"
u8 gseg_code[16] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
void seg_display(u8 dat[], u8 pos)
{
u8 i = 0;
u8 pos_temp = pos - 1;
for(i = pos_temp;i < 8;i++)
{
switch(i)
{
case 0: LSC = 1;LSB = 1;LSA = 1;break;
case 1: LSC = 1;LSB = 1;LSA = 0;break;
case 2: LSC = 1;LSB = 0;LSA = 1;break;
case 3: LSC = 1;LSB = 0;LSA = 0;break;
case 4: LSC = 0;LSB = 1;LSA = 1;break;
case 5: LSC = 0;LSB = 1;LSA = 0;break;
case 6: LSC = 0;LSB = 0;LSA = 1;break;
case 7: LSC = 0;LSB = 0;LSA = 0;break;
}
//此处删去gesg_code[]
SEG_A_DP_PORT = dat[i-pos_temp];
delay_10us(100);
SEG_A_DP_PORT = 0x00;
}
}
主函数
c
#include "public.h"
#include "smg.h"
#include "ds18b20.h"
void main()
{
//读取温度间隔的延时控制变量
u8 i = 0;
//本实验对温度保留一位小数处理,方法为先乘十后提取整数部分,在数码管上添加小数点显示
int temp_value = 0;
//存储每一位供数码管显示的段码值,温度测量结果最高占用5位数码管
u8 temp_buf[5];
//初始化
ds18b20_init();
while(1)
{
//计时器累加
i++;
//计时器延时50ms后读取,可自行调整
if(i%50 == 0)temp_value = ds18b20_read_temperature() * 10;
//判断温度正负
if(temp_value < 0)
{
//若温度为负,将温度转为正以便拆解数位,并使第一个数码管显示负号
temp_value = -temp_value;
temp_buf[0] = 0X40;
}
else
//若温度为正,第一个数码管不显示
temp_buf[0] = 0X00;
//将温度拆解数位并转换为段码值依次存储
temp_buf[1] = gseg_code[temp_value / 1000];
temp_buf[2] = gseg_code[temp_value % 1000 / 100];
temp_buf[3] = gseg_code[temp_value % 100 / 10] | 0X80;
temp_buf[4] = gseg_code[temp_value % 10];
//将温度显示在数码管后5位上
seg_display(temp_buf, 4);
}
}