SHT10型溫溼度傳感器工作時序分析及驅動程序與Proteus仿真的實現

一、傳感器概述

SHT10型傳感器屬於SHT1×系列(其他常用型號還有SHT11、SHT15),SHT1×屬於Sensirion溫溼度傳感器家族中的貼片封裝系列。傳感器將傳感元件和信號處理電路集成在一塊微型電路板上,輸出完全標定的數字信號。傳感器採用專利的CMOSens技術,確保產品具有極高的可靠性與卓越的長期穩定性。傳感器包括一個電容性聚合體測溼敏感元件、一個用能隙材料製成的測溫元件以及串行接口電路實現無縫連接。因此,該產品具有品質優越、響應迅速、抗干擾能力強、性價比高等優點。本篇博文將詳細分析傳感器的工作時序並根據時序編寫驅動程序。最終,該驅動程序將通過Proteus仿真軟件進行仿真驗證。

 

二、傳感器尺寸

 

三、接口定義及電源引腳

(一)接口定義

(二)電源引腳

SHT10的供電電壓範圍爲2.3-5.5V,建議供電電壓爲3.3V。在電源引腳(VDD與GND)之間必須加一個100nF的電容,用於去耦濾波(在仿真中可加可不加)

SHT10的串行接口,在傳感器信號的讀取及電源損耗方面,都做了優化處理;傳感器不能按照I2C協議進行編址,但是,如果I2C總線上沒有掛在別的原件則傳感器可以掛載到I2C總線上,但是單片機與傳感器之間的通信協議不能採用I2C協議,而是要在按照傳感器的協議進行信息交互。

 

四、傳感器電氣特性

傳感器的電氣特性(如:高/低電平、輸入/輸出電壓等)受供電電壓的影響,下表中的參數在沒有特殊說明情況下均代表在5V供電條件下的參數。

下列時序圖中,加粗的DATA線由傳感器控制,普通的DATA線由單片機控制,有效時間由SCK的時序決定。尤其要注意的是,數據讀取的有效時間爲前一個切換的下降沿。

     

 

五、傳感器的通訊過程及對應的驅動程序

(一)啓動傳感器

首先,選擇供電電壓後將傳感器通電,商店速率不能低於1V/ms。通電後傳感器需要有11ms進入休眠狀態,在此之前,不允許單片機對傳感器發送任何命令。

在休眠狀態之後,要用一組“啓動傳輸”時序。來完成數據傳輸的初始化。該”啓動傳輸”時序包括:當SCK時鐘爲高電平時,DATA由高電平反轉爲低電平,隨後是在SCK高電平時DATA由低電平反轉爲高電平。具體時序圖如下:

由上述時序圖,可以得到“啓動傳輸”的驅動程序如下:

void Start(void)
{
	sck=0;//電平初始化
	dat=1;
	_nop_();

	sck=1;//第一次電平跳變
	_nop_();
	dat=0;
	_nop_();
	sck=0;
	_nop_();

	sck=1;//第二次電平跳變
	_nop_();
	dat=1;
	_nop_();
	sck=0;
	_nop_();
}

(二)命令集及“寫一字節”程序

在啓動程序之後,後續命令包括三個地址位(目前只支持000)和五個命令位。SHT10會以下述方式表示已正確接收指令:在第八個SCK的下降沿之後將DATA下拉爲低電平作爲ACK位,並在第九個SCK時鐘的下降沿之後釋放DATA(恢復高電平)。SHT10的命令集如下圖所示。

根據已知命令集,即可通過單總線向傳感器發送命令。發送溼度測量命令的工作時序如下圖所示:

通過上述時序圖所展示的“發送一字節”的工作時序,在“發送一字節數據”的驅動程序中可以採取的思路爲:數據線先傳送高位後傳送低位,取位的方式爲mask=0x80與命令值value進行“相與”,之後通過mask<<=1配合循環操作,即可實現將命令值由高位向低位逐位取出。在每取出value的一位後,首先延時一個_op_()  (在12MHz的工作頻率下爲1us),使DATA引腳能夠建立起穩定的電平,然後使sck產生上升沿並延時兩個_nop_(),使傳感器讀入DATA引腳的數據,然後再恢復sck引腳的低電平,依次循環八次,使傳感器讀入一字節的命令數據。在讀完八位數據之後,使SCK變高電平並檢測DATA引腳是否拉低,以檢測傳感器是否發出了確認信息ACK。然後,再將SCK恢復爲低電平。具體驅動程序如下:

uchar Write_byte(uchar value)//先傳送高位
{
	uchar i;
	for(i=0;i<8;i++)
	{
		if(value&0x80)
		{
			dat=1;
		}
		else
		{
			dat=0;
		}
		_nop_();//建立數據線電平
		sck=1;//sck產生上升沿
		_nop_();
		_nop_();
		sck=0;//恢復sck的低電平
		_nop_();
		value<<=1;
	}
	sck=1;//第八個下降沿後(即產生第九個上升沿)判斷數據線電平
	_nop_();
	if(dat==0)//如果數據線被拉低,說明傳感器響應了
	{
		sck=0;//恢復sck的低電平
		_nop_();
		return 1;	
	}
	sck=0;//恢復sck的低電平
	_nop_();
	return 0;
}

(三)“讀一字節”驅動程序

在發佈完一組測量命令之後,單片機要等待測量結束,這個過程大約需要10/80/120ms,分別對應8/12/14bit測量,確切時間由內部晶振速度決定,最多有-30%的變化。SHT10通過下拉DATA至電平並進入空閒模式表示測量結束。單片機在再次觸發SCK時鐘前必須等待這個“數據備妥”信號來讀出數據。(默認溫度測量14bit,溼度測量12bit),另外,還有一字節的CRC,用於循環冗餘校驗。溼度測量時序圖如下:

根據上述溼度測量時序圖可以類推溫度測量時序圖(區別在於默認情況下溫度值比溼度值多2bit),並由時序圖可寫出具體控制時序,如下:

uchar Read_byte(uchar ack)
{
	uchar i,value,mask=0x80;
	for(i=0;i<8;i++)
	{
		sck=1;
		_nop_();
		if(dat==1)
		{
			value|=mask;
		}
		else
		{
			value&=(~mask);
		}
		sck=0;
		_nop_();
		mask>>=1;
	}	
	if(ack==0)//單片機應答
	{
		dat=0;
	}
	else
	{
		dat=1;
	}
	_nop_();//建立應答/非應答信號電平
	sck=1;
	_nop_();
	sck=0;
	_nop_();
	dat=1;//釋放數據線
	return value;
}

 

六、測量結果轉換

測量結果轉換過程中使用的參數與供電電壓有關,總控制程序中的轉換代碼僅適用於5V供電時進行轉換,其他工作電壓下的轉換關係如下圖所示:

 

七、總控制程序

/*單片機採用AT89C52工作頻率爲12MHz,顯示單元採用LCD1602液晶屏*/

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

typedef unsigned char uchar;
typedef unsigned int uint;


/**************************全局變量區******************************/
uchar str[4];//儲存轉換值對應的字符串
int T,Hum;//溫溼度值
/**************************位定義區*******************************/																   
sbit RS=P3^3;
sbit RW=P3^4;
sbit E=P3^5;
sbit sck=P3^0;
sbit dat=P3^1;

/**************************子函數聲明區**************************/
void Delay(uint n);//延時子函數
void Delay1ms(uint t);//毫秒級延時子函數

void Start(void);//發送一組“啓動傳輸”的時序以完成數據傳輸的初始化
uchar Write_byte(uchar value);//向傳感器寫入一個字節
uchar Read_byte(uchar ack);//從傳感器讀出一個字節(0爲應答,1爲不應答)
int Get_Tep(void);//讀取溫度子函數
int Get_Hum(void);//讀取溼度子函數
void Change(int x);//把整型數值x轉換爲字符串

void Write_com(uchar com);//寫命令子函數
void Write_dat(uchar dat);//寫數據子函數
void Init_1602();//LCD1602初始化子函數
void Show(uchar x,uchar y,uchar *str);//LCD1602顯示子函數


/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
void main()
{

	Init_1602();//LCD1602初始化
	Delay1ms(11);//上電後度過11ms的休眠狀態
	Start();//喚醒
	T=Get_Tep();//獲取溫度值
	Start();//再次喚醒
	Hum=Get_Hum();//獲取溼度值
	Change(Hum);//把溼度值轉換爲字符串
	Show(1,1,"Hum:");//顯示溼度值
	Show(1,5,str);
	Change(T);//把溫度值轉換爲字符串
	Show(2,1,"Temp:");//顯示溫度值
	Show(2,6,str);
	while(1);
}
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
/*********************************延時函數體*****************************/
void Delay(uint n)       //延時函數                       
{ 
    uint x,y;  
    for(x=n;x>0;x--) 
        for(y=110;y>0;y--); 
}
void Delay1ms(uint t)   //毫秒級延時子函數
{
    uchar a,b;
	uint i;
	for(i=0;i<t;i++)
	{
	    for(b=199;b>0;b--)
	        for(a=1;a>0;a--);
	}
}
/********************************啓動傳輸函數體***************************/
void Start(void)
{
	sck=0;//電平初始化
	dat=1;
	_nop_();

	sck=1;//第一次電平跳變
	_nop_();
	dat=0;
	_nop_();
	sck=0;
	_nop_();

	sck=1;//第二次電平跳變
	_nop_();
	dat=1;
	_nop_();
	sck=0;
	_nop_();
}
/********************************寫入一字節函數體************************/
uchar Write_byte(uchar value)//先傳送高位
{
	uchar i;
	for(i=0;i<8;i++)
	{
		if(value&0x80)
		{
			dat=1;
		}
		else
		{
			dat=0;
		}
		_nop_();//建立數據線電平
		sck=1;//sck產生上升沿
		_nop_();
		_nop_();
		sck=0;//恢復sck的低電平
		_nop_();
		value<<=1;
	}
	sck=1;//第八個下降沿後(即產生第九個上升沿)判斷數據線電平
	_nop_();
	if(dat==0)//如果數據線被拉低,說明傳感器響應了
	{
		sck=0;//恢復sck的低電平
		_nop_();
		return 1;	
	}
	sck=0;//恢復sck的低電平
	_nop_();
	return 0;
}
/********************************讀出一字節函數體************************/
uchar Read_byte(uchar ack)
{
	uchar i,value,mask=0x80;
	for(i=0;i<8;i++)
	{
		sck=1;
		_nop_();
		if(dat==1)
		{
			value|=mask;
		}
		else
		{
			value&=(~mask);
		}
		sck=0;
		_nop_();
		mask>>=1;
	}	
	if(ack==0)//單片機應答
	{
		dat=0;
	}
	else
	{
		dat=1;
	}
	_nop_();//建立應答/非應答信號電平
	sck=1;
	_nop_();
	sck=0;
	_nop_();
	dat=1;//釋放數據線
	return value;
}
/**********************************獲取溫度函數體**************************/
int Get_Tep()
{
	int result_l,result_h,check,result;
	float tempt;
	Write_byte(0x03);//讀溫度命令
	while(dat);//等到傳感器轉換完數據後把數據線主動拉低
   	result_h=Read_byte(0);//讀轉換值高八位並應答
	result_l=Read_byte(0);//讀轉換值低八位並應答
	check=Read_byte(1);//讀校驗位並非應答
	result=result_h*256+result_l;
	tempt=0.01*result-40.1;//計算溫度值(5V供電)
	result=tempt;//對溫度值進行四捨五入
	if(tempt-result>=0.5)
	{
		result+=1;
	}
	return result;
}
/**********************************獲取溫度函數體**************************/
int Get_Hum()
{
	int result_l,result_h,check,result;
	float tempt;
	float t1=0.01f,t2=0.00008f,c1=-2.0468f,c2=0.0367f,c3=-1.5955e-6f;
	Write_byte(0x05);//讀溼度命令
	while(dat);//等到傳感器轉換完數據後把數據線主動拉低
   	result_h=Read_byte(0);//讀轉換值高八位並應答
	result_l=Read_byte(0);//讀轉換值低八位並應答
	check=Read_byte(1);//讀校驗位並非應答
	result=result_h*256+result_l;
	tempt=(T-25)*(t1+t2*result)+(c1+c2*result+c3*result*result)-6;
	result=tempt;//對溼度值進行四捨五入
	if(tempt-result>=0.5)
	{
		result+=1;
	}
	return result;
}
/******************************把整型數據轉換爲字符串**********************/
void Change(int x)
{
	str[0]=x/100+48;
	str[1]=(x/10)%10+48;
	str[2]=x%10+48;
	str[3]='\0';
}


/********************************寫命令函數體****************************/
void Write_com(uchar com)
{
	RS=0;
	P2=com;
	Delay(5);
	E=1;
	Delay(5);
	E=0;
}
/********************************寫數據函數體****************************/
void Write_dat(uchar dat)
{
	RS=1;
	P2=dat;
	Delay(5);	 
	E=1;
	Delay(5);
	E=0;
}
/*****************************LCD1602初始化函數體*************************/
void Init_1602()
{
	uchar i=0;
	RW=0;
	Write_com(0x38);//屏幕初始化
	Write_com(0x0c);//打開顯示 無光標 無光標閃爍
	Write_com(0x06);//當讀或寫一個字符是指針後一一位
	Write_com(0x01);//清屏
	Write_com(0x80);//設置位置
}
/*******************************顯示內容函數體**************************/
void Show(uchar x,uchar y,uchar *str)
{
	unsigned char addr;
  	if (x==1)
	{
		addr=0x00+y-1; //從第一行、第y列開始顯示
	}
	else
	{
		addr=0x40+y-1; //第二行、第y列開始顯示
	}					
	Write_com(addr+0x80);
	while (*str!='\0')
	{
		Write_dat(*str++);
	}

}

八、Proteus仿真圖的連接及結果

 

 

 

 

左肩理想右肩擔當,君子不怨永遠不會停下腳步!

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章