BMP280是大气压传感器,大气压和海拔有关,所以测量的大气压可以转换成高度,minifly用BMP280来给四轴飞控定高。
BMP280寄存器
#define BMP280_I2C_ADDR (0x76) //I2C通讯地址
#define BMP280_DEFAULT_CHIP_ID (0x58) //ID值:0X58
#define BMP280_CHIP_ID (0xD0) /* Chip ID Register */
#define BMP280_RST_REG (0xE0) /* Softreset Register */
#define BMP280_STAT_REG (0xF3) /* Status Register */
#define BMP280_CTRL_MEAS_REG (0xF4) /* Ctrl Measure Register */
#define BMP280_CONFIG_REG (0xF5) /* Configuration Register */
#define BMP280_PRESSURE_MSB_REG (0xF7) /* Pressure MSB Register */
#define BMP280_PRESSURE_LSB_REG (0xF8) /* Pressure LSB Register */
#define BMP280_PRESSURE_XLSB_REG (0xF9) /* Pressure XLSB Register */
#define BMP280_TEMPERATURE_MSB_REG (0xFA) /* Temperature MSB Reg */
#define BMP280_TEMPERATURE_LSB_REG (0xFB) /* Temperature LSB Reg */
#define BMP280_TEMPERATURE_XLSB_REG (0xFC) /* Temperature XLSB Reg */
Chip ID Register:寄存器内固定值为0x58,读取地址0xD0数据的时候,传感器返回0x58,代表身份辨认完毕。
Softreset Register:写入0xB6时,所有寄存器(除身份编号寄存器)数据全部清零。
status Register:状态寄存器,包含了两个位的数据。
状态寄存器 | 位名 | 描述 |
---|---|---|
Bit 3 | measuring[0] | 转换发生时设定为1,结果转存后转换为0 |
Bit 0 | im_update[0] | NVM数据拷贝时设定为1,拷贝结束后设置为0 |
Ctrl Measure Register:设备的运行状态寄存器
ctrl_meas 寄存器 | 位名称 | 描述 |
---|---|---|
Bit 7, 6, 5 | osrs_t[2:0] | 控制温度的过采样 |
Bit 4, 3, 2 | osrs_p[2:0] | 控制气压的过采样 |
Bit 1, 0 | mode[1:0] | 控制电流的模式 |
Configuration Register:配置寄存器用于控制采样率,滤波器模式和通讯模式。
config寄存器 | 位名 | 描述 |
---|---|---|
Bit 7, 6, 5 | t_sb[2:0] | 设定normal_mode下的无效时间 |
Bit 4, 3, 2 | filter[2:0] | 设定IIR滤波器 |
Bit 0 | spi3w_en[0] | spi通讯模式开启标志 1表示开启 |
press(_msb, _lsb, _xlsb) :这几个寄存器就是存储ADC读出来的气压数据寄存器了,XLSB应该是小数位的意思,后面用来浮点转化用的。
press寄存器 | 位名 | 描述 |
---|---|---|
0xF7 | press_msb[7:0] | 压力的msb部分 19:12 |
0xF8 | press_lsb[7:0] | 压力的lsb部分 11:4 |
0x F9 (bit 7, 6, 5, 4) | press_xlsb[3:0] | 压力的xlsb部分 3:0 |
temp(_msb, _lsb, _xlsb) :温度数据寄存器
temp寄存器 | 位名 | 描述 |
---|---|---|
0xFA | temp_msb[7:0] | 温度的msb部分 19:12 |
0xFB | temp_lsb[7:0] | 温度的lsb部分 11:4 |
0xFC(bit 7, 6, 5, 4) | temp_xlsb[3:0] | 温度的xlsb部分 3:0 |
BMP280的三种模式
BMP280提供了三种工作模式:
- 休眠模式(Sleep mode) :传感器进入休眠状态,停止采集数据,但寄存器的值不变;
- 强制模式(Forced mode): 进行一次数据采集,采集完成后返回休眠模式;
- 普通模式(Normal mode):循环进行多次数据采集。
#define BMP280_SLEEP_MODE (0x00) //睡眠模式、强制模式、正常模式
#define BMP280_FORCED_MODE (0x01)
#define BMP280_NORMAL_MODE (0x03)
BMP280过采样设置
BMP280提供了温度和气压的过采样设置,其意义是一次测量多个数据,将这些数据进行求和平均得到输出值。例如将气压ADC过采样设置为×16,那么一次测量16个数据,将这16个数据进行求和平均得到最终的输出值。
/*
过采样设置
0 禁用
1 过采样×1
2 过采样×2
4 过采样×4
....
*/
#define BMP280_OVERSAMP_SKIPPED (0x00)
#define BMP280_OVERSAMP_1X (0x01)
#define BMP280_OVERSAMP_2X (0x02)
#define BMP280_OVERSAMP_4X (0x03)
#define BMP280_OVERSAMP_8X (0x04)
#define BMP280_OVERSAMP_16X (0x05)
微调参数
每一个BMP280设备都存在一定的微调参数,这些微调参数在设备生产过程中,就已经被写入到设备的NVM中,并且不支持客户进行修改,在计算采集的温度数据时,都应该通过微调参数的调整。
//校准数据寄存器,总共是26个寄存器,存储了计算压力温度最终值的厂家校准数据,高低两位
typedef struct
{
u16 dig_T1; /* calibration T1 data *///typedef uint16_t u16;
s16 dig_T2; /* calibration T2 data */
s16 dig_T3; /* calibration T3 data */
u16 dig_P1; /* calibration P1 data */
s16 dig_P2; /* calibration P2 data */
s16 dig_P3; /* calibration P3 data */
s16 dig_P4; /* calibration P4 data */
s16 dig_P5; /* calibration P5 data */
s16 dig_P6; /* calibration P6 data */
s16 dig_P7; /* calibration P7 data */
s16 dig_P8; /* calibration P8 data */
s16 dig_P9; /* calibration P9 data */
s32 t_fine; /* calibration t_fine data */
} bmp280Calib;
BMP280 初始化
输入:I2C端口
输出:0:false 1:true
细节在代码解析中
static bool isInit = false;
bool bmp280Init(I2C_Dev *i2cPort)
{
if (isInit)
return true;
I2Cx = i2cPort;
devAddr = BMP280_I2C_ADDR;
delay_xms(50);
i2cdevReadByte(I2Cx, devAddr, BMP280_CHIP_ID, &bmp280ID); /* 读取bmp280 ID*/
if(bmp280ID == BMP280_DEFAULT_CHIP_ID) //若读出的ID等于默认值,则成功且打印显示。
printf("BMP280 ID IS: 0x%X\n",bmp280ID);
else
return false;
/* 读取校准数据,一共有24个,地址从BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG开始*/
i2cdevRead(I2Cx, devAddr, BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG, 24, (u8 *)&bmp280Cal);
/*设置BMP过采样因子 MODE,用到Ctrl Measure Register:设备的运行状态寄存器,上文有介绍*/
i2cdevWriteByte(I2Cx, devAddr, BMP280_CTRL_MEAS_REG, BMP280_MODE);
/*设置保持时间和滤波器分频因子,BMP280内容自带一个IIR滤波器,关于滤波器的具体笔者不了解太多。*/
i2cdevWriteByte(I2Cx, devAddr, BMP280_CONFIG_REG, 5<<2); /*配置IIR滤波*/
isInit = true;//初始化完成,置1
return true;
}
获取气压值
static void bmp280GetPressure(void)
{
u8 data[BMP280_DATA_FRAME_SIZE]; //data[6]:分别为气压的和温度_msb, _lsb, _xlsb
// read data from sensor
i2cdevRead(I2Cx, devAddr, BMP280_PRESSURE_MSB_REG, BMP280_DATA_FRAME_SIZE, data);
bmp280RawPressure = (s32)((((uint32_t)(data[0])) << 12) | (((uint32_t)(data[1])) << 4) | ((uint32_t)data[2] >> 4));//寄存器的值组合起来
bmp280RawTemperature = (s32)((((uint32_t)(data[3])) << 12) | (((uint32_t)(data[4])) << 4) | ((uint32_t)data[5] >> 4));
}
数据拟合
BMP280读取到的数据是电压经过AD转换后的数值,并非最终的气压。需要根据气压和电压的曲线进行拟合,拟合系数储存在BMP280的寄存器当中。测量步骤:1、读取拟合系数;2、读取原始AD数据;3、进行拟合最后得出气压。具体如下:
u32 bmp280CompensateT(s32 adcT)
{
s32 var1, var2, T;
var1 = ((((adcT >> 3) - ((s32)bmp280Cal.dig_T1 << 1))) * ((s32)bmp280Cal.dig_T2)) >> 11;
var2 = (((((adcT >> 4) - ((s32)bmp280Cal.dig_T1)) * ((adcT >> 4) - ((s32)bmp280Cal.dig_T1))) >> 12) * ((s32)bmp280Cal.dig_T3)) >> 14;
bmp280Cal.t_fine = var1 + var2;
T = (bmp280Cal.t_fine * 5 + 128) >> 8;
return T;
}
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa
u32 bmp280CompensateP(s32 adcP)
{
int64_t var1, var2, p;
var1 = ((int64_t)bmp280Cal.t_fine) - 128000;
var2 = var1 * var1 * (int64_t)bmp280Cal.dig_P6;
var2 = var2 + ((var1*(int64_t)bmp280Cal.dig_P5) << 17);
var2 = var2 + (((int64_t)bmp280Cal.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)bmp280Cal.dig_P3) >> 8) + ((var1 * (int64_t)bmp280Cal.dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)bmp280Cal.dig_P1) >> 33;
if (var1 == 0)
return 0;
p = 1048576 - adcP;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)bmp280Cal.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)bmp280Cal.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)bmp280Cal.dig_P7) << 4);
return (uint32_t)p;
}
将压力转换成高度
一个公式转换具体没深究
(81条消息) 根据温度、气压计算海拔高度_hypsometric公式_大强强小强强的博客-CSDN博客
/**
* 将压力转换为海平面以上高度(ASL) ,单位为米
*/
float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/)
{
if(*pressure > 0)
{
return 44330.f * (powf((1015.7f / *pressure), 0.190295f) - 1.0f);
}
else
{
return 0;
}
}
获得最终数据
void bmp280GetData(float* pressure, float* temperature, float* asl)
{
static float t;
static float p;
bmp280GetPressure();
t = bmp280CompensateT(bmp280RawTemperature)/100.0;
p = bmp280CompensateP(bmp280RawPressure)/25600.0;
pressureFilter(&p,pressure);
*temperature = (float)t;/*单位度*/
// *pressure = (float)p ; /*单位hPa*/
*asl=bmp280PressureToAltitude(pressure); /*转换成海拔*/
}
一些参考:(75条消息) STM32驱动BMP280模块_stm32 bmp280_bdjsm_hh的博客-CSDN博客
全部代码:BMP280.c,BMP280.h
#include <math.h>
#include "stdbool.h"
#include "delay.h"
#include "config.h"
#include "bmp280.h"
/********************************************************************************
* 本程序只供学习使用,未经作者许可,不得用于其它任何用途
* ALIENTEK MiniFly
* BMP280驱动代码
* 正点原子@ALIENTEK
* 技术论坛:www.openedv.com
* 创建日期:2017/5/12
* 版本:V1.3
* 版权所有,盗版必究。
* Copyright(C) 广州市星翼电子科技有限公司 2014-2024
* All rights reserved
********************************************************************************/
/*
传感器测试范围:
温度:-45℃~+85℃
大气压强:0~20000hPa(百帕)
*/
/*
BMP280大气压传感器(我直接叫他高度传感器)看似很冷门,或许大家都觉得,大气压不是一个地区就那么一个值.测量它有什么用?
但是这个模块很神奇,它测量精度很高,大气压和所处海拔关系密切,但是精度高的特点使得它可以测你的所在高度,
你拿着传感器起身,他的ADC值会发生变化.大佬们就用它给飞控定高(也就是很秀的操作,四轴悬浮在那里,一动不动,很神奇吧!)
*/
/*bmp280 气压和温度过采样 工作模式*/
//过采样大概是多次采样,获取一个均值,用于滤波
#define BMP280_PRESSURE_OSR (BMP280_OVERSAMP_8X)
#define BMP280_TEMPERATURE_OSR (BMP280_OVERSAMP_8X)
#define BMP280_MODE (BMP280_PRESSURE_OSR << 2 | BMP280_TEMPERATURE_OSR << 5 | BMP280_NORMAL_MODE)
static uint8_t devAddr;
static I2C_Dev *I2Cx;
static bool isInit;
//校准数据寄存器,总共是26个寄存器,存储了计算压力温度最终值的厂家校准数据,高低两位
typedef struct
{
u16 dig_T1; /* calibration T1 data *///typedef uint16_t u16;
s16 dig_T2; /* calibration T2 data */
s16 dig_T3; /* calibration T3 data */
u16 dig_P1; /* calibration P1 data */
s16 dig_P2; /* calibration P2 data */
s16 dig_P3; /* calibration P3 data */
s16 dig_P4; /* calibration P4 data */
s16 dig_P5; /* calibration P5 data */
s16 dig_P6; /* calibration P6 data */
s16 dig_P7; /* calibration P7 data */
s16 dig_P8; /* calibration P8 data */
s16 dig_P9; /* calibration P9 data */
s32 t_fine; /* calibration t_fine data */ 用于计算补偿
} bmp280Calib;
bmp280Calib bmp280Cal;
static u8 bmp280ID = 0;
static bool isInit = false;
static s32 bmp280RawPressure = 0; //初始气压
static s32 bmp280RawTemperature = 0;//初始温度
static void bmp280GetPressure(void);
bool bmp280Init(I2C_Dev *i2cPort)
{
if (isInit)
return true;
I2Cx = i2cPort;
devAddr = BMP280_I2C_ADDR;
delay_xms(50);
i2cdevReadByte(I2Cx, devAddr, BMP280_CHIP_ID, &bmp280ID); /* 读取bmp280 ID*///#define BMP280_CHIP_ID(0xD0) /* Chip ID Register */
if(bmp280ID == BMP280_DEFAULT_CHIP_ID)
printf("BMP280 ID IS: 0x%X\n",bmp280ID);
else
return false;
/* 读取校准数据 */
i2cdevRead(I2Cx, devAddr, BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG, 24, (u8 *)&bmp280Cal);
//设置BMP过采样因子 MODE
i2cdevWriteByte(I2Cx, devAddr, BMP280_CTRL_MEAS_REG, BMP280_MODE);
//设置保持时间和滤波器分频因子
i2cdevWriteByte(I2Cx, devAddr, BMP280_CONFIG_REG, 5<<2); /*配置IIR滤波*/
// printf("BMP280 Calibrate Registor Are: \r\n");
// for(i=0;i<24;i++)
// printf("Registor %2d: 0x%X\n",i,p[i]);
isInit = true;
return true;
}
static void bmp280GetPressure(void)
{
u8 data[BMP280_DATA_FRAME_SIZE];
// read data from sensor
i2cdevRead(I2Cx, devAddr, BMP280_PRESSURE_MSB_REG, BMP280_DATA_FRAME_SIZE, data);
bmp280RawPressure = (s32)((((uint32_t)(data[0])) << 12) | (((uint32_t)(data[1])) << 4) | ((uint32_t)data[2] >> 4));//寄存器的值组合起来
bmp280RawTemperature = (s32)((((uint32_t)(data[3])) << 12) | (((uint32_t)(data[4])) << 4) | ((uint32_t)data[5] >> 4));
}
//数据补偿转化
//浮点补偿
// Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC
// t_fine carries fine temperature as global value
u32 bmp280CompensateT(s32 adcT)
{
s32 var1, var2, T;
var1 = ((((adcT >> 3) - ((s32)bmp280Cal.dig_T1 << 1))) * ((s32)bmp280Cal.dig_T2)) >> 11;
var2 = (((((adcT >> 4) - ((s32)bmp280Cal.dig_T1)) * ((adcT >> 4) - ((s32)bmp280Cal.dig_T1))) >> 12) * ((s32)bmp280Cal.dig_T3)) >> 14;
bmp280Cal.t_fine = var1 + var2;
T = (bmp280Cal.t_fine * 5 + 128) >> 8;
return T;
}
// Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
// Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa
u32 bmp280CompensateP(s32 adcP)
{
int64_t var1, var2, p;
var1 = ((int64_t)bmp280Cal.t_fine) - 128000;
var2 = var1 * var1 * (int64_t)bmp280Cal.dig_P6;
var2 = var2 + ((var1*(int64_t)bmp280Cal.dig_P5) << 17);
var2 = var2 + (((int64_t)bmp280Cal.dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)bmp280Cal.dig_P3) >> 8) + ((var1 * (int64_t)bmp280Cal.dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)bmp280Cal.dig_P1) >> 33;
if (var1 == 0)
return 0;
p = 1048576 - adcP;
p = (((p << 31) - var2) * 3125) / var1;
var1 = (((int64_t)bmp280Cal.dig_P9) * (p >> 13) * (p >> 13)) >> 25;
var2 = (((int64_t)bmp280Cal.dig_P8) * p) >> 19;
p = ((p + var1 + var2) >> 8) + (((int64_t)bmp280Cal.dig_P7) << 4);
return (uint32_t)p;
}
#define FILTER_NUM 5
#define FILTER_A 0.1f
/*限幅平均滤波法*/
void pressureFilter(float* in, float* out)
{
static u8 i=0;
static float filter_buf[FILTER_NUM]={0.0};
double filter_sum=0.0;
u8 cnt=0;
float deta;
if(filter_buf[i] == 0.0f)
{
filter_buf[i]=*in;
*out=*in;
if(++i>=FILTER_NUM) i=0;
} else
{
if(i) deta=*in-filter_buf[i-1];
else deta=*in-filter_buf[FILTER_NUM-1];
if(fabs(deta)<FILTER_A)
{
filter_buf[i]=*in;
if(++i>=FILTER_NUM) i=0;
}
for(cnt=0;cnt<FILTER_NUM;cnt++)
{
filter_sum+=filter_buf[cnt];
}
*out=filter_sum /FILTER_NUM;
}
}
void bmp280GetData(float* pressure, float* temperature, float* asl)
{
static float t;
static float p;
bmp280GetPressure();
t = bmp280CompensateT(bmp280RawTemperature)/100.0;
p = bmp280CompensateP(bmp280RawPressure)/25600.0;
pressureFilter(&p,pressure);
*temperature = (float)t;/*单位度*/
// *pressure = (float)p ; /*单位hPa*/
*asl=bmp280PressureToAltitude(pressure); /*转换成海拔*/
}
/**
* 将压力转换为海平面以上高度(ASL) ,单位为米
*/
float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/)
{
if(*pressure > 0)
{
return 44330.f * (powf((1015.7f / *pressure), 0.190295f) - 1.0f);
}
else
{
return 0;
}
}
#ifndef __BMP280_H
#define __BMP280_H
#include "stm32f4xx.h"
#include "i2cdev.h"
/********************************************************************************
* 本程序只供学习使用,未经作者许可,不得用于其它任何用途
* ALIENTEK MiniFly
* BMP280驱动代码
* 正点原子@ALIENTEK
* 技术论坛:www.openedv.com
* 创建日期:2017/5/12
* 版本:V1.3
* 版权所有,盗版必究。
* Copyright(C) 广州市星翼电子科技有限公司 2014-2024
* All rights reserved
********************************************************************************/
/*
身份编号寄存器(id)(0xD0):
寄存器内固定值为0x58,读取0xD0数据的时候,传感器返回0x58,代表身份辨认完毕。
*/
/*
BMP280读取到的数据是芯片内部 ADC转换后的原始数值,并非最终的大气压力值。需
要进行转换才能得到气压值,根据 BMP280的寄存器中的系数进行计算转换。
*/
#define BMP280_I2C_ADDR (0x76)
#define BMP280_DEFAULT_CHIP_ID (0x58) //ID寄存器,读出来好像是0X58
#define BMP280_CHIP_ID (0xD0) /* Chip ID Register */
#define BMP280_RST_REG (0xE0) /* Softreset Register */
#define BMP280_STAT_REG (0xF3) /* Status Register */
#define BMP280_CTRL_MEAS_REG (0xF4) /* Ctrl Measure Register */
#define BMP280_CONFIG_REG (0xF5) /* Configuration Register */
#define BMP280_PRESSURE_MSB_REG (0xF7) /* Pressure MSB Register */
#define BMP280_PRESSURE_LSB_REG (0xF8) /* Pressure LSB Register */
#define BMP280_PRESSURE_XLSB_REG (0xF9) /* Pressure XLSB Register */
#define BMP280_TEMPERATURE_MSB_REG (0xFA) /* Temperature MSB Reg */
#define BMP280_TEMPERATURE_LSB_REG (0xFB) /* Temperature LSB Reg */
#define BMP280_TEMPERATURE_XLSB_REG (0xFC) /* Temperature XLSB Reg */
/*
睡眠模式就是低功耗了,关了不测了;正常模式即连续测,内部一直转化,然后置状态位,我们读寄存器就可以读出原始数据了;
触发模式挺有用,你测一次后,传感器进入睡眠模式,下次再测只要重新设置为触发模式就可以再测一次
*/
#define BMP280_SLEEP_MODE (0x00) //睡眠模式、触发模式、正常模式
#define BMP280_FORCED_MODE (0x01)
#define BMP280_NORMAL_MODE (0x03)
#define BMP280_TEMPERATURE_CALIB_DIG_T1_LSB_REG (0x88) //矫正参数寄存器
#define BMP280_PRESSURE_TEMPERATURE_CALIB_DATA_LENGTH (24)
#define BMP280_DATA_FRAME_SIZE (6)
#define BMP280_OVERSAMP_SKIPPED (0x00)
#define BMP280_OVERSAMP_1X (0x01)
#define BMP280_OVERSAMP_2X (0x02)
#define BMP280_OVERSAMP_4X (0x03)
#define BMP280_OVERSAMP_8X (0x04)
#define BMP280_OVERSAMP_16X (0x05)
bool bmp280Init(I2C_Dev *i2cPort);
void bmp280GetData(float* pressure, float* temperature, float* asl);
u32 bmp280CompensateT(s32 adcT);
u32 bmp280CompensateP(s32 adcP);
void pressureFilter(float* in, float* out);/*限幅平均滤波法*/
float bmp280PressureToAltitude(float* pressure/*, float* groundPressure, float* groundTemp*/);
#endif