ESP32開發之路(11)— ESP32讀取SHT3X溫溼度傳感器的值

ESP32開發之路(11)— ESP32讀取SHT3X溫溼度傳感器的值

一、前言

我們在之前完成了ESP32的IIC讀取AT24C02:ESP32開發之路(10)— ESP32的I2C通信讀寫AT24C02
接下來我們使用一個IIC通信更復雜一點的器件,SHT3X溫溼度傳感器。

二、SHT3X驅動程序

新建sht3x.c文件,首先設置IIC相關的參數

#define WRITE_BIT 0x00                      /*!< I2C master write */
#define READ_BIT 0x01                       /*!< I2C master read  */

#define ACK_CHECK_EN 0x1                    /*!< I2C master will check ack from slave     */
#define ACK_CHECK_DIS 0x0                   /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0                         /*!< I2C ack value  */
#define NACK_VAL 0x1                        /*!< I2C nack value */

#if 0
#define IIC_CTRL_NUM I2C_NUM_1              /*!< I2C port number */
#define SDA_PIN_NUM 25                      /*!< gpio number for I2C data  */
#define SCL_PIN_NUM 26                      /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0              /*!< I2C port number */
#define SDA_PIN_NUM 18                      /*!< gpio number for I2C data  */
#define SCL_PIN_NUM 19                      /*!< gpio number for I2C clock */
#endif

#define SHT3X_DeviceAddr (0x44<<1)          /* SHT3X的器件地址 */

然後需要對SHT3X的一些命令做定義,將其放入一個枚舉類型:

/* 枚舉SHT3x命令列表 */
typedef enum
{
    /* 軟件復位命令 */
    SOFT_RESET_CMD = 0x30A2,	
    /* 單次測量模式
    命名格式:Repeatability_CS_CMD
    CS: Clock stretching */
    HIGH_ENABLED_CMD    = 0x2C06,
    MEDIUM_ENABLED_CMD  = 0x2C0D,
    LOW_ENABLED_CMD     = 0x2C10,
    HIGH_DISABLED_CMD   = 0x2400,
    MEDIUM_DISABLED_CMD = 0x240B,
    LOW_DISABLED_CMD    = 0x2416,

    /* 週期測量模式
    命名格式:Repeatability_MPS_CMD
    MPS:measurement per second */
    HIGH_0_5_CMD   = 0x2032,
    MEDIUM_0_5_CMD = 0x2024,
    LOW_0_5_CMD    = 0x202F,
    HIGH_1_CMD     = 0x2130,
    MEDIUM_1_CMD   = 0x2126,
    LOW_1_CMD      = 0x212D,
    HIGH_2_CMD     = 0x2236,
    MEDIUM_2_CMD   = 0x2220,
    LOW_2_CMD      = 0x222B,
    HIGH_4_CMD     = 0x2334,
    MEDIUM_4_CMD   = 0x2322,
    LOW_4_CMD      = 0x2329,
    HIGH_10_CMD    = 0x2737,
    MEDIUM_10_CMD  = 0x2721,
    LOW_10_CMD     = 0x272A,
	/* 週期測量模式讀取數據命令 */
	READOUT_FOR_PERIODIC_MODE = 0xE000,
	/* 讀取傳感器編號命令 */
	READ_SERIAL_NUMBER = 0x3780,
} sht3x_cmd_t;

然後編寫發送命令和接收數據函數

/* 描述:向SHT30發送一條16bit指令 
 * 參數cmd:SHT30指令(在SHT30_MODE中枚舉定義)
 * 返回值:成功返回ESP_OK                     */
static esp_err_t SHT3x_Send_Cmd(sht3x_cmd_t sht3x_cmd)
{
    uint8_t cmd_buffer[2];
    cmd_buffer[0] = sht3x_cmd >> 8;
    cmd_buffer[1] = sht3x_cmd;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT3X_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, cmd_buffer[0], ACK_CHECK_EN);
    i2c_master_write_byte(cmd, cmd_buffer[1], ACK_CHECK_EN);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
	
	return ret;
}

/* 描述:從SHT3x讀取數據 
 * 參數data_len:讀取多少個字節數據
 * 參數data_arr:讀取的數據存放在一個數組裏
 * 返回值:讀取成功返回ESP_OK 
*/
static esp_err_t SHT3x_Recv_Data(uint8_t data_len, uint8_t* data_arr)
{
	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT3X_DeviceAddr | READ_BIT, ACK_CHECK_EN);
    if (data_len > 1) {
        i2c_master_read(cmd, data_arr, data_len - 1, ACK_VAL);
    }
    i2c_master_read_byte(cmd, data_arr + data_len - 1, NACK_VAL);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

	return ret;
}

接下來我們嘗試讀取一下傳感器編號

/* 描述:讀取傳感器編號
 * 參數:存儲編號數據的指針
 * 返回值:0-讀取成功,1-讀取失敗 */
uint8_t SHT3x_ReadSerialNumber(uint32_t* serialNumber)
{ 
	uint8_t Num_buf[4] = {0xFF,0xFF,0xFF,0xFF};
	
	SHT3x_Send_Cmd(READ_SERIAL_NUMBER);
	vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延時50ms,有問題時需要適當延長!!!!!!*/
	esp_err_t ret = SHT3x_Recv_Data(4,Num_buf);
	
	*serialNumber = ((Num_buf[0] << 24) | (Num_buf[1] << 16) |(Num_buf[2] << 8) |(Num_buf[3]));
	if(0xFFFFFFFF == *serialNumber) return 1;
    else if(ret == ESP_OK) return 0;
    printf("SHT3x_ReadSerialNumber ERR :%s\n",esp_err_to_name(ret)); 
    return ret;
}

在app_main()中調用,編譯下載運行,可以看到,讀取成功

    /* 初始化IIC控制器 */
    ESP_ERROR_CHECK(i2c_master_init());

    /* 讀取SHT3x的SerialNumber值 */
    if(SHT3x_ReadSerialNumber(&data_buf) == 0)
        printf("Read SerialNumber 0k: 0x%x \n",data_buf);
    else
        printf("Read SerialNumber err \n");

接下來我們編寫一個SHT3X初始化函數,將其設置爲連續週期讀取模式

/* 描述:SHT3x初始化函數,並將其設置爲週期測量模式
 * 參數:無
 * 返回值:初始化成功返回ESP_OK */
esp_err_t sht3x_mode_init(void)
{
    /* 初始化IIC控制器 */
    ESP_ERROR_CHECK(i2c_master_init());
    /* 設置爲週期測量模式 */
    esp_err_t ret = SHT3x_Send_Cmd(MEDIUM_2_CMD);
	return ret;
}

在讀取溫溼度數據之前我們先編寫一個CRC校驗函數:

/* 描述:數據CRC校驗
 * 參數message:需要校驗的數據
 * 參數initial_value:crc初始值
 * 返回值:計算得到的CRC碼 */
#define CRC8_POLYNOMIAL 0x131
uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
    uint8_t  remainder;	    //餘數
    uint8_t  i = 0, j = 0;  //循環變量

    /* 初始化 */
    remainder = initial_value;
    for(j = 0; j < 2;j++)
    {
        remainder ^= message[j];
        /* 從最高位開始依次計算  */
        for (i = 0; i < 8; i++)
        {
            if (remainder & 0x80)
                remainder = (remainder << 1)^CRC8_POLYNOMIAL;
            else
                remainder = (remainder << 1);
        }
    }
    /* 返回計算的CRC碼 */
    return remainder;
}

然後以循環模式讀取溫溼度的值,

/* 描述:溫溼度數據獲取函數,週期讀取,注意,需要提前設置週期模式   
 * 參數Tem_val:存儲溫度數據的指針, 溫度單位爲°C
 * 參數Hum_val:存儲溼度數據的指針, 溫度單位爲%
 * 返回值:0-讀取成功,1-讀取失敗 **********************************/
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val)
{
	uint8_t ret=0;
	uint8_t buff[6];
	uint16_t tem,hum;
	double Temperature=0;
	double Humidity=0;

	ret = SHT3x_Send_Cmd(READOUT_FOR_PERIODIC_MODE);	
	ret = SHT3x_Recv_Data(6,buff);
	
	/* 校驗溫度數據和溼度數據是否接收正確 */
	if(CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5])
	{	
		printf("CRC_ERROR,ret = 0x%x\r\n",ret);
		return 1;
	}
		
	/* 轉換溫度數據 */
	tem = (((uint16_t)buff[0]<<8) | buff[1]);//溫度數據拼接
	Temperature= (175.0*(double)tem/65535.0-45.0) ;	// T = -45 + 175 * tem / (2^16-1)
	
	/* 轉換溼度數據 */
	hum = (((uint16_t)buff[3]<<8) | buff[4]);//溼度數據拼接
	Humidity= (100.0*(double)hum/65535.0);			// RH = hum*100 / (2^16-1)
	
	/* 過濾錯誤數據 */
	if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100))
	{
		*Tem_val = Temperature;
		*Hum_val = Humidity;
		return 0;
	}
	else
		return 1;
}

三、採集數據

在app_main()函數中每隔兩秒調用一次溫溼度採集函數,然後將溫溼度數據打印出來,app_main.c如下:

#include <string.h>
#include <sys/param.h>
#include "driver/i2c.h"

#define GPIO_LED_NUM 2                      /* LED引腳編號 */

esp_err_t sht3x_mode_init(void);
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val);

void app_main(void)
{
    double Tem_val,Hum_val;
    /* 打印Hello world! */
    printf("Hello world!\n");

    ESP_ERROR_CHECK(sht3x_mode_init());

    /* 定義一個gpio配置結構體 初始化LED */
    gpio_config_t gpio_config_structure;

    /* 初始化gpio配置結構體*/
    gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_NUM);/* 選擇gpio2 */
    gpio_config_structure.mode = GPIO_MODE_OUTPUT;              /* 輸出模式 */
    gpio_config_structure.pull_up_en = 0;                       /* 不上拉 */
    gpio_config_structure.pull_down_en = 0;                     /* 不下拉 */
    gpio_config_structure.intr_type = GPIO_PIN_INTR_DISABLE;    /* 禁止中斷 */ 

    /* 根據設定參數初始化並使能 */  
	gpio_config(&gpio_config_structure);
    /* 默認熄滅LED */
    gpio_set_level(GPIO_LED_NUM, 0);        /* 熄滅 */

    while(1)
    {
        /* 採集溫溼度數據 */
        if(sht3x_get_humiture_periodic(&Tem_val,&Hum_val) == 0)
        {
            printf("temperature:%6.2lf °C, humidity:%6.2lf %% \n",Tem_val,Hum_val);
        }
        else
            printf("Get_Humiture ERR\r\n");

        gpio_set_level(GPIO_LED_NUM, 0);        /* 熄滅 */
        vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延時500ms*/
        gpio_set_level(GPIO_LED_NUM, 1);        /* 點亮 */
        vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延時500ms*/
    }
}

編譯燒錄運行,可以看到,讀取成功:
在這裏插入圖片描述

四、代碼

貼上sht3x.c的代碼:

#include <string.h>
#include <sys/param.h>
#include "driver/i2c.h"

#define WRITE_BIT 0x00                      /*!< I2C master write */
#define READ_BIT 0x01                       /*!< I2C master read  */

#define ACK_CHECK_EN 0x1                    /*!< I2C master will check ack from slave     */
#define ACK_CHECK_DIS 0x0                   /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0                         /*!< I2C ack value  */
#define NACK_VAL 0x1                        /*!< I2C nack value */

#if 0
#define IIC_CTRL_NUM I2C_NUM_1              /*!< I2C port number */
#define SDA_PIN_NUM 25                      /*!< gpio number for I2C data  */
#define SCL_PIN_NUM 26                      /*!< gpio number for I2C clock */
#else
#define IIC_CTRL_NUM I2C_NUM_0              /*!< I2C port number */
#define SDA_PIN_NUM 18                      /*!< gpio number for I2C data  */
#define SCL_PIN_NUM 19                      /*!< gpio number for I2C clock */
#endif

#define SHT3X_DeviceAddr (0x44<<1)          /* SHT3X的器件地址 */

/* 枚舉SHT3x命令列表 */
typedef enum
{
    /* 軟件復位命令 */
    SOFT_RESET_CMD = 0x30A2,	
    /* 單次測量模式
    命名格式:Repeatability_CS_CMD
    CS: Clock stretching */
    HIGH_ENABLED_CMD    = 0x2C06,
    MEDIUM_ENABLED_CMD  = 0x2C0D,
    LOW_ENABLED_CMD     = 0x2C10,
    HIGH_DISABLED_CMD   = 0x2400,
    MEDIUM_DISABLED_CMD = 0x240B,
    LOW_DISABLED_CMD    = 0x2416,

    /* 週期測量模式
    命名格式:Repeatability_MPS_CMD
    MPS:measurement per second */
    HIGH_0_5_CMD   = 0x2032,
    MEDIUM_0_5_CMD = 0x2024,
    LOW_0_5_CMD    = 0x202F,
    HIGH_1_CMD     = 0x2130,
    MEDIUM_1_CMD   = 0x2126,
    LOW_1_CMD      = 0x212D,
    HIGH_2_CMD     = 0x2236,
    MEDIUM_2_CMD   = 0x2220,
    LOW_2_CMD      = 0x222B,
    HIGH_4_CMD     = 0x2334,
    MEDIUM_4_CMD   = 0x2322,
    LOW_4_CMD      = 0x2329,
    HIGH_10_CMD    = 0x2737,
    MEDIUM_10_CMD  = 0x2721,
    LOW_10_CMD     = 0x272A,
	/* 週期測量模式讀取數據命令 */
	READOUT_FOR_PERIODIC_MODE = 0xE000,
	/* 讀取傳感器編號命令 */
	READ_SERIAL_NUMBER = 0x3780,
} sht3x_cmd_t;

/**
 * @brief i2c master initialization
 */
static esp_err_t i2c_master_init(void)
{
    int i2c_master_port = IIC_CTRL_NUM;
    i2c_config_t conf;
    conf.mode = I2C_MODE_MASTER;
    conf.sda_io_num = SDA_PIN_NUM;
    conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
    conf.scl_io_num = SCL_PIN_NUM;
    conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
    conf.master.clk_speed = 100000;     /* 標準模式(100 kbit/s) */
    i2c_param_config(i2c_master_port, &conf);
    return i2c_driver_install(i2c_master_port, conf.mode, 0, 0, 0);
}

/* 描述:向SHT30發送一條16bit指令 
 * 參數cmd:SHT30指令(在SHT30_MODE中枚舉定義)
 * 返回值:成功返回ESP_OK                     */
static esp_err_t SHT3x_Send_Cmd(sht3x_cmd_t sht3x_cmd)
{
    uint8_t cmd_buffer[2];
    cmd_buffer[0] = sht3x_cmd >> 8;
    cmd_buffer[1] = sht3x_cmd;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT3X_DeviceAddr | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, cmd_buffer[0], ACK_CHECK_EN);
    i2c_master_write_byte(cmd, cmd_buffer[1], ACK_CHECK_EN);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
	
	return ret;
}

/* 描述:從SHT3x讀取數據 
 * 參數data_len:讀取多少個字節數據
 * 參數data_arr:讀取的數據存放在一個數組裏
 * 返回值:讀取成功返回ESP_OK 
*/
static esp_err_t SHT3x_Recv_Data(size_t data_len, uint8_t* data_arr)
{
	i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT3X_DeviceAddr | READ_BIT, ACK_CHECK_EN);
    if (data_len > 1) {
        i2c_master_read(cmd, data_arr, data_len - 1, ACK_VAL);
    }
    i2c_master_read_byte(cmd, data_arr + data_len - 1, NACK_VAL);
    i2c_master_stop(cmd);
    esp_err_t ret = i2c_master_cmd_begin(IIC_CTRL_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

	return ret;
}

/* 描述:讀取傳感器編號
 * 參數:存儲編號數據的指針
 * 返回值:0-讀取成功,1-讀取失敗 */
uint8_t SHT3x_ReadSerialNumber(uint32_t* serialNumber)
{ 
	uint8_t Num_buf[4] = {0xFF,0xFF,0xFF,0xFF};
	
	SHT3x_Send_Cmd(READ_SERIAL_NUMBER);
	vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延時50ms,有問題時需要適當延長!!!!!!*/
	esp_err_t ret = SHT3x_Recv_Data(4,Num_buf);
	
	*serialNumber = ((Num_buf[0] << 24) | (Num_buf[1] << 16) |(Num_buf[2] << 8) |(Num_buf[3]));
	if(0xFFFFFFFF == *serialNumber) return 1;
    else if(ret == ESP_OK) return 0;
    printf("SHT3x_ReadSerialNumber ERR :%s\n",esp_err_to_name(ret)); 
    return ret;
}

/* 描述:SHT3x初始化函數,並將其設置爲週期測量模式
 * 參數:無
 * 返回值:初始化成功返回ESP_OK */
esp_err_t sht3x_mode_init(void)
{
    /* 初始化IIC控制器 */
    ESP_ERROR_CHECK(i2c_master_init());
    /* 設置爲週期測量模式 */
    esp_err_t ret = SHT3x_Send_Cmd(MEDIUM_2_CMD);
	return ret;
}

/* 描述:數據CRC校驗
 * 參數message:需要校驗的數據
 * 參數initial_value:crc初始值
 * 返回值:計算得到的CRC碼 */
#define CRC8_POLYNOMIAL 0x131
static uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
    uint8_t  remainder;	    //餘數
    uint8_t  i = 0, j = 0;  //循環變量

    /* 初始化 */
    remainder = initial_value;
    for(j = 0; j < 2;j++)
    {
        remainder ^= message[j];
        /* 從最高位開始依次計算  */
        for (i = 0; i < 8; i++)
        {
            if (remainder & 0x80)
                remainder = (remainder << 1)^CRC8_POLYNOMIAL;
            else
                remainder = (remainder << 1);
        }
    }
    /* 返回計算的CRC碼 */
    return remainder;
}

/* 描述:溫溼度數據獲取函數,週期讀取,注意,需要提前設置週期模式   
 * 參數Tem_val:存儲溫度數據的指針, 溫度單位爲°C
 * 參數Hum_val:存儲溼度數據的指針, 溫度單位爲%
 * 返回值:0-讀取成功,1-讀取失敗 **********************************/
uint8_t sht3x_get_humiture_periodic(double *Tem_val,double *Hum_val)
{
	uint8_t ret=0;
	uint8_t buff[6];
	uint16_t tem,hum;
	double Temperature=0;
	double Humidity=0;

	ret = SHT3x_Send_Cmd(READOUT_FOR_PERIODIC_MODE);	
	ret = SHT3x_Recv_Data(6,buff);
	
	/* 校驗溫度數據和溼度數據是否接收正確 */
	if(CheckCrc8(buff, 0xFF) != buff[2] || CheckCrc8(&buff[3], 0xFF) != buff[5])
	{	
		printf("CRC_ERROR,ret = 0x%x\r\n",ret);
		return 1;
	}
		
	/* 轉換溫度數據 */
	tem = (((uint16_t)buff[0]<<8) | buff[1]);//溫度數據拼接
	Temperature= (175.0*(double)tem/65535.0-45.0) ;	// T = -45 + 175 * tem / (2^16-1)
	
	/* 轉換溼度數據 */
	hum = (((uint16_t)buff[3]<<8) | buff[4]);//溼度數據拼接
	Humidity= (100.0*(double)hum/65535.0);			// RH = hum*100 / (2^16-1)
	
	/* 過濾錯誤數據 */
	if((Temperature>=-20)&&(Temperature<=125)&&(Humidity>=0)&&(Humidity<=100))
	{
		*Tem_val = Temperature;
		*Hum_val = Humidity;
		return 0;
	}
	else
		return 1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章