STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度

文章目录

  • 前言
  • 一、ATH20手册连接与注意事项
  • 二、STM32CubeMX配置硬件IIC
  • 三、具体实现代码
  • 四、使用过程中碰到的问题及其解决方案

前言

最近在使用奥松的AHT20对环境温湿度进行采集。本例程采用ST的硬件IIC进行通讯,具体操作如下。

一、ATH20手册连接与注意事项

手册连接:AHT20数据手册
注意事项:
1、手册中器件地址是错误的,如下图:
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第1张图片
此器件地址实际应该是0x70,而不是0x38。

2、在使用模拟IIC读取温湿度数值时,一定注意在停止信号前发送“NACK”非应答信号,而中间读取完一个字节发送的是“ACK”应答信号。如下图所示:
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第2张图片
但是采用HAL库配置的硬件IIC,则直接使用如下两个相关函数即可,无需关注此应答信号。

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

从此处可以明显体现出硬件IIC的优势。

二、STM32CubeMX配置硬件IIC

使用STM32CubeMX配置硬件IIC,如下图:
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第3张图片

三、具体实现代码

1、AHT20.h代码如下:

#ifndef INC_AHT20_H_
#define INC_AHT20_H_

#include "i2c.h"

//****************************************
// 定义 AHT20 命令
//****************************************
#define AHT20_Write             0x00    //读取
#define AHT20_Read              0x01    //写入



//****************************************
// 定义 AHT20 地址
//****************************************
#define AHT20_SLAVE_ADDRESS		0x70		/* I2C从机地址 */



//****************************************
// 定义 AHT20 命令
//****************************************
#define	AHT20_INIT_COMD  		0xBE	//初始化 寄存器地址
#define	AHT20_SoftReset			0xBA	//软复位 单指令
#define	AHT20_TrigMeasure_COMD	0xAC	//触发测量 寄存器地址

// 存储AHT20传感器信息的结构体

typedef struct m_AHT20
{
	uint8_t alive;	// 0-器件不存在; 1-器件存在
	uint8_t flag;	// 读取/计算错误标志位。0-读取/计算数据正常; 1-读取/计算设备失败
	uint32_t HT[2];	// 湿度、温度 原始传感器的值(20Bit).

	float RH;		// 湿度,转换单位后的实际值,标准单位%
	float Temp;		// 温度,转换单位后的实际值,标准单位°C
}AHT20_StructureTypedef;

extern  AHT20_StructureTypedef  Humiture;

uint8_t AHT20_Init(void);
void    AHT20_Start_Init(void);
uint8_t AHT20_ReadHT(uint32_t *HT);
uint8_t StandardUnitCon(AHT20_StructureTypedef *aht);
uint8_t AHT20_Get_Value(AHT20_StructureTypedef *p);

#endif /* INC_AHT20_H_ */

其中,void AHT20_Start_Init(void);参考奥松官方代码编写,未进行验证。

2、AHT20.c代码如下:

#include "AHT20.h"

AHT20_StructureTypedef  Humiture;


/**
  * @brief  读AHT20 设备状态字
  * @param  void
  * @retval uint8_t 设备状态字
  */
static uint8_t AHT20_ReadStatusCmd(void)
{
	uint8_t tmp = 0;
    HAL_I2C_Master_Receive(&hi2c1, AHT20_SLAVE_ADDRESS, &tmp, 1, 0xFFFF);
	return tmp;
}

/**
  * @brief  读AHT20 设备状态字 中的Bit3: 校准使能位
  * @param  void
  * @retval uint8_t 校准使能位:1 - 已校准; 0 - 未校准
  */
static uint8_t AHT20_ReadCalEnableCmd(void)
{
	uint8_t tmp = 0;
	tmp = AHT20_ReadStatusCmd();
	return (tmp>>3)&0x01;
}

/**
  * @brief  读AHT20 设备状态字 中的Bit7: 忙标志
  * @param  void
  * @retval uint8_t 忙标志:1 - 设备忙; 0 - 设备空闲
  */
static uint8_t AHT20_ReadBusyCmd(void)
{
	uint8_t tmp = 0;
	tmp = AHT20_ReadStatusCmd();
	return (tmp>>7)&0x01;
}

/**
  * @brief  AHT20 芯片初始化命令
  * @param  void
  * @retval void
  */
static void AHT20_IcInitCmd(void)
{
	uint8_t tmp = AHT20_INIT_COMD;
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_SLAVE_ADDRESS, &tmp, 1, 0xFFFF);
}

/**
  * @brief  AHT20 软复位命令
  * @param  void
  * @retval void
  */
static void AHT20_SoftResetCmd(void)
{
	uint8_t tmp = AHT20_SoftReset;
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_SLAVE_ADDRESS, &tmp, 1, 0xFFFF);
}

/**
  * @brief  AHT20 触发测量命令
  * @param  void
  * @retval void
  */
static void AHT20_TrigMeasureCmd(void)
{
    static uint8_t tmp[3] = {AHT20_TrigMeasure_COMD, 0x33, 0x00};
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_SLAVE_ADDRESS, tmp, 3, 0xFFFF);
}


/**
  * @brief  AHT20 设备初始化
  * @param  void
  * @retval uint8_t:0 - 初始化AHT20设备成功; 1 - 初始化AHT20失败,可能设备不存在或器件已损坏
  */
uint8_t AHT20_Init(void)
{
	uint8_t rcnt = 2+1;//软复位命令 重试次数,2次
	uint8_t icnt = 2+1;//初始化命令 重试次数,2次

	while(--rcnt)
	{
		icnt = 2+1;

		HAL_Delay(40);//上电后要等待40ms
		// 读取温湿度之前,首先检查[校准使能位]是否为1
		while((!AHT20_ReadCalEnableCmd()) && (--icnt))// 2次重试机会
		{
			HAL_Delay(1);
			// 如果不为1,要发送初始化命令
			AHT20_IcInitCmd();
			HAL_Delay(40);//这个时间手册没说,按上电时间算40ms
		}

		if(icnt)//[校准使能位]为1,校准正常
		{
			break;//退出rcnt循环
		}
		else//[校准使能位]为0,校准错误
		{
			AHT20_SoftResetCmd();//软复位AHT20器件,重试
			HAL_Delay(20);//这个时间手册标明不超过20ms.
		}
	}

	if(rcnt)
	{
		return 0;// AHT20设备初始化正常
	}
	else
	{
		return 1;// AHT20设备初始化失败
	}
}

/**
  * @brief  AHT20 寄存器复位
  * @param  void
  * @retval void
  */
static void AHT20_Register_Reset(uint8_t addr)
{
  uint8_t  iic_tx[3] = {0}, iic_rx[3] = {0};
  
  iic_tx[0] = addr;
  HAL_I2C_Master_Transmit(&hi2c1, AHT20_SLAVE_ADDRESS, iic_tx, 3, 0xFFFF);
  HAL_Delay(5);
  HAL_I2C_Master_Receive(&hi2c1, AHT20_SLAVE_ADDRESS, iic_rx, 3, 0xFFFF);
  HAL_Delay(10);
  iic_tx[0] = 0xB0 | addr;
  iic_tx[1] = iic_rx[1];
  iic_tx[2] = iic_rx[2];
  HAL_I2C_Master_Transmit(&hi2c1, AHT20_SLAVE_ADDRESS, iic_tx, 3, 0xFFFF);
  HAL_Delay(10);
}


/**
  * @brief  AHT20 设备开始初始化
  * @param  void
  * @retval void
  */
void AHT20_Start_Init(void)
{
  static uint8_t    temp[3] = {0x1B, 0x1C, 0x1E}, i;
  for(i = 0; i < 3; i++)
  {
    AHT20_Register_Reset(temp[i]);
  }
}



/**
  * @brief  AHT20 设备读取 相对湿度和温度(原始数据20Bit)
  * @param  uint32_t *HT:存储20Bit原始数据的uint32数组
  * @retval uint8_t:0-读取数据正常; 1-读取设备失败,设备一直处于忙状态,不能获取数据
  */
uint8_t AHT20_ReadHT(uint32_t *HT)
{
	uint8_t cnt=3+1;//忙标志 重试次数,3次
	uint8_t tmp[6];
	uint32_t RetuData = 0;

	// 发送触发测量命令
	AHT20_TrigMeasureCmd();

	do{
		HAL_Delay(75);//等待75ms待测量完成,忙标志Bit7为0
	}while(AHT20_ReadBusyCmd() && (--cnt));//重试3次

	if(cnt)//设备闲,可以读温湿度数据
	{
		HAL_Delay(5);
		// 读温湿度数据
        HAL_I2C_Master_Receive(&hi2c1, AHT20_SLAVE_ADDRESS, tmp, 6, 0XFFFF);
		// 计算相对湿度RH。原始值,未计算为标准单位%。
		RetuData = 0;
		RetuData = (RetuData|tmp[1]) << 8;
		RetuData = (RetuData|tmp[2]) << 8;
		RetuData = (RetuData|tmp[3]);
		RetuData = RetuData >> 4;
		HT[0] = RetuData;

		// 计算温度T。原始值,未计算为标准单位°C。
		RetuData = 0;
		RetuData = (RetuData|tmp[3]) << 8;
		RetuData = (RetuData|tmp[4]) << 8;
		RetuData = (RetuData|tmp[5]);
		RetuData = RetuData&0xfffff;
		HT[1] = RetuData;

		return 0;
	}
	else//设备忙,返回读取失败
	{
		return 1;
	}
}

/**
  * @brief  AHT20 温湿度信号转换(由20Bit原始数据,转换为标准单位RH=%,T=°C)
  * @param  struct m_AHT20* aht:存储AHT20传感器信息的结构体
  * @retval uint8_t:0-计算数据正常; 1-计算数据失败,计算值超出元件手册规格范围
  */
uint8_t StandardUnitCon(AHT20_StructureTypedef *aht)
{
	aht->RH = (double)aht->HT[0] *100 / 1048576;//2^20=1048576 //原式:(double)aht->HT[0] / 1048576 *100,为了浮点精度改为现在的
	aht->Temp = (double)aht->HT[1] *200 / 1048576 -50;

	//限幅,RH=0~100%; Temp=-40~85°C
	if((aht->RH >=0)&&(aht->RH <=10000) && (aht->Temp >=-4000)&&(aht->Temp <=8500))
	{
		aht->flag = 0;
		return 0;//测量数据正常
	}
	else
	{
		aht->flag = 1;
		return 1;//测量数据超出范围,错误
	}
}


/**
  * @brief  AHT20 温湿度信号转换(由20Bit原始数据,转换为标准单位RH=%,T=°C)
  * @param  struct m_AHT20* aht:存储AHT20传感器信息的结构体
  * @retval uint8_t:0-计算数据正常; 1-计算数据失败,计算值超出元件手册规格范围
  */
uint8_t AHT20_Get_Value(AHT20_StructureTypedef *p)
{
  uint8_t   temp = 0;
  
  temp = AHT20_ReadHT(p->HT);
  
  if(temp == 0)
  {
    temp = StandardUnitCon(p);
  }
  
  return temp;
}

3、main.c代码如下:

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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_I2C1_Init();
  /* USER CODE BEGIN 2 */
  if(AHT20_Init() != 0)
  {
    Humiture.alive = 0;
    printf("AHT20 Initialization failed\r\n");
  }
  else
  {
    Humiture.alive = 1;
    printf("AHT20 Initialization succeeded\r\n");
  }  
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {   
    AHT20_Get_Value(&Humiture);
    printf("environment temperature is : %.2f;\r\nenvironment humidity is : %.2f",Humiture.Temp, Humiture.RH);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

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

以上代码均已验证可用。

试验结果如下图:
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第4张图片

四、使用过程中碰到的问题及其解决方案

1、问题:突然发现读取的温度为0.0,湿度为99.99%,重新下载程序在线调试,波形无改变,且IIC初始化后,SR2寄存器的BUSY位为1,表示IIC忙碌。
通过逻辑分析仪查看IIC引脚波形如下图,SCL为高,SDA为低。
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第5张图片
2、原因:因为此时从机正在发数据给主机,但是此时主机因为重启,所以clk被上拉电阻一直置为高。从机此时发送给主机的数据位刚好是低电平,若clk不为低,从机讲一直保持低电平数据(CLK为低:SDA改变数据;CLK为高:SDA数据保持)。详见博客:I2C通讯过程中SDA被一直拉低。

3、解决方案:
上电后初始化IIC前,检测SCL和SDA引脚电平,只要有一个为低电平,则进行软件模拟IIC起始位。实现代码放在i2c.c文件。具体如下:

在i2c.h文件中声明函数:

/* USER CODE BEGIN Prototypes */
void  I2C1_Recover(void);
/* USER CODE END Prototypes */

在i2c.c文件中定义函数:

/* USER CODE BEGIN 1 */
void  I2C1_Recover(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  
  __HAL_RCC_GPIOB_CLK_ENABLE();
  /**I2C1 GPIO Configuration
  PB6     ------> I2C1_SCL
  PB7     ------> I2C1_SDA
  */
  //SCL   SDA配置普通引脚
  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;                      
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  
  //软件模拟起始位
  if ((HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_6) & HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)) == GPIO_PIN_RESET)
  {
    
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 | GPIO_PIN_7, GPIO_PIN_SET);
    for (uint16_t i = 0; i < 0x0fff; i++)
    {
      __NOP();
    }
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);  
    for (uint16_t i = 0; i < 0x0fff; i++)
    {
      __NOP();
    }
  }
  
  //SCL   SDA配置为引脚IIC引脚
  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_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE END 1 */

在两个文件中调用函数I2C1_Recover(),第一个是在i2c.c文件的MX_I2C1_Init()函数中调用:

/* I2C1 init function */
void MX_I2C1_Init(void)
{
  /* USER CODE BEGIN I2C1_Init 0 */
  I2C1_Recover();
    
  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */

  /* USER CODE END I2C1_Init 2 */

}

第二个是在main.c的主函数while(1)中调用:

iic_state = AHT20_Get_Value(&Humiture);		//iic_state 在while(1)之前定义。
if(iic_state)
{
  SYSlog("AHT20 Get Date is Fault, iic state code :%d\r\n", iic_state);
  I2C1_Recover();
}

修改AHT20.C文件中函数AHT20_ReadHT(uint32_t *HT)的定义:

/**
  * @brief  AHT20 设备读取 相对湿度和温度(原始数据20Bit)
  * @param  uint32_t *HT:存储20Bit原始数据的uint32数组
  * @retval uint8_t:0-读取数据正常; 1-读取设备失败,设备一直处于忙状态,不能获取数据; 2-通讯异常
  */
uint8_t AHT20_ReadHT(uint32_t *HT)
{
	uint8_t cnt=3+1;//忙标志 重试次数,3次
	uint8_t tmp[6];
	uint32_t RetuData[2] = {0, 0};

	// 发送触发测量命令
	AHT20_TrigMeasureCmd();

	do{
		HAL_Delay(100);//等待75ms待测量完成,忙标志Bit7为0
	}while(AHT20_ReadBusyCmd() && (--cnt));//重试3次

	if(cnt)//设备闲,可以读温湿度数据
	{
		HAL_Delay(5);
		// 读温湿度数据
    HAL_I2C_Master_Receive(&hi2c1, AHT20_SLAVE_ADDRESS, tmp, 6, 0XFFFF);
		// 计算相对湿度RH。原始值,未计算为标准单位%。
		RetuData[0] = 0;
		RetuData[0] = (RetuData[0] | tmp[1]) << 8;
		RetuData[0] = (RetuData[0] | tmp[2]) << 8;
		RetuData[0] = (RetuData[0] | tmp[3]);
		RetuData[0] = RetuData[0] >> 4;
		

		// 计算温度T。原始值,未计算为标准单位°C。
		RetuData[1] = 0;
		RetuData[1] = (RetuData[1] | tmp[3]) << 8;
		RetuData[1] = (RetuData[1] | tmp[4]) << 8;
		RetuData[1] = (RetuData[1] | tmp[5]);
		RetuData[1] = RetuData[1] & 0xfffff;
    
    //对原始值进行判断,如果都为0,则通讯异常
    if((RetuData[0] == 0) && (RetuData[1] == 0))
    {
        return 2;
    }
    
    HT[0] = RetuData[0];
		HT[1] = RetuData[1];

		return 0;
	}
	else//设备忙,返回读取失败
	{
		return 1;
	}
}

4、结果:
STM32CubeMX配置STM32F407的硬件IIC采集AHT20的温湿度_第6张图片

本文参考模拟IIC文章:AHT20温湿度传感器STM32-I2C驱动,替代DHT11/DHT12/AM2320/SHT20/SHT30,IIC代码兼容AHT10/15-MEMS温湿度传感器
及奥松官网例程。

本程序几乎适用于所有基于IIC通讯的温湿度传感器。

你可能感兴趣的:(stm32,单片机)