STM32驱动AHT10/AHT20/AHT30温湿度传感器源码

 

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可以显示全局变量,所以放在外面。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值