蓝桥杯单片机设计与开发⑩ --- DS18B20温度传感器

1、DS18B20

DS18B20本身就是一个温度传感器,只需要将DS18B20的数据引脚和单片机的一个I/O口接上,单片机通过1-Wire协议与DS18B20进行通信,读出温度。
CT107开发板相关模块电路图:
在这里插入图片描述

2、温度转换规则

DS18B20可以直接读出数字的温度数值。温度传感器的精度为用户可编程的9、10、11或12位,分别以0.5℃,0.25℃,0.125℃和0.0625℃增量递增。在上电状态下默认精度为12位。也就是温度每变化0.0625度,二进制数字变化1。转换的精度由配置寄存器决定,如下:(R1R0出厂默认11)
在这里插入图片描述
DS18B20启动后保持低功耗等待状态,当需要执行温度测量和AD转换时,总线控制器必须发出[44h]命令,启动温度转换,即Write18b20(0x44)。转换完以后,产生的温度数据以两个字节的形式被存储到 高速暂存器 的温度寄存器中(先低后高),DS18b20继续保持等待状态。

DS18B20的温度数据格式如下,转化后得到12位数据,存储在DS18B20的两个8位的RAM中。MSB里面存储的是高字节LSB里面存储的是低字节。 高字节的前5位是符号位,如果测得的温度大于0,这5位为‘0’,只要将测到的数值乘以0.0625即可得到实际温度;如果温度小于0,这5位为‘1’,测到的数值需要先减1再取反再乘以0.0625即可得到实际温度。
在这里插入图片描述
在这里插入图片描述

3、ROM&RAM操作指令

(1)ROM指令集
指令约定代码功能
Read ROM33H读DS18B20温度传感器ROM的编码,即64位地址
Match ROM55H后跟64位ROM序列,让总线控制器在多点总线上匹配某一特定的DS18B20。匹配后的从机才能响应后续命令,其余不匹配的从机等待复位脉冲。总线上有单个或多个器件时都可使用该命令。
Skip ROMCCH当只有一个从机时,忽略64位ROM地址,直接向DS18B20发出温度转换命令。
Search ROMF0H用于确定挂在同一总线上DS18B20的个数和识别64位ROM地址,为操作各器件做准备。
Alarm ROMECH执行后,温度超过上限或下限的片子做出响应。

当我们只挂了一个DS18B20时,只需要写一条关于ROM的指令,即Write18b20(0xcc);。

(2) RAM指令集
指令约定代码功能
启动温度转换(Convert T)44H启动DS18B20进行温度转换,从转换到获取温度的时间取决于DS18B20的精度,12位转换最长750ms,结果存入9字节RAM。
读暂存器BEH读9字节RAM的内容
写暂存器4EH发出向内部RAM的3、4字节写上、下限温度数据命令,后面紧跟着传送两字节数据
复制暂存器48H将RAM中第3、4字节的内容复制到EEPROM中
重调E2PROMB8H将EEPROM内容恢复到RAM中的3、4字节
读供电方式B4H读DS18B20的供电模式,寄生供电时DS18B20发送“0”,外接电源供电时DS18B20发送“1”。

4、通过单线总线端口访问DS18B20流程

① 初始化
类似于I2C的寻址,1-Wire总线开始也需要检测这条总线上是否存在DS18B20这个器件。如果存在,总线会根据时序要求返回一个低电平;如果不存在,则不返回,即总线保持高电平。该过程称为检测存在脉冲。
获取存在脉冲有两个作用:①检测是否存在DS18B20 ②通过这个过程通知DS18B20做准备,单片机要对它进行操作。

检测存在脉冲的时序图如下:在这里插入图片描述
整个过程描述如下:

①单片机拉低这个引脚,持续480~960us (以持续500us为例)
②单片机释放总线,即拉高电平。
③15 ~60us后,如果DS18B20这个器件存在,就会主动拉低这个引脚,返回一个低脉冲(为了确保读到这个脉冲,选择延时60us,但不能超过75us)
④持续60 ~240us后,DS18B20释放总线,I0端口被上拉电阻拉高。

对应程序如下:

void Delayus(uint us)		//@11.0592MHz
{
	do{
		_nop_();
		_nop_();
		_nop_();
	  }while(us--);
} 
bit Init_18b20()
{
	bit initflag;
	EA = 0;				//关闭总中断
	DQ = 0;				//读写之前都要将该引脚拉低
	Delayus(500);		//延时500us
	DQ = 1;
	Delayus(60);		//延时60us
	initflag = DQ; 		//读取存在脉冲,0存在,1不存在
	while(!DQ);			//等待存在脉冲结束,即DQ = 1
	EA = 1;				//打开总中断
	return initflag;	
}
	

注:需要说明的是,DS18B20对时序的要求非常严格,所以在开始对某一位操作前要先关闭中断,防止中途受到干扰。但是位与位之间的间隔是可以无穷大的,完全可以在完成一位的操作之后,去干别的事情,结束之后再回来操作下一位。

② ROM操作指令
一条总线上只接一个器件的情况,此时只需要直接跳过ROM,不进行ROM检测。用到的语句如下:

Write18b20(0xcc);//跳过ROM操作

③ RAM操作指令
常用到的两条如下:

Write18b20(0x44);//启动一次温度转换
Write18b20(0xbe);//发送读指令

④ DS18B20读\写时序
在这里插入图片描述
写数据
过程描述如下:在给DS18b20写数据之前,单片机要先把引脚拉低,持续一段时间(2us),而后DS18b20会在60us之内读完这位数据。然后释放总线(拉高引脚)
代码如下:

//向DS18B20写入一个字节,dat为带写入字节
void Write_18b20(u8 dat)
{
	u8 i;
	EA = 0;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		Delayus(2);//产生2us的低电平脉冲
		DQ = dat & 0x01; //先读取低位
		Delayus(60);
		dat >>= 1; //右移一位,准备写入下一位
		DQ = 1;		
	}
	EA = 1;
}

读数据
过程描述如下:在读取DS18B20数据之前,单片机首先要拉低这个引脚,并且至少保持1us。而后释放这个引脚(拉高电平),尽快读取。从拉低这个引脚到读取不能超过15us。再延时60us,确保读取完毕。

//从DS18B20读取一个字节,返回值为读到的字节
u8 Read_18b20()
{
	u8 i,dat;
	EA = 0;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		Delayus(2);
		DQ = 1;
		Delayus(2);
		dat >>= 1;
		if(DQ)
			dat |= 0x80;//先读的是低位,依次右移
		Delayus(60);
	}
	EA = 1;
	return dat;
}

实时测量室内温度并通过数码管显示完整程序:

main.c

#include "sys.h"
#include "18b20.h"
bit GET_TEMP_FLAG;
void main()
{
	int Temp = 0;				      		    //读取当前的温度值
	//int Temp_int = 999, Temp_dec = 999;	    //温度值的整数和小数部分

	ALL_Init();
	Timer0Init();
	while(1)
	{
		
		if(GET_TEMP_FLAG)
		{
			GET_TEMP_FLAG = 0;
			if(Start_18b20())
			{	//放大10倍 让数码管显示小数点后一位
				Temp = Temp_Get()*0.0625*10+0.5;	//+0.5 --> 4舍5入
			//	Temp_int = Temp >> 4;				//分离出温度值整数部分
			//	Temp_dec = Temp & 0xF;		        //分离出温度值小数部分
			//	Temp_dec = Temp_dec * (10000 / 16);	//二进制小数部分转换为4位十进制			
			}
		}
		Nixie_Drive(Temp);		
	}
}

18b20.c

#include "sys.h"
#include "18b20.h"

extern uchar smg1;
void Delayus(uint us)		//@11.0592MHz
{
	do{
		_nop_();
		_nop_();
		_nop_();
	  }while(us--);
} 
bit Init_18b20()
{
	bit initflag;
	EA = 0;//关闭总中断
	DQ = 0;//读写之前都要将该引脚拉低
	Delayus(500);//延时500us
	DQ = 1;
	Delayus(60);//延时60us
	initflag = DQ; //读取存在脉冲,0存在,1不存在
	while(!DQ);//等待存在脉冲结束,即DQ = 1
	EA = 1;//打开总中断
	return initflag;	
}
		 
//向DS18B20写入一个字节,dat为带写入字节
void Write_18b20(u8 dat)
{
	u8 i;
	EA = 0;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		Delayus(2);//产生2us的低电平脉冲
		DQ = dat & 0x01; //先读取低位
		Delayus(60);
		dat >>= 1; //右移一位,准备写入下一位
		DQ = 1;		
	}
	EA = 1;
}

//从DS18B20读取一个字节,返回值为读到的字节
u8 Read_18b20()
{
	u8 i,dat;
	EA = 0;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		Delayus(2);
		DQ = 1;
		Delayus(2);
		dat >>= 1;
		if(DQ)
			dat |= 0x80;//先读的是低位,依次右移
		Delayus(60);
	}
	EA = 1;
	return dat;
}

bit Start_18b20()
{
	bit ack;
	ack = Init_18b20();//执行总线复位,获取18b20的应答
	if(ack == 0)//如果18b20应答,启动一次转换
	{
		Write_18b20(0xcc);//跳过RAM操作
		Write_18b20(0x44);//启动一次温度转换
	}
	return ~ack; //ack == 0 表示操作成功,返回值对其取反
	
}

int Temp_Get()
{
	bit ack;
	u8 LSB,MSB;
	u16 temp;
	ack = Init_18b20();//执行总线复位,获取18b20的应答
	if(ack == 0)
	{
		uchar check;
		Write_18b20(0xcc);
		Write_18b20(0xbe);
		LSB = Read_18b20();
		//MSB的3~7这5位为符号位,当5位都为0时为正温度 都为1时为负温度
		MSB = Read_18b20();
		
		//MSB = 0xfc; // -55℃ 负温度检验代码
		//LSB = 0x90;
		//检测正负温度
		check = MSB; 
		temp = ((int) MSB << 8) + LSB;	//合成16bit的整数
		//当check大于等于0x08时,说明采集到的数据为负温度值
		//负数是以补码的形式存在内存中,要求:原码 = 补码取反 + 1 
		if(check >= 0x08)
		{
			temp = ~temp + 1;
			smg1 = 1; //数码管1显示 - 符号
		}else
			smg1= 0;  //数码管1不显示任何
	}
	return temp;
}

nixie.c

#include "sys.h"


					// 0    1    2    3    4    5    6    7
uchar code nixie[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
				    // 8	9    a    b    c    d     e    f	u
					  0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xc1};	//共阳数码管码字

uchar NixieBuff[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

uchar smg1,smg2,smg3,smg4,smg5,smg6,smg7,smg8;
uchar code Symbol[] = {0xff,0xbf};	//全灭,-


void Nixie_Scan()
{
	static uchar index;
	HC138_Set(7);
	P0 = 0xff;//消影
	HC138_Set(6);
	P0 = 0x01<<index;
	HC138_Set(7);
	P0 = NixieBuff[index];
	HC138_Set(0);
	
	index++;
	index &= 0x07;
}

void Nixie_Show()
{
	NixieBuff[0] = Symbol[smg1];
	NixieBuff[1] = nixie[smg2];
	NixieBuff[2] = nixie[smg3]&0x7f;
	NixieBuff[3] = nixie[smg4];
	NixieBuff[4] = nixie[smg5];
	NixieBuff[5] = Symbol[smg6];
	NixieBuff[6] = Symbol[smg7];
	NixieBuff[7] = Symbol[smg8];
	
}

void Nixie_Drive(u16 dat)
{
	smg2 = dat/100;
	smg3 = dat%100/10;
	smg4 = dat%10;
	smg5 = 12;
	smg6 = smg7 = smg8 = 0;
}

sys.c

#include "sys.h"
extern bit GET_TEMP_FLAG;
/**
*@brief     外设初始化  
*@param[in] none
*@return    none
**/
void ALL_Init()
{
	P2 = (P2&0x1f)|0xa0;	//打开Y5C
	P0 = 0x00;				//关闭蜂鸣器&继电器
	P2 = (P2&0x1f)|0xe0;	//打开Y7C
	P0 = 0xff;				//关闭数码管
	P2 = (P2&0x1f)|0x80;	//打开Y4C
	P0 = 0xff;				//关闭LED
	P2 = P2&0x1f;			//关闭所用使能
}
/**
*@brief     延时函数
*@param[in] 延时多少ms(0~65535)
*@return    none
**/
void Operate_Delay(u16 ms)
{
	u16 i;
	for(ms;ms>0;ms--)
		for(i=921;i>0;i--);
}
/**
*@brief      74HC138译码器通道选择
*@param[in]  channel (通道)
*@return     none
**/
void HC138_Set(u8 channel)
{
	switch(channel)
	{
		case 0: P2 =  P2 & 0x1f;     break;     //关闭所有通道
		case 4: P2 = (P2&0x1f)|0x80; break;     //选择Y4对应的模块 (LED),		 运算结果为P2高三位 100
		case 5: P2 = (P2&0x1f)|0xa0; break;     //选择Y5对应的模块 (蜂鸣器),	 运算结果为P2高三位 101
		case 6: P2 = (P2&0x1f)|0xc0; break;     //选择Y6对应的模块 (数码管位选), 运算结果为P2高三位 110
		case 7: P2 = (P2&0x1f)|0xe0; break;     //选择Y7对应的模块 (数码管段选), 运算结果为P2高三位 111
	}
}

void Timer0Init(void)		//1毫秒@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0xCD;		//设置定时初值
	TH0 = 0xD4;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

void Timer0() interrupt 1
{
	static uint i;
	i++;
	if(i==1200)
	{
		i = 0;
		GET_TEMP_FLAG = 1;
	}
	Nixie_Show();
	Nixie_Scan();
}

在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值