嵌入式开发学习(STC51-13-温度传感器)

内容

通过DS18B20温度传感器,在数码管显示检测到的温度值;

DS18B20介绍

简介

DS18B20是由DALLAS半导体公司推出的一种的“一线总线(单总线)”接口的温度传感器;

与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器;

特点

DS18B20温度传感器具有如下特点:

  • 适应电压范围更宽,电压范围:3.0~5.5V,在寄生电源方式下可由数据线供电;
  • 温范围-55℃~+125℃,在-10~+85℃时精度为±0.5℃;
  • 可编程的分辨率为9~12位,对应的可分辨温度分别为0.5℃、0.25℃、0.125℃和 0.0625℃,可实现高精度测温;
  • 在9位分辨率时最多在93.75ms内把温度转换为数字,12位分辨率时最多在750ms内把温度值转换为数字,速度更慢;
  • 测量结果直接输出数字温度信号,以"一根总线"串行传送给CPU,同时可传送CRC校验码,具有极强的抗干扰纠错能力;
  • 负压特性:电源极性接反时,芯片不会因发热而烧毁,但不能正常工作;

结构

DS18B20一共有三个管脚,当我们正对传感器切面(传感器型号字符那一面)时,传感器的管脚顺序是从左到右排列;

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

如果把传感器插反,那么电源将短路,传感器就会发烫,很容易损坏,所以一定要注意传感器方向;

通常在开发板上都会标出传感器的凸起出,所以只需要把传感器凸起的方向对着开发板凸起方向插入即可;

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

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

TM R1 R0 1 1 1 1 1

低五位一直都是"1",TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式,在DS18B20出厂时该位被设置为0,用户不需要去改动;

R1和R0用来设置DS18B20的精度(分辨率),可设置为9,10,11或12位,对应的分辨率温度是0.5℃,0.25℃,0.125℃和0.0625℃;

R0和R1配置如下图:
嵌入式开发学习(STC51-13-温度传感器)_第1张图片
在初始状态下默认的精度是12位,即R0=1、R1=1;

高速暂存存储器由9个字节组成,其分配如下:
嵌入式开发学习(STC51-13-温度传感器)_第2张图片
当温度转换命令(44H)发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节;

存储由两个字节组成,高字节的前5位是符号位S,单片机可通过单线接口读到该数据,读取时低位在前,高位在后,数据格式如下:
嵌入式开发学习(STC51-13-温度传感器)_第3张图片
如果测得的温度大于0,这5位为‘0’,只要将测到的数值乘以0.0625(默认精度是12位)即可得到实际温度;
如果温度小于0,这5位为‘1’,测到的数值需要取反加1再乘以0.0625即可得到实际温度;

温度计算

温度与数据对应关系如下:
嵌入式开发学习(STC51-13-温度传感器)_第4张图片
比如我们要计算+85度,数据输出十六进制是0X0550,因为高字节的高5位为0,表明检测的温度是正温度,0X0550对应的十进制为1360,将这个值乘以12位精度0.0625,所以可以得到+85度;

DS18B20使用

知道了怎么计算温度,接下来我们就来看看如何读取温度数据;

由于DS18B20是单总线器件,所有的单总线器件都要求采用严格的信号时序,以保证数据的完整性;

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

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

这里我们简单介绍这几个信号的时序:

初始化时序

初始化时序图如下:
嵌入式开发学习(STC51-13-温度传感器)_第5张图片

单总线上的所有通信都是以初始化序列开始;

主机输出低电平,保持低电平时间至少480us(该时间的时间范围可以从480到960us),以产生复位脉冲;

接着主机释放总线,外部的上拉电阻将单总线拉高,延时15~60us,并进入接收模式;

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

写时序

写时序图如下:
嵌入式开发学习(STC51-13-温度传感器)_第6张图片
写时序包括写0时序和写1时序;

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

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

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

读时序

读时序图如下:
嵌入式开发学习(STC51-13-温度传感器)_第7张图片
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据;

所有读时序至少需要60us,且在2次独立的读时序之间至少需要1us的恢复时间;

每个读时序都由主机发起,至少拉低总线1us;

主机在读时序期间必须释放总线,并且在时序起始后的15us之内采样总线状态;

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

完整过程

了解了单总线时序之后,我们来看看DS18B20的温度读取过程:
DS18B20的温度读取过程为:复位→发SKIP ROM命令(0XCC)→发开始转换命令(0X44)→延时→复位→发送SKIP ROM命令(0XCC)→发读存储器命令(0XBE)→连续读出两个字节数据(即温度)→结束;

原理图

嵌入式开发学习(STC51-13-温度传感器)_第8张图片
由图可知,总线连接p37口,所以我们通过控制该io口的电位变化即可实现初始化以及读写时序;

思路

根据时序图编写初始化、读、写程序;(初始化包括复位和检测DS18B20是否存在)

编写检测DS18B20是否存在的程序(如果信号口一直为低电位,即判定为不存在);

按使用步骤,读取温度值,转换为十进制后,使其在数码管上显示;

编码

User

main.c

/*
 * @Description: 通过DS18B20温度传感器,在数码管显示检测到的温度值
 */
#include "public.h"
#include "smg.h"
#include "ds18b20.h"

void main()
{
	u8 i = 0;
	int temp_value;
	u8 temp_buf[5];

	ds18b20_init(); // 初始化DS18B20

	while (1)
	{
		i++;
		if (i % 50 == 0)								 // 间隔一段时间读取温度值,间隔时间要大于温度传感器转换温度时间(12位分辨率时转换时间为750ms)
			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);
	}
}

Public

public.h

#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

public.c

#include "public.h"

/**
 * @description: 延时函数,ten_us=1时,大约延时10us
 * @param {u16} ten_us 延时倍数
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**ms延时函数,ms=1时,大约延时1ms***
 * @param {u16} ms 延时倍数
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

App/ds18b20

ds18b20.h

#ifndef _ds18b20_H
#define _ds18b20_H

#include "public.h"

// 管脚定义
sbit DS18B20_PORT = P3 ^ 7; // DS18B20数据口定义

// 函数声明
u8 ds18b20_init(void);
float ds18b20_read_temperture(void);

#endif

ds18b20.c

#include "ds18b20.h"
#include "intrins.h"

/**
 * @description: 复位DS18B20
 * @return {*}
 */
void ds18b20_reset(void)
{
	DS18B20_PORT = 0; // 拉低DQ
	delay_10us(75);	  // 拉低750us
	DS18B20_PORT = 1; // DQ=1
	delay_10us(2);	  // 20US
}

/**
 * @description: 检测DS18B20是否存在
 * @return {u8} 1:未检测到DS18B20的存在,0:存在
 */
u8 ds18b20_check(void)
{
	u8 time_temp = 0;

	while (DS18B20_PORT && time_temp < 20) // 等待DQ为低电平
	{
		time_temp++;
		delay_10us(1);
	}
	if (time_temp >= 20)
		return 1; // 如果超时则强制返回1
	else
		time_temp = 0;
	while ((!DS18B20_PORT) && time_temp < 20) // 等待DQ为高电平
	{
		time_temp++;
		delay_10us(1);
	}
	if (time_temp >= 20)
		return 1; // 如果超时则强制返回1
	return 0;
}

/**
 * @description: 从DS18B20读取一个位
 * @return {u8} 1/0
 */
u8 ds18b20_read_bit(void)
{
	u8 dat = 0;

	DS18B20_PORT = 0;
	_nop_();
	_nop_();
	DS18B20_PORT = 1;
	_nop_();
	_nop_(); // 该段时间不能过长,必须在15us内读取数据
	if (DS18B20_PORT)
		dat = 1; // 如果总线上为1则数据dat为1,否则为0
	else
		dat = 0;
	delay_10us(5);
	return dat;
}

/**
 * @description: 从DS18B20读取一个字节
 * @return {u8} 一个字节数据
 */
u8 ds18b20_read_byte(void)
{
	u8 i = 0;
	u8 dat = 0;
	u8 temp = 0;

	for (i = 0; i < 8; i++) // 循环8次,每次读取一位,且先读低位再读高位
	{
		temp = ds18b20_read_bit();
		dat = (temp << 7) | (dat >> 1);
	}
	return dat;
}

/**
 * @description: 写一个字节到DS18B20
 * @param {u8} dat 要写入的字节
 * @return {*}
 */
void ds18b20_write_byte(u8 dat)
{
	u8 i = 0;
	u8 temp = 0;

	for (i = 0; i < 8; i++) // 循环8次,每次写一位,且先写低位再写高位
	{
		temp = dat & 0x01; // 选择低位准备写入
		dat >>= 1;		   // 将次高位移到低位
		if (temp)
		{
			DS18B20_PORT = 0;
			_nop_();
			_nop_();
			DS18B20_PORT = 1;
			delay_10us(6);
		}
		else
		{
			DS18B20_PORT = 0;
			delay_10us(6);
			DS18B20_PORT = 1;
			_nop_();
			_nop_();
		}
	}
}

/**
 * @description: 开始温度转换
 * @return {*}
 */
void ds18b20_start(void)
{
	ds18b20_reset();		  // 复位
	ds18b20_check();		  // 检查DS18B20
	ds18b20_write_byte(0xcc); // SKIP ROM
	ds18b20_write_byte(0x44); // 转换命令
}

/**
 * @description: 初始化DS18B20的IO口DQ,同时检测DS的存在
 * @return {u8} 1:不存在,0:存在
 */
u8 ds18b20_init(void)
{
	ds18b20_reset();
	return ds18b20_check();
}

/**
 * @description: 从ds18b20得到温度值
 * @return {float} 温度数据
 */
float ds18b20_read_temperture(void)
{
	float temp;
	u8 dath = 0;
	u8 datl = 0;
	u16 value = 0;

	ds18b20_start(); // 开始转换
	ds18b20_reset(); // 复位
	ds18b20_check();
	ds18b20_write_byte(0xcc); // SKIP ROM
	ds18b20_write_byte(0xbe); // 读存储器

	datl = ds18b20_read_byte(); // 低字节
	dath = ds18b20_read_byte(); // 高字节
	value = (dath << 8) + datl; // 合并为16位数据

	if ((value & 0xf800) == 0xf800) // 判断符号位,负温度
	{
		value = (~value) + 1;	  // 数据取反再加1
		temp = value * (-0.0625); // 乘以精度
	}
	else // 正温度
	{
		temp = value * 0.0625;
	}
	return temp;
}

App/smg

smg.h

#ifndef _smg_H
#define _smg_H

#include "public.h"

#define SMG_A_DP_PORT P0 // 使用宏定义数码管段码口

// 定义数码管位选信号控制脚
sbit LSA = P2 ^ 2;
sbit LSB = P2 ^ 3;
sbit LSC = P2 ^ 4;

extern u8 gsmg_code[17]; // 使“共阴极数码管显示0~F的段码数据”这个变量定义为外部可用

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

#endif

smg.c

#include "smg.h"

// 共阴极数码管显示0~F的段码数据
u8 gsmg_code[17] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};

/**
 * @description: 动态数码管显示函数
 * @param {u8} dat 要显示的数据
 * @param {u8} pos 从左开始第几个位置开始显示,范围1-8
 * @return {*}
 */
void smg_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;
		}
		SMG_A_DP_PORT = dat[i - pos_temp]; // 传送段选数据
		delay_10us(100);							  // 延时一段时间,等待显示稳定
		SMG_A_DP_PORT = 0x00;						  // 消影
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:显示检测到的温度
嵌入式开发学习(STC51-13-温度传感器)_第9张图片

你可能感兴趣的:(嵌入式开发学习,学习,51单片机)