一、前言
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读出的温度与气压数据。我们还可以根据这个气压数据算出我们此时身处的海拔。
五、总结
还是挺有挑战的,网上各种信息壁垒,许多稍微有价值的技术都被打上了价签,知识摆在那,各种门槛令真心想学习的人颇为无奈。这篇分享的是我的学习成果和自己敲的代码,仅供交流学习。