基于HAL库的温湿度传感器AHT20
)
本次实验中,需要利用AHT20采集环境温度与湿度并在串口中显示出来,使用的主控设备是STM32G474。AHT20使用IIC通信协议进行通信,它利用IIC接口与STM32G474进行数据交换。
首先,我们需要了解IIC通信协议。IIC是一种常见的串行通信协议,它允许多个设备通过同一条总线进行通信,每个设备有唯一的地址。
IIC通信协议
串口用两根线进行传递数据,可以实现同时进行双向通信,即全双工通信。在串口通信中,通信的双方需要先约定好通信的速度(即比特率),然后双方按照约定好的速度在合适的时机去设置或读取数据线上的高低电平。这种通信模式称为异步通信,在这种模式下,对通信速度也就是对数据线操作的时机的选取是基于双方各自的时钟。
异步通信的好处是比较便捷,但缺点是通信双方必须保证各自的系统时钟是精确的,若有一方的时钟有误,那么双方通信就会出现问题。
与串口通信不同的是,IIC通信只有一根数据线(SDA)可以传递数据,另一根线则用于提供同步时钟脉冲的时钟线(SCL)。IIC通信虽然允许进行双向通信,但是同一时刻只能一端发送一端接受,即半双工通信。由于IIC通信同一时刻只能进行一个方向的通信,为了避免冲突,IIC通信采用主从模式。
主从模式就是一台设备为主机,另一台(可多台)为从机。在这种模式下,通信只能由主机先发起,从机根据主机的指令回复相应的信息。主从模式使得IIC可以支持多设备通信,像IIC支持多个设备进行通信的通信协议称为总线协议。在IIC总线上,每个设备都有唯一的地址。当主机需要操作IIC总线上的某个设备时,只需要在发送数据的最开始发送该设备的设备地址,那么该设备就可以知道此数据是发送给它的,从而做出反应。而其他设备则明白此数据与自己无关,则不做出反应。例如本次实验要用到的AHT20的设备地址就是0x70,所以我们在最开始需要向主机发送0x70。
考虑到许多小型传感器并没有精确的晶振提供时钟基准等情况,IIC可以选择同步通信。同步通信就是由主机通过时钟线发送固定频率的脉冲信号来作为IIC总线上所有设备通信的统一时钟源,本次实验要实现的AHT20温湿度读取就是采用同步通信。
在简单了解了IIC通信协议后,需要基于IIC通信协议读取AHT20传感器中的温湿度信息,并在串口中打印出来。
读取AHT20模块中的温湿度信息
由于IIC的数据线与时钟线往往都有上拉电阻进行上拉,在未开始通信时,数据线和时钟线都处于高电平。主机(STM32)发送IIC通信启动信号,即在时钟线依旧是高电平时将数据线提前下拉,所有从机就可以准备接收命令,正式开始IIC通信。在通信过程中,主机会在时钟线上产生一个恒定频率的时钟脉冲信号,主机与从机依靠时钟线上的脉冲信号来同步对数据线的读写,也就是当时钟线处于低电平时,主机设置数据线的电平,而时钟线处于高电平时,从机读取数据线的电平。显然,从机读取到的电平就是主机在时钟线低电平时设置的电平。
AHT20的初始化
按照AHT20的数据手册,AHT20的IIC地址是0x38(0011 1000).虽然IIC通信一般使用7位地址码,但是由于IIC通信过程中每次发送都是1字节(8位),所以规定从机地址要向左移一位(0111 0000)。IIC协议规定,如果主机发起通信的目的是为了设置(写)从机,那最后一位(左移后多出来的那位)应该设置为0;如果主机发起通信的目的是为了从从机读取数据,那这一位为1。不过这一位的设置,HAL的相关函数会自动进行处理,所以一般默认这一位为0。
在AHT20的读取流程中,主机应该先发送AHT20的数据地址(0x70),实际发送的是0x71。时钟线低电平时,主机先给数据线设置低电平,然后时钟线高电平,从机读取到0。时钟线低电平,主机给数据线设置高电平,时钟线高电平,从机读取到1(0111 0001),依次循环,直到主机发完8位(1字节数据)。
void AHT20_Init()
{
uint8_t readBuffer;
HAL_Delay(40);
//上电后要等待40ms
HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DELAY);
//由于这个命令是读取从机的数据,所以0X70会被HAL库改为0X71使用
if((readBuffer & 0x08) == 0x00) //检验状态字的校准使能位(Bit[3])是否为1
{
uint8_t sendBuffer[3] = { 0xBE, 0x08, 0x00 };
//Bit[3] != 1,要发送0xBE命令(初始化),此命令参数有两个字节:0x08,0x00
HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DELAY);
}
}
AHT20的读取函数
在主机读取AHT20的过程中,按照IIC协议的约定,数据的接收方需要发送一个ACK信号(应答信号)确认已经接收到数据。所谓ACK信号就是在时钟线低电平时,由接收方(从机)将数据线拉低。接下来,作为从机的AHT20来控制数据线,时钟依旧是由主机STM32产生的时钟脉冲信号。AHT20像主机一样,在时钟线低电平时设置数据线。主机也是在时钟线高电平时读取数据线上的数据,如此反复,直到发送完1字节。此时,由现在的接收方(主机)来发送一次ACK信号,然后AHT20继续发送,如此反复直到发送完所有的数据。主机会在时钟线处于高电平时将数据线拉高(就是发送IIC通信结束信号,意味整段通信的完成)。从上述过程中可以发现,在整个IIC通信过程中,只有主机发送开始和结束信号时,才会在时钟线为高时控制数据线,其他阶段都只能在时钟线为低时设置数据线。
//为了能将数据传递到AHT20——Read函数的调用方,需要将数据定义为指针类型
void AHT20_Read(float *Temperature, float *Humidity)
{
uint8_t sendBuffer[3] = { 0xAC, 0x33, 0x00};
//要触发测量,需要直接发送0xAC命令,此命令参数有两个字节:0x33,0x00
uint8_t readBUffer[6];
HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DELAY);
HAL_Delay(75);
//等待75ms待测量完成
HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,readBUffer,6,HAL_MAX_DELAY);
//读取六个字节(发送0x71可以读取)
if((readBUffer[0] & 0x80) == 0x00)
//如果第0字节的第7位位0,就说明获取的数据确实是刚刚测量完成的数据,就可以开始温湿度的计算
{
//AHT20的数据手册指出温度数据和湿度数据各占两个半字节,定义data用于数据拼接
uint32_t data = 0;
//第3字节的高4位是湿度数据,低4位是温度数据;第2字节和第1字节为湿度数据
// 0000 xxxx + xxxx xxxx 0000+ xxxx xxxx 0000 0000 0000
data = ((uint32_t)readBUffer[3] >> 4) + ((uint32_t)readBUffer[2] << 4) + ((uint32_t)readBUffer[1] << 12);
//要将uint8_t的数据改为uint32_t,否则会出现溢出而导致数据丢失
*Humidity = data * 100.0f / (1 << 20);
//相对湿度(%)=(SDA输出的相对湿度信号/2的20次方)*100%,转换为浮点数计算,避免丢失掉小数部分
//xxxx 0000 0000 0000 0000 + xxxx xxxx 0000 0000 + xxxx xxxx
data = (((uint32_t)readBUffer[3] & 0x0F) << 16) + ((uint32_t)readBUffer[4] << 8) + (uint32_t)readBUffer[5];
*Temperature = data * 200.0f / (1 << 20) - 50;
// 温度 = (温度输出信号 / 2的20次方)* 200 - 50
}
}
串口打印温湿度数据
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_GPIO_Init();
MX_LPUART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
AHT20_Init();
float temperature, humidity;
char message[50];
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
AHT20_Read(&temperature, &humidity);
sprintf(message,"温度: %.1f ℃, 湿度: %.1f %%\r\n",temperature,humidity);
//%本身会被理解为占位符,可以用%%来代表一个真正的%
HAL_UART_Transmit(&hlpuart1,(uint8_t*)message,strlen(message),HAL_MAX_DELAY);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
代码中使用的sprintf函数是在对STM32串口重定向后,实现的数据接收。在串口中打印出来读取的温湿度如下图所示:
CubeMX的配置
IIC的配置
“Custom Timing”(自定义时序)选项允许您手动设置IIC总线的时序参数,而不依赖于自动生成的推荐值。本次实验直接使用CubeMX的推荐值。
UART的配置
串口通信可以选择LPUART1,它是STM32系列微控制器提供的一种低功耗UART串行通信接口,适合需要低功耗和稳定通信的应用场景。
“Asynchronous”(异步)通常指的是异步串行通信接口(USART),这种通信接口用于通过串行线路进行点对点的数据传输。配置UART需要选择STM32微控制器上的GPIO引脚来连接USART的TX(发送)和RX(接收)引脚,这里选择PA2作为接收引脚,PA3作为发送引脚。这里设置波特率为115200bits/s,数据位数为8位,无奇偶校验,停止位数为1。