STM32:AHT20温湿度传感器驱动程序开发

注:温湿度传感器AHT20数据手册.pdf

http://www.aosong.com/userfiles/files/AHT20%E4%BA%A7%E5%93%81%E8%A7%84%E6%A0%BC%E4%B9%A6(%E4%B8%AD%E6%96%87%E7%89%88)%20B1.pdf

一、分析AHT数据手册文档

(1).准备工作

1.新建工程。配置UART2

2.配置I2C1为I2C标准模式,并开启中断和DMA设置

3.设置工程参数为每个外设初始化生成头文件和源文件,而后生成代码。

(2).根据数据手册,编写AHT20驱动

数据手册中的传感器读取流程如下:

1.上电后要等待40ms,读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通
过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xBE命令(初始化),此命令参数 有两个字节, 第一个字节为0x08,第二个字节为0x00。
2.直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00。
3.等待75ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。
4.计算温湿度值
1.第一条的意思是,开机后,要等待40ms才能够与AHT20通信。因此在AHT20建立通信前要等待40ms。而后0x71地址实际上AHT20作为IIC从机的地址。按照AHT20手册,在启动传输后,随后传输的 II C首字节包括 7位的 I I C设备地址0x38。因为IIC通信一般使用7为地址码,但是读写数据都是一个字节一个字节的读写。0x38的七位二进制为0111000。规定从机地址要左一位。多出来的第八位就是读写位。IIC协议规定,如果主机发起通信的目的是为了写从机,那么读写位是0,此时AHT20的地址是01110000,即0x70.如果主机发起通信的目的是为了读从机传入的数据,那么读写位就是1。此时AHT20的地址是0x71。对于第8位的读写设置,HAL库已经帮我们封装好了,所以不用特意去操作。用户只当作AHT20的地址是0x70就行。
2.直接发送信息,略
3.等待75ms后,读取6个字节数据,里面包含了状态信息,湿度信息,和温度信息。其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。而后,湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。
4.解析完温度、湿度数据后,进行计算

(3),关键代码

aht.h声明函数, aht.c函数定义如下

#include "aht20.h"
#define AHT20_ADDRESS 0x70
//AHT20初始化
void AHT20_Init(){
	uint8_t readBuffer;
	//1.工作前延迟40ms
	HAL_Delay(40);
	//2.从AHT20收取一个字节,判断第Bit[3]是否为1
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readBuffer, 1, HAL_MAX_DELAY);
	//加上状态位后实际上要判断Bit[4]
	if( (readBuffer & 0x08)== 0x00){
		//如果不为1,要发送0xBE命令(初始化)
		//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
		uint8_t sendBuffer[3] ={0xBE,0x08,0x00};
		HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	}
}

void AHT20_Read(float *O_Temperature,float* O_Humidity){
	//输入触发命令和参数
	uint8_t sendBuffer[3] ={0xAC,0x33,0x00};
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	//等待75ms测量完成
	HAL_Delay(75);
	//读6个字节
	uint8_t readBuffer[6];
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, readBuffer, 6, HAL_MAX_DELAY);
	//其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。为0则不再工作
	if((readBuffer[0] & 0x80 )==0x00){
		uint32_t tempdata = 0;
		//湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。
		tempdata =((uint32_t)readBuffer[1] << 12 ) + ((uint32_t)readBuffer[2] <<4 ) +((uint32_t)readBuffer[3] >>4 );
		//相对湿度计算
		*O_Humidity = tempdata *1.0f /(1<<20);

		//温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。
		tempdata = (((uint32_t)readBuffer[3] & 0x0F ) <<16 ) +((uint32_t)readBuffer[4] <<8 ) + (uint32_t)readBuffer[5];
		//转化成摄氏度
		*O_Temperature= tempdata*200.0f /(1<<20)-50;
	}


}

main.c 关键函数如下:

/* USER CODE BEGIN Includes */
#include "aht20.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  AHT20_Init();
  char message[50];
  float temperature, humidity;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 AHT20_Read(&temperature, &humidity);
	 sprintf(message,"温度:%.1f °C,湿度: %.1f %% \r\n",temperature,humidity*100);
	 HAL_UART_Transmit(&huart2, message, 50, HAL_MAX_DELAY);
	 HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

注:起初 sprintf(message,"温度:%.1f °C,湿度: %.1f %% \r\n",temperature,humidity*100);会报不支持浮点数输出的错误。

菜单栏Project ->properties解决

二、基于状态机编程实现AHT20的中断程序

        上面一节实现的是AHT20的轮询模式。发送HAL_I2C_Master_Transmit ,接收HAL_I2C_Master_Receive都会阻塞主程序,待完全执行完发送/接收内容时程序才会执行下一步操作。而在中断或DMA模式下,发送和接收消息不会阻塞主程序,那么就有可能发生还没接收完数据就对温度、湿度变量进行计算,造成脏读。
HAL_I2C_Master_Transmit_IT()  // 采用中断模式发送
HAL_I2C_Master_Transmit_DMA()  //采用DMA模式发送

HAL_I2C_Master_Receive_IT()        // 采用中断模式接收

HAL_I2C_Master_Receive_DMA()   // 采用DMA模式接收

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c);  //主机发送完成回调函数

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c); //主机接收完成回调函数

        所谓状态机编程实际上类似与设计模式中的状态模式类型,把AHT20的通信流程拆分开来。每个状态标识分别对应着自己的处理逻辑,并且指明下一个的状态。
        
        改造上述代码。
1).保持AHT20初始化不变
2).拆分温度、湿度发送/接收/计算模块
  • 在状态为0时,发送测温湿度的命令,并将状态值为1。此时要等待DMA或者中断函数处理完成
  • 触发 IIC发送完成回调函数,则表示发送命令完成,将状态置为2
  • 当状态为2时,等待75 ms,让AHT20测温湿度结束。而后发送接收AHT20测温湿度数据的命令,并将状态置为3.
  • 触发 IIC接收完成回调函数,则表示数据接收完成,测试接收到的6字节数据就是温湿度数据。并将状态置为4
  • 当状态为4时,解析接收到的6字节数据,并打印
这样就完成了中断/DMA的测量温湿度数据的案例

三、中断程序主要代码

aht.h声明函数, aht.c函数定义如下
#include <aht20.h>

//AHT20设备地址
static uint8_t AHT20_ADDRESS=0x70;
//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
static uint8_t AHT20InitCmd[3]={0xBE,0x08,0x00} ;
//输入测量触发命令和参数
static uint8_t AHT20MeasureCmd[3]={0xAC,0x33,0x00};
static uint8_t AHT20readBuffer[6];

//AHT20初始化
void AHT20_Init(){
	uint8_t readOneByte;
	//1.工作前延迟40ms
	HAL_Delay(40);
	//2.从AHT20收取一个字节,判断第Bit[3]是否为1
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readOneByte, 1, HAL_MAX_DELAY);
	//加上状态位后实际上要判断Bit[4]
	if( (readOneByte & 0x08)== 0x00){
		//如果不为1,要发送0xBE命令(初始化)
		//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
		HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, AHT20InitCmd, 3, HAL_MAX_DELAY);
	}
}

//发送测量指令
void AHT20_Transmit(){
	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, AHT20MeasureCmd, 3);
}
//接收测量数据到AHT20readBuffer
void AHT20_Receive(){
	HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS, AHT20readBuffer, 6);
}
//解析AHT20readBuffer输出O_Temperature和O_Humidity
void AHT20_Analysis(float *O_Temperature,float* O_Humidity){
	//其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。为0则不再工作
	if((AHT20readBuffer[0] & 0x80 )==0x00){
		uint32_t tempdata = 0;
		//湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。
		tempdata =((uint32_t)AHT20readBuffer[1] << 12 ) + ((uint32_t)AHT20readBuffer[2] <<4 ) +((uint32_t)AHT20readBuffer[3] >>4 );
		//相对湿度计算
		*O_Humidity = tempdata *1.0f /(1<<20);

		//温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。
		tempdata = (((uint32_t)AHT20readBuffer[3] & 0x0F ) <<16 ) +((uint32_t)AHT20readBuffer[4] <<8 ) + (uint32_t)AHT20readBuffer[5];
		//转化成摄氏度
		*O_Temperature= tempdata*200.0f /(1<<20)-50;
	}
}

main.c关键代码

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
//状态:0 初始状态,1正在发送测量指令 2测量指令发送完成  3 IIC读取ANT20数据中 4 读取完成
uint8_t  aht20State =0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
	if(hi2c == &hi2c1){
		aht20State =2;
	}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){
	if(hi2c == &hi2c1){
		aht20State =4;
	}
}
/* USER CODE END 0 */



int main(void)
{

  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  AHT20_Init();
  char message[50];
  float temperature, humidity;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 if(aht20State == 0){
		 //初始状态
		 //测量数据
		 AHT20_Transmit();
		 aht20State=1;
	 }else if(aht20State == 2){
		HAL_Delay(75);
		//读取数据
		AHT20_Receive();
		aht20State=3;
	 }else if(aht20State == 4){
		//AHT20_
		 AHT20_Analysis(&temperature, &humidity);
		 sprintf(message,"温度:%.1f °C,湿度: %.1f %% ",temperature,humidity*100);
		 HAL_UART_Transmit(&huart2, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
		 HAL_Delay(1000);
		 aht20State= 0;
	 }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

查看效果:波特律动 串口助手

        

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据抽取+dataworks的使用+ADB的应用

一&#xff0c;大数据处理之数据抽取 1&#xff0c;什么是数据抽取 在大数据领域中&#xff0c;数据抽取是指从原始数据源中提取所需的数据子集或特定数据项的过程&#xff0c; 数据抽取是数据预处理的重要步骤&#xff0c;它为后续的数据分析和建模提供了基础。 2&#xff…

ESP32S3入手体验测试

ESP32S3入手体验测试 &#x1f516;所入手的型号是YD-ESP32-S3 N16R8,该款和乐鑫官方推出的ESP32-S3-DevKitC-1配置差不多。 &#x1f388;乐鑫官方介绍&#xff1a;ESP32-S3-DevKitC-1 v1.1 &#x1f530;两者采用的模组&#xff1a;ESP32-S3-WROOM-1 和ESP32-S3-WROOM-1U模组…

Postgresql在linux环境下以源码方式安装

linux环境下源码方式的安装 1.下载安装包&#xff08;源码安装方式&#xff09; 安装包下载 https://www.postgresql.org/ftp/source/ 2.安装postgresql ① 创建安装目录 mkdir /opt/pgsql12② 解压下载的安装包 cd /opt/pgsql12 tar -zxvf postgresql-12.16.tar.gz ③编…

代理模式(静态代理、JDK代理、CGLIB代理)

简介 代理模式有三种不同的形式&#xff1a;静态代理、动态代理&#xff08;JDK代理、接口代理&#xff09;、CGLIB代理 目标&#xff1a;在不修改目标对象的前提下&#xff0c;对目标对象进行扩展。 静态代理 需要定义接口或父类对象&#xff0c;被代理对象和代理对象通过实…

【RabbitMQ】 RabbitMQ 消息的延迟 —— 深入探索 RabbitMQ 的死信交换机,消息的 TTL 以及延迟队列

文章目录 一、死信交换机1.1 什么是死信和死信交换机1.2 死信交换机和死信队列的创建方式 二、消息的 TTL2.1 什么是消息的 TTL2.2 基于死信交换机和 TTL 实现消息的延迟 三、基于 DelayExchang 插件实现延迟队列3.1 安装 DelayExchang 插件3.2 DelayExchang 实现消息延迟的原理…

【触想智能】工业显示器上市前的检测项目分享

工业显示器在上市前&#xff0c;需要做一项重要的工作&#xff0c;那就是工业显示器出厂前的产品可靠性检测。 工业显示器选择的测试项目相比商用端更为严格&#xff0c;常见的性能测试项目包括高温老化、防尘防水、电磁静电干扰、防摔防撞等&#xff0c;在工业级应用领域&…

吴恩达《机器学习》4-6->4-7:正规方程

一、正规方程基本思想 正规方程是一种通过数学推导来求解线性回归参数的方法&#xff0c;它通过最小化代价函数来找到最优参数。 代价函数 J(θ) 用于度量模型预测值与实际值之间的误差&#xff0c;通常采用均方误差。 二、步骤 准备数据集&#xff0c;包括特征矩阵 X 和目标…

unity中移动方案--物理渲染分层

一、三种基本移动方案 unity中的移动分为Transform和Rigidbody以及CharacterController&#xff0c;其中CharacterController功能完善&#xff0c;已经可以避免了穿墙&#xff0c;并实现了贴墙走等情况&#xff0c;需要结合性能考虑选择不同的方式。 1.使用transform,直接修改…

Winform 实现俄罗斯方块游戏(一)

第一步&#xff0c;先用GDI绘制小正方形方块&#xff0c;其它形状的用这个方块合成 如何绘制一个方块&#xff1f;先绘制两个正方形&#xff0c;如下&#xff1a; 然后四周用梯形填充&#xff0c;内部颜色用渐变&#xff0c;这样更有立体感&#xff0c;下篇介绍如何实现。

[iOS开发]iOS中TabBar中间按钮凸起的实现

在日常使用app的过程中&#xff0c;经常能看到人家实现了底部分栏控制器的中间按钮凸起的效果&#xff0c;那么这是怎么实现的呢&#xff1f; 效果演示&#xff1a; 实现原理&#xff1a; 创建按钮 创建一个UITabBar的子类&#xff0c;重写它的layoutSubviews方法&#xff1…

安全与HTTP协议:为何明文传输数据成为争议焦点?

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 一、H…

线性代数 第五章 特征值与特征向量

一、特征值定义 二、特征值求法 定义法&#xff1b;&#xff1b;相似。 三、特征向量求法 定义法&#xff1b;基础解系法&#xff1b;&#xff1b;相似。 四、特征值性质 不同特征值的特征向量线性无关k重特征值至多有k个线性无关的特征向量 五、相似的定义 若&#xff…

0003Java安卓程序设计-springboot基于Android的学习生活交流APP

文章目录 **摘** **要**目 录系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 网络的广泛应用给生活带来了十分的便利。所以把学习生活交流管理与现在网络相结合&#xff0c;利用java技术建设学习生活交流APP&…

【深蓝学院】手写VIO第8章--相机与IMU时间戳同步--作业

0. 题目 1. T1 逆深度参数化时的特征匀速模型的重投影误差 参考常鑫助教的答案&#xff1a;思路是将i时刻的观测投到world系&#xff0c;再用j时刻pose和外参投到j时刻camera坐标系下&#xff0c;归一化得到预测的二维坐标&#xff08;这里忽略了camera的内参&#xff0c;逆深…

Rust语言和curl库编写程序

这是一个使用Rust语言和curl库编写的爬虫程序&#xff0c;用于爬取视频。 use std::env; use std::net::TcpStream; use std::io::{BufReader, BufWriter}; ​ fn main() {// 获取命令行参数let args: Vec<String> env::args().collect();let proxy_host args[1].clon…

Scala语言用Selenium库写一个爬虫模版

首先&#xff0c;我将使用Scala编写一个使用Selenium库下载yuanfudao内容的下载器程序。 然后我们需要在项目的build.sbt文件中添加selenium的依赖项。以下是添加Selenium依赖项的代码&#xff1a; libraryDependencies "org.openqa.selenium" % "selenium-ja…

Python:PDF转长图像和分页图像

简介&#xff1a;随着电子化文档的普及&#xff0c;PDF文件的使用频率越来越高。有时我们需要将PDF中的内容转化为图片格式进行分享或编辑&#xff0c;那么如何才能轻松地完成此任务呢&#xff1f;本文将为你展示一个Python工具&#xff1a;如何将PDF文件转化为图片&#xff0c…

Android ConstraintLayout分组堆叠圆角ShapeableImageView

Android ConstraintLayout分组堆叠圆角ShapeableImageView <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"…

零代码复现-TCGA联合GEO免疫基因结合代谢基因生信套路(三)

前面的分析中&#xff0c;整理好的关键基因集表达谱矩阵&#xff0c;接下来就准备分子亚型的相关分析。 六、一致性聚类构建分子亚型 在6.TCGA和GEO差异基因获取和预后数据的整理\TCGA文件中获取文件 准备一个生存数据和表达谱矩阵&#xff0c;这里需要注意的是&#xff0c;…

【10套模拟】【1】

关键字&#xff1a; 快排空间复杂度、算法目标、广义表与树、后缀表达式、AOV网、完全图、子表