简介
esp32 使用硬件I2C读取温湿度传感器SHT30,例程基于EDP-IDF-4.4.X 的I2C Simple Example 例程修改
工程创建
- 打开 VSCODE ,通过 查看-- 命令面板(快捷键Ctrl+Shift+P),打开 ESP-IDF 的例程后,选择 i2c_simple 例程,点击 Create project using example i2c_simple,选择自己要存储的目录。【PS:工程的目录不要有中文路径】
- 打开刚刚创建的 i2c_simple 例程,该例程是使用 I2C 读取三轴加速度传感器 MPU9250,我们在这个基础上开始修改 ,首先删除 MPU9250 相关的函数,具体如下:
3. 工程添加 SHT3X 温湿度传感器的驱动
创建components/sht3x/src、components/sht3x/include 这两个文件夹,将 i2c_sht3x.c 和i2c_sht3x.h 分别放入 src 和 include 文件夹中;复制CMakeLists.txt、component.mk到components/sht3x下。工程目录结构如下:
- i2c_sht3x.c
#include "i2c_sht3x.h"
#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 */
/*
1. 在使用SHT3X传感器的驱动之前,需要先调用
esp_err_t i2c_driver_install(i2c_port_t i2c_num, i2c_mode_t mode, size_t slv_rx_buf_len, size_t slv_tx_buf_len,int intr_alloc_flags)
初始化I2C
2. 在 i2c_sht3x.h 中根据实际使用的I2C总线修改宏定义 SHT3X_I2C_BUS 的值
*/
/**
* @brief I2Cx-写从设备的寄存器值
* - 带有写器件寄存器的方式,适用于 MPU6050、ADXL345、HMC5983、MS5611、BMP280等绝大多数I2C设备
* - 例:i2c_master_write_slave_reg(I2C_NUM_0, 0x68, 0x75, &test, 1, 100 / portTICK_RATE_MS);
*
* ____________________________________________________________________________________
* | start | slave_addr + wr_bit + ack | reg_addr + ack | write n bytes + ack | stop |
* --------|---------------------------|----------------|----------------------|------|
*
* @param i2c_num I2C端口号。I2C_NUM_0 / I2C_NUM_1
* @param slave_addr I2C写从机的器件地址
* @param reg_addr I2C写从机的寄存器地址
* @param data_wr 写入的值的指针,存放写入进的数据
* @param size 写入的寄存器数目
* @param ticks_to_wait 超时等待时间
*
* @return
* - esp_err_t
*/
esp_err_t i2c_master_write_slave_reg(i2c_port_t i2c_num, uint8_t slave_addr, uint8_t reg_addr, uint8_t *data_wr, size_t size, TickType_t ticks_to_wait)
{
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
i2c_cmd_link_delete(cmd);
return ret;
}
/**
* @brief I2Cx-读从设备的寄存器值(寄存器地址 或 命令 为2字节的器件)
* - 带有读器件寄存器的方式,适用于 SHT20、GT911 这种寄存器地址为16位的I2C设备
* - 例:i2c_master_read_slave_reg_16bit(I2C_NUM_0, 0x44, 0xE000, &test, 6, 100 / portTICK_RATE_MS);
*
* ____________________________________________________________________________________________________________________________________________________
* | start | slave_addr + rd_bit + ack | reg_addr(2byte) + ack | start | slave_addr + wr_bit + ack | read n-1 bytes + ack | read 1 byte + nack | stop |
* --------|---------------------------|-------------------------------|---------------------------|----------------------|--------------------|------|
*
* @param i2c_num I2C端口号。I2C_NUM_0 / I2C_NUM_1
* @param slave_addr I2C读从机的器件地址
* @param reg_addr I2C读从机的寄存器地址(2byte)
* @param data_rd 读出的值的指针,存放读取出的数据
* @param size 读取的寄存器数目
* @param ticks_to_wait 超时等待时间
*
* @return
* - esp_err_t
*/
esp_err_t i2c_master_read_slave_reg_16bit(i2c_port_t i2c_num, uint8_t slave_addr, uint16_t reg_addr, uint8_t *data_rd, size_t size, TickType_t ticks_to_wait)
{
if (size == 0) {
return ESP_OK;
}
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_addr>>8, ACK_CHECK_EN);
i2c_master_write_byte(cmd, reg_addr, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (slave_addr << 1) | I2C_MASTER_READ, ACK_CHECK_EN);
if (size > 1) {
i2c_master_read(cmd, data_rd, size - 1, ACK_VAL);
}
i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, ticks_to_wait);
i2c_cmd_link_delete(cmd);
return ret;
}
/**
* @brief 向SHT3x发送一条指令(16bit)
*
* @param cmd —— SHT3x指令(在SHT3x_MODE中枚举定义)
*
* @retval 成功返回HAL_OK(ESP_OK)
*/
static uint8_t SHT3X_Send_Cmd(SHT30_CMD cmd)
{
uint8_t cmd_buffer[2];
cmd_buffer[0] = cmd >> 8;
cmd_buffer[1] = cmd;
return i2c_master_write_slave_reg(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, cmd_buffer[0], cmd_buffer+1, 1, SHT3X_TICKS_TO_WAIT);
}
/**
* @brief 复位SHT3X
*
* @param none
*
* @retval none
*/
void sht3x_reset(void)
{
SHT3X_Send_Cmd(SOFT_RESET_CMD);
vTaskDelay(20 / portTICK_PERIOD_MS);
}
/**
* @brief 初始化SHT30
*
* @param none
*
* @retval 成功返回HAL_OK(ESP_OK)
*
* @note 周期测量模式
*/
esp_err_t sht3x_init(void)
{
return SHT3X_Send_Cmd(MEDIUM_2_CMD);
}
/**
* @brief 从SHT3X读取一次数据
*
* @param dat —— 存储读取数据的地址(6个字节数组)
*
* @retval 成功 —— 返回HAL_OK(ESP_OK)
*/
esp_err_t sht3x_read_th_raw_dat(uint8_t* dat)
{
return i2c_master_read_slave_reg_16bit(SHT3X_I2C_BUS, SHT3X_SLAVE_ADDRESS, READOUT_FOR_PERIODIC_MODE, dat, 6, SHT3X_TICKS_TO_WAIT);
}
#define CRC8_POLYNOMIAL 0x31
static uint8_t SHT3X_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;
}
/**
* @brief 将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
*
* @param dat —— 存储接收数据的地址(6个字节数组)
*
* @retval 校验成功 —— 返回0
* 校验失败 —— 返回1,并设置温度值和湿度值为0
*/
uint8_t sht3x_dat2float(uint8_t* const dat, float* temperature, float* humidity)
{
uint16_t recv_temperature = 0;
uint16_t recv_humidity = 0;
/* 校验温度数据和湿度数据是否接收正确 */
if(SHT3X_CheckCrc8(dat, 0xFF) != dat[2] || SHT3X_CheckCrc8(&dat[3], 0xFF) != dat[5])
return 1;
/* 转换温度数据 */
recv_temperature = ((uint16_t)dat[0]<<8)|dat[1];
*temperature = -45 + 175*((float)recv_temperature/65535);
/* 转换湿度数据 */
recv_humidity = ((uint16_t)dat[3]<<8)|dat[4];
*humidity = 100 * ((float)recv_humidity / 65535);
return 0;
}
- i2c_sht3x.h
#ifndef __I2C_SHT3X_H__
#define __I2C_SHT3X_H__
#include "driver/i2c.h"
#include "esp_log.h"
#define SHT3X_TICKS_TO_WAIT (100 / portTICK_RATE_MS) // I2C读写的超时等待时间
#define SHT3X_I2C_BUS I2C_NUM_0 // SHT30所在的I2C总线
#define SHT3X_SLAVE_ADDRESS 0x44 // SHT30在I2C总线上的从机器件地址
// SHT30命令列表
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,
} SHT30_CMD;
/**
* @brief 复位SHT3X
*
* @param none
*
* @retval none
*/
void sht3x_reset(void);
/**
* @brief 初始化SHT3X
*
* @param none
*
* @retval 成功返回HAL_OK(ESP_OK)
*
* @note 周期测量模式
*/
uint8_t sht3x_init(void);
/**
* @brief 从SHT3X读取一次数据
*
* @param dat —— 存储读取数据的地址(6个字节数组)
*
* @retval 成功 —— 返回HAL_OK(ESP_OK)
*/
uint8_t sht3x_read_dat(uint8_t* dat);
/**
* @brief 将SHT3X接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
*
* @param dat —— 存储接收数据的地址(6个字节数组)
*
* @retval 校验成功 —— 返回0
* 校验失败 —— 返回1,并设置温度值和湿度值为0
*/
uint8_t sht3x_dat2float(uint8_t* const dat, float* temperature, float* humidity);
#endif
- CMakeLists.txt
set(sht3x_srcs "src/i2c_sht3x.c")
idf_component_register(SRCS "${sht3x_srcs}"
INCLUDE_DIRS "include")
- component.mk
#
# Component Makefile
#
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_SRCDIRS := src
- 删除不需要的代码后,我们根据实际使用的 I2C 引脚,对 I2C 进行配置,并且编写读取 sht30 传感器的函数
/* i2c - Simple example
Simple I2C example that shows how to initialize I2C
as well as reading and writing from and to registers for a sensor connected over I2C.
The sensor used in this example is a MPU9250 inertial measurement unit.
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
See README.md file to get detailed usage of this example.
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#include "i2c_sht3x.h"
static const char *TAG = "i2c-sht3x";//"i2c-simple-example";
#define I2C_MASTER_SCL_IO GPIO_NUM_14//CONFIG_I2C_MASTER_SCL /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO GPIO_NUM_4//CONFIG_I2C_MASTER_SDA /*!< GPIO number used for I2C master data */
#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS 1000
/**
* @brief i2c master initialization
*/
static esp_err_t i2c_master_init(void)
{
int i2c_master_port = I2C_MASTER_NUM;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(i2c_master_port, &conf);
return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}
// i2c_sht3x_task 任务。初始化 SHT3x工作于周期测量模式,获取环境温湿度数据
void i2c_sht3x_task(void* arg)
{
// 配置I2C0-主机模式,400K,指定 SCL-14,SDA-4
i2c_master_init();
uint8_t recv_dat[6] = {0};
float temperature = 0.0;
float humidity = 0.0;
ESP_LOGI(TAG, "esp32 sht3x project starting ……");
sht3x_reset(); // 复位SHT3X
if(sht3x_init() == ESP_OK) // 初始化SHT3X(周期测量模式)
ESP_LOGI(TAG, "sht3x init ok.\n");
else
ESP_LOGE(TAG, "sht3x init fail.\n");
vTaskDelay(1000 / portTICK_PERIOD_MS); //延时1s 等待SHT3X传感器内部采样完成
for (;;)
{
if(sht3x_read_th_raw_dat(recv_dat) == ESP_OK) // 从SHT3X读取一次数据(周期测量模式下)
{
// 将SHT3X接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
if(sht3x_dat2float(recv_dat, &temperature, &humidity) == 0)
{
ESP_LOGI(TAG, "temperature = %.2f ℃,humidity = %.2f %%RH", temperature, humidity);
}
else
{
ESP_LOGE(TAG, "crc check fail.\n");
}
}
else
{
ESP_LOGE(TAG, "read data from sht3x fail.\n");
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void app_main(void)
{
// 创建 i2c_sht3x_task 任务。初始化 SHT30工作于周期测量模式,获取环境温湿度数据
xTaskCreate(i2c_sht3x_task, "i2c_sht3x_task", 2048, NULL, 3, NULL);
}
- 点击 VSCODE IDF 插件的 编译,下载,数据监测,即可在终端处查看 SHT30 传感器测量到的温湿度的值
工程下载
可以点击此处下载工程,工程是不需要积分的,随意下载,如果觉得文章对您有帮助,请关注分享!
PS:该工程需要电脑已经安装了 ESP-IDF 才能编译通过