基於樹莓派的sht20溫溼度傳感器應用(c語言)

一.I2C協議

協議詳解

1.1 I2C總線特徵

I2C主要靠2根線來控制,一根是SDA(串行數據線),一根是SCL(串行時鐘線),通過對SCL和SDA線電平高低的控制,來產生I2C總線協議所需要的信號進行數據傳遞。空閒時,兩根線一般被接上上拉電阻拉高,保持高電平。

1.2 I2C總線特徵

I2C總線上分爲主設備與從設備,而且每一個設備都會對應一個唯一的地址,通常爲7位,最後一位用來作爲傳輸方向的標緻,主從設備就通過地址來確定通訊雙方。

在通常的應用中,我們把CPU帶I2C總線接口的模塊作爲主設備,把掛接在總線上的其他設備都作爲從設備。

1.3 I2C總線協議

I2C協議規定,總線上數據的傳輸必須以一個其實信號作爲開始條件,以一個結束信號作爲傳輸的停止條件。這些信號有主設備控制產生。

  • 起始條件
    總線處於空閒時,SCL與SDA均保持高電平,在此狀態下,SDA線由高電平轉爲低電平,表示產生了一個起始信號
  • 結束信號
    當SCL爲高而SDA由低到高跳變,則表示產生了一個結束信號

在這裏插入圖片描述
數據傳輸原理:

主設備在SCL線上產生每個時鐘脈衝的過程中將在SDA線上傳輸一個數據位,當一個字節按數據位從高位到低位的順序傳輸完後,緊接着從設備將拉低SDA線,回傳給主設備一個應答位, 此時才認爲一個字節真正的被傳輸完成。當然,並不是所有的字節傳輸都必須有一個應答位,比如:當從設備不能再接收主設備發送的數據時,從設備將回傳一個否定應答位。

在這裏插入圖片描述
從上圖可知
1.SCL爲高,SDA由高跳變爲低,表示起始條件;
2.SCL沒產生一個時鐘信號,SDA上傳遞1個位的數據;
3.傳輸完一個字節後,由從設備拉低SDA,回傳給主設備一個ACK才能表示這一字節傳輸完成;
4.SCL爲高,SDA由低跳變爲高,表示主設備結束對總線的控制,總線重新處於空閒狀態。

按地址傳輸:

I2C總線上的每一個設備都對應一個唯一的地址,主從設備之間的數據傳輸是建立在地址的基礎上,也就是說,主設備在傳輸有效數據之前 要先指定從設備的地址,地址指定的過程和上面數據傳輸的過程一樣,只不過大多數從設備的地址是7位的,然後協議規定再給地址添加一個最低位用來表示接下來 數據傳輸的方向,0表示主設備向從設備寫數據,1表示主設備向從設備讀數據

在這裏插入圖片描述
1.起始條件結束後,前7個位爲從設備地址,第8個位用作傳輸方向的標緻,即主設備 --> 從設備 或者從設備 --> 主設備;
2.每傳輸完一個字節,都需要從設備回傳一個ACK位;
3.傳輸結束。

1.4 常見的幾種傳輸

主從設備數據的傳輸,是通過讀寫文件描述符的方式展開的,下面是3種讀寫操作:

  1. 主設備向從設備寫數據
    在這裏插入圖片描述

  2. 主設備從從設備中讀數據
    在這裏插入圖片描述

  3. 主設備往從設備中寫數據,然後重啓起始條件,緊接着從從設備中讀取數據;或者是主設備從從設備中讀數據,然後重啓起始條件,緊接着主設備往從設備中寫數據
    在這裏插入圖片描述

二. sht2x溫溼度傳感器模塊

用戶手冊:Users Guide SHT20
SHT20是使用標準I2C接口的溫溼度傳感器,通過sht20的datasheet我們可以瞭解很多信息,例如:
在這裏插入圖片描述
1111 1110是16進制的0xFE,當使用這個模塊前,需要給從設備寫入這個十六進制數,從而在不重啓電源的情況下讓sht20傳感器重啓並初始化各項指標。
又如:
在這裏插入圖片描述
向從設備發送 1110 0011 表示溫度測量命令,1110 0101 表示溼度測量命令,前面的1110表示主機模式,而我們應該將命令改爲:1111 0011 與 1111 0101 代表非主機模式
只要區別是主機模式時,SCL線被封鎖,有傳感器進行控制,非主機模式的SCL線處於開放狀態,可進行其他通信.
詳細請參考sht20用戶手冊。

三. 基於樹莓派的溫溼度傳感器的應用

3.1 使能i2c驅動

sudo raspi-config 

在這裏插入圖片描述
在這裏插入圖片描述
重啓樹莓派才能生效

3.2 接線

在這裏插入圖片描述
前面提到,I2C總線上的每個設備都有自己的地址,用下面命令可查看地址:

pi@raspberrypi:~ $  sudo i2cdetect -y 1 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

可以看到,我的sht20傳感器位於0x40這個地址上
接下來就是獲取溫溼度的代碼了:

3.3 C代碼

/*********************************************************************************
 *      Copyright:  (C) 2020 Xiao yang IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  sht20.c
 *    Description:  獲取溫溼度 
 *                 
 *        Version:  1.0.0(06/20/2020)
 *         Author:  Lu Xiaoyang <[email protected]>
 *      ChangeLog:  1, Release initial version on "06/20/2020 09:06:49 AM"
 *                 
 ********************************************************************************/
#include <stdio.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <sys/ioctl.h> 
#include <linux/types.h> 
#include <sys/stat.h> 
#include <linux/i2c.h> 
#include <linux/i2c-dev.h> 
#include <stdio.h> 
#include <stdlib.h>
#include <sys/types.h> 
#include <string.h> 
#include <stdint.h> 
#include <time.h> 
#include <errno.h> 
#include <string.h>
#define SOFTRESET                        0xFE 
#define TRIGGER_TEMPERATURE_NO_HOLD      0xF3   //監測溫度
#define TRIGGER_HUMIDITY_NO_HOLD         0xF5   //監測溼度
//#define I2C_API_IOCTL  /* Use I2C userspace driver ioctl API */
 #define I2C_API_RDWR /* Use I2C userspace driver read/write API */
 
static inline void msleep(unsigned long ms); 
static inline void dump_buf(const char *prompt, uint8_t *buf, int size); 
int sht2x_init(void); int sht2x_softreset(int fd); 
int sht2x_get_serialnumber(int fd, uint8_t *serialnumber, int size); 
int sht2x_get_temp_humidity(int fd, float *temp, float *rh); 

int main(int argc, char **argv) 
{        
        int          fd;        
        float        temp;        
        float        rh;        
        uint8_t      serialnumber[8];
        fd = sht2x_init();     
           
        if(fd < 0)        
        {                
                 printf("SHT2x initialize failure\n");                
                 return 1;        
        }
        if( sht2x_softreset(fd) < 0 )        
        {                
                 printf("SHT2x softreset failure\n");                
                 return 2;        
        }
        
        if( sht2x_get_serialnumber(fd, serialnumber, 8) < 0)        
        {
                printf("SHT2x get serial number failure\n");                
                return 3;        
        }
        if( sht2x_get_temp_humidity(fd, &temp, &rh) < 0 )        
        {                
                printf("SHT2x get get temperature and relative humidity failure\n");                
                return 3;        
        }
        
        printf("Temperature=%lf ℃ relative humidity=%lf%\n", temp, rh);
        
        close(fd); 
 }
 
static inline void msleep(unsigned long ms) 
{        
        struct timespec cSleep;        
        unsigned long ulTmp;               
        cSleep.tv_sec = ms / 1000;  
              
        if (cSleep.tv_sec == 0)        
        {                   
                ulTmp = ms * 10000;                
                cSleep.tv_nsec = ulTmp * 100;        
        }           
        else        
        {                   
                cSleep.tv_nsec = 0;        
        }   
        nanosleep(&cSleep, 0); 
}

static inline void dump_buf(const char *prompt, uint8_t *buf, int size) 
{        
        int          i;
        if( !buf )        
        {                
                return ;        
        }
        if( prompt )        
        {                
                printf("%s ", prompt);        
        }
        for(i=0; i<size; i++)        
        {                
                 printf("%02x ", buf[i]);        
        }        
        printf("\n");
        return ; 
}

int sht2x_init(void) 
{        
        int     fd;
        if( (fd=open("/dev/i2c-1", O_RDWR)) < 0)        
        {                
                printf("i2c device open failed: %s\n", strerror(errno));                
                return -1;        
        }
        /* set I2C mode and SHT2x slave address */        
        ioctl(fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
        ioctl(fd, I2C_SLAVE, 0x40); /* 我的sht20設備地址 */
        return fd; 
}

/* 初始化設備 */
int sht2x_softreset(int fd) 
{        
        uint8_t           buf[4];
        if( fd<0 )        
        {                
                printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ ); 
                return -1;        
        }
        /* software reset SHT2x */        
        memset(buf, 0, sizeof(buf));
        buf[0] = SOFTRESET;        
        write(fd, buf, 1);
        msleep(50);
        
        return 0; 
}

int sht2x_get_temp_humidity(int fd, float *temp, float *rh) 
{        
        uint8_t           buf[4];
        if( fd<0 || !temp || !rh )        
        {                
                printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );                   
                return -1;        
        }
        /* send trigger temperature measure command and read the data */        
        memset(buf, 0, sizeof(buf));        
        buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;          
        write(fd, buf, 1);  //發送監測溫度命令
        
        msleep(85); /* datasheet: typ=66, max=85 */
        memset(buf, 0, sizeof(buf));        
        read(fd, buf, 3);  //讀取溫度  
        dump_buf("Temperature sample data: ", buf, 3);        
        *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
        
        /* send trigger humidity measure command and read the data */       
         memset(buf, 0, sizeof(buf));        
         buf[0] = TRIGGER_HUMIDITY_NO_HOLD;        
         write(fd, buf, 1);  //發送溼度監測命令
         msleep(29); /* datasheet: typ=22, max=29 */        
         memset(buf, 0, sizeof(buf));
         read(fd, buf, 3);        
         dump_buf("Relative humidity sample data: ", buf, 3);        
         *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
         
        return 0; 
}

3.4 運行實現

在這裏插入圖片描述
sht20溫溼度傳感器獲取數據的方式與ds18b20溫度傳感器有着很大的差別,一個是通過I2C協議通訊的到相關信息,而後者則是通過一線協議在文件中讀取,較爲簡單。

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