基于STM32和AHT20+BMP280制作的的温度、湿度和气压采集器

一、前言

        aht20是一个温度&湿度传感器,bmp280是一个温度&气压传感器。

        前一阵子在某宝几乎0成本购入了一个野生的aht20+bmp280一体模块,询问店家发现店家手上只有一个驱动例程,没有别的资料。观察了这个模块,我发现它应该是用的i2c总线连接了两个从设备做成了个一体的模块,所以我就从官网上找到了这两个模块的数据手册,抱着学习的心自己写了一个更加通俗易懂的驱动,经过运行调试之后测得数据没有问题。

二、思路

        aht20和bmp280都可以使用i2c进行通信,所以我们只要知道i2c的通信原理,然后照着它们的数据手册一步步配置寄存器并使用hal库中封装好的i2c通信函数就可以把驱动敲出来了。

        这里我们就不说引脚配置了,驱动与引脚无关。

三、实践分析

一、aht20模块

1、上电

        我们从数据手册中可以得知,首先通电延时100ms

2、读状态字

        我们需要使用HAL_I2C_Master_Receive(&hi2c1, 0x71, data, 1, 1000);可以来读取到一个状态字并且存放在data变量中(因为设备通电初始化完成后会默认指向0x38地址的寄存器。为什么接收是0x71?因为i2c的从机地址占7位,但是它需要传8位地址,最后一位是读取操作时为1,所以是01110001,也就是0x71)。

3、发送测量命令

        然后我们等待10ms后发送命令0xAC(触发测量),此命令参数有两个字节,第一个字节为0x33,第二个字节为0x00,等待80ms待测量完成。

uint8_t send[3] = {0xAC,0x33,0x00};//读取数据指令
HAL_I2C_Master_Transmit(&hi2c1, aht20_device_address,send,3,1000);//发送指令
HAL_Delay(80);//延时

4、读取状态字       

        然后我们继续根据手册看 “若读取状态字Bit[7]为0,表示测量完成,然后可以连续
读取六个字节;否则继续等待。”

        这里我测试过了,正常情况下延时80ms是可以读到数据的,所以直接写了个if没有处理else。

	HAL_I2C_Master_Receive(&hi2c1, 0x71, data, 6, 1000);//读取数据
	HAL_Delay(80);
	if((data[0] & 0x80) == 0x00)
	{
	    uint32_t shidu,wendu; 
		shidu = ((uint32_t)data[1] << 12) + ((uint32_t)data[2] << 8) + (data[3] >> 4);
		*humidity = (shidu * 100.0f)/(1<<20);
		
		wendu = (((uint32_t)data[3]&0x0f) << 16) + ((uint32_t)data[4] << 8) + (data[5]);
		*temperature = ((wendu * 200.0f)/(1<<20)) - 50.0;
	}

5、读取数据

我们根据手册可知:

        收到的第一个字节是状态位,后面第2到6个字节收到的是温湿度数据,其中第二个字节、第三个字节和第四个字节的高四位是湿度数据,第四个字节的低四位、第五个字节和第六个字节是温度数据。我们只需要将对应字节拿出进行拼接即可。

uint32_t shidu,wendu; 
shidu = ((uint32_t)data[1] << 12) + ((uint32_t)data[2] << 8) + (data[3] >> 4);	
wendu = (((uint32_t)data[3]&0x0f) << 16) + ((uint32_t)data[4] << 8) + (data[5]);

注意移位操作时将数据长度拉长再移位,避免数据丢失。

6、数据处理

        没啥好说的我们继续看文档

*humidity = (shidu * 100.0f)/(1<<20);
*temperature = ((wendu * 200.0f)/(1<<20)) - 50.0;

二、bmp280模块

1、上电读取设备id

        根据官方文档可知我们需要商上电读取0xD0,看id是否为0x58,若不是0x58我们就给它发送初始化指令。

void bmp280_init()
{
	uint8_t data;
	uint8_t idAddress = 0xD0;
	uint8_t reset[2] = {0xE0,0x86};//0xE0 寄存器地址 0x86 复位命令
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&idAddress,1,1000);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address , &data, 1, 1000);
	if(data != 0x58)
	{
		HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,reset,2,1000);
	}
}

参考:

2、发送测量命令

        格式如图

 

uint8_t data[6];
	uint8_t Compensation_parameter[24];
	uint8_t StartReadData[2] = {0xF4,0x47};//0xF4 寄存器地址 000 温度 000 气压 00 工作模式 这里是010 001 11
	uint8_t ReadData = 0xF7;//数据存储寄存器的起始地址,一共6个
	uint8_t Compensation = 0x88;//校准参数的设定,存储在0x88寄存器起始地址,一共26个寄存器
	
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,StartReadData,2,1000);//发送命令
	HAL_Delay(2);
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&ReadData,1,1000);//指定数据寄存器0xF7
	HAL_Delay(1);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, data, 6, 1000);//读取数据寄存器,并连续读取6个寄存器的数据
	HAL_Delay(6);

 

 

3、读取校准数据

        文档有点小问题,经过检验,下面这个是存储压强数据寄存器地址

        下面这个是存储温度的寄存器地址

代码:

	//读取校准数据
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&Compensation,1,1000);
	HAL_Delay(1);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, Compensation_parameter, 24, 1000);
	HAL_Delay(26);
	uint16_t dig_T1 = (((uint16_t)Compensation_parameter[1])<<8) + ((uint16_t)Compensation_parameter[0]);
	int16_t dig_T2= (((int16_t)Compensation_parameter[3])<<8) + ((int16_t)Compensation_parameter[2]);
	int16_t dig_T3= (((int16_t)Compensation_parameter[5])<<8) + ((int16_t)Compensation_parameter[4]);
	uint16_t dig_P1 = (((uint16_t)Compensation_parameter[7])<<8) + ((uint16_t)Compensation_parameter[6]);
	int16_t dig_P2= (((int16_t)Compensation_parameter[9])<<8) + ((int16_t)Compensation_parameter[8]);
	int16_t dig_P3= (((int16_t)Compensation_parameter[11])<<8) + ((int16_t)Compensation_parameter[10]);
	int16_t dig_P4= (((int16_t)Compensation_parameter[13])<<8) + ((int16_t)Compensation_parameter[12]);
	int16_t dig_P5= (((int16_t)Compensation_parameter[15])<<8) + ((int16_t)Compensation_parameter[14]);
	int16_t dig_P6= (((int16_t)Compensation_parameter[17])<<8) + ((int16_t)Compensation_parameter[16]);
	int16_t dig_P7= (((int16_t)Compensation_parameter[19])<<8) + ((int16_t)Compensation_parameter[18]);
	int16_t dig_P8= (((int16_t)Compensation_parameter[21])<<8) + ((int16_t)Compensation_parameter[20]);
	int16_t dig_P9= (((int16_t)Compensation_parameter[23])<<8) + ((int16_t)Compensation_parameter[22]);
	HAL_Delay(1);

4、数据处理

        这里是完完全全跟着官方文档给的公式敲的:

代码:

    double adc_T,adc_P;
	double var1,var2;
	int32_t t_fine;
	adc_P = (((uint32_t)data[0])<<12) + (((uint32_t)data[1])<<4) + ((((uint32_t)data[2])>>4)&0x0f);
	
	adc_T = (((uint32_t)data[3])<<12) + (((uint32_t)data[4])<<4) + ((((uint32_t)data[5])>>4)&0x0f);
	var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2);
	var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) *
	(((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3);
	t_fine = (signed int)(var1 + var2);
	
	*temp = (t_fine)/5120.0;
	HAL_Delay(1);
	var1 = ((double)t_fine/2.0) - 64000.0;
	var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
	var2 = var2 + var1 * ((double)dig_P5) * 2.0;
	var2 = (var2/4.0) + (((double)dig_P4) * 65536.0);
	var1 = (((double)dig_P3) * var1*var1 / 524288.0 + ((double)dig_P2) * var1)/524288.0;
	var1 = (1.0 + var1 / 32768.0) * ((double)dig_P1);
	HAL_Delay(1);
	double p = 1048576.0 - (double)adc_P;
	p = (p-(var2/4096.0)) * 6250.0 / var1;
	
	var1 = ((double)dig_P9) * p * p / 2147483648.0;
	var2 = p * ((double)dig_P8) / 32768.0;
	
	*press = p + (var1 + var2 + ((double)dig_P7)) / 16.0;

 

三、完整代码

#include "AHT20_BMP280.h"

	
void aht20_init()
{
	HAL_Delay(100);//上电延时100秒
//	HAL_I2C_Master_Receive(&hi2c1, 0x70, data, 1, 1000);
}

void aht20_read(float* humidity,float* temperature)
{
	uint8_t data[6];//存放读取到的数据
	HAL_Delay(10);//延时
	uint8_t send[3] = {0xAC,0x33,0x00};//读取数据指令
	HAL_I2C_Master_Transmit(&hi2c1, aht20_device_address,send,3,1000);//发送指令
	HAL_Delay(80);//延时
	
	HAL_I2C_Master_Receive(&hi2c1,aht20_device_address, data, 6, 1000);//读取数据
	HAL_Delay(80);
	if((data[0] & 0x80) == 0x00)
	{
	    uint32_t shidu,wendu; 
		shidu = ((uint32_t)data[1] << 12) + ((uint32_t)data[2] << 8) + (data[3] >> 4);
		*humidity = (shidu * 100.0f)/(1<<20);
		
		wendu = (((uint32_t)data[3]&0x0f) << 16) + ((uint32_t)data[4] << 8) + (data[5]);
		*temperature = ((wendu * 200.0f)/(1<<20)) - 50.0;
	}
}

void bmp280_init()
{
	uint8_t data;
	uint8_t idAddress = 0xD0;
	uint8_t reset[2] = {0xE0,0x86};//0xE0 寄存器地址 0x86 复位命令
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&idAddress,1,1000);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address , &data, 1, 1000);
	if(data != 0x58)
	{
		HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,reset,2,1000);
	}
}


void bmp280_read(double* press,double* temp)
{
	uint8_t data[6];
	uint8_t Compensation_parameter[24];
	uint8_t StartReadData[2] = {0xF4,0x47};//0xF4 寄存器地址 000 温度 000 气压 00 工作模式 这里是010 001 11
	uint8_t ReadData = 0xF7;//数据存储寄存器的开始,一共6个
	uint8_t Compensation = 0x88;//校准参数的设定,存储在0x88寄存器开始,一共26个寄存器
	
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,StartReadData,2,1000);//发送命令
	HAL_Delay(1);
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&ReadData,1,1000);//指定数据寄存器0xF7
	HAL_Delay(1);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, data, 6, 1000);//读取数据寄存器,并连续读取6个寄存器的数据
	HAL_Delay(6);
	//读取校准数据
	HAL_I2C_Master_Transmit(&hi2c1, bmp280_device_address,&Compensation,1,1000);
	HAL_Delay(1);
	HAL_I2C_Master_Receive(&hi2c1, bmp280_device_address, Compensation_parameter, 24, 1000);
	HAL_Delay(26);
	uint16_t dig_T1 = (((uint16_t)Compensation_parameter[1])<<8) + ((uint16_t)Compensation_parameter[0]);
	int16_t dig_T2= (((int16_t)Compensation_parameter[3])<<8) + ((int16_t)Compensation_parameter[2]);
	int16_t dig_T3= (((int16_t)Compensation_parameter[5])<<8) + ((int16_t)Compensation_parameter[4]);
	uint16_t dig_P1 = (((uint16_t)Compensation_parameter[7])<<8) + ((uint16_t)Compensation_parameter[6]);
	int16_t dig_P2= (((int16_t)Compensation_parameter[9])<<8) + ((int16_t)Compensation_parameter[8]);
	int16_t dig_P3= (((int16_t)Compensation_parameter[11])<<8) + ((int16_t)Compensation_parameter[10]);
	int16_t dig_P4= (((int16_t)Compensation_parameter[13])<<8) + ((int16_t)Compensation_parameter[12]);
	int16_t dig_P5= (((int16_t)Compensation_parameter[15])<<8) + ((int16_t)Compensation_parameter[14]);
	int16_t dig_P6= (((int16_t)Compensation_parameter[17])<<8) + ((int16_t)Compensation_parameter[16]);
	int16_t dig_P7= (((int16_t)Compensation_parameter[19])<<8) + ((int16_t)Compensation_parameter[18]);
	int16_t dig_P8= (((int16_t)Compensation_parameter[21])<<8) + ((int16_t)Compensation_parameter[20]);
	int16_t dig_P9= (((int16_t)Compensation_parameter[23])<<8) + ((int16_t)Compensation_parameter[22]);
	HAL_Delay(1);
	//数据处理
	double adc_T,adc_P;
	double var1,var2;
	int32_t t_fine;
	adc_P = (((uint32_t)data[0])<<12) + (((uint32_t)data[1])<<4) + ((((uint32_t)data[2])>>4)&0x0f);
	
	adc_T = (((uint32_t)data[3])<<12) + (((uint32_t)data[4])<<4) + ((((uint32_t)data[5])>>4)&0x0f);
	var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2);
	var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) *
	(((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3);
	t_fine = (signed int)(var1 + var2);
	
	*temp = (t_fine)/5120.0;
	HAL_Delay(1);
	var1 = ((double)t_fine/2.0) - 64000.0;
	var2 = var1 * var1 * ((double)dig_P6) / 32768.0;
	var2 = var2 + var1 * ((double)dig_P5) * 2.0;
	var2 = (var2/4.0) + (((double)dig_P4) * 65536.0);
	var1 = (((double)dig_P3) * var1*var1 / 524288.0 + ((double)dig_P2) * var1)/524288.0;
	var1 = (1.0 + var1 / 32768.0) * ((double)dig_P1);
	HAL_Delay(1);
	double p = 1048576.0 - (double)adc_P;
	p = (p-(var2/4096.0)) * 6250.0 / var1;
	
	var1 = ((double)dig_P9) * p * p / 2147483648.0;
	var2 = p * ((double)dig_P8) / 32768.0;
	
	*press = p + (var1 + var2 + ((double)dig_P7)) / 16.0;
}


四、结果

        第一第二行是aht20读出的温度与湿度数据,第三第四行是bmp280读出的温度与气压数据。我们还可以根据这个气压数据算出我们此时身处的海拔。

五、总结

        还是挺有挑战的,网上各种信息壁垒,许多稍微有价值的技术都被打上了价签,知识摆在那,各种门槛令真心想学习的人颇为无奈。这篇分享的是我的学习成果和自己敲的代码,仅供交流学习。

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值