AHT30、AHT20和AHT10都是数字温湿度传感器,它们各自具有不同的特性和应用场景。以下是对这三种传感器的详细分析:
AHT30
主要特点:
- 高精度:AHT30通常具有更高的测量精度,能够提供更准确的温度和湿度数据。
- 低功耗:设计用于低功耗运行,有助于延长电池寿命。
- 快速响应:能够迅速感知并响应环境中温度和湿度的变化。
- 数字输出:通过数字接口(如I2C)输出数据,便于与微控制器或其他数字系统集成。
- 宽电压支持:支持较宽的电压范围(如2.2-5.5VDC),适应不同的供电环境。
- 广泛应用:适用于智能家居、消费电子、医疗、汽车、工业、气象等领域,如暖通空调、除湿器和冰箱等家电产品,以及测试和检测设备。
技术规格(以广州奥松电子股份有限公司提供的信息为例):
- 供电电压:DC 2.2-5.5V
- 测量范围:温度-40120℃、湿度0100%RH
- 典型精度:温度±0.5℃、湿度±3%RH(@25℃)
- 分辨率:温度0.01℃、湿度0.024%RH
- 响应时间:温度530s、湿度830s
AHT20
主要特点:
- 高精度:AHT20提供高精度的温度和湿度测量,适用于需要精确控制环境条件的场合。
- 小尺寸:体积小巧,便于在有限的空间内安装和使用。
- 国产化:由国内奥松生成,具有成本优势。
- I2C接口:通过I2C接口与微控制器等数字系统集成。
- 高ADC位数:ADC位数为20Bit,提高了测量精度。
技术规格:
- 供电电压(可能因型号而异):通常为3.3V或5V
- 测量范围:温度-4085℃、湿度0100%RH
- 精度:温度精度T=±0.3°C,相对湿度精度RH=±2%
AHT10
主要特点:
- 高精度:虽然精度可能略低于AHT20和AHT30,但仍能满足大多数应用需求。
- 成本效益:相对于其他高精度传感器,AHT10具有成本优势。
- 广泛应用:可替代SHT20等传感器,适用于各种环境监测应用。
技术规格(可能因型号和制造商而异):
- 供电电压:通常为3.3V或5V
- 测量范围:温度、湿度的测量范围与AHT20相似
- 精度:也具有较高的温度和湿度测量精度,但具体数值可能因制造商而异
总结
AHT30、AHT20和AHT10都是高精度数字温湿度传感器,它们各自具有不同的特点和优势。在选择时,应根据具体的应用需求、成本预算和环境条件进行综合考虑。例如,对于需要极高精度的场合,可以选择AHT30;而对于成本较为敏感的应用,AHT10则是一个不错的选择。同时,也应注意不同制造商和型号之间的技术规格差异。
驱动代码
本文主要介绍温湿度传感器.c/.h文件,其他用户仅需要将h文件进行修改,即可使用。
#include "MYI2C.h"
#include "main.h"
/**
* @brief IIC底层延时函数
*
* @param void
*
* @return void
*/
#define SYSCLK_MHZ (SystemCoreClock/1000000) //系统时钟频率
/**********************************************
//MYI2C_Delay_us
**********************************************/
void MYI2C_Delay_us(unsigned long nTim)
{
uint32_t Tdata = nTim*SYSCLK_MHZ/5;
for(uint32_t i=0;i<Tdata;i++)
{
__NOP();
}
}
void IIC_Delay(void)
{
MYI2C_Delay_us(100);
}
/**
* @brief IIC初始化函数
*
* @param void
*
* @return void
*/
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOC_CLK_ENABLE(); //使能GPIOC时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOD时钟
/*
SCL - PD6 SDA-PC1
*/
GPIO_Initure.Pin = GPIO_PIN_7;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; //快速
HAL_GPIO_Init(GPIOC, &GPIO_Initure);
GPIO_Initure.Pin = GPIO_PIN_10;
GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull = GPIO_PULLUP; //上拉
GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH; //快速
HAL_GPIO_Init(GPIOA, &GPIO_Initure);
IIC_SDA(1);
IIC_SCL(1);
}
/**
* @brief 产生IIC起始信号
*
* @param void
*
* @return void
*/
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA(1);
IIC_SCL(1);
IIC_Delay();
IIC_SDA(0);//START:when CLK is high,DATA change form high to low
IIC_Delay();
IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}
/**
* @brief 产生IIC停止信号
*
* @param void
*
* @return void
*/
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SDA(0);
IIC_SCL(1);
IIC_Delay();
IIC_SDA(1);//STOP:when CLK is high DATA change form low to high
IIC_Delay();
IIC_SCL(0);//发送I2C总线结束信号
}
/**
* @brief 等待应答信号到来
*
* @param void
*
* @return u8 1,接收应答失败
* 0,接收应答成功
*/
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN(); //SDA设置为输入
IIC_SDA(1);
IIC_Delay();
IIC_SCL(1);
IIC_Delay();
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime > 250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL(0);//时钟输出0
return 0;
}
/**
* @brief 产生ACK应答
*
* @param void
*
* @return void
*/
void IIC_Ack(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(0);
IIC_Delay();
IIC_SCL(1);
IIC_Delay();
IIC_SCL(0);
}
/**
* @brief 不产生ACK应答
*
* @param void
*
* @return void
*/
void IIC_NAck(void)
{
IIC_SCL(0);
SDA_OUT();
IIC_SDA(1);
IIC_Delay();
IIC_SCL(1);
IIC_Delay();
IIC_SCL(0);
}
/**
* @brief IIC发送一个字节
*
* @param txd 需要发送的数据
*
* @return void
*/
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL(0);//拉低时钟开始数据传输
for(t = 0; t < 8; t++)
{
IIC_SDA((txd & 0x80) >> 7);
txd <<= 1;
IIC_SCL(1);
IIC_Delay();
IIC_SCL(0);
IIC_Delay();
}
}
/**
* @brief 读1个字节数据
*
* @param ack 1,发送ACK 0,发送nACK
*
* @return u8 返回读取数据
*/
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i, receive = 0;
SDA_IN();//SDA设置为输入
for(i = 0; i < 8; i++)
{
IIC_SCL(0);
IIC_Delay();
IIC_SCL(1);
receive <<= 1;
if(READ_SDA)receive++;
IIC_Delay();
}
if(!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
/**
* @brief 向ATH10写入数据
*
* @param cmd 命令
* @param data 要写入的数据
* @param len 写入数据大小
*
* @return u8 0,正常,其他,错误代码
*/
u8 AHT10_Write_Data(u8 cmd, u8 *data, u8 len)
{
IIC_Start();
IIC_Send_Byte((AHT10_IIC_ADDR << 1) | 0); //发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(cmd); //写寄存器地址
IIC_Wait_Ack(); //等待应答
for(u8 i = 0; i < len; i++)
{
IIC_Send_Byte(data[i]); //发送数据
IIC_Wait_Ack(); //等待应答
}
IIC_Stop();
return 0;
}
/**
* @brief 读一个字节
*
* @param void
*
* @return u8 读到的数据
*/
u8 AHT10_ReadOneByte(void)
{
u8 res = 0;
IIC_Start();
IIC_Send_Byte((AHT10_IIC_ADDR << 1) | 0X01); //发送器件地址+读命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
res = IIC_Read_Byte(0); //读数据,发送nACK
IIC_Stop(); //产生一个停止条件
return res;
}
/**
* @brief 读数据
*
* @param data 数据缓存
* @param len 读数据大小
*
* @return u8 0,正常,其他,错误代码
*/
u8 AHT10_Read_Data(u8 *data, u8 len)
{
IIC_Start();
IIC_Send_Byte((AHT10_IIC_ADDR << 1) | 0x01); //发送器件地址+读命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
for(u8 i = 0; i < len; i++)
{
if(i == (len - 1))
data[i] = IIC_Read_Byte(0); //读数据,发送nACK
else
data[i] = IIC_Read_Byte(1); //读数据,发送ACK
}
IIC_Stop();
return 0;
}
/**
* @brief 读取温度数据
*
* @param void
*
* @return float 温度数据(单位:摄氏度)
*/
float AHT10_Read_Temperature(void)
{
u8 res = 0;
u8 cmd[2] = {0, 0};
u8 temp[6];
float cur_temp;
res = AHT10_Write_Data(AHT10_GET_DATA, cmd, 2); //发送读取数据命令
if(res) return 1;
res = AHT10_Read_Data(temp, 6); //读取数据
if(res) return 1;
cur_temp = ((temp[3] & 0xf) << 16 | temp[4] << 8 | temp[5]) * 200.0 / (1 << 20) - 50;
return cur_temp;
}
/**
* @brief 读取湿度数据
*
* @param void
*
* @return float 湿度数据(单位:%RH)
*/
float AHT10_Read_Humidity(void)
{
u8 res = 0;
u8 cmd[2] = {0, 0};
u8 humi[6];
float cur_humi;
res = AHT10_Write_Data(AHT10_GET_DATA, cmd, 2); //发送读取数据命令
if(res) return 1;
res = AHT10_Read_Data(humi, 6); //读取数据
if(res) return 1;
cur_humi = ((humi[1]) << 12 | humi[2] << 4 | (humi[3] & 0xF0)) * 100.0 / (1 << 20);
return cur_humi;
}
/**
* @brief ATH10传感器初始化
*
* @param void
*
* @return u8 0,初始化成功,其他,失败
*/
u8 AHT10_Init(void)
{
u8 res;
u8 temp[2] = {0, 0};
IIC_Init(); //初始化IIC接口:注意这里的IIC总线为:SCL-PD6 SDA-PC1
res = AHT10_Write_Data(AHT10_NORMAL_CMD, temp, 2);
if(res != 0) return 1;
HAL_Delay(300);
temp[0] = 0x08;
temp[1] = 0x00;
res = AHT10_Write_Data(AHT10_CALIBRATION_CMD, temp, 2);
if(res != 0) return 1;
HAL_Delay(300);
return 0;
}
上述代码延时函数很是厉害,根据系统主时钟进行计算的粗延时,实测误差小,任何iic设备均可采用上述延时方法。这里解决了HAL库玩家无us延时的问题。
上述代码.h文件如下:
#ifndef _MYI2C_h_
#define _MYI2C_h_
#include <main.h>
//IO方向设置
#define SDA_IN() {GPIOA->MODER&=~(3<<(10*2));GPIOA->MODER|=0<<(10*2);} //PC1输入模式
#define SDA_OUT() {GPIOA->MODER&=~(3<<(10*2));GPIOA->MODER|=1<<(10*2);} //PC1输出模式
//IO操作
#define IIC_SCL(n) (n?HAL_GPIO_WritePin(GPIOC,GPIO_PIN_7,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOC,GPIO_PIN_7,GPIO_PIN_RESET))//SCL
#define IIC_SDA(n) (n?HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_RESET))//SDA
#define READ_SDA HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)//输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr, u8 addr, u8 data);
u8 IIC_Read_One_Byte(u8 daddr, u8 addr);
#define AHT10_IIC_ADDR 0x38 //AHT10 IIC地址
#define AHT10_CALIBRATION_CMD 0xE1 //校准命令(上电后只需要发送一次)
#define AHT10_NORMAL_CMD 0xA8 //正常工作模式
#define AHT10_GET_DATA 0xAC //读取数据命令
u8 AHT10_Init(void);
float AHT10_Read_Temperature(void);
float AHT10_Read_Humidity(void);
#endif //_MYI2C_h_
看到这句
SDA_IN() {GPIOA->MODER&=~(3<<(10*2));GPIOA->MODER|=0<<(10*2);}
HAL库玩家又不会了,寄存器又把人难倒了,且听我说,这句话配置的是PA10,那么我用x,y代表A、10此时
SDA_IN() {GPIOx->MODER&=~(3<<(y*2));GPIOx->MODER|=0<<(y*2);} ,手把手教,那么有的人用PC7,需要改为
SDA_IN() {GPIOC->MODER&=~(3<<(7*2));GPIOC->MODER|=0<<(7*2);} 看到这还不懂,就放弃吧。
main程序写法如下
/**
* @brief The application entry point.
* @retval int
*/
float temperature, humidity;
int main(void)
{
/* USER CODE BEGIN 1 */
uint16_t test[5];
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
while(AHT10_Init());
/* USER CODE BEGIN WHILE */
while (1)
{
temperature = AHT10_Read_Temperature();
humidity = AHT10_Read_Humidity();
HAL_Delay(2000);
}
/* USER CODE END 3 */
}
因为工程是stm32cubemx生成的,所以里面多了好多的无用注释, while(AHT10_Init());是为了验证配置,当模块坏掉或者程序配置有问题,都会卡死在这个死循环,当然可以加上报警等,初始化成功后就可以直接跳过,进入测温湿度的环节,在主程序里面 两秒测试一次。
float temperature, humidity; 放在main外面定义keil仿真时watch可以显示全局变量,所以放在外面。