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

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

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

​
#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,然后赋值给crcfor(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的代码,

​
// 读取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头文件

#include "myi2c.h"
#include "driver/i2c.h"

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

extern esp_err_t gxhtc3_read_id(void);

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

#include "esp_err.h"

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

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传感器的IDESP_LOGI(TAG,"GXHTC3 READ ID"); //打印一条信息,表示正在尝试读取GXHTC3传感器的IDvTaskDelay(1000 / portTICK_PERIOD_MS); //延迟1000毫秒(1秒),然后再次尝试读取ID}ESP_LOGI(TAG,"GXHTC3 OK"); //如果成功读取GXHTC3传感器的ID(ret == ESP_OK),打印一条信息,表示读取成功
}

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

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

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

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

//唤醒
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; //表示函数执行的结果
}

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

// 测量
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; //表示函数执行的结果
}

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

// 读出温湿度数据
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字节

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

// 休眠
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个命令函数使用上,获取温湿度

uint16_t rawValueTemp, rawValueHumi; //rawValueTemp: 用于存储从传感器读取的原始温度数据。// rawValueHumi: 用于存储从传感器读取的原始湿度数据
float temp=0, humi=0; 
uint8_t temp_int, humi_int;
// 获取并计算温湿度数据
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_FAILhumi = 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头文件

#include <math.h>

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

extern esp_err_t gxhtc3_get_tah(void);

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

#include "esp_err.h"

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

xTaskCreate(gxhtc3_task, "gxhtc3_task", 4096, NULL, 6, NULL);

然后编写这个任务函数

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这几个变量,所以我们需要在函数前面声明一下来自外部文件

extern float temp,humi;
extern uint8_t temp_int,humi_int;

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

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传感器的IDESP_LOGI(TAG,"GXHTC3 READ ID"); //打印一条信息,表示正在尝试读取GXHTC3传感器的IDvTaskDelay(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传感器的温湿度数据
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458038.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

[大模型学习推理]资料

https://juejin.cn/post/7353963878541361192 lancedb是个不错的数据库&#xff0c;有很多学习资料 https://github.com/lancedb/vectordb-recipes/tree/main/tutorials/Multi-Head-RAG-from-Scratch 博主讲了很多讲解&#xff0c;可以参考 https://juejin.cn/post/7362789…

js纯操作dom版购物车(实现购物车功能)

代码&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title>&l…

Linux 进程间通信_匿名管道

1.程间通信目的 : 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff08;如…

vue3+vite 部署npm 包

公司需要所以研究了一下怎么部署安装&#xff0c;比较简单 先下载个vue项目 用vue脚手架安装vue3项目 删掉App.vue 中的其它组件 npm create vuelatest 开始写自定义组件 新建一个el-text 组件, name是重点&#xff0c;vue3中也得这么导出name属性 <script> export de…

JAVA-----线程池

目录 1.简单理解一下线程池 2.线程池参数 3.Executors ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 1.简单理解一下线程池 线程池&#xff0c;就是用来存储线程的&#xff0c;然后如果有任务传进线程池…

PortQry下载安装使用教程(超详细),Windows测试UDP端口

《网络安全自学教程》 PortQry是微软官方提供的一款TCP/IP连接「排障工具」&#xff0c;用来「检查」TCP/UDP「端口状态」。 平时检查端口状态&#xff0c;最常用的是telnet&#xff0c;但它是基于TCP协议的&#xff0c;无法检测「UDP端口」&#xff0c;这篇文章教大家如何在W…

Flink-cdc Schema Evolution 详解

Flink-cdc Schema Evolution 详解 github原文 glimpse flink-cdc-3 glimpse 源码基于 ~/project/flink_src/flink-cdc master !4 ❯ git remote -v origin https://github.com/apache/flink-cdc.git (fetch) origin https://github.com/apache/flink-cdc.git (push) ~/p…

介绍一款Java开发的企业接口管理系统和开放平台

介绍一款Java开发的企业接口管理系统和开放平台&#xff0c;YesApi接口管理平台Java版本。该系统基于Java开发&#xff0c;可以帮助企业进行统一接口管理、API接口开放&#xff0c;以及用于PaaS系统和SaaS产品平台的快速二次开发和搭建。 一、系统概述 YesApi接口大师&#x…

ClickHouse 5节点集群安装

ClickHouse 5节点集群安装 在此架构中&#xff0c;配置了五台服务器。其中两个用于托管数据副本。其他三台服务器用于协调数据的复制。在此示例中&#xff0c;我们将创建一个数据库和表&#xff0c;将使用 ReplicatedMergeTree 表引擎在两个数据节点之间复制该数据库和表。 官…

RHCE作业二

1.要求&#xff1a; 配置nginx服务通过ip访问多网站 2. 1关闭防火墙 2创建ip 3配置 4创建文件 5测试

logback 如何将日志输出到文件

如何作 将日志输出到文件需要使用 RollingFileAppender&#xff0c;该 Appender 必须定义 rollingPolicy &#xff0c;另外 rollingPollicy 下必须定义 fileNamePattern 和 encoder <appender name"fileAppender" class"ch.qos.logback.core.rolling.Rollin…

二、Spring的执行流程

文章目录 1. spring的初始化过程1.1 ClassPathXmlApplicationContext的构造方法1.2 refresh方法&#xff08;核心流程&#xff09;1.2.1 prepareRefresh() 方法1.2.2 obtainFreshBeanFactory() 方法1.2.3 prepareBeanFactory() 方法1.2.4 invokeBeanFactoryPostProcessors() 方…

shodan2---清风

注&#xff1a;本文章源于泷羽SEC&#xff0c;如有侵权请联系我&#xff0c;违规必删 学习请认准泷羽SEC学习视频:https://space.bilibili.com/350329294 实验一&#xff1a;search 存在CVE-2019-0708的网络设备 CVE - 2019 - 0708**漏洞&#xff1a;** 该漏洞存在于远程桌面…

解读数字化转型的敏捷架构:从理论到实践的深度分析

在当今数字经济的推动下&#xff0c;企业要在瞬息万变的市场中保持竞争力&#xff0c;数字化转型已经不再是一种选择&#xff0c;而是不可避免的战略需求。然而&#xff0c;企业如何从理论到实践进行有效的转型&#xff0c;尤其是在复杂的技术环境中&#xff0c;如何通过正确的…

来源爬虫程序调研报告

来源爬虫程序调研报告 一、什么是爬虫 爬虫&#xff1a;就是抓取网页数据的程序。从网站某一个页面&#xff08;通常是首页&#xff09;开始&#xff0c;读取网页的内容&#xff0c;找到在网页中的其它链接地址&#xff0c;然后通过这些链接地址寻找下一个网页&#xff0c;这…

中小型门诊管理系统源码,云诊所管理系统源码,前端技术栈:Vue 2 , Vite , Vue Router 3

中小型门诊管理系统源码&#xff0c;云诊所管理系统源码&#xff0c; 前端技术栈&#xff1a;Vue 2 Vite Vue Router 3 Vuex 3 Element Plus Axios TypeScript Quill Election 后端技术栈&#xff1a;Spring Boot MyBatis MyBatis-Plus Spring Security Swagger2 …

使用Python计算相对强弱指数(RSI)进阶

使用Python计算相对强弱指数&#xff08;RSI&#xff09;进阶 废话不多说&#xff0c;直接上主题&#xff1a;> 代码实现 以下是实现RSI计算的完整代码&#xff1a; # 创建一个DataFramedata {DATE: date_list, # 日期CLOSE: close_px_list, # 收盘价格 }df pd.DataF…

基于丑萌气质狗--C#的sqlserver学习

#region 常用取值 查询List<string> isName new List<string> { "第一", "第二", "第三", "第四" }; List<string> result isName.Where(m > m "第三").ToList();MyDBContext myDBnew MyDBContext(…

【数据分享】中国汽车市场年鉴(2013-2023)

数据介绍 在这十年里&#xff0c;中国自主品牌汽车迅速崛起。吉利、长城、比亚迪等品牌不断推出具有竞争力的车型&#xff0c;在国内市场乃至全球市场都占据了一席之地。同时&#xff0c;新能源汽车的发展更是如日中天。随着环保意识的提高和政策的大力支持&#xff0c;电动汽车…