关联:LORA 、HAL、气压传感器
这个传感器也是比较常见的,但灵敏度什么的都没啥问题,不过气压传感器不是很好去观察这个变化,毕竟一个地方的大气压基本不会有太大波动,我们可以在百度搜索所在地的平均大气压,与我们测量得到的大气压进行对比,如果差的不是很多则认为没啥问题。
比如我测得大气压为101.4kpa,百度搜索南京平均大气压,差不多也在101.1左右,所以我们的测量是没问题的,说完这个如何判断我们的测量值是否正确,下面我们来说一下如何通过代码获取到这个气压值
这里我们使用硬件I2C来与气压传感器通信,首先我们需要定义如下寄存器地址
#define BMP280_REG_TEMP_XLSB 0xFC /* bits: 7-4 */
#define BMP280_REG_TEMP_LSB 0xFB
#define BMP280_REG_TEMP_MSB 0xFA
#define BMP280_REG_TEMP (BMP280_REG_TEMP_MSB)
#define BMP280_REG_PRESS_XLSB 0xF9 /* bits: 7-4 */
#define BMP280_REG_PRESS_LSB 0xF8
#define BMP280_REG_PRESS_MSB 0xF7
#define BMP280_REG_PRESSURE (BMP280_REG_PRESS_MSB)
#define BMP280_REG_CONFIG 0xF5 /* bits: 7-5 t_sb; 4-2 filter; 0 spi3w_en */
#define BMP280_REG_CTRL 0xF4 /* bits: 7-5 osrs_t; 4-2 osrs_p; 1-0 mode */
#define BMP280_REG_STATUS 0xF3 /* bits: 3 measuring; 0 im_update */
#define BMP280_REG_CTRL_HUM 0xF2 /* bits: 2-0 osrs_h; */
#define BMP280_REG_RESET 0xE0
#define BMP280_REG_ID 0xD0
#define BMP280_REG_CALIB 0x88
#define BMP280_REG_HUM_CALIB 0x88
#define BMP280_RESET_VALUE 0xB6
这些地址我们无需关心,如果只是使用这个传感器获取测量值我们无需关心太多底层的东西,我们只需要把精力放在代码逻辑以及实现上,我们可以粗略的理解为这些寄存器有一部分是控制功能,以及配置,有一部分是存放数据的,需要我么去读这个寄存器。
网上目前能找到的寄存器手册是博士的,手册是全英文的,但我们只需要关系重点信息,比如这个ID,这个ID就是我们去读器件指定的一个地址,可以读到一个值,这个值就是这个器件的ID,这个寄存器是可读寄存器,一般这个ID是出厂就固定的,不可修改。
我们读这个ID的作用是什么呢?
1、通过读这个ID我们可以判断设备的大概工作状态,以及与设备通信的链路是否正常,手册可以看到设备的ID为0x58,并且要读到这个ID,我们必须在传感器完成上电和重启之后
2、这个ID我们可以写到器件初始化里面,通过初始化时读取这个器件ID实现判断器件是否正常工作和准备就绪
这个寄存器是设备状态寄存器,一共有两个bit构成,手册了分别有对这两个bit的具体含义说明,这个我们一般也不用太过关心
这个是我们需要好好去研究的寄存器,我们所需要的压力数据就是从这个寄存器获得,分别有三个寄存器,这三个寄存器分别保存压力数据的高位、低位、小数位,注意0xF9里面只有bit7-bit4是我们的需要的,其余四个bit我们不用关心。
知道从哪个寄存器读取数据,我们也要关心一下读到的数据是不是直接可用,还是需要进行转换,通过手册我们可以看到读到的单位为Pa,我们日常使用的是KPa,我们将得到的值缩小一千倍就可以。
下面是部分核心代码
/**
* @brief 读取 BMP280 或 BME280 传感器的温度、压力和湿度数据。
*
* @param dev 指向 BMP280 设备的句柄,包含设备的配置信息和标识符。
* @param temperature 指向整型变量的指针,用于存储读取到的温度值(单位:摄氏度)。
* @param pressure 指向无符号整型变量的指针,用于存储读取到的压力值(单位:Pa)。
* @param humidity 指向无符号整型变量的指针,用于存储读取到的湿度值(单位:%RH)。
*
* @return
* - true: 数据读取成功。
* - false: 数据读取失败,可能由于设备通信错误或读取数据不正确。
*
* @note
* - 该函数根据传感器的 ID 检查是否支持湿度读取。如果设备不是 BME280,则湿度值将被置为 0,且湿度指针将被设置为 NULL。
* - 温度、压力和湿度的读取操作在一个序列中完成,以确保数据的一致性。
* - 读取的数据格式为 6 或 8 字节,具体取决于湿度是否被请求。对应的读取地址为 0xf7。
* - 原始压力和温度数据由传感器返回,并通过相应的补偿函数进行转换,得出实际的物理值。
* - 如果请求湿度数据,则还将读取 2 字节的湿度值,并使用补偿函数进行处理。
*/
bool bmp280_read_fixed(BMP280_HandleTypedef *dev, int32_t *temperature, uint32_t *pressure,
uint32_t *humidity) {
int32_t adc_pressure; // 原始压力数据
int32_t adc_temp; // 原始温度数据
uint8_t data[8]; // 存储读取的原始数据
// 只有 BME280 支持读取湿度。
if (dev->id != BME280_CHIP_ID) {
if (humidity)
*humidity = 0; // 如果不支持湿度,设置湿度为 0
humidity = NULL; // 将湿度指针设置为 NULL
}
// 需要一次性读取,以确保数据一致性。
size_t size = humidity ? 8 : 6; // 根据是否读取湿度确定读取字节数
if (read_data(dev, 0xf7, data, size)) {
return false; // 读取数据失败
}
// 解析原始压力和温度数据
adc_pressure = data[0] << 12 | data[1] << 4 | data[2] >> 4;
adc_temp = data[3] << 12 | data[4] << 4 | data[5] >> 4;
int32_t fine_temp; // 用于补偿温度计算的临时变量
*temperature = compensate_temperature(dev, adc_temp, &fine_temp); // 补偿温度
*pressure = compensate_pressure(dev, adc_pressure, fine_temp); // 补偿压力
if (humidity) {
// 读取并补偿湿度数据
int32_t adc_humidity = data[6] << 8 | data[7];
*humidity = compensate_humidity(dev, adc_humidity, fine_temp); // 补偿湿度
}
return true; // 成功读取所有数据
}
忘记说了,BMP280传感器也能测温度,但是我们有AHT20所以就不需要他的温度值,从上面的代码来看,比较关键的函数有两个,这两个函数分别处理温度和气压,这里我们只关心气压compensate_pressure,具体实现如下:
/**
* @brief 补偿 BMP280 传感器的原始压力数据,计算实际压力值。
*
* @param dev 指向 BMP280 设备的句柄,包含设备的校准系数。
* @param adc_press 原始压力数据,从传感器读取的 ADC 值。
* @param fine_temp 经过补偿的温度值,用于压力计算的精确度提高。
*
* @return 计算得到的实际压力值(单位:Pa)。
* 如果计算过程中出现除零错误,则返回 0。
*
* @note
* - 该函数根据传感器的校准系数(如 dig_P1, dig_P2 等)进行复杂的补偿计算。
* - 计算过程中使用了多个中间变量(var1 和 var2)来帮助获得最终的压力值。
* - 为了避免除零错误,函数首先检查 var1 是否为 0。如果是,则返回 0。
* - 返回的压力值是以 Pa 为单位的实际压力值,可用于进一步的数据处理和分析。
*/
static inline uint32_t compensate_pressure(BMP280_HandleTypedef *dev, int32_t adc_press,
int32_t fine_temp) {
int64_t var1, var2, p;
var1 = (int64_t) fine_temp - 128000; // 将温度值转换为 var1
var2 = var1 * var1 * (int64_t) dev->dig_P6; // 根据校准系数计算 var2
var2 = var2 + ((var1 * (int64_t) dev->dig_P5) << 17); // 累加压力校准系数
var2 = var2 + (((int64_t) dev->dig_P4) << 35); // 累加压力校准系数
var1 = ((var1 * var1 * (int64_t) dev->dig_P3) >> 8)
+ ((var1 * (int64_t) dev->dig_P2) << 12); // 完成 var1 的计算
var1 = (((int64_t) 1 << 47) + var1) * ((int64_t) dev->dig_P1) >> 33; // 计算最终的 var1
if (var1 == 0) {
return 0; // 避免因除零而引发的异常
}
p = 1048576 - adc_press; // 计算压力的初步值
p = (((p << 31) - var2) * 3125) / var1; // 应用补偿公式
// 根据校准系数计算最终压力值的修正
var1 = ((int64_t) dev->dig_P9 * (p >> 13) * (p >> 13)) >> 25;
var2 = ((int64_t) dev->dig_P8 * p) >> 19;
p = ((p + var1 + var2) >> 8) + ((int64_t) dev->dig_P7 << 4); // 计算最终压力值
return p; // 返回实际压力值
}
这个玩意具体实现不需要理解,我们只用知道返回的为实际压力值即可,找个变量接着返回值就OK啦,
手册上也提供了一些计算代码,感兴趣可以去研究一下,我们这里调用这个函数的时候传入指向无符号整型变量的指针,用来保存数据。
我们在实际展示的时候缩小1000倍就可以,至此气压传感器的驱动实现完成。