ESP32-C3入门教程 基础篇(四、I2C总线 — 与SHT21温湿度传感器通讯)

简介: 测试第四课,了解ESP32-C3的 I2C 总线使用,与SHT21 温湿度传感器通讯这一课把基础介绍放在前面,先看基本流程,再去修改代码
测试第四课,了解ESP32-C3的 I2C 总线使用,与SHT21 温湿度传感器通讯
这一课把基础介绍放在前面,先看基本流程,再去修改代码

前言

接下来的ESP32-C3 功能测试都是基于自己设计的开发板:

自己画一块ESP32-C3 的开发板(第一次使用立创EDA)(PCB到手)

开发环境是乐鑫官方的 ESP-IDF, 基于VScode插件搭建好的:

ESP32-C3 VScode开发环境搭建(基于乐鑫官方ESP-IDF——Windows和Ubuntu双环境)

在开发板上面,我画了一个 HTU21D I2C温湿度传感器(和 SHT21 pin to pin,驱动也一样),使用的是 GPIO3 (SDA)和GPIO10 (SCL):
在这里插入图片描述

示例程序我们选用i2c_self_test,要注意这个示例的说明,下图中右边介绍这一部分,好好看看:
在这里插入图片描述

1、 ESP32-C3 I2C基础介绍

对于ESP32-C3 I2C的介绍,乐鑫的官网的说明链接如下:

乐鑫官方ESP32-C3 I2C部分说明

国产的芯片终于遇到个中文资料了,呵呵~

ESP32-C3 只有一个 I2C接口,可做主机也可以做从机。

本文的测试以及说明是以 ESP23-C3 作为主机来说明

1.1 I2C初始化

根据官方文档,ESP32-C3 UART使用步骤如下:

  1. 设置参数,使用i2c_config_t结构体可以统一设置:

在这里插入图片描述
例如示例中:
在这里插入图片描述
示例中,最后使用了i2c_param_config配置好I2C的所有参数,除了自己定义的,其他的参数会被配置成 I2C 总线协议规范中定义的默认值。
和 UART 一样,I2C的这些默认值 也可以使用一些函数对某些参数单独进行设置:
在这里插入图片描述

  1. 使用i2c_driver_install函数进行 I2C 设备的初始化,其中包括 端口好、通讯模式,发送接收缓存区, 中断标志:

在这里插入图片描述
以示例中的主机初始化为例:
在这里插入图片描述

1.2 I2C读写

  1. 完成,上述步骤以后,下面就可以发起通讯,在官方文档中的说明有一张流程图:

在这里插入图片描述
流程说明如下:
在这里插入图片描述
通过示例说明一下上述流程:
在这里插入图片描述
最后放一张官方介绍的 I2C 工作的整体流程图:
在这里插入图片描述

2、 I2C 示例测试 — SHT21驱动移植

通过示例工程i2c_self_test创建好工程,通过上面的基础介绍和分析,基本上知道了I2C通讯的步骤和方式了,因为示例代码是与 BH1750 传感器进行的 通讯,所以这里示例代码是无法测试的,直接修改 SHT21 的代码。

2.1 驱动移植修改

因为SHT21驱动函数以前在 STM32上用过,这里就相当于移植过来,先把sht21.csht21.h文件放进来,当然得注意包含关系,宏定义等一些东西:
在这里插入图片描述
既然增加了驱动,所以代码就放在驱这两个文件里面,对于sht21.h 文件,宏定义放在此文件中,函数在sht21.c文件中实现(下面会放源码),在此处声明 :

在这里插入图片描述

sht21.c 中的温湿度读取函数是如何实现的呢, 先来看一张 STH21 读取的时序图,是我在另一篇博文中写好的:
在这里插入图片描述
上图出自:nRF52832学习记录(十一、TWI总线的应用 SHT21程序移植)

这里我就直接上源码 :

#include "sht21.h"

struct {
      sint16 value;
      uint16 raw;
      uint8  crc;
} aTemperature, aHumidity;


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,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
        // .clk_flags = 0,          /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
    };
    esp_err_t err = i2c_param_config(i2c_master_port, &conf);
    if (err != ESP_OK) {
        return err;
    }
    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}


//    fT  = 175.72*u16T/65536.0 - 46.85;
//    fRH = 125.0*u16RH/65536.0 - 6.0;

// -------------------------------------------------------------------
sint16 sht21_calcRH(uint16 u16RH)
{
  sint16 humidityRH;              // variable for result

  u16RH &= ~0x0003;          // clear bits [1..0] (status bits)
  //-- calculate relative humidity [%RH] --

  humidityRH = (sint16)(-600 + (12500*(sint32)u16RH)/65536 ); // RH = -6 + 125 * SRH/2^16
  return humidityRH;                                          // Return RH*100
}

// -------------------------------------------------------------------
sint16 sht21_calcTemperature(uint16 u16T)
{
  sint16 temperature;            // variable for result

  u16T &= ~0x0003;           // clear bits [1..0] (status bits)

  //-- calculate temperature [癈] --
  temperature= (sint16)(-4685 + (17572*(sint32)u16T)/65536); //T = -46.85 + 175.72 * ST/2^16
  return temperature;                                        //return T*100
}


// i2c_cmd_handle_t cmd = i2c_cmd_link_create();
// i2c_master_start(cmd);
// i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | WRITE_BIT, ACK_CHECK_EN);
// i2c_master_write_byte(cmd, BH1750_CMD_START, ACK_CHECK_EN);
// i2c_master_stop(cmd);
// ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
// i2c_cmd_link_delete(cmd);
// if (ret != ESP_OK) {
//     return ret;
// }
// vTaskDelay(30 / portTICK_RATE_MS);
// cmd = i2c_cmd_link_create();
// i2c_master_start(cmd);
// i2c_master_write_byte(cmd, BH1750_SENSOR_ADDR << 1 | READ_BIT, ACK_CHECK_EN);
// i2c_master_read_byte(cmd, data_h, ACK_VAL);
// i2c_master_read_byte(cmd, data_l, NACK_VAL);
// i2c_master_stop(cmd);
// ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_RATE_MS);
// i2c_cmd_link_delete(cmd);

esp_err_t SHT2X_THMeasure(i2c_port_t i2c_num){
        // uint8 u8Ack;
    int ret;
    uint8 t_value[3];
    uint8 h_value[3];

#if (SHT2X_RESOLUTION != 0x00)                                              // only needed if used resolution other than default
    // i2c_start();                                                        // send start sequence (S)
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_WRITE);               // write to slave 0x40
    // u8Ack = (u8Ack<<1)|i2c_write(SHT2X_CMD_RD_REG);                     // request to read from user register  
      
    // i2c_start();                                                          // send start sequence (S)
    // u8Ack = (u8Ack<<1)|i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_READ);     // read from slave 0x40
    // u8UserReg = i2c_read(I2C_NACK);                                       // read user register       
      
    // i2c_start();                                                          // send start sequence (S)  
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_WRITE);               // write to slave 0x40
    // u8Ack = (u8Ack<<1)|i2c_write(SHT2X_CMD_WR_REG);                     // request to write user register
    // u8Ack = (u8Ack<<1)|i2c_write(SHT2X_RESOLUTION | (u8UserReg & ~0x81)); // write new user register data
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT2X_SLAVEADDRESS<<1 | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, SHT2X_CMD_WR_REG, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 500 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK) {
        return ret;
    }
#endif//(SHT2X_RESOLUTION != 0x00)
    
    // --------------------
    // measure temperature
    // --------------------
    // i2c_start();     // send start sequence (S)
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_WRITE);//----IIC???(8?)???7??IIC????0x80(???HT2X_SLAVEADDRESS=0x40)???DATA???(?R:1 ,?WRITE?0) //a write to slave 0x40 
    // u8Ack = (u8Ack<<1)|i2c_write(SHT2X_CMD_MEAS_T);      //----SHT2X_CMD_MEAS_T=0xF3(??????,???)//request to measure temperature          
    // i2c_stop();

    i2c_cmd_handle_t  cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT2X_SLAVEADDRESS<<1 | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, SHT2X_CMD_MEAS_T, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 500 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK) {
        return ret;
    }

    vTaskDelay(SHT2X_TEMP_MEAS_TIME / portTICK_RATE_MS);

    // shortTermSleep(SHT2X_TEMP_MEAS_TIME);  
    // HAL_Delay(SHT2X_TEMP_MEAS_TIME);
    // time_wait(SHT2X_TEMP_MEAS_TIME);
            
    // i2c_start();                 // send start sequence (SR)
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_READ); //----IIC???(8?)???7??IIC????0x80(???HT2X_SLAVEADDRESS=0x40)???DATA???(?R:1 ,?WRITE?0)  // a read from slave 0x40
    
    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT2X_SLAVEADDRESS << 1 | READ_BIT, ACK_CHECK_EN);
    i2c_master_read_byte(cmd, &t_value[0], ACK_VAL);
    i2c_master_read_byte(cmd, &t_value[1], ACK_VAL);
    i2c_master_read_byte(cmd, &t_value[2], NACK_VAL);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 500 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

    // if(u8Ack==I2C_ACK)  {
    //     aTemperature.raw = i2c_read(I2C_ACK)<<8;                        // read hi byte 
    //     aTemperature.raw |= i2c_read(I2C_ACK);                          // read lo byte
    //     aTemperature.crc = i2c_read(I2C_NACK);                          // read check sum and finish transfere
    // }else {
    //     aTemperature.raw = 0;                       
    // }
    // i2c_stop();
    aTemperature.raw = t_value[0]<<8;
    aTemperature.raw |= t_value[1];
    aTemperature.crc = t_value[2]; 

    // -------------------------
// Humidity Measure 
// -------------------------
    // i2c_start();                                                      // send start sequence (S)
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_WRITE);             // a write to slave 0x40               1000 0000
    // u8Ack = (u8Ack<<1)|i2c_write(SHT2X_CMD_MEAS_RH);                  // request to measure humidity F5  1110 0101
      // i2c_stop();
                
        cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT2X_SLAVEADDRESS<<1 | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, SHT2X_CMD_MEAS_RH, ACK_CHECK_EN);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 500 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);
    if (ret != ESP_OK) {
        return ret;
    }
        // shortTermSleep(SHT2X_HUMI_MEAS_TIME);
    // HAL_Delay(SHT2X_HUMI_MEAS_TIME);
    vTaskDelay(SHT2X_HUMI_MEAS_TIME / portTICK_RATE_MS);

        
    // i2c_start();                                                      // send start sequence (SR)
    // u8Ack = i2c_write((SHT2X_SLAVEADDRESS<<1)|I2C_READ);              // read from slave 0x40                  1000 0001

    cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, SHT2X_SLAVEADDRESS << 1 | READ_BIT, ACK_CHECK_EN);
    i2c_master_read_byte(cmd, &h_value[0], ACK_VAL);
    i2c_master_read_byte(cmd, &h_value[1], ACK_VAL);
    i2c_master_read_byte(cmd, &h_value[2], NACK_VAL);
    i2c_master_stop(cmd);
    ret = i2c_master_cmd_begin(i2c_num, cmd, 500 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

    // if(u8Ack==I2C_ACK)  {                                             // timeout
    //     aHumidity.raw = i2c_read(I2C_ACK)<<8;                           // read hi byte 
    //     aHumidity.raw |= i2c_read(I2C_ACK);                             // read lo byte
    //     aHumidity.crc = i2c_read(I2C_NACK);                           // read check sum and finish transfere
    // }else{
    //     aHumidity.raw = 0;
    // }
        // i2c_stop();
      // PowerOff();
    aHumidity.raw = h_value[0] <<8;                           // read hi byte 
    aHumidity.raw |= h_value[1];                             // read lo byte
    aHumidity.crc = h_value[2];                           // read check sum and finish transfere 

                       
    aTemperature.value = sht21_calcTemperature(aTemperature.raw);
    aHumidity.value = sht21_calcRH(aHumidity.raw);      // signed value, temperature = aTemperature.value * 0.01?
    if(aTemperature.crc!= sht21_CRC((uint8*)&aTemperature.raw, 2))  {}
    if(aHumidity.crc!= sht21_CRC((uint8*)&aHumidity.raw, 2))  {}
    if(aTemperature.value>5100) aTemperature.value=5100;              //prevent temperature over-/underflow
    else if(aTemperature.value<0) aTemperature.value=0;
    if(aHumidity.value>10000) aTemperature.value=10000;              //prevent temperature over-/underflow
    else if(aTemperature.value<0) aTemperature.value=0;

    return ret;
}

uint16_t getTemperature() {
  return aTemperature.value;
}

uint16_t getHumidity() {
  return aHumidity.value;
}

//==============================================================================
uint8 sht21_CRC(uint8 value[], uint8 u8Bytes) {
//  CRC
    const uint16 POLYNOMIAL = 0x131;  //P(x)=x^8+x^5+x^4+1 = 100110001
    uint8 crc = 0;    
    uint8 byteCtr;
    uint8 bitCtr;

    //calculates 8-Bit checksum with given polynomial
    for (byteCtr = 0; byteCtr < u8Bytes; ++byteCtr) { 
        crc ^= (value[byteCtr]);
        for (bitCtr = 8; bitCtr > 0; --bitCtr)
        { 
          if (crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;
          else crc = (crc << 1);
        }
    }
    return crc;
}  

2.2 测试

最后只需要在任务中调用SHT2X_THMeasure 函数即可:
在这里插入图片描述
可以看一下整体,i2c_example_main.c文件简单明了多了:
在这里插入图片描述
测试结果如下,正常:

在这里插入图片描述

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
算法 Ubuntu 物联网
ESP32-C3入门教程 网络 篇(二、 Wi-Fi 配网 — Smart_config方式 和 BlueIF方式)
经过上一篇的WiFI入门篇,我们知道了WiFi初始化方式 和学会了WiFi的几种工作方式, 在实际应用中,环境复杂多变,在固件中输入SSID 的方式太不通用了, 所以肯定是需要学习一下如何在不同的环境中联网,就是所谓的配网。 ESP32-C3的配网方式有多种,本文主要说明测试 Smart方式 和 BlueIF方式。
2249 0
ESP32-C3入门教程 网络 篇(二、 Wi-Fi 配网 — Smart_config方式 和 BlueIF方式)
|
传感器 算法 API
ESP-IDF Modbus 主站示例程序
ESP-IDF Modbus 主站示例程序
521 0
|
Ubuntu 测试技术 开发工具
ESP32-C3入门教程 基础篇(一、ADC采样)
经过前面的折腾,设计好了自己的测试开发板 搭建好了开发环境, 然后正式开始进行功能测试了,测试顺序先从简单的开始吧,一步一步来
2146 1
ESP32-C3入门教程 基础篇(一、ADC采样)
|
Java 关系型数据库 MySQL
如何用java的虚拟线程连接数据库
本文介绍了如何使用Java虚拟线程连接数据库,包括设置JDK版本、创建虚拟线程的方法和使用虚拟线程连接MySQL数据库的示例代码。
282 6
如何用java的虚拟线程连接数据库
|
Ubuntu 开发工具 git
ESP32-C3 VScode开发环境搭建(基于ESP-IDF—Windows和Ubuntu双环境)
对于ESP32-C3开发,自己对Arduino环境使用起来很是不习惯,既然乐鑫官方都出对应的环境,还是来试试官方环境
3715 0
ESP32-C3 VScode开发环境搭建(基于ESP-IDF—Windows和Ubuntu双环境)
|
传感器 芯片 内存技术
自己画一块ESP32-C3 的开发板(立创EDA)(PCB到手)
记录一下第一次使用立创EDA第一次设计ESP-C3的开发板
2992 2
自己画一块ESP32-C3 的开发板(立创EDA)(PCB到手)
|
IDE 编译器 Linux
你应该搞懂的 C 语言头文件路径问题
聊聊系统路径位置,绝对路径与相对路径,正斜杠 `/` 与 反斜杠 `\` 使用说明 ...... by 矜辰所致
633 0
你应该搞懂的 C 语言头文件路径问题
|
消息中间件 传感器 Ubuntu
ESP32-C3入门教程 基础篇(六、TIMG 硬件定时器 与 软件定时器)
到了测试第6课,还没有玩过ESP32-C3的基本定时器,虽然FreeRTOS,可以使用软件定时器 但是软件定时器毕竟也有不适用的时候,这个在我FreeRTOS博文中有单独说明。 所以硬件定时器也得熟悉,同时附带简单用一下软件定时器。
1135 0
ESP32-C3入门教程 基础篇(六、TIMG 硬件定时器 与 软件定时器)
|
网络协议 Ubuntu 测试技术
ESP32-C3入门教程 网络 篇(一、 Wi-Fi 使用入门 — 初始化及STA、AP模式)
前面的8节基础课算是把 ESP32-C3 的外设和一些基本功能都测试过, 接下来就要进行无线协议 WIFI 和 蓝牙的功能测试。 这节课我们就从 WIFI 开始,了解 ESP32-C3 的WIFI 功能。
2005 1
ESP32-C3入门教程 网络 篇(一、 Wi-Fi 使用入门 — 初始化及STA、AP模式)
|
传感器 Ubuntu 物联网
ESP32-C3 学习测试 蓝牙 篇(一、认识 ESP-IDF 的蓝牙框架、简单的了解蓝牙协议栈)
在我们前面 ESP32-C3 的教程中,从基本的外设,到wifi,到最后使用MQTT连接云平台完成了一个 简单的项目,我们已经掌握了ESP32-C3 的大部分功能了。 但是作为一款蓝牙芯片,蓝牙的使用是必不可少的,今天我们就开始对 ESP32-C3 蓝牙的使用进行学习测试。 蓝牙部分已经不敢叫教学了,为了搞清楚ESP-IDF的那些示例程序到底是关于什么内容, 因为自己也是边学习边测试花了大量时间补充蓝牙的基本知识,希望小伙伴指出不足之处! ...更新说明,ESP32-C3只支持BLE
3962 2
ESP32-C3 学习测试 蓝牙 篇(一、认识 ESP-IDF 的蓝牙框架、简单的了解蓝牙协议栈)

热门文章

最新文章