在我們的產品中,經常需要檢測溫濕度數據。有很多檢測溫濕度的方法和模塊,其中SHT1x系列溫濕度傳感器就是一種成本較低使用方便的溫濕度檢測模塊。下面我們就來說一說如何實現SHT1x系列溫濕度傳感器的驅動。
1 、功能概述
SHT1x包括 SHT10, SHT11 和 SHT15 屬于Sensirion溫濕度傳感器家族中的貼片封裝系列。傳感器將傳感元件和信號處理電路集成在一塊微型電路板上,輸出完全標定的數字信號。
1.1 、硬件描述
SHT1x傳感器包括一個電容性聚合體測濕敏感元件、一個用能隙材料制成的測溫元件,并在同一芯片上,與14 位的A/D 轉換器以及串行接口電路實現無縫連接。其引腳定義如下:
SHT1x溫濕度傳感器使用的2線通訊,類似于I2C總線,但并不相同,使用普通的GPIO就可實現通訊。此次采用STM32F103VET6來操作SHT15,具體的連接方式如下:
SCK 用于微處理器與SHT1x 之間的通訊同步。由于接口包含了完全靜態邏輯,因而不存在最小SCK 頻率。
DATA 引腳為三態結構,用于讀取傳感器數據 . 當向傳感器發送命令時, DATA 在 SCK 上升沿有效且在 SCK 高電平時必須保持穩定。 DATA 在 SCK 下降沿之后改變。為避免信號沖突,微處理器應驅動DATA 在低電平。需要一個外部的上拉電阻(例如:10kΩ)將信號提拉至高電平。上拉電阻通常已包含在微處理器的I/O 電路中。
1.2 、數據通訊
選擇供電電壓后將傳感器通電,上電速率不能低于1V/ms。通電后傳感器需要11ms 進入休眠狀態,在此之前不允許對傳感器發送任何命令。
SHT1x溫濕度傳感器采用一組“啟動傳輸”時序,來完成數據傳輸的初始化。而后續命令包含三個地址位(目前只支持000”),和五個命令位。SHT1x 會以下述方式表示已正確地接收到指令:在第8 個SCK 時鐘的下降沿之后,將DATA 下拉為低電平(ACK 位)。在第9 個SCK 時鐘的下降沿之后,釋放DATA(恢復高電平)。SHT1x溫濕度傳感器的指令表如下:
后續我們開發SHT1x溫濕度傳感器的驅動時,就是通過這些操作命令來實現不同的操作。
1.3 、數據計算
濕度的測量數據并不是一個線性變化的過程濕度的非線性,為獲得更為精確的測量數據,我們一般要采用非線性補償公式進行信號轉換。濕度的非線性補償公式及參數如下:
一般來說,傳感器濕度的校準都是在一定的參考溫度下進行的,但在我們的使用過程中,實際溫度與測試參考溫度25℃ (~77℉)明顯是不同的,所以我們需要對實際的濕度數據進行補償。濕度的溫度補償公式及系數如下:
SHT1x系列溫濕度傳感器的溫度傳感器采用的能隙材料PTAT。而能隙材料PTAT一般與絕對溫度存在正比關系,因而溫度傳感器具有極好的線性??捎萌缦鹿綄底州敵?SOT)轉換為溫度值,溫度轉換系數如下:
SHT1x 并不直接進行露點測量,但露點可以通過溫度和濕度讀數計算得到.。由于溫度和濕度在同一塊集成電路上測量,SHT1x可測量露點。露點的計算方法很多,絕大多數都很復雜。 對于-40 – 50°C 溫度范圍的測量,通過下面的的公式可得到較好的精度。
通過上述幾個公式就可以計算出SHT1x監測的溫度、濕度及露點數據。
2 、驅動設計與實現
我們已經了解了SHT1x系列溫濕度傳感器基本技術特性,接下來我們進一步考慮如何設計并實現SHT1x系列溫濕度傳感器的驅動。
2.1 、對象定義
在使用一個對象之前我們需要獲得一個對象。同樣的我們想要SHT1x系列溫濕度傳感器就需要先定義SHT1x系列溫濕度傳感器的對象。
2.1.1 、對象的抽象
我們要得到SHT1x系列溫濕度傳感器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下SHT1x系列溫濕度傳感器的對象。
先來考慮屬性,作為屬性肯定是用于標識或記錄對象特征的東西。我們來考慮SHT1x系列溫濕度傳感器對象屬性。首先SHT1x系列溫濕度傳感器有一個狀態寄存器,用于表示狀態和配置操作特性,所以我們將讀取的狀態寄存器的數據作為標識SHT1x系列溫濕度傳感器對象的一個屬性。我們根據前面SHT1x系列溫濕度傳感器的數據計算公式可知,溫度單位和工作電壓對溫度測量結果的計算有直接影響,所以我們將溫度單位和工作電壓也作為SHT1x系列溫濕度傳感器對象的屬性,用于區別計算過程。此外溫度、濕度、露點的數據我們將其作為屬性用于記錄當前狀態。
接著我們還需要考慮SHT1x系列溫濕度傳感器對象的操作問題。我們是使用GPIO來模擬數字通訊,所以SCK引腳和DATA引腳都需要控制輸出,而控制函數的實現與具體的硬件相關,所以我們將控制這兩個引腳輸出的函數作為對象的操作。對于DATA引腳還有可能需要控制方向和讀取輸入,同樣的原因我們也將其作為對象的操作。此外,我們在與SHT1X通訊時需要控制時鐘,以及操作等待都是與硬件有關系的時間操作,所以我們也將其作為對象的操作。
根據上述我們對SHT1x溫濕度傳感器的分析,我們可以定義SHT1x溫濕度傳感器的對象類型如下:
1 /* 定義SHT1x對象類型 */
2 typedef struct Sht1xObject {
3 uint8_t statusReg; //狀態寄存器
4 uint32_t period; //SCK時鐘周期
5 SHT1xTempUnitType tempUnit; //溫度單位
6 float vdd; //工作電壓
7 float temperature; //溫度
8 float humidity; //濕度
9 float dewPoint; //露點
10 SHT1xSetBusPin *SetBusPin; //總線操作函數
11 uint8_t (*ReadSDABit)(void); //讀數據總線函數
12 void (*SDADirection)(SHT1xIODirectionType direction); //數據總線方向控制函數
13 void (*Delayus)(volatile uint32_t period); //微秒延時函數
14 void (*Delayms)(volatile uint32_t nTime); //毫秒秒延時函數
15 }Sht1xObjectType;
2.1.2 、對象初始化
我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮SHT1x系列溫濕度傳感器對象的初始化函數。一般來說,初始化函數需要處理幾個方面的問題。一是檢查輸入參數是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據此我們設計SHT1x系列溫濕度傳感器對象的初始化函數如下:
1 /* 初始化SHT1x對象 */
2 void SHT1xInitialization(Sht1xObjectType *sht,
3 uint32_t sck,
4 float vdd,
5 SHT1xTempUnitType uint,
6 SHT1xHeaterType heater,
7 SHT1xOTPType otp,
8 SHT1xResolutionType resolution,
9 SHT1xSetBusPin setSckPin,
10 SHT1xSetBusPin setDataPin,
11 SHT1xReadSDABit readSDA,
12 SHT1xSDADirection direction,
13 SHT1xDelay delayus,
14 SHT1xDelay delayms)
15 {
16 uint8_t regSetup=0x00;
17 uint8_t heaterSet[]={ONCHIPHEATERDISABLE,ONCHIPHEATERENABLE}; //是否啟用片內加熱配置集
18 uint8_t otpSet[]={OTPENABLE,OTPDISABLE}; //是否加載OTP配置集
19 uint8_t dpiSet[]={HIGH_RESOLUTION_DATA,LOW_RESOLUTION_DATA}; //數據分辨率配置集
20
21 if((sht==NULL)||(setSckPin==NULL)||(setDataPin==NULL)
22 ||(readSDA==NULL)||(delayus==NULL)||(delayms==NULL))
23 {
24 return;
25 }
26
27 setBusPin[0]=setSckPin;
28 setBusPin[1]=setDataPin;
29 sht->SetBusPin=setBusPin;
30 sht->ReadSDABit=readSDA;
31 sht->Delayus=delayus;
32 sht->Delayms=delayms;
33
34 if(direction!=NULL)
35 {
36 sht->SDADirection=direction;
37 }
38 else
39 {
40 sht->SDADirection=DefaultSDADirection;
41 }
42
43 /*初始化速度,默認100K*/
44 if((sck>0)&&(sck<=500))
45 {
46 sht->period=500/sck;
47 }
48 else
49 {
50 sht->period=5;
51 }
52
53 sht->temperature=0.0;
54 sht->humidity=0.0;
55 sht->dewPoint=0.0;
56 sht->vdd=vdd;
57 sht->tempUnit=uint;
58
59 regSetup=regSetup|heaterSet[heater]|otpSet[otp]|dpiSet[resolution];
60
61 WriteStatusRegister(sht,®Setup);
62
63 sht->Delayms(10);
64
65 ReadStatusRegister(sht);
66 }
2.2 、對象操作
我們已經完成了SHT1x系列溫濕度傳感器對象類型的定義和對象初始化函數的設計。但我們的主要目標是獲取對象的信息,接下來我們還要實現面向SHT1x溫濕度傳感器的各類操作。
2.2.1 、啟動通訊
每次發起與SHT1x溫濕度傳感器的通訊都需要用一組“啟動傳輸”時序,來完成數據傳輸的初始化。它包括:當SCK時鐘高電平時DATA翻轉為低電平,緊接著SCK變為低電平,隨后是在SCK時鐘高電平時DATA翻轉為高電平。啟動通訊時序如下圖:
根據上述時序圖我們可以實現啟動通訊的操作函數如下:
1 /*SHT1X啟動時序操作*/
2 static void StartSHT1XOperation(Sht1xObjectType *sht)
3 {
4 /*將data線設置為輸出模式*/
5 sht->SDADirection(Out);
6
7 sht->SetBusPin[DataPin](SHT1xSet);
8 sht->SetBusPin[SckPin](SHT1xReset);
9 sht->Delayus(sht->period);
10
11 sht->SetBusPin[SckPin](SHT1xSet);
12 sht->Delayus(sht->period);
13 sht->SetBusPin[DataPin](SHT1xReset);
14 sht->Delayus(sht->period);
15 sht->SetBusPin[SckPin](SHT1xReset);
16 sht->Delayus(sht->period);
17 sht->SetBusPin[SckPin](SHT1xSet);
18 sht->Delayus(sht->period);
19 sht->SetBusPin[DataPin](SHT1xSet);
20 sht->Delayus(sht->period);
21 sht->SetBusPin[SckPin](SHT1xReset);
22 }
2.2.2 、復位通訊
如果與SHT1x通訊中斷,可通過下列信號時序復位:當DATA保持高電平時,觸發SCK時鐘9 次或更多。接著發送一個“傳輸啟動”時序。這些時序只復位串口,狀態寄存器內容仍然保留。具體的時序圖如下:
根據上述的時序圖,我們設計通訊復位操作函數如下:
1 /*SHT1X通訊復位*/
2 void ResetSHT1XCommunication(Sht1xObjectType *sht)
3 {
4 /*將data線設置為輸出模式*/
5 sht->SDADirection(Out);
6 sht->Delayms(1);
7
8 sht->SetBusPin[DataPin](SHT1xSet);
9 sht->SetBusPin[SckPin](SHT1xReset);
10
11 for(int i=0;i<9;i++)
12 {
13 sht->SetBusPin[SckPin](SHT1xSet);
14 sht->Delayus(sht->period);
15 sht->SetBusPin[SckPin](SHT1xReset);
16 sht->Delayus(sht->period);
17 }
18
19 StartSHT1XOperation(sht);
20 }
2.2.3 、數據獲取
在前面我們已經了解了SHT1x通訊命令,根據命令定義,我們發送命令“00000101”就表示相對濕度RH測量,發送命令“00000011”就表示溫度T的測量。測量過程需要大約20/80/320ms,分別對應8/12/14bit分辨率。SHT1x通過下拉DATA至低電平并進入空閑模式,表示測量的結束。控制器在再次觸發SCK時鐘前,必須等待這個“數據備妥”信號來讀出數據。檢測數據可以先被存儲,這樣控制器可以繼續執行其它任務在需要時再讀出數據。
接著傳輸2個字節的測量數據和1個字節的CRC奇偶校驗(可選擇讀?。?刂破餍枰ㄟ^下拉DATA為低電平,以確認每個字節。所有的數據從MSB 開,右值有效(例如:對于12bit 數據,從第5個SCK時鐘起算作MSB;而對于8bit 數據,首字節則無意始義)。
在收到CRC的確認位之后,表明通訊結束。如果不使用CRC-8 校驗,控制器可以在測量值LSB后,通過保持ACK高電平終止通訊。在測量和通訊完成后,SHT1x自動轉入休眠模式。數據測量時序圖如下所示:
根據上述描述和時序圖,我們可以實現溫濕度數據的獲取函數如下:
1 /*獲取SHT1X的濕度值*/
2 float GetSht1xHumidityValue(Sht1xObjectType *sht)
3 {
4 float humiValue=0.0;
5 uint16_t sorh=0;
6 uint8_t err=0;
7 uint8_t humiCode[2]={0,0};
8 uint8_t checkSum=0;
9
10 StartSHT1XOperation(sht);
11 WriteByteToSht1x(sht,HUMI_MEAS_COMMAND);
12 sht->SDADirection(In);
13
14 if((sht->statusReg&0x01)==0x01)
15 {
16 sht->Delayms(20);
17 }
18 else
19 {
20 sht->Delayms(80);
21 }
22
23 if(sht->ReadSDABit() == 1)
24 {
25 err += 1;
26 }
27 humiCode[0]=ReadByteFromSht1x(sht,Ack);
28 humiCode[1]=ReadByteFromSht1x(sht,Ack);
29 checkSum=ReadByteFromSht1x(sht,noAck);
30 if(CheckCRC8ForSHT1x(humiCode,2,checkSum))
31 {
32 sorh=(humiCode[0]<<8)|humiCode[1];
33 }
34 else
35 {
36 err += 1;
37 }
38
39 if(err != 0)
40 {
41 ResetSHT1XCommunication(sht);
42 }
43 else
44 {
45 humiValue=ConvertHumidityData(sht,sorh);
46 }
47
48 return humiValue;
49 }
2.2.4 、狀態寄存器操作
SHT1x的某些高級功能可以通過給狀態寄存器發送指令來實現,如選擇測量分辨率,電量不足提醒,使用OTP加載或啟動加熱功能等。SHT1x的狀態寄存器可以讀或者寫。其實寫狀態寄存器就是配置設備的一些特性,一般情況下在初始化時完成即可。讀寫狀態寄存器的格式如下:
1 /*讀狀態寄存器*/
2 static uint8_t ReadStatusRegister(Sht1xObjectType *sht)
3 {
4 uint8_t err=0;
5 uint8_t status;
6 uint8_t checkSum;
7
8 StartSHT1XOperation(sht);
9 err=WriteByteToSht1x(sht,READ_STATUS_REGISTER);
10 status=ReadByteFromSht1x(sht,Ack);
11 checkSum=ReadByteFromSht1x(sht,noAck);
12
13 if(CheckCRC8ForSHT1x(&status,1,checkSum))
14 {
15 sht->statusReg=status;
16 }
17 else
18 {
19 err+=1;
20 }
21 return err;
22 }
23
24 /*寫狀態寄存器*/
25 static uint8_t WriteStatusRegister(Sht1xObjectType *sht,uint8_t *pValue)
26 {
27 uint8_t err=0;
28
29 StartSHT1XOperation(sht);
30 err +=WriteByteToSht1x(sht,WRITE_STATUS_REGISTER);
31 err +=WriteByteToSht1x(sht,*pValue);
32
33 err +=ReadStatusRegister(sht);
34 return err;
35 }
3 、驅動的使用
我們已經設計并實現了SHT1x溫濕度傳感器驅動,接下來我們還需要對這一驅動進行驗證,所以我們要基于此驅動設計一個簡單的應用。
3.1 、聲明并初始化對象
使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的SHT1x溫濕度傳感器對象類型聲明一個SHT1x溫濕度傳感器對象變量,具體操作格式如下:
Sht1xObjectType sht1x;
聲明了這個對象變量并不能立即使用,我們還需要使用驅動中定義的初始化函數對這個變量進行初始化。這個初始化函數所需要的輸入參數如下:
Sht1xObjectType *sht,SHT1X對象變量
uint32_t sck,SCK時鐘頻率
float vdd,工作電壓
SHT1xTempUnitType uint,溫度單位
SHT1xHeaterType heater,是否啟用加熱器設置
SHT1xOTPType otp,是否加在OTP設置
SHT1xResolutionType resolution,測量分辨率設置
SHT1xSetBusPin setSckPin,SCK引腳操作函數
SHT1xSetBusPin setDataPin,DATA引腳操作函數
SHT1xReadSDABit readSDA,讀DATA引腳函數
SHT1xSDADirection direction,DATA引腳方向配置函數
SHT1xDelay delayus,微秒延時函數
SHT1xDelay delayms,毫秒延時函數
對于這些參數,對象變量我們已經定義了。時鐘頻率根據實際輸入,以k為單位,默認為100k。工作電壓根據實際情況輸入。溫度單位、加熱設置、OTP配置、分辨率配置均為枚舉,根據實際情況選擇就好了。主要的是我們需要定義幾個函數,并將函數指針作為參數。這幾個函數的類型如下:
1 /* 定義GPIO引腳輸出操作的函數指針 */
2 typedef void(*SHT1xSetBusPin)(SHT1xPinValueType value);
3
4 /* 讀數據總線函數 */
5 typedef uint8_t (*SHT1xReadSDABit)(void);
6
7 /* 數據總線方向控制函數 */
8 typedef void (*SHT1xSDADirection)(SHT1xIODirectionType direction);
9
10 /* 微秒延時函數 */
11 typedef void (*SHT1xDelay)(volatile uint32_t period);
對于這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。片選操作函數用于多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數定義如下:
1 /*操作SCK引腳,設置高低操作*/
2 static void OperationSckPin(SHT1xPinValueType value)
3 {
4 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,(GPIO_PinState)value);
5 }
6
7 /*操作DATA引腳,設置高低操作*/
8 static void OperationDataPin(SHT1xPinValueType value)
9 {
10 HAL_GPIO_WritePin(GPIOB,GPIO_PIN_9,(GPIO_PinState)value);
11 }
12
13 /*讀取DATA引腳位*/
14 uint8_t ReadDataPinBit(void)
15 {
16 return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_9);
17 }
18
19 /*將DATA線設置為輸入輸出方向模式*/
20 void SetDataPineDirection(SHT1xIODirectionType direction)
21 {
22 GPIO_InitTypeDef GPIO_InitStruct;
23
24 GPIO_InitStruct.Pin = GPIO_PIN_9;
25 if(direction)
26 {
27 GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
28 GPIO_InitStruct.Pull = GPIO_NOPULL;
29 }
30 else
31 {
32 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
33 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
34 }
35 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
36 }
對于延時函數我們可以采用各種方法實現。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數。于是我們可以調用初始化函數如下:
SHT1xInitialization(&sht1x,100,3.3,DegreeCentigrade,SHT1xHeaterDisable,SHT1xOTPEbable,SHT1xHighResolution,OperationSckPin,OperationDataPin,ReadDataPinBit,SetDataPineDirection,Delayus,HAL_Delay);
這里我們將SHT1x對象初始化為速度100k,3.3伏工作電壓,采用攝氏溫度單位,禁用片上加熱器,加載OTP并使用高分辨率。
3.2 、基于對象進行操作
我們定義了對象變量并使用初始化函數給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數據。我們在驅動中已經將獲取數據并轉換為轉換值的比例值,接下來我們使用這一驅動開發我們的應用實例。
這里我們設計一個簡單應用,使用SHT1X溫濕度傳感器獲取溫度、濕度及露點數據,具體實現如下:
1 /* 獲取SHT1X數據 */
2 void GetSHT1xData(void)
3 {
4 float temperature=0.0;
5 float humidity=0.0;
6 float dewPoint=0.0;
7
8 GetSht1xMeasureValue(&sht1x);
9
10 temperature=sht1x.temperature;
11 humidity=sht1x.humidity;
12 dewPoint=sht1x.dewPoint;
13 }
4 、應用總結
我們實現了SHT1X溫濕度傳感器的驅動,并使用這一驅動開發了簡單的驗證應用。所得到的結果與我們預期的結果是一致的,這說明我們的驅動開發沒有問題。
在使用驅動程序時需要注意一點,對象有一個控制DATA總線引腳輸入輸出方向的操作。對于一般情況下我們編寫引腳的輸入輸出方向控制函數,在初始化函數中將函數指針作為參數傳入即可。如果硬件上可以配置為開漏輸出,則可以不用單獨控制引腳的輸入輸出方向。在初始化函數中以NULL作為參數輸入。
關于通訊速率問題需要注意。在不同工作電壓時所支持的最大通訊速率是不同的,但不論如何我都能支持到1MHz,所以沒有特殊要求,電壓的影響可以不用考慮。在我們的驅動中,最多能支持到500kHz,這主要是考慮到SHT1X的典型速度只有100k,而且大多數應用中不會有高速要求。
完整的源代碼可在GitHub下載:https://github.com/foxclever/ExPeriphDriver
評論
查看更多