DS18B20温度传感器~

继续努力鸭!!!!


今天想再复习一遍DS18B20的知识,把底层再弄一遍,,,不然该忘了,,鹅鹅鹅,大家也要勤加复习呀!


写在前面:

  1. DS18B20用到的是1-Wire协议;
  2. DS18B20的温度数据格式要记清楚,看图:
    DS18B20温度传感器~_第1张图片
    LSB是低字节,MSB是高字节,一般我们显示的温度用到图上红色线框的部分即可,S部分是符号位;
  3. DS18B20的二进制数字变化一位,温度变化0.0625°C,温度表如图:
    DS18B20温度传感器~_第2张图片
  4. DS18B20的时序要求非常严格,在操作时序的时候要先关闭总中断!!!
  5. 几个操作指令: SkipROM(跳过ROM):0xCC(总线上只有一个器件时,可跳过ROM检测,ROM内部存储着一个序列号,而这个序列号上存有总线上每个器件的序号)
    !!!RAM读取指令: 读暂存寄存器(0xBE) 启动温度转换(0x44
    注意:DS18B20读取数据时 先读低位再读高位从低到高依次读取

DS18B20时序图

  1. 初始化
    DS18B20温度传感器~_第3张图片
    程序:
/*延时函数*/
void Delayus(u8 us)
{
	do{
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
		_nop_();
	}while(--us);
}

bit Get18B20ACK()//检测是否存在DS18B20这个器件
{
	bit ack;
	EA = 0;//关闭总中断
	
	IO_18B20 = 0;//先拉低IO口
	DelayUs(250);//延时500us
	DelayUs(250);
	IO_18B20 = 1;//拉高电平,单片机释放总线,准备读取脉冲数据
	DelayUs(60);//等待60us
	ack = IO_18B20;//读取存在脉冲(注意,存在脉冲是一个低电平脉冲)
	while(!IO_18B20);//等待存在脉冲结束
	EA = 1;//重新使能中断
	
	return ~ack;
}

做几点解释:1. 和I2C寻址类似,1-Wire总线开始也要检测是否存在相应的器件,若存在相应器件,会返回一个低电平脉冲
2. 只有在单片机释放总线(拉高电平)的情况下,单片机才能从从机上读取数据,这点和其它两个通信协议是一样的;
3. 关于下面这行代码:从时序图上可以看出,DS18B20的存在脉冲会持续60-240us,所以我们需要等待这个存在脉冲结束,那什么时候结束呢?当然是这个脉冲变成高电平了呀,所以就有了这行代码,while循环,非0即为真,如果存在脉冲没有结束(一直为0),就会一直执行while循环

while(!IO_18B20);//等待存在脉冲结束
  1. 位读写操作
    写操作
    DS18B20温度传感器~_第4张图片
    写0 :直接将引脚拉低持续60us
    写1:先拉低,再拉高,同样持续60us
void Write18B20(u8 dat)
{
	u8 mask;
	EA = 0;//要在18B20内部执行读或者写操作时要关闭总中断!!!!
	
	for(mask = 0x01; mask != 0; mask <<= 1)
	{
		IO_18B20 = 0;//产生2us的低电平脉冲
		DelayUs(2);//为了确保IO-18B20口确实处于拉低状态
		if((mask & dat) == 0)
			IO_18B20 = 0;
		else
			IO_18B20 = 1;
		DelayUs(60); 
		IO_18B20 = 1;//最后要把IO_18B20拉高,释放总线!!!!
		//DelayUs(2);//这里不需要再延时了
	}
	EA = 1;//上面关闭了,最后要记得开启!!!!!
}

注意!!! 写入数据时,要先产生一个2us的低电平脉冲
读操作
DS18B20温度传感器~_第5张图片
注意!!! 读数据是单片机读,那么就必须是在释放总线的情况下读取数据的,同样的,读数据前也要先产生一个2us的低电平脉冲,然后再迅速拉高(因为必须在15us的时间内读取数据)

u8 Read18B20()
{
	u8 mask;
	u8 dat = 0;//定义一个变量保存读到的数据
	
	EA = 0;//关闭总中断
	for(mask = 0x01; mask != 0; mask <<= 1)
	{
		IO_18B20 = 0;//先产生一个2us的低电平脉冲      
		DelayUs(2);
		IO_18B20 = 1;//单片机释放总线,准备开始读取数据
		DelayUs(2);
		if(IO_18B20 == 1)
			dat |= mask;
		else
			dat &= ~mask;
		DelayUs(60);//延时60us
	}
	EA = 1;//重新使能
	
	return dat;
}
  1. 开始温度转换:
/*返回值表示是否操作成功,ack= 0表示成功*/
bit Start18B20()//只是调用了write函数,不必关闭总中断
{
	bit ack;
	
	ack = Get18B20ACK();//判断是否存在DS18B20这个器件
	if(ack == 1)//存在器件,因为Get18B20的函数返回值是~ack
	{
		Write18B20(0xCC);//跳过ROM操作,我们的开发板上1-Wire总线上只挂了一个器件
		Write18B20(0x44);//开始温度转换
	}
	
	return ~ack;//ack = 0表示操作成功
}
  1. 读取转换温度
bit Get18B20Temp(int *temp)
{
	bit ack;
	u8 MSB,LSB;
	
	EA = 0;
	ack = Get18B20ACK();
	if(ack == 1)
	{
		Write18B20(0xCC);
		Write18B20(0xBE);//发送读命令
		LSB = Read18B20();//读取DS18B20的低字节
		MSB = Read18B20();//读取DS18B20的高字节
		
		MSB &= 0x0F;//清除符号位,符号位是高四位
		*temp = ((int)MSB << 8) | LSB;//注意这里是左移八位,将MSB高八位的0移走!!!!!!!!!!  
		*temp = *temp * 6.25;//将读取到的16进制数转换为十进制的温度
	}
	EA = 1;
	
	return ~ack;
}

说明几点: 1. 注意下面这行代码:(int)是将8位的MSB强制转换为16位高八位补0),我们要得到完整的16位温度数据,还需要把MSB和LSB合在一起;

*temp = ((int)MSB << 8) | LSB;

2.这行代码是如何实现数据转换的呢?还记得我们上面说过,二进制改变1位,温度改变0.0625°C,但是这里的温度是扩大100后的温度,所以0.0625°C也要扩大100倍,可以这么理解,*temp代表n个刻度而一个刻度代表的温度是6.25°C,二者想乘就得到温度的数值了。

*temp = *temp * 6.25;//将读取到的16进制数转换为十进制的温度

底层部分结束,我们还要在主函数里写一个刷新温度(显示温度)的函数:

void RefreshTemp()//显示小数部分
{
	int temp;
	u8 i;
	
	Get18B20Temp(&temp);//主函数中已经开始了温度转换,故这里先获取温度
	Start18B20();//温度转换需要一定的时间,故需要再次开始温度转换,为下一次获取温度做准备
	//temp >>= 4;//去掉温度上的小数部分,temp低四位是小数部分
	for(i = 7; i>2; i--)
	{
		LedBuff[i] = 0xFF;//关闭不用的数码管,记住如果要关闭数码管,必须对LedBuff进行操作
	}
	
    for(i = 1; i<5; i++)
	{
		LedBuff[i] = LedChar[temp % 10];
		temp /= 10;
	}
	/*i循环赋值的函数还可以像下面这样写*/
//	LedBuff[4] = LedChar[(temp / 1000) % 10];
//	LedBuff[3] = LedChar[(temp / 100) % 10];
//	LedBuff[2] = LedChar[(temp / 10) % 10];
//	LedBuff[1] = LedChar[temp % 10];
	
	LedBuff[0] = LedChar[12];
	LedBuff[3] &= 0x7F;//0x7F=01111111,该行代码相当于把第四个数码管的第七位置0,即点亮dp,这样就实现了加上小数点的操作
}

刷新温度有两种写法,一种是上面的显示小数部分的写法,另一种是不显示小数的写法,不显示小数部分比较好写,只要加上我注释掉的这行代码“ temp >>= 4;”就好,然后数码管只用三个就可以,小数点也不需要加,读者可以自己写一下。
这里提一下数码管扫描的函数叭:

u8 LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};
u8 LedBuff[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

void LedScan()
{
	static u8 index = 0;
	
	P2 = (P2 & 0x1F) | 0xE0;
	P0 = 0xFF;
	P2 &= 0x1F; 
	
	P2 = (P2 & 0x1F) | 0xC0;
	P0 = 0x80 >> index;
	P2 &= 0x1F;
	
	P2 = (P2 & 0x1F) | 0xE0;
	P0 = LedBuff[index];
	P2 &= 0x1F;
	
	index++;
	index &= 7;
}

emmm,会再详细写一篇关于数码管的博客的。

你可能感兴趣的:(蓝桥备赛)