前置装备-手册下载
数据手册直接上奥松电子官网下载,可能很多年后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);
}
}
}
-
现象