一、温湿度传感器AHT20–IIC通讯轮询模式

1、创建新项目,芯片选用STM32F103C8T6,项目名为iic。

2、根据AHT20所连接的引脚,开启IIC1外设。

3、项目管理,为每个外设生成一个.c、.h文件,这样就可以include头文件,拿到huart1或者hi2c这类外设的操作句柄了,之后保存生成代码。

4、创建自己的.c、.h文件

  1. Core→Inc右键→New→Hreader File,命名aht20.h
  2. Core→src右键→New→Source File,命名aht20.c

5、代码部分

  1. 根据数据手册的传感器读取流程1,可以写出AHT20的初始化函数。
#define AHT20_ADDRESS	0X70
/* HAL库会根据用户发送或者接受数据将地址最后一位置1或者置0 */

void AHT20_Init(void) {
	uint8_t	readBuffer;
	HAL_Delay(40);
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, &readBuffer, 1, HAL_MAX_DELAY);

	if ((readBuffer & 0x08) == 0x00) {
		uint8_t	sendBuffer[3] = {0xBE, 0x08, 0x00};
		HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	}
}
  1. 根据数据手册的传感器读取流程2跟3,还有数据表可以写出AHT20的读取测量数据的函数。
void AHT20_Read(float *Temperature, float *Humidity) {
	uint8_t	sendBuffer[3] = {0xAC, 0x33, 0x00};
	uint8_t	readBuffer[6];

	HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	HAL_Delay(75);
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, readBuffer, 6, HAL_MAX_DELAY);

	if ((readBuffer[0] & 0x80) == 0x00) {
		uint32_t data = 0;

		data = ((uint32_t)readBuffer[3] >> 4) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12);
		*Humidity = data * 100.0f / (1 << 20);

		data = ((uint32_t)readBuffer[5]) + ((uint32_t)readBuffer[4] << 8) + (((uint32_t)readBuffer[3] & 0x0F) << 16);
		*Temperature = (data * 200.0f / (1 << 20)) - 50;
	}

}
  1. 由于我们使用到浮点数,Cube默认没有启用编译器对浮点数输出的支持,在菜单栏project→properties进入设置页面勾选图纸选项开启此功能。
  2. 主函数部分。
  • 先将AHT20初始化,创建两个变量和一个用来临时存放数据的数组。
  •   AHT20_Init();
    
      float temperature, humidity;
      char dataArray[50];
    
  • 在循环语句种,读取测量数据并通过串口发送出来。
  •   while (1)
      {
    	  AHT20_Read(&temperature, &humidity);
    	  sprintf(dataArray, "温度:%.f ℃, 湿度:%.f %%\r\n", temperature, humidity);
    		/* sprintf() 函数只是将特定格式存于数组dataArray中,再通过串口将数组发送出去 */
    	  HAL_UART_Transmit(&huart1, (uint8_t*)dataArray, strlen(dataArray), HAL_MAX_DELAY);
    	  HAL_Delay(1000);
      }
    
    1. 编译代码并烧录,再通过连接串口获取单片机发送的数据。

    二、IIC中断模式与状态机

    1、IIC中断模式

    1. 打开IIC的中断向量并保存生成代码。

    2、利用状态机编程代码。

  • 前面我们使用的轮询的方式,读取模块的数据时,有等待时间,等待读取成功之后才发送到串口显示出来。如果我们只是简单的将轮询模式中IIC的发送和接受改成中断模式,会导致数据的读取还未完成时就将数据给发送出去,造成数据的错误,所以我们需要对代码进行改造,利用状态机来编码,不同的状态做不同的事。
  • 1.因为状态机的原理就是不同的状态做不同的事情,所以我们将上面读取数据的函数AHT20_Read()拆分为三个单独的函数,如下:

    1. 发送测量指令:
    /* 测量函数 */
    void AHT20_Measure(void) {
    	//因为函数用的是sendBuffer指针的地址,函数运行结束之后就会被释放,被其他变量所使用,所以这里需要使用static来创建数组,保证数组的地址一直存在。
    	static uint8_t	sendBuffer[3] = {0xAC, 0x33, 0x00};
    	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
    }
    
    1. 获取模块的数据:
    /* 获取数据 */
    void AHT20_GetData(void) {
    	//将获取的数据存于readBuffer数组之中,readBuffer为全局变量,方便其他函数使用
    	HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS, readBuffer, 6);
    }
    
    1. 解析获取的数据:
    uint8_t	readBuffer[6] = {0};
    
    /* 解析获取到的数据 */
    void AHT20_AnalysisData(float *Temperature, float *Humidity) {
    	if ((readBuffer[0] & 0x80) == 0x00) {
    		uint32_t data = 0;
    
    		data = ((uint32_t)readBuffer[3] >> 4) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12);
    		*Humidity = data * 100.0f / (1 << 20);
    
    		data = ((uint32_t)readBuffer[5]) + ((uint32_t)readBuffer[4] << 8) + (((uint32_t)readBuffer[3] & 0x0F) << 16);
    		*Temperature = (data * 200.0f / (1 << 20)) - 50;
    	}
    }
    
    1. 函数注意要在头文件中申明,方便其他文件使用。

    2.创建状态机模型

    /* USER CODE BEGIN PV */
    uint8_t aht20State = 0;
    /* USER CODE END PV */
    

  • 0:初始状态,发送测量指令,然后将状态变为1
  • 1:正在发送测量指令中
  • 2:发送完成,按照手册中需要等75ms后才可以进行读取
  • 3:读取中
  • 4:读取完成,对数据进行解析并展示数据并且需要将状态恢复到初始状态,进行下一个循环。
  • 3.代码部分

  • 主函数的循环语句中,我们发现没有状态1切换到状态2、状态3切换到状态4的语句,我们需要所以我们需要借助IIC中断的回调函数来实现切换状态的功能。也因为状态aht20State需要在其他函数中使用,所以我们需要将次变量在头文件中加上extern来声明,其他文件中才可以使用。
  • main.h文件中
  • /* Exported constants --------------------------------------------------------*/
    /* USER CODE BEGIN EC */
    extern uint8_t aht20State;
    /* USER CODE END EC */
    
  • main.c文件中
  •   while (1)
      {
    	  if (aht20State == 0) {			//初始状态,发送测量指令
    		  AHT20_Measure();
    		  aht20State = 1;
    	  }  else if (aht20State == 2) {		//发送完成,等75ms后才可以进行读取
    		  HAL_Delay(75);
    		  AHT20_GetData();
    		  aht20State = 3;
    	  } else if (aht20State == 4) {		//读取完成,对数据进行解析并展示数据
    		  AHT20_AnalysisData(Temperature, Humidity);
    
    		  sprintf(dataArray, "温度:%.f ℃, 湿度:%.f %%\r\n", temperature, humidity);
    		  HAL_UART_Transmit(&huart1, (uint8_t*)dataArray, strlen(dataArray), HAL_MAX_DELAY);
    
    		  HAL_Delay(5000);
    
    		  aht20State = 0;
    	  }
    
  • 在i2c.c文件中,发送完成和接受完成的回调函数。
  • void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c) {
    	if (hi2c == &hi2c1) {
    		aht20State = 2;
    	}
    }
    
    void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c) {
    	if (hi2c == &hi2c1) {
    		aht20State = 4;
    	}
    }
    
  • 在上个实例中我们用得是阻塞式IIC通讯,关于AHT20_Init()的函数中也要修改成中断模式:
  • void AHT20_Init(void) {
    	uint8_t	readBuffer;
    	HAL_Delay(40);
    	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, &readBuffer, 1);
    
    	if ((readBuffer & 0x08) == 0x00) {
    		uint8_t	sendBuffer[3] = {0xBE, 0x08, 0x00};
    		HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
    	}
    }
    
  • 烧录后通过串口发送数据,一切正常。
  • 三、IIC通讯DMA模式

    1、开启DMA模式

  • 开启IIC的DMA模式,添加两个通道,一个RX一个TX,其他设置无需修改,保持默认。保存生成代码
  • 2、将中断模式的代码修改为DMA模式的代码。

  • 初始化函数,将后缀_IT改为_DMA即可。
  • void AHT20_Init(void) {
    	uint8_t	readBuffer;
    	HAL_Delay(40);
    	HAL_I2C_Master_Transmit_DMA(&hi2c1, AHT20_ADDRESS, &readBuffer, 1);
    
    	if ((readBuffer & 0x08) == 0x00) {
    		uint8_t	sendBuffer[3] = {0xBE, 0x08, 0x00};
    		HAL_I2C_Master_Receive_DMA(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
    	}
    }
    
  • 测量函数,将后缀_IT改为_DMA即可。
  • /* 测量函数 */
    void AHT20_Measure(void) {
    	//因为函数用的是sendBuffer指针的地址,函数运行结束之后就会被释放,被其他变量所使用,所以这里需要使用static来创建数组,保证数组的地址一直存在。
    	static uint8_t	sendBuffer[3] = {0xAC, 0x33, 0x00};
    	HAL_I2C_Master_Transmit_DMA(&hi2c1, AHT20_ADDRESS, sendBuffer, 3);
    }
    
  • 获取数据函数,将后缀_IT改为_DMA即可。
  • /* 获取数据 */
    void AHT20_GetData(void) {
    	//将获取的数据存于readBuffer数组之中,readBuffer为全局变量,方便其他函数使用
    	HAL_I2C_Master_Receive_DMA(&hi2c1, AHT20_ADDRESS, readBuffer, 6);
    }
    
  • 完成修改,功能没变,只是IIC的模式改变而已。
  • 此文章仅自己学习记录用,是参考小破站keysking的教程。
  • 作者:人生若只如初见645

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32-Cube-IIC通讯

    发表回复