欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-12 10:54:43
...

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

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_();	
}

 

上一篇: DS18b20

下一篇: 线程的加入