温湿传感器(学习笔记下)

接着我们温湿传感器上半部分的学习,现在我们学习接下来的部分,编写GXHTC3驱动程序,也就是给gxhtc3.c文件添加代码,我们要判断gxhtc3芯片是否存在和正常,就要先读取gxhtc3的ID号,根据gxhtc3的数据手册,读取命令为0xEFC8,发送命令后,可以读出16位的ID号和1个CRC字节。CRC字节用来校验判断读取的数据是否正确。

首先,我们需要先写一个校验代码,如下代码所示,

cs 复制代码
​
#define POLYNOMIAL  0x31 // P(x) = x^8 + x^5 + x^4 + 1 = 00110001,POLYNOMIAL是多项式因子

//CRC校验
uint8_t gxhtc3_calc_crc(uint8_t *crcdata, uint8_t len) //两个参数分别是需要校验的数据和数据长度
{
    uint8_t crc = 0xFF; 
  
    for(uint8_t i = 0; i < len; i++)
    {
        crc ^= (crcdata[i]); //crc初始值是0xFF,crcdata与crc按位与或运算后,把计算后的值赋值给crc,crcdata中位是0的位置会变成1,位是1的位置会变成0,这条语句的作用就是把crcdata的值,1变成0,0变成1,然后赋值给crc
        for(uint8_t j = 8; j > 0; --j) //这里的for循环按位计算,如果位是0,直接左移1位,如果位是1,左移1位后再与多项式按位与或运算
        {
            if(crc & 0x80) crc = (crc << 1) ^ POLYNOMIAL;
            else           crc = (crc << 1);
        }
    }
    return crc; //返回值是计算好的CRC字节(需要和读出的CRC字节做对比,一致的话正确)
}

​

接下来,我们需要写一个读取ID的代码,

cs 复制代码
​
// 读取ID
esp_err_t gxhtc3_read_id(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    esp_err_t ret; // 用于存储函数执行过程中返回的错误码
    uint8_t data[3]; //用于存储从传感器读取的3字节数据(包括ID和CRC校验值)

    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_WRITE, true); //写入I2C设备的地址(0x70),并设置为写模式
    i2c_master_write_byte(cmd, 0xEF, true); //写入命令字节0xEF,用于选择传感器的ID读取命令
    i2c_master_write_byte(cmd, 0xC8, true); //写入命令字节0xC8,可能是用于配置或确认读取操作
    i2c_master_stop(cmd); //发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒
    if (ret != ESP_OK) { //如果执行失败(ret != ESP_OK),则跳转到 end 标签,释放资源并返回错误码
        goto end;
    }
    cmd = i2c_cmd_link_create(); //重新创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_READ, true); //写入I2C设备的地址(0x70),并设置为读模式
    i2c_master_read(cmd, data, 3, I2C_MASTER_LAST_NACK); //读取3字节数据到 data 数组中,最后一个字节读取后发送NACK信号
    i2c_master_stop(cmd); //发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒

    if(data[2]!=gxhtc3_calc_crc(data,2)){   //计算前两个字节的CRC校验值  
        ret = ESP_FAIL; //如果计算的CRC值与读取的CRC值不匹配(data[2]),则将 ret 设置为 ESP_FAIL,表示校验失败
    }
end:
    i2c_cmd_link_delete(cmd); //释放I2C命令链资源

    return ret; //表示函数执行的结
}

​

因为函数中用到了i2c的函数,所以给这个文件也添加一个i2c.h头文件。上面的函数中用到了I2C_MASTER_NUM这个宏定义,这个宏定义是在myi2c.h文件中定义的,所以也需要添加myi2c.h头文件

cs 复制代码
#include "myi2c.h"
#include "driver/i2c.h"

接下来给gxhtc3.h文件中添加读取ID函数的声明

cs 复制代码
extern esp_err_t gxhtc3_read_id(void);

这里用到了esp_err_t类型,所以也需要调用一下对应的头文件

cs 复制代码
#include "esp_err.h"

然后我们在main.c文件中调用这个函看能正确读取ID号

cs 复制代码
void app_main(void)
{
    ESP_ERROR_CHECK(i2c_master_init()); //调用 i2c_master_init() 函数初始化I2C总线,并使用 ESP_ERROR_CHECK 宏检查初始化是否成功。如果初始化失败,程序将停止并打印错误信息
    ESP_LOGI(TAG, "I2C initialized successfully"); //如果I2C初始化成功,使用 ESP_LOGI 宏打印一条信息,表示I2C总线已成功初始化

    esp_err_t ret = gxhtc3_read_id(); //调用 gxhtc3_read_id() 函数读取GXHTC3传感器的ID,并将返回值存储在 ret 变量中
    while(ret != ESP_OK) // 如果读取ID失败(ret != ESP_OK),则进入循环
    {
         ret = gxhtc3_read_id(); //再次尝试读取GXHTC3传感器的ID
         ESP_LOGI(TAG,"GXHTC3 READ ID"); //打印一条信息,表示正在尝试读取GXHTC3传感器的ID
         vTaskDelay(1000 / portTICK_PERIOD_MS); //延迟1000毫秒(1秒),然后再次尝试读取ID
    }
    ESP_LOGI(TAG,"GXHTC3 OK"); //如果成功读取GXHTC3传感器的ID(ret == ESP_OK),打印一条信息,表示读取成功
}

这里使用了vTaskDelay函数,需要在main.c文件中添加freeRTOS相关头文件

cs 复制代码
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

以上完成之后,我们就可以试着编译一下了。

还没有完,接着,我们继续学习编写读取温湿度数据程序,根据学习,我们再给gxhtc3.c文件中添加读取温湿度的相关函数。根据gxhtc3的数据手册上介绍,每一次读取数据,都需要经过四组命令,按照执行顺序,分别是唤醒、测量、读出、休眠,我们分别写这四个命令的函数,首先,我们要写一下唤醒的代码,以下是我写的

cs 复制代码
//唤醒
esp_err_t gxhtc3_wake_up(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    int ret; //用于存储函数执行过程中返回的错误码

    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_WRITE, true); //写入I2C设备的地址(0x70),并设置为写模式 
    i2c_master_write_byte(cmd, 0x35, true); //写入命令字节0x35,用于唤醒传感器
    i2c_master_write_byte(cmd, 0x17, true); //写入命令字节0x17,可能是用于配置或确认唤醒操作
    i2c_master_stop(cmd); //发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒,执行结果存储在 ret 变量中

    i2c_cmd_link_delete(cmd); //释放I2C命令链资源
    return ret; //表示函数执行的结果
}

接着,我们在写一下测量部分的函数

cs 复制代码
// 测量
esp_err_t gxhtc3_measure(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    int ret; //用于存储函数执行过程中返回的错误码

    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_WRITE, true); //写入I2C设备的地址(0x70),并设置为写模式
    i2c_master_write_byte(cmd, 0x7c, true); //写入命令字节0x7c,用于触发传感器进行测量
    i2c_master_write_byte(cmd, 0xa2, true); //写入命令字节0xa2,可能是用于配置或确认测量操作
    i2c_master_stop(cmd); // 发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒

    i2c_cmd_link_delete(cmd); // 释放I2C命令链资源
    return ret; //表示函数执行的结果
}

接着,我们写一下读取温湿数据部分的代码

cs 复制代码
// 读出温湿度数据
esp_err_t gxhtc3_read_tah(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    int ret; //用于存储函数执行过程中返回的错误码

    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_READ, true); //写入I2C设备的地址(0x70),并设置为读模式
    i2c_master_read(cmd, tah_data, 6, I2C_MASTER_LAST_NACK); //从传感器读取6字节数据到 tah_data 数组中,最后一个字节读取后发送NACK信号
    i2c_master_stop(cmd); //发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒

    i2c_cmd_link_delete(cmd); //释放I2C命令链资源
    return ret; //表示函数执行的结果
}

以上是读出温湿度数据的函数。读到的数据字节放到tah_data这个数组里面,需要在gxhtc3.c文件的include下面定义tah_data数组。读出函数需要跟在测量函数后使用,一次读取6个字节,分别是2个温度数据+1个温度CRC字节+2个湿度数据+1个湿度CRC字节

接着,我们写休眠部分的函数

cs 复制代码
// 休眠
esp_err_t gxhtc3_sleep(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    int ret; //用于存储函数执行过程中返回的错误码

    i2c_cmd_handle_t cmd = i2c_cmd_link_create(); //创建一个新的I2C命令链
    i2c_master_start(cmd); //发送I2C起始信号
    i2c_master_write_byte(cmd, 0x70 << 1 | I2C_MASTER_WRITE, true); //写入I2C设备的地址(0x70),并设置为写模式
    i2c_master_write_byte(cmd, 0xB0, true); //写入命令字节0xB0,用于使传感器进入休眠模式
    i2c_master_write_byte(cmd, 0x98, true); //写入命令字节0x98,可能是用于配置或确认休眠操作
    i2c_master_stop(cmd); //发送I2C停止信号

    ret = i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS); //执行I2C命令链,超时时间为1000毫秒

    i2c_cmd_link_delete(cmd); //执行结果存储在 ret 变量中
    return ret; //表示函数执行的结果
}

接下来,我们再写一个函数,把上面的4个命令函数使用上,获取温湿度

cs 复制代码
uint16_t rawValueTemp, rawValueHumi; //rawValueTemp: 用于存储从传感器读取的原始温度数据。

 // rawValueHumi: 用于存储从传感器读取的原始湿度数据
float temp=0, humi=0; 
uint8_t temp_int, humi_int;
cs 复制代码
// 获取并计算温湿度数据
esp_err_t gxhtc3_get_tah(void) //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功
{
    int ret; //用于存储函数执行过程中返回的错误码

    gxhtc3_wake_up(); //调用 gxhtc3_wake_up() 函数唤醒传感器,使其进入工作状态
    gxhtc3_measure(); //调用 gxhtc3_measure() 函数触发传感器进行温湿度测量
    vTaskDelay(20 / portTICK_PERIOD_MS); //延迟20毫秒,等待传感器完成测量
    gxhtc3_read_tah(); //调用 gxhtc3_read_tah() 函数从传感器读取温湿度数据,并存储在 tah_data 数组中
    gxhtc3_sleep(); //调用 gxhtc3_sleep() 函数使传感器进入休眠模式,以节省功耗

    if((tah_data[2]!=gxhtc3_calc_crc(tah_data,2)|| //gxhtc3_calc_crc(tah_data, 2): 计算前两个字节的CRC校验值
(tah_data[5]!=gxhtc3_calc_crc(&tah_data[3],2)))){      //gxhtc3_calc_crc(&tah_data[3], 2): 计算后两个字节的CRC校验值
        temp = 0; //如果CRC校验失败,将温度和湿度值设为0,并将 ret 设置为 ESP_FAIL
        humi = 0;
        temp_int = 0;
        humi_int = 0;
        ret = ESP_FAIL;
    }
    else{
        rawValueTemp = (tah_data[0]<<8) | tah_data[1]; // 将前两个字节合并为一个16位整数,表示原始温度数据
        rawValueHumi = (tah_data[3]<<8) | tah_data[4]; //将后两个字节合并为一个16位整数,表示原始湿度数据
        temp = (175.0 * (float)rawValueTemp) / 65535.0 - 45.0; //将原始温度数据转换为实际温度值(单位:摄氏度)
        humi = (100.0 * (float)rawValueHumi) / 65535.0; //将原始湿度数据转换为实际湿度值(单位:百分比)
        temp_int = round(temp); //将温度值四舍五入为整数
        humi_int = round(humi);
        ret = ESP_OK;
    }
    return ret;
}

转换整形数据的时候,用到了round函数,需要添加math.h头文件

cs 复制代码
#include <math.h>

然后我们在gxhtc3.h文件中,添加gxhtc3_get_tah函数声明,因为接下来要在main.c文件中调用

cs 复制代码
extern esp_err_t gxhtc3_get_tah(void);

因为用到了esp_err_t类型,所以还需要添加esp_err.h头文件

cs 复制代码
#include "esp_err.h"

现在打开main.c文件,在app_main函数中的while循环读取ID的下面,创建一个gxhtc3_task任务

cs 复制代码
xTaskCreate(gxhtc3_task, "gxhtc3_task", 4096, NULL, 6, NULL);

然后编写这个任务函数

cs 复制代码
static void gxhtc3_task(void *args) //该函数接受一个 void * 类型的参数 args,通常用于传递任务参数
{
    esp_err_t ret; //用于存储 gxhtc3_get_tah() 函数返回的错误码

    while(1)
    {
        ret = gxhtc3_get_tah(); //调用 gxhtc3_get_tah() 函数获取温湿度数据,并将返回值存储在 ret 变量中
        if (ret!=ESP_OK) {
            ESP_LOGE(TAG,"GXHTC3 READ TAH ERROR."); //如果 gxhtc3_get_tah() 返回的错误码不是 ESP_OK,则使用 ESP_LOGE 宏打印错误信息,表示读取温湿度数据失败
        }
        else{
            ESP_LOGI(TAG, "TEMP:%.1f HUMI:%.1f", temp, humi); //如果 gxhtc3_get_tah() 返回成功(ESP_OK),则使用 ESP_LOGI 宏打印温湿度数据
            ESP_LOGI(TAG, "TEMP:%d HUMI:%d", temp_int, humi_int); //打印整数格式的温度和湿度值
        }
        vTaskDelay(1000 / portTICK_PERIOD_MS); //调用 vTaskDelay 函数延迟1000毫秒(1秒),然后再次执行循环
    }
}

这其中用到了temp humi temp_int humi_int这几个变量,所以我们需要在函数前面声明一下来自外部文件

cs 复制代码
extern float temp,humi;
extern uint8_t temp_int,humi_int;

接下来,我们在主函数中调用这个任务函数

cs 复制代码
void app_main(void)
{
    ESP_ERROR_CHECK(i2c_master_init()); //调用 i2c_master_init() 函数初始化I2C总线,并使用 ESP_ERROR_CHECK 宏检查初始化是否成功。如果初始化失败,程序将停止并打印错误信息
    ESP_LOGI(TAG, "I2C initialized successfully"); //如果I2C初始化成功,使用 ESP_LOGI 宏打印一条信息,表示I2C总线已成功初始化

    esp_err_t ret = gxhtc3_read_id(); //调用 gxhtc3_read_id() 函数读取GXHTC3传感器的ID,并将返回值存储在 ret 变量中
    while(ret != ESP_OK) //如果读取ID失败(ret != ESP_OK),则进入循环
    {
         ret = gxhtc3_read_id(); //再次尝试读取GXHTC3传感器的ID
         ESP_LOGI(TAG,"GXHTC3 READ ID"); //打印一条信息,表示正在尝试读取GXHTC3传感器的ID
         vTaskDelay(1000 / portTICK_PERIOD_MS); //延迟1000毫秒(1秒),然后再次尝试读取ID
    }
    ESP_LOGI(TAG,"GXHTC3 OK"); // 如果成功读取GXHTC3传感器的ID(ret == ESP_OK),打印一条信息,表示读取成功

    xTaskCreate(gxhtc3_task, "gxhtc3_task", 4096, NULL, 6, NULL); // 创建并启动一个任务,用于持续读取GXHTC3传感器的温湿度数据
}

最后,我们就可以进行编译了。

相关推荐
刘婉晴1 分钟前
【信息安全工程师备考笔记】第三章 密码学基本理论
笔记·安全·密码学
球求了19 分钟前
C++:继承机制详解
开发语言·c++·学习
时光追逐者1 小时前
MongoDB从入门到实战之MongoDB快速入门(附带学习路线图)
数据库·学习·mongodb
一弓虽1 小时前
SpringBoot 学习
java·spring boot·后端·学习
晓数2 小时前
【硬核干货】JetBrains AI Assistant 干货笔记
人工智能·笔记·jetbrains·ai assistant
我的golang之路果然有问题2 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
genggeng不会代码2 小时前
用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)
学习
lwewan2 小时前
26考研——存储系统(3)
c语言·笔记·考研
搞机小能手3 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类
nongcunqq4 小时前
爬虫练习 js 逆向
笔记·爬虫