天天看點

51單片機——DS18B20溫度傳感器-1總線通訊1.1

寫的不知道好不好,有什麼不對的地方還請指出,謝了。

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

繼續閱讀