[stm32标准库]AHT30温湿度采集代码(温度乱飘不稳定解决)

前置装备-手册下载

数据手册直接上奥松电子官网下载,可能很多年后AHT30都不是官网首推了........至少我现在看csdn没什么正经的aht30代码hhhhh,aht20跟30有一点点细微的差别(aht30多了CRC校验码)。

直接给出官网AHT30地址AHT30温湿度传感器-温湿度传感器-温湿度传感器 温湿度芯片 温湿度变送器模块 气体传感器 流量传感器 广州奥松电子股份有限公司

通讯协议是I2C协议不清楚的去csdn直接搜索学习,或者去b站看视频学不多说。


  • 温度乱飘但湿度稳定分析解决

先说温度乱飘 湿度稳定的问题,主要是数据手册没直接说这里1-8位不是字节的1-8位,是接收的1-8位,新手可能理解错了。

问题出在接收到的第四个字节,因为I2C协议读取数据是高位先行,所以读取到的数据也是先接收高位再低位。所以手册这里的1-8位也是从高位到低位,他的意思是手册的第一位其实是字节的第八位。手册上1-4位是接收到的字节的5-8位。

综上所属,这里就是将湿度的最低四位跟温度的最高四位搞反了,湿度的最低四位很灵敏,不停的跳变,给到温度的最高四位就表现为数据乱飘,温度的最高四位很稳定给到湿度最低四位就是很稳定不会跳变。

只要将这两部分拼回属于自己的部分就解决问题了。


  • 完整代码(软件I2C,MYI2C源自恩师江科大,OLED显示代码自己有部分更改)

最后解算出的数据我封装在一个结构体里面了,其他文件用数据,记得加上

extern AHT30_Member AHT30;

AHT30.h

#ifndef _AHT30_H_
#define _AHT30_H_

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "MyI2C.h"

//前置文件Delay MyI2C(软件I2C).

typedef struct{
	float Humi, Temp;
}AHT30_Member;

void AHT30_Init(void);
void AHT30_Reset(void);
uint8_t AHT30_ReadData(void);

#endif

AHT30.c

#include "AHT30.h"

uint8_t AHT30_DataByte[7];
AHT30_Member AHT30;

/**
  * @brief 初始化I2C通信引脚,等待上电5ms。
  * @param	无
  * @retval	无
  */
  
void AHT30_Init(void){
	MyI2C_Init();
	Delay_ms(5);
}

/**
  * @brief CRC8位校验函数,返回值跟接收到的CRC校验码比价确认传输无误。
  * @param	*message	存储接收到的数据数组指针
  * @param	Num			接收到的字节个数(不包含CRC校验字节)
  * @retval	无
  */

unsigned char Calc_CRC8(unsigned char *message,unsigned char Num)
 {
	unsigned char i;
	unsigned char byte;
	unsigned char crc =0xFF;
	 
	for (byte = 0;byte<Num;byte++){
		crc^=(message[byte]);
		for(i=8;i>0;--i){
		if(crc&0x80)
		crc=(crc<<1)^0x31;
		else
		crc=(crc<<1);
		}
	}
	return crc;
 }

 /**
  * @brief	重置AHT30开始测温
  * @param	无
  * @retval	无
  */

void AHT30_Reset(void){
	MyI2C_Start();
	MyI2C_SendByte(0x70);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(0xAC);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(0x33);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(0x00);
	MyI2C_ReceiveAck();
	MyI2C_Stop();
	Delay_ms(80);
}

/**
  * @brief	启动AHT30并解算数据,用于刷新温湿度数据。
  * @param	无
  * @retval	返回值为1代表校验成功数据可用,为0代表校验失败数据不可用。
  */

uint8_t AHT30_ReadData(void){
	uint8_t i;
	uint32_t H = 0, T = 0;
	
	AHT30_Reset();
	MyI2C_Start();
	MyI2C_SendByte(0x71);
	MyI2C_ReceiveAck();
	for(i = 0; i < 7; i++){
		if(i != 6){
			AHT30_DataByte[i] = MyI2C_ReceiveByte();
			MyI2C_SendAck(0);
		}else{
			AHT30_DataByte[i] = MyI2C_ReceiveByte();
			MyI2C_SendAck(1);
		}
	}
	MyI2C_Stop();
	if((Calc_CRC8(AHT30_DataByte, 6) == AHT30_DataByte[6])){
		H |= AHT30_DataByte[1];
		H <<= 8;
		H |= AHT30_DataByte[2];
		H <<= 8;
		H |= AHT30_DataByte[3];
		H >>= 4;
		
		T |= AHT30_DataByte[3] & 0x0F;
		T <<= 8;
		T |= AHT30_DataByte[4];
		T <<= 8;
		T |= AHT30_DataByte[5];
		
		AHT30.Temp = (float)T * 200.0 / 1024.0 / 1024.0 - 50.0;
		AHT30.Humi = (float)H / 1024.0 / 1024.0 * 100;
		return 1;
	}
	else{
		return 0;
	}
}

MYI2C.h

#ifndef __MyI2C_H__
#define __MyI2C_H__

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif

MYI2C.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*
	I2C协议的开漏输出给1就是默认1分不清是谁说的, 开漏输出下拉给0才能确认是从机主动给的。
	
*/

void MyI2C_W_SCL(uint8_t Bit){
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)Bit);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t Bit){
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)Bit);
	Delay_us(10);
}

uint8_t MyI2C_R_SDA(void){
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_Initstruct;
	GPIO_Initstruct.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_Initstruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_Initstruct);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}

void MyI2C_Start(void){
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void){
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte){
	//传入非0数据就会写1,不管是0x80, 0x40, 0x20.
	uint8_t i;
	for(i = 0; i < 8; i++){
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
	
}

uint8_t MyI2C_ReceiveByte(void){
	uint8_t Byte = 0x00, i;
	MyI2C_W_SDA(1);		//释放数据线给从机放数据
	for(i = 0 ; i < 8; i++){
		MyI2C_W_SCL(1);		//释放时钟线准备读数据
		if(MyI2C_R_SDA() == 1){
			Byte |= (0x80 >> i);  //判断1就赋1,不是就保持默认0.
		}
		MyI2C_W_SCL(0);
	}
	return Byte;
}
//发送应答为0, 不应答为1.
void MyI2C_SendAck(uint8_t AckBit){
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

//正确接收则返回0,不正确则为1.
uint8_t MyI2C_ReceiveAck(void){
	uint8_t AckBit;
	MyI2C_W_SDA(1);		//释放数据线给从机放数据
	MyI2C_W_SCL(1);		//释放时钟线准备读数据
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);		//进入下一个时钟单元
	return AckBit;
}

main.c

#include "stm32f10x.h"     // Device header
#include "OLED.h"
#include "AHT30.h"

extern AHT30_Member AHT30;

int main(){
	OLED_Init();
	AHT30_Init();
	OLED_ShowString(1, 1, "Temp:    C");
	OLED_ShowString(2, 1, "Humi:    %");
	while(1){
		if(AHT30_ReadData()){
			OLED_ShowFloatNum(1, 6, AHT30.Temp, 2, 1);
			OLED_ShowFloatNum(2, 6, AHT30.Humi, 2, 1);
		}
	}
}
  • 现象


结尾附图一张(买的优新电子的模块,他家工程师画的图)

### 使用 STM32 标准库实现 AHT20 数据采集 #### 初始化 I2C 接口 为了使能 I2C 功能并初始化相应的 GPIO 和外设,在 `stm32f1xx_hal_msp.c` 文件中定义如下函数来配置硬件资源: ```c void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* 开启 I2C 时钟 */ __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); /**I2C1 GPIO Configuration PB6 ------> I2C1_SCL PB7 ------> I2C1_SDA */ GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置中断优先级 (如果需要的话) } ``` 此部分代码负责设置 SCL 和 SDA 引脚模式为开漏输出,并启用内部上拉电阻[^1]。 #### 实现 AHT20 的初始化命令发送 根据 AHT20 芯片手册中的描述,首次使用前需向其写入特定指令以激活设备。这可以通过调用 `HAL_I2C_Master_Transmit()` 函数完成: ```c uint8_t aht_init(void){ uint8_t cmd[] = {0xE1, 0x08}; // 初始化命令序列 if(HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS << 1, cmd, sizeof(cmd), HAL_MAX_DELAY)!= HAL_OK){ return ERROR; } HAL_Delay(20); // 等待启动完成 return SUCCESS; } ``` 这段程序会尝试向地址为 `AHT20_ADDRESS` 的器件发送一条包含两个字节的消息作为唤醒信号[^2]。 #### 获取温湿度测量值 当传感器准备好之后就可以请求新的采样数据了。这里采用连续两次读取的方式获取状态寄存器和实际测得的结果: ```c float get_temperature_humidity(float *temperature, float *humidity){ uint8_t status; uint8_t data[6]; do { // 发送触发测量命令 uint8_t trigger_cmd = 0xAC; if(HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS<<1,&trigger_cmd ,1,HAL_MAX_DELAY) != HAL_OK){ return ERROR; } HAL_Delay(80); // 延迟等待转换结束 // 检查忙标志位 if(HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS<<1,&status,1,HAL_MAX_DELAY) != HAL_OK){ return ERROR; } } while ((status&0x80)==0); // 如果忙碌则继续循环直到不繁忙为止... // 此处省略错误处理逻辑... // 成功后接收有效负载 if(HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS<<1,data,sizeof(data)-1,HAL_MAX_DELAY) != HAL_OK){ return ERROR; } // 解析原始二进制流成浮点数表示形式 int32_t raw_temp = (((int32_t)data[3])<<12)|(((int32_t)data[4])<<4)|(data[5]>>4); int32_t raw_humi = ((((int32_t)data[1]))<<12)|((((int32_t)data[2])))|((data[3]&0xF)<<8); *temperature = (raw_temp / 1048576.0)*200 - 50; *humidity = (raw_humi / 1048576.0)*100; return SUCCESS; } ``` 上述流程展示了如何利用标准库接口执行完整的交互操作——从发出查询到最终解析得到物理量度单位下的数值[^3].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值