STM32:AHT20温湿度传感器驱动程序开发

注:温湿度传感器AHT20数据手册.pdf

http://www.aosong.com/userfiles/files/AHT20%E4%BA%A7%E5%93%81%E8%A7%84%E6%A0%BC%E4%B9%A6(%E4%B8%AD%E6%96%87%E7%89%88)%20B1.pdf

一、分析AHT数据手册文档

(1).准备工作

1.新建工程。配置UART2

2.配置I2C1为I2C标准模式,并开启中断和DMA设置

3.设置工程参数为每个外设初始化生成头文件和源文件,而后生成代码。

(2).根据数据手册,编写AHT20驱动

数据手册中的传感器读取流程如下:

1.上电后要等待40ms,读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通
过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xBE命令(初始化),此命令参数 有两个字节, 第一个字节为0x08,第二个字节为0x00。
2.直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00。
3.等待75ms待测量完成,忙状态Bit[7]为0,然后可以读取六个字节(发0X71即可以读取)。
4.计算温湿度值
1.第一条的意思是,开机后,要等待40ms才能够与AHT20通信。因此在AHT20建立通信前要等待40ms。而后0x71地址实际上AHT20作为IIC从机的地址。按照AHT20手册,在启动传输后,随后传输的 II C首字节包括 7位的 I I C设备地址0x38。因为IIC通信一般使用7为地址码,但是读写数据都是一个字节一个字节的读写。0x38的七位二进制为0111000。规定从机地址要左一位。多出来的第八位就是读写位。IIC协议规定,如果主机发起通信的目的是为了写从机,那么读写位是0,此时AHT20的地址是01110000,即0x70.如果主机发起通信的目的是为了读从机传入的数据,那么读写位就是1。此时AHT20的地址是0x71。对于第8位的读写设置,HAL库已经帮我们封装好了,所以不用特意去操作。用户只当作AHT20的地址是0x70就行。

2.直接发送信息,略
3.等待75ms后,读取6个字节数据,里面包含了状态信息,湿度信息,和温度信息。其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。而后,湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。

4.解析完温度、湿度数据后,进行计算

(3),关键代码

aht.h声明函数, aht.c函数定义如下

cpp 复制代码
#include "aht20.h"
#define AHT20_ADDRESS 0x70
//AHT20初始化
void AHT20_Init(){
	uint8_t readBuffer;
	//1.工作前延迟40ms
	HAL_Delay(40);
	//2.从AHT20收取一个字节,判断第Bit[3]是否为1
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readBuffer, 1, HAL_MAX_DELAY);
	//加上状态位后实际上要判断Bit[4]
	if( (readBuffer & 0x08)== 0x00){
		//如果不为1,要发送0xBE命令(初始化)
		//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
		uint8_t sendBuffer[3] ={0xBE,0x08,0x00};
		HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	}
}

void AHT20_Read(float *O_Temperature,float* O_Humidity){
	//输入触发命令和参数
	uint8_t sendBuffer[3] ={0xAC,0x33,0x00};
	HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, sendBuffer, 3, HAL_MAX_DELAY);
	//等待75ms测量完成
	HAL_Delay(75);
	//读6个字节
	uint8_t readBuffer[6];
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, readBuffer, 6, HAL_MAX_DELAY);
	//其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。为0则不再工作
	if((readBuffer[0] & 0x80 )==0x00){
		uint32_t tempdata = 0;
		//湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。
		tempdata =((uint32_t)readBuffer[1] << 12 ) + ((uint32_t)readBuffer[2] <<4 ) +((uint32_t)readBuffer[3] >>4 );
		//相对湿度计算
		*O_Humidity = tempdata *1.0f /(1<<20);

		//温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。
		tempdata = (((uint32_t)readBuffer[3] & 0x0F ) <<16 ) +((uint32_t)readBuffer[4] <<8 ) + (uint32_t)readBuffer[5];
		//转化成摄氏度
		*O_Temperature= tempdata*200.0f /(1<<20)-50;
	}


}

main.c 关键函数如下:

cpp 复制代码
/* USER CODE BEGIN Includes */
#include "aht20.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();


  /* Configure the system clock */
  SystemClock_Config();
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  AHT20_Init();
  char message[50];
  float temperature, humidity;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 AHT20_Read(&temperature, &humidity);
	 sprintf(message,"温度:%.1f °C,湿度: %.1f %% \r\n",temperature,humidity*100);
	 HAL_UART_Transmit(&huart2, message, 50, HAL_MAX_DELAY);
	 HAL_Delay(1000);
    /* USER CODE END WHILE */

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

注:起初 sprintf(message,"温度:%.1f °C,湿度: %.1f %% \r\n",temperature,humidity*100);会报不支持浮点数输出的错误。
菜单栏Project ->properties解决

二、基于状态机编程实现AHT20的中断程序

上面一节实现的是AHT20的轮询模式。发送HAL_I2C_Master_Transmit ,接收HAL_I2C_Master_Receive都会阻塞主程序,待完全执行完发送/接收内容时程序才会执行下一步操作。而在中断或DMA模式下,发送和接收消息不会阻塞主程序,那么就有可能发生还没接收完数据就对温度、湿度变量进行计算,造成脏读。

HAL_I2C_Master_Transmit_IT() // 采用中断模式发送
HAL_I2C_Master_Transmit_DMA() //采用DMA模式发送
HAL_I2C_Master_Receive_IT() // 采用中断模式接收

HAL_I2C_Master_Receive_DMA() // 采用DMA模式接收

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c); //主机发送完成回调函数

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c); //主机接收完成回调函数
所谓状态机编程实际上类似与设计模式中的状态模式类型,把AHT20的通信流程拆分开来。每个状态标识分别对应着自己的处理逻辑,并且指明下一个的状态。

改造上述代码。
1).保持AHT20初始化不变
2).拆分温度、湿度发送/接收/计算模块

  • 在状态为0时,发送测温湿度的命令,并将状态值为1。此时要等待DMA或者中断函数处理完成
  • 触发 IIC发送完成回调函数,则表示发送命令完成,将状态置为2
  • 当状态为2时,等待75 ms,让AHT20测温湿度结束。而后发送接收AHT20测温湿度数据的命令,并将状态置为3.
  • 触发 IIC接收完成回调函数,则表示数据接收完成,测试接收到的6字节数据就是温湿度数据。并将状态置为4
  • 当状态为4时,解析接收到的6字节数据,并打印
    这样就完成了中断/DMA的测量温湿度数据的案例

三、中断程序主要代码

aht.h声明函数, aht.c函数定义如下

cpp 复制代码
#include <aht20.h>

//AHT20设备地址
static uint8_t AHT20_ADDRESS=0x70;
//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
static uint8_t AHT20InitCmd[3]={0xBE,0x08,0x00} ;
//输入测量触发命令和参数
static uint8_t AHT20MeasureCmd[3]={0xAC,0x33,0x00};
static uint8_t AHT20readBuffer[6];

//AHT20初始化
void AHT20_Init(){
	uint8_t readOneByte;
	//1.工作前延迟40ms
	HAL_Delay(40);
	//2.从AHT20收取一个字节,判断第Bit[3]是否为1
	HAL_I2C_Master_Receive(&hi2c1, AHT20_ADDRESS, &readOneByte, 1, HAL_MAX_DELAY);
	//加上状态位后实际上要判断Bit[4]
	if( (readOneByte & 0x08)== 0x00){
		//如果不为1,要发送0xBE命令(初始化)
		//发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二个字节为0x00。
		HAL_I2C_Master_Transmit(&hi2c1, AHT20_ADDRESS, AHT20InitCmd, 3, HAL_MAX_DELAY);
	}
}

//发送测量指令
void AHT20_Transmit(){
	HAL_I2C_Master_Transmit_IT(&hi2c1, AHT20_ADDRESS, AHT20MeasureCmd, 3);
}
//接收测量数据到AHT20readBuffer
void AHT20_Receive(){
	HAL_I2C_Master_Receive_IT(&hi2c1, AHT20_ADDRESS, AHT20readBuffer, 6);
}
//解析AHT20readBuffer输出O_Temperature和O_Humidity
void AHT20_Analysis(float *O_Temperature,float* O_Humidity){
	//其中第0个字节是状态位,需获取bit[7]判断设备是否空闲。为0则不再工作
	if((AHT20readBuffer[0] & 0x80 )==0x00){
		uint32_t tempdata = 0;
		//湿度数据由20个bit位组成:第1个字节是湿度的高8位,第2个字节是湿度的次高8位.第3个字节的高4个bit位是湿度的低4位。
		tempdata =((uint32_t)AHT20readBuffer[1] << 12 ) + ((uint32_t)AHT20readBuffer[2] <<4 ) +((uint32_t)AHT20readBuffer[3] >>4 );
		//相对湿度计算
		*O_Humidity = tempdata *1.0f /(1<<20);

		//温度数据也由20个bit位组成。第3个字节的低4个bit位是温度的高4位,第4个字节是温度的次高8位,第5个字节是温度的低8位。
		tempdata = (((uint32_t)AHT20readBuffer[3] & 0x0F ) <<16 ) +((uint32_t)AHT20readBuffer[4] <<8 ) + (uint32_t)AHT20readBuffer[5];
		//转化成摄氏度
		*O_Temperature= tempdata*200.0f /(1<<20)-50;
	}
}

main.c关键代码

cpp 复制代码
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
//状态:0 初始状态,1正在发送测量指令 2测量指令发送完成  3 IIC读取ANT20数据中 4 读取完成
uint8_t  aht20State =0;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c){
	if(hi2c == &hi2c1){
		aht20State =2;
	}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c){
	if(hi2c == &hi2c1){
		aht20State =4;
	}
}
/* USER CODE END 0 */



int main(void)
{

  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  AHT20_Init();
  char message[50];
  float temperature, humidity;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	 if(aht20State == 0){
		 //初始状态
		 //测量数据
		 AHT20_Transmit();
		 aht20State=1;
	 }else if(aht20State == 2){
		HAL_Delay(75);
		//读取数据
		AHT20_Receive();
		aht20State=3;
	 }else if(aht20State == 4){
		//AHT20_
		 AHT20_Analysis(&temperature, &humidity);
		 sprintf(message,"温度:%.1f °C,湿度: %.1f %% ",temperature,humidity*100);
		 HAL_UART_Transmit(&huart2, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
		 HAL_Delay(1000);
		 aht20State= 0;
	 }
    /* USER CODE END WHILE */

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

查看效果:波特律动 串口助手

相关推荐
hollq16 小时前
STM32F103RCT6+STM32CubeMX+keil5(MDK-ARM)+Flymcu实现串口重定向
arm开发·stm32·嵌入式硬件
小鱼儿电子18 小时前
17-基于STM32的宠物饲养系统设计与实现
stm32·嵌入式硬件·物联网·宠物·宠物饲养系统
小莞尔19 小时前
【51单片机】【protues仿真】基于51单片机四层电梯系统
单片机·嵌入式硬件
CFZPL19 小时前
使用江科大串口发送函数发送freertos的vTaskList出现跑飞
单片机
F1331689295720 小时前
WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
stm32·单片机·嵌入式硬件·51单片机·硬件工程·pcb工艺
易享电子21 小时前
基于单片机电器断路器保护器系统Proteus仿真(含全部资料)
单片机·嵌入式硬件·fpga开发·51单片机·proteus
爱倒腾的老唐1 天前
01、如何学习单片机
单片机·嵌入式硬件·学习
点灯小铭1 天前
基于单片机的夹具压力控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计
雾削木1 天前
stm32解锁芯片
javascript·stm32·单片机·嵌入式硬件·gitee
热爱编程的小刘1 天前
STM32学习路线开启篇:外部中断
stm32