基于stm32f103c8t6和AHT20-21的IIC协议温湿度采集

一、IIC协议简介

1. 概念

   IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接 微控制器及其外围设备。它是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据。 在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送。 I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。

结束信号:SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,
表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU 接
收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为
受控单元出现故障。
这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

2、主从设备连接方式

IIC协议用于主机和从机之间的通信,主机可以向从机读写数据,常用连接线为:
11660e6d2bc447619298fc65132104cf.png
主从设备之间有两根连接线,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。
数据线即用来表示数据,时钟线用于数据收发同步。
具有三种传输模式:
                                 标准模式传输速率为 100kbit/s 
                                 快速模式为 400kbit/s 
                                 高速模式 可达 3.4Mbit/s,但目前大多 I 2C 设备尚不支持高速模式

3. 传输协议(读、写及读和写数据)

     I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地
址广播等环节。I2C协议中。
417bce6e20794299a76b4b7ecaa6b3a2.png

                 S: 起始位

                 SLAVE ADDRESS:从机地址,一般为7位,即一个主机最多连接128个从机

                RW:读或写,1 为读,0 为写

                A:应答信号

                DATA:数据

                P:停止位

                A/ A- : 应答(ACK)或非应答(NACK)信号
      起始信号产生后,所有从机就开始等待主机紧接下来 广播 的从机地址信号在 I2C 总线上,每个设备的地址都是唯一的,当主机广播的地址与 某个设备地址相同时,这个设备就被选中了,没被选中的设备将会忽略之后的数据信号。
写数据
      若配置的方向传输位为“写数据”方向,即第一幅图的情况,广播完地址,接收到应
答信号后,主机开始正式向从机传输数据(DATA),数据包的大小为 8 位,主机每发送完一
个字节数据,都要等待从机的应答信号(ACK),重复这个过程,可以向从机传输 N 个数据,
这个 N 没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号(P),表示不
再传输数据。
读数据
     若配置的方向传输位为“读数据”方向,即第二幅图的情况,广播完地址,接收到应
答信号后,从机开始向主机返回数据(DATA),数据包大小也为 8 位,从机每发送完一个数
据,都会等待主机的应答信号(ACK),重复这个过程,可以返回 N 个数据,这个 N 也没有
大小限制。当主机希望停止接收数据时,就向从机返回一个非应答信号(NACK),则从机自
动停止数据传输。
读和写数据
     除了基本的读写,I2C 通讯更常用的是复合格式,即第三幅图的情况,该传输过程有
两次起始信号(S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后,
发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它
与 SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是
说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。

4. 起始信号、停止信号及数据有效性

        起始信号S和停止信号P

      前文中提到的起始(S)和停止(P)信号是两种特殊的状态。如图:当 SCL 线是高电
平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。当 SCL 是高电平时 SDA
线由低电平向高电平切换,表示通讯的停止。起始和停止信号一般由主机产生。因此IIC是通过切换电平的方式进行通信的,这个和串口通信的方式是差不多的。

81096ff65b2a4e599d7ef6d5ce49e3c7.png

      I2C 使用 SDA 信号线来传输数据,使用 SCL信号线进行数据同步。如下图。SDA 数据线在 SCL的每个时钟周期传输一位数据。传输时,SCL为高电平的时候 SDA表示的数据有效,即此时的 SDA为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL为低电平时,SDA的数据无效,一般在这个时候 SDA进行电平切换,为下一次表示数据做好准备。

caaca99c0c6d4b28a4ebee7a8587ce29.png

接收端接收信息时,通过SCL和SDA进行与运算,就能解析出传输的数据。

5. 软件IIC与硬件IIC

     软件IIC: 通过上面的 介绍,我们知道IIC通信的标准频率是100khz,且其发送和读取数据都是                       通过修改SCL和SDA的高低电平状态完成的,在之前的实验中,我们知道STM32的                         GPIO输出速度可以高达50Mhz,且可以改变高低电平状态,所以这两者都满足IIC的                       条件,即通过配置GPIO状态来完成IIC协议的方式就是软件IIC

     例如:STM32基础:IIC概述与软件模拟IIC - 知乎 (zhihu.com)

     硬件IIC: 硬件IIC是指在stm32单片机中,工程师完成的内部集成电路,我们只需要配置对应                         的参数就可以使用IIC,如配置IIC时钟,配置IICDMA模式发送和接收,初始化IIC等

     区别:二者都是遵循IIC协议的,但是软件方式比较灵活,可以自己选择GPIO端口,硬件是固                    定的引脚,但是执行效率比软件更高

二、AHT20-21简介

1.  温湿度(AHT20)外观

                                 2d873bf21db245909e6f5f4e7d906b95.png

2.  AHT20功能

      AHT20,新一代温湿度传感器在尺寸与智能方面建立了新的标准:它嵌入了适 于回流焊的双列扁平无引脚SMD封装,底面3 x 3mm ,高度1.0mm。传感器输出经过 标定的数字信号,标准 I 2 C 格式。AHT20 配有一个全新设计的 ASIC专用芯片、一 个经过改进的MEMS半导体电容式湿度传感元件和一个标准的片上温度传感元件,其 性能已经大大提升甚至超出了前一代传感器的可靠性水平,新一代温湿度传感器, 经过改进使其在恶劣环境下的性能更稳定。每一个传感器都经过校准和测试,在产 品表面印有产品批号。由于对传感器做了改良和微型化改进,因此它的性价比更高, 并且最终所有设备都将得益于尖端的节能运行模式

完全标定

数字输出,IIC 接口

优异的长期稳定性

响应迅速、抗干扰能力强

宽电压支持 2.2-5.5 VDC

3. 参数介绍

湿度特性:

247081a708954d3cb1ba0b45a28da818.png

温度:

3a3122af037d4283859ddb6dc3ffcd40.png

4. 引脚定义及连接

      总共四个引脚:

                             528acb9a0118472bb8e62bf134d162cd.png

       1.  VDD接单片机的电源

       2.   SDA数据接口

       3.  GND接单片机的电源

       4.  SCL时钟接口

3ec28449d6f64e5d91fffd11e06619c0.png

5. 数据读取流程及温湿度转换

 1. 上电后要等待40ms,读取温湿度值之前, 首 先要看状态字的校准使能位Bit[3]是否为 1(通 过发送0x71可以获取一个字节的状态字),如果 不为1,要发送0xBE命令(初始化),此命令参数 有两个字节, 第一个字节为0x08,第二个字节 为0x00,然后等待10ms。

2.直接发送 0xAC命令(触发测量),此命令参数 有两个字节,第一个字节为 0x33,第二个字节 为0x00。

3.等待80ms待测量完成,如果读取状态字Bit[7] 为0,表示测量完成,然后可以连续读取六个字 节;否则继续等待。

4.当接收完六个字节后,紧接着下一个字节是 CRC校验数据,用户可以根据需要读出,如果接 收端需要CRC校验,则在接收完第六个字节后发 ACK应答,否则发NACK结束,CRC初始值为0XFF, CRC8校验多项式为:

                                                        G(x)=1+x^4+x^5+x^8

5.计算温湿度值。

主机向从机发送测量数据:

32e759d40d7249c581c52fab919b2441.png

读取温湿度数据:

28ffae9cf9054e9081688e03a29666c6.png

367c6946814e4e44abfca229f00c728f.png

相对湿度转换

相对湿度 RH 都可以根据 SDA 输出的相对湿度 信号 SRH 通过如下公式计算获得 (结果以 %RH 表示):

                  9f851ae7c28845428219b117c9d96a09.png

温度转换

温度 T 都可以通过将温度输出信号 ST 代入到 下面的公式计算得到 (结果以温度 ℃ 表示):  

                    7c3bafe6bf2e43d1b1def7f36ffe1731.png

三、STM32CubeMX配置

 1.  选择stm32f03c8t6芯片

0f78bb15158149009d0471dab7a785d9.png

  2.  配置RCC

51253c5c996f475da0d72c27108e2a88.png

 3. 配置sys

34d0c22ba06a413b87bdb2e848eba8b9.png

4. 配置UART1

6113098f54424707b4ebb3050f0f4ebd.png

1892bde9772048ceafed645945df758e.png

 5. 配置DMA 

       9ff3d231cbe5486ca252ce33e6fe058f.png

6. 配置IIC

26a51f52b1e8491a9353958c044be130.png

7. 配置中断

17d10d444f66448da9913dea5f763979.png

 8.  配置时钟

86536071cf9649d0a3de44597c0bfa2b.png

9. 工程输出配置

73a819b1fff14793936290eafdc2002a.png

cba371c9533640148bf03142e19dba2b.png

四、添加官方AHT20源码资料

 下载地址:

https://files.cnblogs.com/files/blogs/746187/AHT20-21_DEMO_V1_3.rar?t=1669050285

下载后需要在工程中添加该源码:

1. 在工程目录中新建工程AHT20

2bb7b2a4b45b4f3d8c4e2f0fcce4dbc0.png

2. 将上面下载文件中的源文件和头文件复制到新建工程中

7a71a3b908554790bb21648cacbc2eb2.png

3. 在工程中添加头文件路径

aa2cede2e3b64bbd86f3c841ee4e1814.png

                         ad371c0ec754448ab5416ea5e72acd59.png

4. 在工程下加入源文件

                     2b9c0dae1a944144959106fd05040c23.png

                  5e7ba3afe0024e4c83a36d7cd24cb4c6.png

添加后,由于该库是基于库函数的源码,需要修改成HAL库,对应修改可以参考:


【精选】STM32通过I2C接口实现温湿度(AHT20)的采集_aht20 stm32-优快云博客  

修改后的头文件:

#ifndef _AHT20_DEMO_
#define _AHT20_DEMO_

#include "main.h"  

void Delay_N10us(uint32_t t);//延时函数
void SensorDelay_us(uint32_t t);//延时函数
void Delay_4us(void);		//延时函数
void Delay_5us(void);		//延时函数
void Delay_1ms(uint32_t t);	
void AHT20_Clock_Init(void);		//延时函数
void SDA_Pin_Output_High(void)  ; //将PB15配置为输出 , 并设置为高电平, PB15作为I2C的SDA
void SDA_Pin_Output_Low(void);  //将P15配置为输出  并设置为低电平
void SDA_Pin_IN_FLOATING(void);  //SDA配置为浮空输入
void SCL_Pin_Output_High(void); //SCL输出高电平,P14作为I2C的SCL
void SCL_Pin_Output_Low(void); //SCL输出低电平
void Init_I2C_Sensor_Port(void); //初始化I2C接口,输出为高电平
void I2C_Start(void);		 //I2C主机发送START信号
void AHT20_WR_Byte(uint8_t Byte); //往AHT20写一个字节
uint8_t AHT20_RD_Byte(void);//从AHT20读取一个字节
uint8_t Receive_ACK(void);   //看AHT20是否有回复ACK
void Send_ACK(void)	;	  //主机回复ACK信号
void Send_NOT_ACK(void);	//主机不回复ACK
void Stop_I2C(void);	  //一条协议结束
uint8_t AHT20_Read_Status(void);//读取AHT20的状态寄存器
uint8_t AHT20_Read_Cal_Enable(void);  //查询cal enable位有没有使能
void AHT20_SendAC(void); //向AHT20发送AC命令
uint8_t Calc_CRC8(uint8_t *message,uint8_t Num);
void AHT20_Read_CTdata(uint32_t *ct); //没有CRC校验,直接读取AHT20的温度和湿度数据
void AHT20_Read_CTdata_crc(uint32_t *ct); //CRC校验后,读取AHT20的温度和湿度数据
void AHT20_Init(void);   //初始化AHT20
void JH_Reset_REG(uint8_t addr);///重置寄存器
void AHT20_Start_Init(void);///上电初始化进入正常测量状态
#endif


修改后的c文件:

/*******************************************/
/*@版权所有:广州奥松电子有限公司          */
/*@作者:温湿度传感器事业部                */
/*@版本:V1.2                              */
/*******************************************/
//#include "main.h" 
#include "AHT20-21_DEMO_V1_3.h" 
#include "gpio.h"
#include "i2c.h"


void Delay_N10us(uint32_t t)//延时函数
{
  uint32_t k;

   while(t--)
  {
    for (k = 0; k < 2; k++);//110
  }
}

void SensorDelay_us(uint32_t t)//延时函数
{
		
	for(t = t-2; t>0; t--)
	{
		Delay_N10us(1);
	}
}

void Delay_4us(void)		//延时函数
{	
	Delay_N10us(1);
	Delay_N10us(1);
	Delay_N10us(1);
	Delay_N10us(1);
}
void Delay_5us(void)		//延时函数
{	
	Delay_N10us(1);
	Delay_N10us(1);
	Delay_N10us(1);
	Delay_N10us(1);
	Delay_N10us(1);

}

void Delay_1ms(uint32_t t)		//延时函数
{
   while(t--)
  {
    SensorDelay_us(1000);//延时1ms
  }
}


//void AHT20_Clock_Init(void)		//延时函数
//{
//	RCC_APB2PeriphClockCmd(CC_APB2Periph_GPIOB,ENABLE);
//}

void SDA_Pin_Output_High(void)   //将PB7配置为输出 , 并设置为高电平, PB7作为I2C的SDA
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
	GPIO_InitStruct.Pin = GPIO_PIN_7;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
}

void SDA_Pin_Output_Low(void)  //将P7配置为输出  并设置为低电平
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
	GPIO_InitStruct.Pin = GPIO_PIN_7;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
}

void SDA_Pin_IN_FLOATING(void)  //SDA配置为浮空输入
{
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;//浮空
	GPIO_InitStruct.Pin = GPIO_PIN_7;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init( GPIOB,&GPIO_InitStruct);
}


void SCL_Pin_Output_High(void) //SCL输出高电平,P14作为I2C的SCL
{
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}

void SCL_Pin_Output_Low(void) //SCL输出低电平
{
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}

void Init_I2C_Sensor_Port(void) //初始化I2C接口,输出为高电平
{	
	GPIO_InitTypeDef  GPIO_InitStruct;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
	GPIO_InitStruct.Pin = GPIO_PIN_7;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);

	
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
	GPIO_InitStruct.Pin = GPIO_PIN_6;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
	
}
void I2C_Start(void)		 //I2C主机发送START信号
{
	SDA_Pin_Output_High();
	SensorDelay_us(8);
	SCL_Pin_Output_High();
	SensorDelay_us(8);
	SDA_Pin_Output_Low();
	SensorDelay_us(8);
	SCL_Pin_Output_Low();
	SensorDelay_us(8);   
}


void AHT20_WR_Byte(uint8_t Byte) //往AHT20写一个字节
{
	uint8_t Data,N,i;	
	Data=Byte;
	i = 0x80;
	for(N=0;N<8;N++)
	{
		SCL_Pin_Output_Low(); 
		Delay_4us();	
		if(i&Data)
		{
			SDA_Pin_Output_High();
		}
		else
		{
			SDA_Pin_Output_Low();
		}	
			
    SCL_Pin_Output_High();
		Delay_4us();
		Data <<= 1;
		 
	}
	SCL_Pin_Output_Low();
	SensorDelay_us(8);   
	SDA_Pin_IN_FLOATING();
	SensorDelay_us(8);	
}	


uint8_t AHT20_RD_Byte(void)//从AHT20读取一个字节
{
		uint8_t Byte,i,a;
	Byte = 0;
	SCL_Pin_Output_Low();
	
	SDA_Pin_IN_FLOATING();
	SensorDelay_us(8);	
	
	for(i=0;i<8;i++)
	{
    SCL_Pin_Output_High();
		
		Delay_5us();
		a=0;
		
		//if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)) a=1;
		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)) a=1;
		Byte = (Byte<<1)|a;
		
		//SCL_Pin_Output_Low();
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
		Delay_5us();
	}
  SDA_Pin_IN_FLOATING();
	SensorDelay_us(8);	
	return Byte;
}


uint8_t Receive_ACK(void)   //看AHT20是否有回复ACK
{
	uint16_t CNT;
	CNT = 0;
	SCL_Pin_Output_Low();	
	SDA_Pin_IN_FLOATING();
	SensorDelay_us(8);	
	SCL_Pin_Output_High();	
	SensorDelay_us(8);	
	while((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7))  && CNT < 100) 
	CNT++;
	if(CNT == 100)
	{
		return 0;
	}
 	SCL_Pin_Output_Low();	
	SensorDelay_us(8);	
	return 1;
}

void Send_ACK(void)		  //主机回复ACK信号
{
	SCL_Pin_Output_Low();	
	SensorDelay_us(8);	
	SDA_Pin_Output_Low();
	SensorDelay_us(8);	
	SCL_Pin_Output_High();	
	SensorDelay_us(8);
	SCL_Pin_Output_Low();	
	SensorDelay_us(8);
	SDA_Pin_IN_FLOATING();
	SensorDelay_us(8);
}

void Send_NOT_ACK(void)	//主机不回复ACK
{
	SCL_Pin_Output_Low();	
	SensorDelay_us(8);
	SDA_Pin_Output_High();
	SensorDelay_us(8);
	SCL_Pin_Output_High();	
	SensorDelay_us(8);		
	SCL_Pin_Output_Low();	
	SensorDelay_us(8);
    SDA_Pin_Output_Low();
	SensorDelay_us(8);
}

void Stop_I2C(void)	  //一条协议结束
{
	SDA_Pin_Output_Low();
	SensorDelay_us(8);
	SCL_Pin_Output_High();	
	SensorDelay_us(8);
	SDA_Pin_Output_High();
	SensorDelay_us(8);
}

uint8_t AHT20_Read_Status(void)//读取AHT20的状态寄存器
{

	uint8_t Byte_first;	
	I2C_Start();
	AHT20_WR_Byte(0x71);
	Receive_ACK();
	Byte_first = AHT20_RD_Byte();
	Send_NOT_ACK();
	Stop_I2C();
	return Byte_first;
}

uint8_t AHT20_Read_Cal_Enable(void)  //查询cal enable位有没有使能
{
	uint8_t val = 0;//ret = 0,
  val = AHT20_Read_Status();
	 if((val & 0x68)==0x08)
		 return 1;
   else  return 0;
 }

void AHT20_SendAC(void) //向AHT20发送AC命令
{

	I2C_Start();
	AHT20_WR_Byte(0x70);
	Receive_ACK();
	AHT20_WR_Byte(0xac);//0xAC采集命令
	Receive_ACK();
	AHT20_WR_Byte(0x33);
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	Stop_I2C();

}

//CRC校验类型:CRC8/MAXIM
//多项式:X8+X5+X4+1
//Poly:0011 0001  0x31
//高位放到后面就变成 1000 1100 0x8c
//C现实代码:
uint8_t Calc_CRC8(uint8_t *message,uint8_t Num)
{
	uint8_t i;
	uint8_t byte;
	uint8_t crc=0xFF;
  for(byte=0; byte<Num; byte++)
  {
    crc^=(message[byte]);
    for(i=8;i>0;--i)
    {
      if(crc&0x80) crc=(crc<<1)^0x31;
      else crc=(crc<<1);
    }
  }
        return crc;
}

void AHT20_Read_CTdata(uint32_t *ct) //没有CRC校验,直接读取AHT20的温度和湿度数据
{
		volatile uint8_t  Byte_1th=0;
	volatile uint8_t  Byte_2th=0;
	volatile uint8_t  Byte_3th=0;
	volatile uint8_t  Byte_4th=0;
	volatile uint8_t  Byte_5th=0;
	volatile uint8_t  Byte_6th=0;
	 uint32_t RetuData = 0;
	uint16_t cnt = 0;
	AHT20_SendAC();//向AHT10发送AC命令
	Delay_1ms(80);//延时80ms左右	
    cnt = 0;
	while(((AHT20_Read_Status()&0x80)==0x80))//直到状态bit[7]为0,表示为空闲状态,若为1,表示忙状态
	{
		SensorDelay_us(1508);
		if(cnt++>=100)
		{
		 break;
		 }
	}
	I2C_Start();
	AHT20_WR_Byte(0x71);
	Receive_ACK();
	Byte_1th = AHT20_RD_Byte();//状态字,查询到状态为0x98,表示为忙状态,bit[7]为1;状态为0x1C,或者0x0C,或者0x08表示为空闲状态,bit[7]为0
	Send_ACK();
	Byte_2th = AHT20_RD_Byte();//湿度
	Send_ACK();
	Byte_3th = AHT20_RD_Byte();//湿度
	Send_ACK();
	Byte_4th = AHT20_RD_Byte();//湿度/温度
	Send_ACK();
	Byte_5th = AHT20_RD_Byte();//温度
	Send_ACK();
	Byte_6th = AHT20_RD_Byte();//温度
	Send_NOT_ACK();
	Stop_I2C();

	RetuData = (RetuData|Byte_2th)<<8;
	RetuData = (RetuData|Byte_3th)<<8;
	RetuData = (RetuData|Byte_4th);
	RetuData =RetuData >>4;
	ct[0] = RetuData;//湿度
	RetuData = 0;
	RetuData = (RetuData|Byte_4th)<<8;
	RetuData = (RetuData|Byte_5th)<<8;
	RetuData = (RetuData|Byte_6th);
	RetuData = RetuData&0xfffff;
	ct[1] =RetuData; //温度

}


void AHT20_Read_CTdata_crc(uint32_t *ct) //CRC校验后,读取AHT20的温度和湿度数据
{
		volatile uint8_t  Byte_1th=0;
	volatile uint8_t  Byte_2th=0;
	volatile uint8_t  Byte_3th=0;
	volatile uint8_t  Byte_4th=0;
	volatile uint8_t  Byte_5th=0;
	volatile uint8_t  Byte_6th=0;
	volatile uint8_t  Byte_7th=0;
	 uint32_t RetuData = 0;
	 uint16_t cnt = 0;
	// uint8_t  CRCDATA=0;
	 uint8_t  CTDATA[6]={0};//用于CRC传递数组
	
	AHT20_SendAC();//向AHT10发送AC命令
	Delay_1ms(80);//延时80ms左右	
    cnt = 0;
	while(((AHT20_Read_Status()&0x80)==0x80))//直到状态bit[7]为0,表示为空闲状态,若为1,表示忙状态
	{
		SensorDelay_us(1508);
		if(cnt++>=100)
		{
		 break;
		}
	}
	
	I2C_Start();

	AHT20_WR_Byte(0x71);
	Receive_ACK();
	CTDATA[0]=Byte_1th = AHT20_RD_Byte();//状态字,查询到状态为0x98,表示为忙状态,bit[7]为1;状态为0x1C,或者0x0C,或者0x08表示为空闲状态,bit[7]为0
	Send_ACK();
	CTDATA[1]=Byte_2th = AHT20_RD_Byte();//湿度
	Send_ACK();
	CTDATA[2]=Byte_3th = AHT20_RD_Byte();//湿度
	Send_ACK();
	CTDATA[3]=Byte_4th = AHT20_RD_Byte();//湿度/温度
	Send_ACK();
	CTDATA[4]=Byte_5th = AHT20_RD_Byte();//温度
	Send_ACK();
	CTDATA[5]=Byte_6th = AHT20_RD_Byte();//温度
	Send_ACK();
	Byte_7th = AHT20_RD_Byte();//CRC数据
	Send_NOT_ACK();                           //注意: 最后是发送NAK
	Stop_I2C();
	
	if(Calc_CRC8(CTDATA,6)==Byte_7th)
	{
	RetuData = (RetuData|Byte_2th)<<8;
	RetuData = (RetuData|Byte_3th)<<8;
	RetuData = (RetuData|Byte_4th);
	RetuData =RetuData >>4;
	ct[0] = RetuData;//湿度
	RetuData = 0;
	RetuData = (RetuData|Byte_4th)<<8;
	RetuData = (RetuData|Byte_5th)<<8;
	RetuData = (RetuData|Byte_6th);
	RetuData = RetuData&0xfffff;
	ct[1] =RetuData; //温度
		
	}
	else
	{
		ct[0]=0x00;
		ct[1]=0x00;//校验错误返回值,客户可以根据自己需要更改
	}//CRC数据
}


void AHT20_Init(void)   //初始化AHT20
{	
	Init_I2C_Sensor_Port();
	I2C_Start();
	AHT20_WR_Byte(0x70);
	Receive_ACK();
	AHT20_WR_Byte(0xa8);//0xA8进入NOR工作模式
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	Stop_I2C();

	Delay_1ms(10);//延时10ms左右

	I2C_Start();
	AHT20_WR_Byte(0x70);
	Receive_ACK();
	AHT20_WR_Byte(0xbe);//0xBE初始化命令,AHT20的初始化命令是0xBE,   AHT10的初始化命令是0xE1
	Receive_ACK();
	AHT20_WR_Byte(0x08);//相关寄存器bit[3]置1,为校准输出
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	Stop_I2C();
	Delay_1ms(10);//延时10ms左右
}
void JH_Reset_REG(uint8_t addr)
{
	
	uint8_t Byte_first,Byte_second,Byte_third;
	I2C_Start();
	AHT20_WR_Byte(0x70);//原来是0x70
	Receive_ACK();
	AHT20_WR_Byte(addr);
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	AHT20_WR_Byte(0x00);
	Receive_ACK();
	Stop_I2C();

	Delay_1ms(5);//延时5ms左右
	I2C_Start();
	AHT20_WR_Byte(0x71);//
	Receive_ACK();
	Byte_first = AHT20_RD_Byte();
	Send_ACK();
	Byte_second = AHT20_RD_Byte();
	Send_ACK();
	Byte_third = AHT20_RD_Byte();
	Send_NOT_ACK();
	Stop_I2C();
	
  Delay_1ms(10);//延时10ms左右
	I2C_Start();
	AHT20_WR_Byte(0x70);///
	Receive_ACK();
	AHT20_WR_Byte(0xB0|addr);//寄存器命令
	Receive_ACK();
	AHT20_WR_Byte(Byte_second);
	Receive_ACK();
	AHT20_WR_Byte(Byte_third);
	Receive_ACK();
	Stop_I2C();
	
	Byte_second=0x00;
	Byte_third =0x00;
}

void AHT20_Start_Init(void)
{
	JH_Reset_REG(0x1b);
	JH_Reset_REG(0x1c);
	JH_Reset_REG(0x1e);
}

五、函数编写

1.  重定义printf函数编写

int fputc(int ch,FILE *f)
{
    HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);    
		while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET){
		}		
    return ch;
}

2. 主函数编写

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_USART1_UART_Init();
  MX_DMA_Init();
  MX_I2C1_Init();	
	AHT20_Init();
  if((AHT20_Read_Status()&0x18)!=0x18) //读取寄存器,判断是否读取完成
	{
		AHT20_Start_Init();   //完成手动后更新寄存器
		Delay_1ms(10);
	}


  while (1)
  {
   
      AHT20_Read_CTdata(CT_data);   //从AHT20中读取信息
	  c1 = CT_data[0]*100/1024/1024;  // 读取湿度信息
	  t1 = CT_data[1]*200/1024/1024-50;	//读取温度信息
      printf("\rhumidity: %d%s\n",c1,"%"); 
	  printf("temperature: %d%s\n",t1,"C"); //串口打印信息
	  printf("\r\n");
      HAL_Delay(3000); //3s采集一次

  }
 
}

六、实验效果

47c8e6ec41f74194b9dd3f9a1f493005.gif

b8cb4f3128f7452c846663d665f3b1e3.gif

七、总结与参考

         通过此次实验理解和学习了IIC的协议

         通过上面的结果显示,成功通过串口将温度和湿度信息读取,且发送到电脑上。

         湿度测试;

        在测试时,我用手将温湿度传感器捏住,可以在GIF中看出,湿度逐渐上升,从50%上升到90%左右,当收松开后,湿度从90%逐渐下降到50%

         温度测试:

        由于在实验中,电脑发热,我将温湿度传感器放在发热地方,可以看出温度从28度逐渐上升

        参考网址:

   STM32 —— IIC 读取 ATH20(DTH20)温度传感器-优快云博客

【精选】STM32通过I2C接口实现温湿度(AHT20)的采集_aht20 stm32-优快云博客

     STM32基础:IIC概述与软件模拟IIC - 知乎 (zhihu.com)

STM32完成基于I2C协议的AHT20温湿度传感器的数据采集并通过串口输出_aht20 stm32代码_漠影zy的博客-优快云博客

《AHT20产品手册》

《零死角玩转STM32-F103指南者.pdf》

《STM32F1开发指南精英版-HAL库版本_v1.2.pdf》

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值