51单片机——DS18B20温度传感器-1总线通讯1.1

本文档介绍了如何使用51单片机驱动DS18B20温度传感器,包括初始化、读写操作以及温度转换和数码管显示的优化。代码实现了不同精度的温度转换,并解决了数码管显示小数精度问题,以及针对-0.0000异常的调试。此外,详细展示了驱动和读取DS18B20的底层函数实现。
摘要由CSDN通过智能技术生成

写的不知道好不好,有什么不对的地方还请指出,谢了。

1、本节基于DS18B20 1总线通讯。DS18B20温度转换很慢,初始化复位也慢,读取它的温度不需要那么频繁,可按照需要修改。

2、驱动DS18B20,代码3个:初始化DS18B20、写1字节、读1字节。

3、在《51单片机——DS18B20温度传感器-1总线通讯1.0》章节上做代码优化了,主要解决了小数转换及显示问题。

4、温度读取出来后用数码管来显示小数,上章节数码管显示小数有精度问题,本次直接优化OK,只是12位精度显示的时候最后面那位老是0应该为0或者5的。

5、在调试的时候发现有时候会出现-0.0000,不知道是什么问题,可能是通讯时序上的问题吧,我在通讯时序中修改了延迟代码,故以下程序好像不会出现-0.0000

6、我觉的对于DS18B20主要是温度读取出来后进行不失真的算法转换,然后就是数码管对于正负小数温度的显示。本节已完美达到要求。

7、感觉不错的,清点赞或关注我,我会不定期更新优化代码。

main.c

#include "dt.h"
#include "ds18b20.h"
#include "time.h"
#include <reg52.h>
void main(void)
{
	RES_dt_dispaly();
	Timer0_16bit(20);

	while(1)//大概2秒转换一下温度
	{
		convert_temp_DS18B20(12);//800毫秒了
		delay_ms(1000);//软件延迟1秒
		TR0=0;
		read_temp_DS18B20(12);//大概3毫秒
		dt_convert_DS18B20();//大概1毫秒
		TR0=1;	
	}
}

ds18b20.h

#ifndef __DS18B20_H__
#define __DS18B20_H__

#include <reg52.h>
#include "delay.h"
#include <intrins.h>

sbit wire = P2^2;//GPIO P2.2
//DS18B20温度存放在全局变量
extern unsigned int DS18B20_float_bit;//小数位
extern unsigned char DS18B20_int_bit;//整数位
extern unsigned char DS18B20_Accuracy_bit;//精度位
extern bit DS18B20_flag_bit;//符号位

void convert_temp_DS18B20(unsigned char); //6、转换温度,x为选择使用的精度9位 10位 11位 12位,默认12位

void read_temp_DS18B20(unsigned char Accuracy);//10、读取DS18B20的温度,输入选择的精度9位 10位 11位 12位 9 10 11 12,默认12位

#endif

ds18b20.c

#include "ds18b20.h"
//与DS18B20通讯传输数据的时候均是先传送低位最后传送高位

//DS18B20温度存放在全局变量
unsigned int DS18B20_float_bit;//小数位
unsigned char DS18B20_int_bit;//整数位
unsigned char DS18B20_Accuracy_bit;//精度位
bit DS18B20_flag_bit;//符号位

//1、复位初始化DS18B20
static void DS18B20_RST(void)//大概1毫秒 //后期加入超时等待故障代码
{
	wire = 0;
	delay_us(75);	//拉低480us复位DS18B20
	wire = 1;		//释放数据线
	while(wire);//	超时60us判断是否有应答信号,超时代码后期在加入
	/*if(wire)		//没有应答的话
	{
		delay_us(26);//延迟180us二次判断DS18B20回复存在信号,即应答ACK信号
		if (wire)//还没有应答的话
		{
			//DS18B20初始化失败,添加处理代码
		}
		else//有应答信号了
		{
			delay_us(26);//延迟180us判断是否器件释放总线
			if (wire)//释放总线的话
			{
				delay_us(26);//再次延长180us
			}
			else
			{
				//DS18B20初始化失败,添加处理代码
			}
		}
	}*/
	//else//有应答信号了
	{
		while(!wire);//等待器件释放总线//延长240us
		/*if (wire == 1)//如果器件释放总线的话
		{
			delay_us(38);//再次延长240us
		}
		else
		{
			//DS18B20初始化失败,添加处理代码
		}*/
	}
}

//2、向DS18B20写1字节
static void write_1_byte_DS18B20(unsigned char x)
{
	unsigned char i;
	for (i=0;i < 8;i++)
	{
		wire = 0;
		_nop_();	//延迟大于1us
		wire = x & 0x01;
		delay_us(8);//延迟60us让DS18B20好采样
		wire = 1;//释放总线
		x = x >> 1;
	}
}

//3、读DS18B20 1字节
static unsigned char read_1_byte_DS18B20(void)
{
	unsigned char i;
	unsigned char dat=0;
	for ( i=0 ; i  < 8; i ++)
	{
		dat = dat >> 1;//放在for循环后面有问题,会向右多移动一位,怪不得之前的数据读取出来有问题
		wire = 0;
		_nop_();
		wire = 1;//释放总线,等待DS18B20发送数据到总线上
		_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();//延迟个10us时间在采集数据
		_nop_();_nop_();_nop_();_nop_();
		if(wire)//这个if里面的数不建议写成if(wire);推荐写成if(wire==1);
		{
			dat += 0x80;
		}
		wire = 1;//释放总线
		delay_us(4);//延迟45us	
	}
	return dat;
}


//6、转换温度,x为选择使用的精度9位 10位 11位 12位,默认12位
void convert_temp_DS18B20(unsigned char x)
{
	DS18B20_RST();	//初始化函数
	write_1_byte_DS18B20(0xcc);	//跳过64位ROM码检测
	write_1_byte_DS18B20(0x44);	//温度转换指令-功能命令
					//x=0001 1111 0x1f 9位精度,转换时间93.75ms
					//x=0011 1111 0x3f 10位精度,转换时间187.5ms
					//x=0101 1111 0x5f 11位精度,转换时间375ms
					//x=0111 1111 0x7f 12位精度,转换时间750ms
	switch (x)
	{
		case 9:	delay_ms(95);	break;
		case 10:delay_ms(190);break;
		case 11:delay_ms(380);break;
		default:delay_ms(750);break;//默认按照12位精度转换时间
	}
}

//10、读取DS18B20的温度,输入选择的精度9位 10位 11位 12位 9 10 11 12,默认12位
void read_temp_DS18B20(unsigned char Accuracy)
{
	unsigned int TLTL=0;//小数部分
	unsigned char TL = 0;//辅助处理小数部分
	unsigned char TH = 0;//处理整数部分
	bit flag=0;//存放符号

	DS18B20_RST();//复位器件
	write_1_byte_DS18B20(0xcc);//跳过ROM检测指令
	write_1_byte_DS18B20(0xbe);//读取暂存寄存器使用的指令
	TL = read_1_byte_DS18B20();//将读取的低温8位存起来
	TH = read_1_byte_DS18B20();//将读取的高温8位存起来
	DS18B20_RST();//强制复位,后面的数据不在读取了
	//开始使用算法计算温度
	TH = (TH << 4) + (TL >> 4);//得到整数
	TL = TL & 0x0f;//得到小数部分
	if (TH < 128)//先判断是正温度还是负温度
	{//为正数
		switch (Accuracy)//匹配精度
		{
		case	9:	TL >>= 3;	TLTL = TL * 5;	break;//匹配9位精度
		case	10:	TL >>= 2;	TLTL = TL * 25;	break;//匹配10位精度
		case	11:	TL >>= 1; TLTL = TL * 125;break;//匹配11位精度
		case	12:	TL >>= 0; TLTL = TL * 625;break;//匹配12位精度
		default:TL >>= 0;  	TLTL = TL * 625;break;//默认匹配12位精度
		}
	}
	else//为负数
	{
		flag = 1;
		switch (Accuracy)//匹配精度
		{
			case	9:	TL >>= 3;	if (TL)
													{
														TLTL = TL * 5;
														TH = ~TH;
													}
													else
													{
														TLTL = 0;
														TH = ~TH + 1;
													}			break;//匹配9位精度 0.5

		case	10:	TL >>= 2;	if (TL)
												{
													TLTL = (~TL & 0x03 + 1) * 25;
													TH = ~TH;
												}
												else
												{
												TLTL = 0;
												TH = ~TH + 1;
												}			break;//匹配10位精度0.75
		case	11:	TL >>= 1; if (TL)
												{
													TLTL = (~TL & 0x07 + 1) * 125;
													TH = ~TH;
												}
												else
												{
													TLTL = 0;
													TH = ~TH + 1;
												}			break;//匹配11位精度0.875
		default:	TL >>= 0; if (TL)
												{
													TLTL = (~TL & 0x0f + 1) * 625;
													TH = ~TH;
												}
												else
												{
													TLTL = 0;
													TH = ~TH + 1;
												}			break;//匹配12位精度0.9375
		}
	}
	DS18B20_flag_bit = flag;//存放符号位
	DS18B20_int_bit= TH;//存放正整数部分
	DS18B20_float_bit = TLTL;//存放小数部分
	DS18B20_Accuracy_bit = Accuracy;//存放精度
}

dt.h

#ifndef __DT_H__
#define	__DT_H__

#include "delay.h"
#include "ds18b20.h"

void RES_dt_dispaly(void);//复位初始化数码管,并显示8个0~8个9

//0≤x≤99999999 unsigned int 无符号整型
//将数据x转换到数组里面
void convert_long_int(unsigned long int x);

//小数转换后存放到数组里面
void dt_convert_DS18B20(void);

//循环显示数组里的元素到数码管
void dt_display(void);

#endif

dt.c

//数码管dt  使用共阴极数码管即位选是阴极时数码管才选中
//使用P0-8个GPIO口
//P2.7位选脚(控制哪个数码管亮)、P2.6段选脚(送数码管值) 控制2个74锁存器

#include <reg52.h>
#include "dt.h"

#define dt_dt  P0
sbit wei = P2^7;		//锁存器位选
sbit duan = P2^6;		//锁存器段选

static unsigned char datax[8];//定义局部变量//用来存放小数及正整数,然后发给数码管进行显示低位到高位存放
static code unsigned char dt_duan[25] =   //数码管段选真值表   dt_duan[i] + 0x80  数码管显示数据并且此数据的右下角有小数点
{
	0x3F,  //"0"
	0x06,  //"1"
	0x5B,  //"2"
	0x4F,  //"3"
	0x66,  //"4"
	0x6D,  //"5"
	0x7D,  //"6"
	0x07,  //"7"
	0x7F,  //"8"
	0x6F,  //"9"
	0x80,  //"."
	0x40,  //"-"
	0x77,  //"A"
	0x7C,  //"B"
	0x39,  //"C"
	0x5E,  //"D"
	0x79,  //"E"
	0x71,  //"F"
	0x76,  //"H"
	0x38,  //"L"
	0x37,  //"n"
	0x3E,  //"u"
	0x73,  //"P"
	0x5C,  //"o"
	0x00  //熄灭
};

static code unsigned char dt_wei[9]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0xff};//1位-8位(0x7f表示最右边哪个数码管)0xff数码管就不亮了

//刷新数码管进行显示
void dt_display(void)//循环显示数组里的元素到数码管
{
	static unsigned char a=0;
	for (a = 0; a < 8; a++)//a亮——a值——a值灭;a+1亮——a+1值——a+1值灭;开始循环(亮,真值,假值)
	{
		dt_dt = dt_wei[a];//灯亮
		wei = 1;            //打开位选
		wei = 0;            //锁存位选
			
		dt_dt = datax[a];//数字
		duan = 1;           //打开段选
		duan = 0;           //锁存段选
		
		delay_ms(1);//这个时间乘以8不能大于定时器时间
		
		dt_dt = dt_duan[24];//清楚段码数字熄灭,假值,防止下位数码管显示上次的值
		duan = 1;           //打开段选
		duan = 0;           //锁存段选
	}
	dt_dt = dt_wei[8];//清除位码,关闭所有数码管
}

//将要显示的正整数临时存储在数组里面,然后循环显示数组里的元素即可
//x传递的数据10进制(x不是8位则高位不会亮)
void convert_long_int(unsigned long int x)
{
	static unsigned char i = 0;		//数组标号也是段选真值
	static unsigned char a = 0;		//流水号,用来表示数码管1~8个
	for (a = 0;a < 8;a++)		//将x没位的值存放到数组里面
	{
		i = x % 10;//获取最后一位数字
		if ((a!=0)&&(x==0))//防止x=0时,导致数码管不显示0
		{
			i = 24;//即0x00,数码管不会亮
		}
		x = x / 10;//每次舍去最低位
		datax[a] = dt_duan[i];
	}
}
//对于DS18B20温度的
void dt_convert_DS18B20(void)//小数转换后存放到数组里面
{
	static unsigned int value;		//计算结果值
	static unsigned char a;		//流水号,用来表示数码管1~8个
	static unsigned char b;//流水号
	switch(DS18B20_Accuracy_bit)//判断精度
	{
		case 9:b=1;break;
		case 10:b=2;break;
		case 11:b=3;break;
		default:b=4;break;
	}
	for (a = 0;a < b;a++)//Accuracy_bit的值存放到数组里面
	{
		value = DS18B20_float_bit % 10;//获取最后一位数字
		DS18B20_float_bit = DS18B20_float_bit / 10;//每次舍去最低位
		datax[a] = dt_duan[value];
	}
	for(;a<8;a++)
	{
		value=DS18B20_int_bit % 10;//获取最后一位数字
		if(a==b)//要加入小数学
		{
			datax[a] = dt_duan[value]+dt_duan[10];
		}
		else if(DS18B20_int_bit==0)
		{
			if(DS18B20_flag_bit)
			{
				DS18B20_flag_bit=0;
				datax[a]=dt_duan[11];//显示-号
			}
			else
				datax[a]=dt_duan[24];//不显示0
		}
		else
		{
			datax[a] = dt_duan[value];
		}
		DS18B20_int_bit = DS18B20_int_bit / 10;//每次舍去最低位
	}
}


void RES_dt_dispaly(void)//清除数码管,关闭数码管,初始化数码管
{
	unsigned char i;
	unsigned long int y=11111111;
	unsigned long int z = 63;//大概1秒
	dt_dt = dt_wei[8];//清除位码,关闭所有数码管
	wei = 1;
	wei = 0;

	duan = 1;
	duan = 0;//恢复单片机IO口默认高电平状态
	for (i = 0; i < 8; i++)
	{
		convert_long_int(y);
		while (z--!=0)
		{
			dt_display();
		}
		y += 11111111;
		z = 20;//大概很快
	}
	dt_dt = dt_wei[8];//清除位码,关闭所有数码管
	wei = 1;
	wei = 0;

	duan = 1;
	duan = 0;//恢复单片机IO口默认高电平状态
}

time.h

#ifndef __TIME_H__
#define __TIME_H__

//定时器0初始化,并设定ms毫秒且工作方式1,16位模式
extern void Timer0_16bit(unsigned char time0_16_ms);

#endif // !__TIME_H__

time.c

#include"REG52.h"
#include "dt.h"
sfr T2MOD = 0xC9;

static unsigned char H0 = 0, L0 = 0;
//定时器0初始化,设定ms毫秒且工作方式1,16位模式
void Timer0_16bit(unsigned char time0_16_ms)
{
    TMOD |= 0x01;
    TH0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) / 256;//需将计算的小数转换整形
    TL0 =(65536 - (unsigned int)(time0_16_ms * 921.6)) % 256;//需将计算的小数转换整形
    H0 = TH0;
    L0 = TL0;
    EA = 1;//开启总中断
    ET0 = 1;//允许定时器0溢出进入中断
    TR0 = 1;//启动定时器
}
void Timer0Interrupt(void) interrupt 1
{
  TH0 = H0;
  TL0 = L0;
		
	dt_display();//刷新一次10毫秒
}

delay.h

//这是一个延迟函数为毫秒级//
#ifndef __DELAY_H__
#define __DELAY_H__

void delay_us(unsigned char us);//us级别延迟,最大输入255,us--1次用6.5us,进入1次函数需要11.95us

void delay_ms(unsigned int ms);//最大输入65535,ms=1,最小1ms时间

void delay_5us(void);//延迟5us时间

#endif // !__DELAY_H__

delay.c

//软件延迟ms级别、us级别、5us
#include <intrins.h>

void delay_us(unsigned char us)	//us级别延迟,最大输入255,us--1次用6.5us,进入1次函数需要11.95us
{
	while (us--);
}

void delay_ms(unsigned int ms)	//设置毫秒级别延迟函数,z最大输入65535
{
	unsigned char x;
	for (ms; ms > 0; ms--)
		for (x = 114; x > 0; x--);
}

void delay_5us(void)//一进一出,延迟5us时间
{
	_nop_();	
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金丝草

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值