51单片机——DS18B20温度传感器

由于DS18B20数字温度传感器是单总线接口,所以需要使用51单片机的一个IO口模拟单总线时序与DS18B20通信,将检测的环境温度读取出来

1、DS18B20模块电路

传感器接口的单总线管脚接至单片机P3.7IO口上

2、DS18B20介绍

2.1 DS18B20外观实物图

管脚1为GND,管脚2为数据DQ,管脚3为VDD

2.2 DS18B20内部结构图

2.2.1 64位光刻ROM

64位光刻ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列号。

64位光刻 ROM的排列是:开始8位(28H)是产品类型标号,接着的48位是该DS18B20自身的序列号,最后 8位是前面56位的循环冗余校验码。

光刻ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的

2.2.2 高速缓存存储器

DS18B20温度传感器的内部存储器包括一个高速的暂存器RAM和一个非易失性的可电擦除的 EEPROM,后者存放高温度和低温度触发器TH、TL和配置寄存器

2.2.2.1 配置寄存器

配置寄存器是配置不同的位数来确定温度和数字的转化,配置寄存器结构如下图所示:

(1)低五位一直都是"1"

(2)TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式。在 DS18B20出厂时该位被设置为0,用户不需要去改动

(3)R1和R0用来设置DS18B20的精度(分辨率),精度可设置为9,10,11或12位,对应的分辨率温度是 0.5℃,0.25℃,0.125℃和0.0625℃。R0和R1配置如下图所示:

在初始状态下默认的精度是12位,即R0=1、R1=1

2.2.2.2 高速暂存存储器

(1)高速暂存存储器由9个字节组成,其分配如下图所示:

当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节

(2)存储的两个字节,高字节的前5位是符号位S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式如下:

如果测得的温度大于0,这5位为"0",只要将测到的数值乘以0.0625(默认精度是12位)即可得到实际温度

如果温度小于0,这5位为"1", 测到的数值需要取反加1再乘以0.0625即可得到实际温度。

温度与数据对应关系如下图所示:

2.3 DS18B20时序

DS18B20时序包括如下几种:初始化时序、写(0和1)时序、 读(0和1)时序

DS18B20发送所有的命令和数据都是字节的低位在前

2.3.1 初始化时序

主机输出低电平,保持低电平至少480us(该时间范围:480-960us),以产生复位脉冲。接着主机释放总线,外部的上拉电阻将单总线拉高,延时15-60us,并进入接收模式

接着DS18B20拉低总线60-240us,以产生低电平应答脉冲,若为低电平,还要做延时,其延时的时间从外部上拉电阻将单总线拉高算起最少要480us

//初始化时序图13的左半段:复位脉冲时序

void ds18b20_reset(){

DS18B20_PORT=0; //拉低DQ线(单总线) 主机输出低电平

delay_10us(75); //750us(480us-960us) 保持低电平至少480us

DS18B20_PORT=1; //拉高DQ线 主机释放总线,外部的上拉电阻将单总线拉高

delay_10us(2); //20us(15us-60us) 延时15-60us

}
//初始化时序图13的右半段:检测DS18B20是否存在,返回值0存在;返回值1不存在

u8 ds18b20_check(){

u8 time_temp=0;

while(DS18B20_PORT&&time_temp<20){ //DS18B20_PORT:1

time_temp++;

delay_10us(1); //10us,要循环20次,相当于200us(60-240us)

}

if(time_temp>=20){

return 1; //超时了,仍没有等到低电平

}else{

time_temp=0;

}

//如果等到了低电平,DS18B20_PORT变为0

while((!DS18B20_PORT)&&time_temp<20){ //DS18B20_PORT:0

time_temp++;

delay_10us(1);

}

if(time_temp>=20){

return 1; //超时了,仍没有等到高电平

}

return 0;

}

2.3.2 写时序

写时序包括写0时序和写1时序。所有写时序至少需要60us,且在2次独立的写时序之间至少需要1us的恢复时间,两种写时序均起始于主机拉低总线

写1时序:主机输出低电平,延时2us,然后释放总线,延时60us

写0时序:主机输出低电平,延时60us,然后释放总线,延时2us

//写时序:写一个字节到ds18b20中,提前准备好数据

void ds18b20_write_byte(u8 dat){

u8 i=0;

u8 temp=0;

//从低位向高位写 eg:1001 0011

for(i=0;i<8;i++){

temp=dat&0x01; //拿到dat中低位的数据

dat>>=1;

if(temp){

//写1时序

DS18B20_PORT=0; 主机输出低电平

nop(); //1us 延时2us

nop();

DS18B20_PORT=1; 释放总线

delay_10us(10); //100us(60-120us) 延时60us

}else{

//写0时序

DS18B20_PORT=0; 主机输出低电平

delay_10us(6); 延时60us

DS18B20_PORT=1; 释放总线

nop(); //1us 延时2us

nop();

}

}

}

2.3.3 读时序

所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间。每个读时序都由主机发起,至少拉低总线1us。主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态

典型的读时序过程为:主机输出低电平延时2us,然后主机转入输入模式延时12us,然后读取单总线当前的电平,然后延时50us

//读一位

u8 ds18b20_read_bit(){

u8 dat=0; //存的是某一位(0或1)

//主机输出低电平

DS18B20_PORT=0;

//延时2us

nop(); //1us

nop();

DS18B20_PORT=1; 主机转入输入模式

//延时2us,时间不能过长,必须在15us内读到数据 延时12us

nop(); //1us

nop();

if(DS18B20_PORT){ 读取单总线当前的电平

dat=1;

}else{

dat=0;

}

delay_10us(5); 延时50us

return dat;

}

//读一个字节

u8 ds18b20_read_byte(){

u8 i=0;

u8 dat=0;

u8 temp=0;

for(i=0;i<8;i++){

temp=ds18b20_read_bit(); //i=0时,temp=1;i=1时,temp=1

dat>>=1; //i=0时,dat=000 0000;i=1时,dat=0100 0000

dat|=temp<<7; //i=0时,dat=1000 0000;i=1时,dat=1100 0000

}

return dat;

}

2.3.4 DS18B20的典型温度读取过程

复位→发SKIPROM命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送SKIPROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束

/*

DS18B20 的典型温度读取过程为:

复位→发SKIPROM命令(0XCC)→发开始转换命令(0X44)

→延时→

复位→发送SKIPROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束。

*/

//开始温度转换

void ds18b20_start(){

ds18b20_init(); //初始化:复位和检查

ds18b20_write_byte(0xcc); //发SKIPROM命令(0XCC)

ds18b20_write_byte(0x44); //发开始转换命令(0X44)

}

//从ds18b20得到温度值

double ds18b20_read_temperture(){

double temp;

u8 dath=0;

u8 datl=0;

u16 value=0;

ds18b20_start();

ds18b20_init(); //初始化:复位和检查

ds18b20_write_byte(0xcc); //发送SKIPROM命令(0XCC)

ds18b20_write_byte(0xbe); //发读存储器命令(0XBE)

//连续读出两个字节数据(即温度)

datl=ds18b20_read_byte(); //低8位

dath=ds18b20_read_byte(); //高8位

//高8位和低8位连接起来,拼成16位

value=(dath<<8)+datl;

//判断是正温度还是负温度

if((value&0xf800)==0xf800){ //负温度

value=(~value)+1;

temp=value*(-0.0625);

}else{ //正温度

temp=value*0.0625;

}

return temp;

}

3、实验

要实现的功能是:插上DS18B20温度传感器,数码管显示检测的温度值

如果不知道怎么进行多文件编程,51单片机------I2C-EEPROM-CSDN博客中有

3.1 Public文件

3.1.1 public.h

//头文件中放置函数的声明、全局变量的定义

#ifndef _public_H

#define _public_H

#include "reg52.h"

//全局变量

typedef unsigned int u16;

typedef unsigned char u8;

//两个延迟函数声明

void delay_10us(u16 us);

void delay_ms(u16 ms);

#endif

3.1.2 public.c

#include "public.h"

void delay_10us(u16 us){

while(us--);

}

void delay_ms(u16 ms){

u16 i=0,j=0;

for(i=0;i<ms;i++){

for(j=0;j<110;j++);

}

}

3.2 动态数码管

3.2.1 smg.h

#ifndef _smg_H

#define _smg_H

#include "public.h"

sbit LSA=P2^2;

sbit LSB=P2^3;

sbit LSC=P2^4;

#define SMG_A_DP_PORT P0

extern u8 gsmg_code[];

void smg_display(u8 save_buff[],u8 pos);

#endif

3.2.2 smg.c

#include "smg.h"

u8 gsmg_code[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

//save_buff是一个u8类型的数组,方便外部传入要显示的数据

//pos是数码管从左开始第几个位置开始显示,取值范围是1-8

void smg_display(u8 save_buff[],u8 pos){

u16 i=0;

u16 pos_temp=pos-1;

for(i=pos_temp;i<8;i++){

//位选

switch(i){

case 0:

LSC=1,LSB=1,LSA=1; //7

break;

case 1:

LSC=1,LSB=1,LSA=0; //6

break;

case 2:

LSC=1,LSB=0,LSA=1; //5

break;

case 3:

LSC=1,LSB=0,LSA=0; //4

break;

case 4:

LSC=0,LSB=1,LSA=1; //3

break;

case 5:

LSC=0,LSB=1,LSA=0; //2

break;

case 6:

LSC=0,LSB=0,LSA=1; //1

break;

case 7:

LSC=0,LSB=0,LSA=0; //0

break;

}

SMG_A_DP_PORT=save_buff[i-pos_temp];

delay_10us(100);

SMG_A_DP_PORT=0x00; //消隐

}

}

3.3 DS18B20

3.3.1 ds18b20.h

#ifndef _ds18b20_H

#define _ds18b20_H

#include "public.h"

#include "intrins.h" //nop():1us

sbit DS18B20_PORT=P3^7; //DQ

//初始化时序图13的左半段:复位脉冲时序

void ds18b20_reset();

//初始化时序图13的右半段:检测DS18B20是否存在,返回值0存在;返回值1不存在

u8 ds18b20_check();

//初始化时序

u8 ds18b20_init();

//写时序:写一个字节到ds18b20中,提前准备好数据

void ds18b20_write_byte(u8 dat);

//读一位

u8 ds18b20_read_bit();

//读一个字节

u8 ds18b20_read_byte();

//开始温度转换

void ds18b20_start();

//从ds18b20得到温度值

double ds18b20_read_temperture();

#endif

3.3.2 ds18b20.c

#include "ds18b20.h"

//协议层

//初始化时序图13的左半段:复位脉冲时序

void ds18b20_reset(){

DS18B20_PORT=0; //拉低DQ线(单总线)

delay_10us(75); //750us(480us-960us)

DS18B20_PORT=1; //拉高DQ线

delay_10us(2); //20us(15us-60us)

}

//初始化时序图13的右半段:检测DS18B20是否存在,返回值0存在;返回值1不存在

u8 ds18b20_check(){

u8 time_temp=0;

while(DS18B20_PORT&&time_temp<20){ //DS18B20_PORT:1

time_temp++;

delay_10us(1); //10us,要循环20次,相当于200us(60-240us)

}

if(time_temp>=20){

return 1; //超时了,仍没有等到低电平

}else{

time_temp=0;

}

//如果等到了低电平,DS18B20_PORT变为0

while((!DS18B20_PORT)&&time_temp<20){ //DS18B20_PORT:0

time_temp++;

delay_10us(1);

}

if(time_temp>=20){

return 1; //超时了,仍没有等到高电平

}

return 0;

}

//初始化时序(将初始化时序图13的左半段和右半段连接起来)

u8 ds18b20_init(){

ds18b20_reset();

return ds18b20_check();

}

//ds18b20中读写操作

//写时序:写一个字节到ds18b20中,提前准备好数据

void ds18b20_write_byte(u8 dat){

u8 i=0;

u8 temp=0;

//从低位向高位写 1001 0011

for(i=0;i<8;i++){

temp=dat&0x01; //拿到dat中低位的数据

dat>>=1;

if(temp){

//写1时序

DS18B20_PORT=0;

nop(); //1us

nop();

DS18B20_PORT=1;

delay_10us(10); //100us(60-120us)

}else{

//写0时序

DS18B20_PORT=0;

delay_10us(6);

DS18B20_PORT=1;

nop(); //1us

nop();

}

}

}

//读时序:读位、读字节 1001 0011

//读一位

u8 ds18b20_read_bit(){

u8 dat=0; //存的是某一位(0或1)

//主机输出低电平

DS18B20_PORT=0;

//延时2us

nop(); //1us

nop();

DS18B20_PORT=1;

//延时2us,时间不能过长,必须在15us内读到数据

nop(); //1us

nop();

if(DS18B20_PORT){

dat=1;

}else{

dat=0;

}

delay_10us(5);

return dat;

}

//读一个字节

u8 ds18b20_read_byte(){

u8 i=0;

u8 dat=0;

u8 temp=0;

for(i=0;i<8;i++){

temp=ds18b20_read_bit(); //i=0时,temp=1;i=1时,temp=1

dat>>=1; //i=0时,dat=000 0000;i=1时,dat=0100 0000

dat|=temp<<7; //i=0时,dat=1000 0000;i=1时,dat=1100 0000

}

return dat;

}

/*

DS18B20 的典型温度读取过程为:

复位→发SKIPROM命令(0XCC)→发开始转换命令(0X44)

→延时→

复位→发送SKIPROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束。

*/

//开始温度转换

void ds18b20_start(){

ds18b20_init(); //初始化:复位和检查

ds18b20_write_byte(0xcc); //发SKIPROM命令(0XCC)

ds18b20_write_byte(0x44); //发开始转换命令(0X44)

}

//从ds18b20得到温度值

double ds18b20_read_temperture(){

double temp;

u8 dath=0;

u8 datl=0;

u16 value=0;

ds18b20_start();

ds18b20_init(); //初始化:复位和检查

ds18b20_write_byte(0xcc); //发送SKIPROM命令(0XCC)

ds18b20_write_byte(0xbe); //发读存储器命令(0XBE)

//连续读出两个字节数据(即温度)

datl=ds18b20_read_byte(); //低8位

dath=ds18b20_read_byte(); //高8位

//高8位和低8位连接起来,拼成16位

value=(dath<<8)+datl;

//判断是正温度还是负温度

if((value&0xf800)==0xf800){ //负温度

value=(~value)+1;

temp=value*(-0.0625);

}else{ //正温度

temp=value*0.0625;

}

return temp;

}

3.4 main.c

#include "public.h"

#include "smg.h"

#include "ds18b20.h"

/*

下载程序后,插上DS18B20温度传感器,数码管显示检测的温度值

*/

void main(){

u8 i=0;

int temp_value; //有负温度

u8 temp_buf[5];

ds18b20_init(); //起始信号,检查ds18b20在不在,要不要都可以

while(1){

i++;

if(i%50==0){ //减少读的频率,750us转换一次

temp_value=ds18b20_read_temperture()*10;

}

if(temp_value<0){

//显示符号位

temp_value=-temp_value;

temp_buf[0]=0x40; //显示负号

}else{

temp_buf[0]=0x00; //不显示

}

//读的温度要在数码管显示

temp_buf[1]=gsmg_code[temp_value/1000];

temp_buf[2]=gsmg_code[temp_value%1000/100];

temp_buf[3]=gsmg_code[temp_value%1000%100/10]|0x80;

temp_buf[4]=gsmg_code[temp_value%1000%100%10];

smg_display(temp_buf,4);

}

}

相关推荐
冰糖雪莲IO1 小时前
【江协STM32】11-1 SPI通信协议
stm32·单片机·嵌入式硬件
lantiandianzi10 小时前
基于单片机的语音控制玩具汽车的设计
单片机
不听念经11 小时前
ESP嵌入式开发环境安装
单片机·物联网
就叫飞六吧13 小时前
51单片机 和 STM32 的烧录方式和通信协议的区别
stm32·嵌入式硬件·51单片机
冰糖雪莲IO14 小时前
【江协STM32】11-2/3 W25Q64简介、软件SPI读写W25Q64
stm32·单片机·嵌入式硬件
飞凌嵌入式15 小时前
飞凌嵌入式i.MX8M Mini核心板已支持Linux6.1
嵌入式硬件·嵌入式·飞凌嵌入式
年轮不改15 小时前
STM32——点亮LED
stm32·单片机·嵌入式硬件
SkyXZ~15 小时前
地瓜机器人RDK Studio使用入门教程
人工智能·嵌入式硬件·物联网·目标检测·ubuntu·机器学习·机器人
不能只会打代码15 小时前
32单片机从入门到精通之安全性与可靠性——防护措施(十八)
单片机·嵌入式硬件·32单片机