当前位置: 首页 > news >正文

bmp280的压力数据采集(i2c设备驱动+设备树编写)

i2c时序

1. bmp280传感器基本控制寄存器

(1)测量控制寄存器(0xF4)
  • 功能:配置温度/压力采样精度和工作模式。
  • 位定义
    • bit 7:5 (osrs_t):温度过采样设置(0-5对应关闭/1x/2x/4x/8x/16x)
    • bit 4:2 (osrs_p压力过采样设置(同上)
    • bit 1:0 (mode):工作模式(00=休眠,01=强制模式,11=正常模式)
(2)配置寄存器(0xF5)
  • 功能:设置滤波器和数据输出速率。
  • 位定义
    • bit 7:5 (t_sb):待机时间(0-7对应0.5ms-4000ms)
    • bit 4:2 (filter):IIR滤波系数(0-4对应关闭/2/4/8/16)
    • bit 0 (spi3w_en):SPI三线模式使能(I2C模式下保持0)

2. 状态与识别寄存器

(3)状态寄存器(0xF3)
  • 功能:指示传感器当前状态。
  • 位定义
    • bit 3 (measuring):1表示正在测量(数据未就绪)
    • bit 0 (im_update):1表示校准参数正在更新
(4)芯片ID寄存器(0xD0)
  • 功能:验证设备型号,固定值为0x58(BMP280标识符)
(5)复位寄存器(0xE0)
  • 功能:写入0xB6触发软复位,恢复默认配置

3. 数据寄存器

(6)压力数据寄存器(0xF7-0xF9)
  • 地址0xF7(MSB)、0xF8(LSB)、0xF9(XLSB)
  • 功能:存储20位原始压力数据(需左移4位对齐)
(7)温度数据寄存器(0xFA-0xFC)
  • 地址0xFA(MSB)、0xFB(LSB)、0xFC(XLSB)
  • 功能:存储20位原始温度数据(需左移4位对齐)

4. 校准参数寄存器(0x88-0xA1)

  • 功能:存储出厂校准系数(共26个寄存器),用于温度和压力数据的补偿计算。
  • 关键参数
    • dig_T1-T3:温度补偿系数(无符号16位/有符号16位)
    • dig_P1-P9:压力补偿系数(混合类型)

I2C时序控制要点

  1. 地址选择
    • SDO引脚电平决定I2C地址:低电平为0x76,高电平为0x77
  2. 读写时序
    • 写操作:发送设备地址(写模式)+寄存器地址+数据。
    • 读操作:先发送寄存器地址,再启动重复起始条件读取数据。
  3. 典型流程
    • 复位 → 读取校准参数 → 配置采样模式 → 轮询状态寄存器 → 读取数据。

数据处理代码:主要读取原始数据包括3字节压力数据(20位)、3字节温度数据(20位)还有26字节校准数据:

手册数据处理代码

BMP280的数据寄存器结构

BMP280的温度和压力ADC值均为20位数据,存储格式如下:

  • 温度数据寄存器0xFA-0xFC):
    • 0xFA(MSB):高8位
    • 0xFB(LSB):中8位
    • 0xFC(XLSB):低4位(有效位在bit7-4,其余位无效)
  • 压力数据寄存器0xF7-0xF9):
    • 类似温度寄存器,但压力值的XLSB在0xF9中。
2. 原始数据组合方法

假设通过I2C读取的3字节数据为 [msb, lsb, xlsb],需按以下步骤处理:

// 组合成20位原始值(左对齐,低4位无效)
uint32_t raw_data = ((uint32_t)msb << 12) | ((uint32_t)lsb << 4) | (xlsb >> 4);
  • 左对齐特性:BMP280的ADC值默认左对齐(即高20位有效),因此无需右移补零。
  • 低4位处理xlsb的低4位是无效数据,需右移4位丢弃。
3. 调用函数时的传参
  • 正确做法:传入组合后的20位原始值(直接传raw_data):
    int32_t adc_T = (int32_t)raw_data;  // 转换为有符号整数
    int32_t compensated_temp = bmp280_compensate_T_int32(adc_T);
    
  • 错误做法:直接传入未处理的字节数组首地址(函数内部无法解析3字节分散数据)。
4. 为什么函数内部还需要移位?
  • 函数内移位(如adc_T >> 3)是补偿算法的要求,用于对齐校准公式的位宽。
  • 传参前移位是为了将3字节数据合并为完整的20位原始值,二者目的不同。

示例代码(完整流程)

// 从传感器读取3字节温度数据(假设已通过I2C读取)
uint8_t temp_data[3] = {0x12, 0x34, 0x50}; // msb, lsb, xlsb// 组合成20位原始值(左对齐)
uint32_t raw_temp = ((uint32_t)temp_data[0] << 12) | ((uint32_t)temp_data[1] << 4) | (temp_data[2] >> 4);// 转换为有符号整数并传入补偿函数
int32_t adc_T = (int32_t)raw_temp;
int32_t temperature = bmp280_compensate_T_int32(adc_T);  // 单位:0.01°C

关键总结

  1. 必须组合数据:调用函数前需将3字节合并为20位原始值(左对齐)。
  2. 无需额外移位:传入的是完整20位值,函数内部会处理校准所需的移位。
  3. 数据有效性:确保丢弃xlsb的低4位无效数据。

代码分析总结

1. 代码功能

这段代码实现了BMP280传感器的温度和压力数据补偿计算,主要包含两个函数:

  • bmp280_compensate_T_int32:计算温度值(单位:0.01°C)
  • bmp280_compensate_P_int64:计算压力值(单位:Pa,Q24.8格式)
2. 关键实现细节

温度计算:

  • 输入:ADC原始温度值(adc_T
  • 输出:补偿后的温度值(分辨率0.01°C)
  • 核心算法:
    var1 = (((adc_T>>3) - (t_fine_dig_T1<<1)) * t_fine_dig_T2) >> 11;
    var2 = ((((adc_T>>4) - t_fine_dig_T1)^2 >> 12) * t_fine_dig_T3) >> 14;
    t_fine = var1 + var2;  // 存储到全局变量
    T = (t_fine * 5 + 128) >> 8;  // 最终温度
    

压力计算:

  • 输入:ADC原始压力值(adc_P
  • 输出:补偿后的压力值(Q24.8格式)
  • 核心算法:
    // 多阶段多项式补偿计算
    var1 = t_fine - 128000;
    var2 = var1^2 * t_fine_dig_P6 + (var1*t_fine_dig_P5<<17) + (t_fine_dig_P4<<35);
    var1 = (var1^2*t_fine_dig_P3>>8) + (var1*t_fine_dig_P2<<12);
    var1 = ((1<<47)+var1)*t_fine_dig_P1>>33;p = 1048576 - adc_P;
    p = (((p<<31)-var2)*3125)/var1;  // 注意防除零处理// 二次补偿
    p += ((t_fine_dig_P9*(p>>13)^2)>>25) + ((t_fine_dig_P8*p)>>19);
    p = (p>>8) + (t_fine_dig_P7<<4);
    
3. 技术亮点
  1. 定点数优化

    • 全程使用整数运算(无浮点)
    • 通过移位操作(>>/<<)实现2的幂次乘除
    • Q24.8格式处理压力值(24位整数+8位小数)
  2. 校准参数应用

    • 使用t_fine_dig_T1-T3t_fine_dig_P1-P9等传感器校准参数
    • 温度补偿结果t_fine会用于压力计算
  3. 异常处理

    • 压力计算中对var1==0的情况进行了保护
4. 典型输出示例
  • 温度:输出5123表示51.23°C
  • 压力:输出24674867表示96386.2 Pa(963.862 hPa)

1. 温度计算函数 bmp280_compensate_T_int32

参数与变量解析
名称类型(实际)意义数据来源
adc_TBMP280_S32_t(32位有符号整数)ADC原始温度值(20位数据左对齐,需右移4位使用)传感器寄存器0xFA-0xFC
dig_T1uint16_t温度校准系数1(无符号,用于线性补偿)校准寄存器0x88-0x89
dig_T2int16_t温度校准系数2(有符号,用于一阶非线性补偿)校准寄存器0x8A-0x8B
dig_T3int16_t温度校准系数3(有符号,用于二阶非线性补偿)校准寄存器0x8C-0x8D
var1/var2BMP280_S32_t中间计算变量,存储补偿公式的中间结果代码内部计算
t_fineBMP280_S32_t全局变量,存储精细温度值,用于压力补偿计算代码内部计算
返回值BMP280_S32_t补偿后温度值(单位:0.01°C,如5123表示51.23°C)公式计算
关键操作
  • 位运算优化:通过右移操作(>>3/>>4)对齐ADC数据位,避免浮点运算。
  • 校准公式
    t_fine = var1 + var2;  // var1为线性补偿项,var2为非线性补偿项
    T = (t_fine * 5 + 128) >> 8;  // 转换为0.01°C分辨率
    

2. 压力计算函数 bmp280_compensate_P_int64

参数与变量解析
名称类型(实际)意义数据来源
adc_PBMP280_S32_t(32位有符号整数)ADC原始压力值(20位数据左对齐,需右移4位使用)传感器寄存器0xF7-0xF9
dig_P1-P9混合类型(uint16_t/int16_t压力校准系数(P1无符号,P2-P9有符号),用于多阶段多项式补偿校准寄存器0x8E-0x9F
var1/var2BMP280_S64_t(64位有符号整数)中间计算变量,处理大数运算以避免溢出代码内部计算
pBMP280_S64_t补偿过程中的压力中间值,最终转换为Q24.8格式代码内部计算
返回值BMP280_U32_t(32位无符号整数)补偿后压力值(Q24.8格式,如24674867表示96386.2 Pa)公式计算
关键操作
  • 64位整数运算:处理大范围数值(如<<35)和防止溢出。
  • Q24.8格式转换:通过移位和除法实现定点数小数部分保留:
    p = ((p + var1 + var2) >> 8) + (dig_P7 << 4);  // 最终转换为24.8格式
    
  • 防除零保护:检查var1==0避免异常。

3. 校准参数的意义与作用

参数组功能寄存器地址范围
dig_T1-T3补偿温度传感器的非线性误差,包括偏移量、灵敏度漂移等0x88-0x8D
dig_P1-P9补偿压力传感器的温度漂移和非线性特性,通过多项式拟合提高精度0x8E-0x9F

设备树编写:

直接在i2c设备节点下加入我们的设备子节点,主要写上设备地值和compatible属性,地址根据sdo接线判断,接地0x76,接vcc  0x77。

驱动编写:

#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/string.h>static struct i2c_client *bmp280_client;static ssize_t bmp280_read(struct file *fp, char __user *puser, size_t n,loff_t *off)
{int ret;int i;struct i2c_msg msg;char data[2];char buf1[6] = {0};//存温度压力char buf2[26] = {0};//存校准参数char buf[32] = {0};
//传感器复位data[0] = 0xE0;//复位寄存器地址data[1] = 0xB6;//要写的数据msg.addr = bmp280_client->addr;//从机地址msg.flags = 0;//0写,1读msg.buf = data;msg.len = 2;//长度2个字节,表示把data数组的前两个数据写入bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//调用函数接口传输msg消息//启动测量data[0] = 0xF4;//测量控制寄存器(0xF4)data[1] = 0x27;//要写的数据msg.addr = bmp280_client->addr;//从机地址msg.flags = 0;//0写,1读msg.buf = data;msg.len = 2;//长度2个字节,表示把data数组的前两个数据写入bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//调用函数接口传输msg消息msleep(10);
//读取传感器校准参数data[0] = 0x88;//校准参数寄存器(0x88-0xA1),总共26个msg.addr = bmp280_client->addr;//从机地址msg.flags = 0;//0写msg.buf = data;msg.len = 1;//长度2个字节,表示把data数组的前两个数据写入bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//调用函数接口传输msg消息msg.addr = bmp280_client->addr;msg.flags = I2C_M_RD;msg.buf = buf2;//把校准参数读到datamsg.len = 26;bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//读取温度、压强数据data[0] = 0xF7;//压力数据寄存器(0xF7-0xF9)温度数据寄存器(0xFA-0xFC)msg.addr = bmp280_client->addr;//从机地址msg.flags = 0;//0写,1读msg.buf = data;msg.len = 1;bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//调用函数接口传输msg消息msg.addr = bmp280_client->addr;msg.flags = I2C_M_RD;msg.buf = buf1;msg.len = 6;bmp280_client->adapter->algo->master_xfer(bmp280_client->adapter, &msg, 1);//buf1[6] + buf2[26] -->buf[32]for(i = 0;i < 6;++i){buf[i] = buf1[i];}for(i = 6;i < 32;++i){buf[i] = buf2[i - 6];}ret = copy_to_user(puser, buf, sizeof(buf));return sizeof(buf);
}//文件开关/////////////////
static int bmp280_open(struct inode *node, struct file *fp)
{pr_info("open success\n");return 0;
}
static int bmp280_release(struct inode *node, struct file *fp)
{pr_info("release success\n");return 0;
}
///////////////////////////OK//文件操作函数结构体//////
static struct file_operations fops = {.owner = THIS_MODULE,  //计数,表示有几个模块调用文件操作.open = bmp280_open,.release = bmp280_release,.read = bmp280_read,
};
//////////////OK///注册混杂设备////////////////
static struct miscdevice misc_device = {.minor = MISC_DYNAMIC_MINOR,  //次设备号申请.name = "misc_bmp280",          //设备节点名.fops = &fops,                //绑定文件操作函数结构体
};
///////////////////OK///////////probe///////////
static int bmp280_probe(struct i2c_client *pclient,const struct i2c_device_id *pdevice)
{misc_register(&misc_device);bmp280_client = pclient;pr_info("bmp280_probe ok!\n");return 0;
}
/////////////////////NO/////////remove///////
static int bmp280_remove(struct i2c_client *pclient)
{misc_deregister(&misc_device);pr_info("bmp280_remove ok!\n");return 0;
}
/////////////////////OK// i2c设备驱动匹配////////////////
static struct of_device_id bmp280_of_match_table[] = {{.compatible = "pute,bmp280"},{},
};
static struct i2c_device_id bmp280_id_table[] = {{.name = "bmp280"},{},
};
static struct i2c_driver bmp280_i2c_drv = {.driver ={.name = "bmp280",.of_match_table = bmp280_of_match_table,},.id_table = bmp280_id_table,.probe = bmp280_probe,.remove = bmp280_remove,
};
//////////////////////////////OK//驱动入口内调函数//////////
static int __init bmp280_init(void)
{//注册i2c设备驱动i2c_register_driver(THIS_MODULE, &bmp280_i2c_drv);pr_info("bmp280 init success\n");return 0;
}//驱动出口内调函数
static void __exit bmp280_exit(void)
{//销毁i2c设备驱动i2c_del_driver(&bmp280_i2c_drv);pr_info("bmp280 exit success\n");return;
}
/////////////////OK//驱动入口
module_init(bmp280_init);
//驱动出口
module_exit(bmp280_exit);MODULE_LICENSE("GPL");

驱动的读时序遇到的大问题:

以前的传感器都是直接读写寄存器,发指令,没加延时等待,但这次的bmp280传感器启动测量后必须加延时等待测量完成再采集数据,否则只会采集的0x80000 等无效数据,切记,以后要仔细查看传感器手册相关时序要求。

已经采集到原始数据了,接下来根据前面手册提供的数据处理函数进行自己的测试程序编写:

必须利用校验数据结合原始数据进行计算才能得到准确压强值。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>typedef struct {uint16_t dig_T1;int16_t dig_T2;int16_t dig_T3;uint16_t dig_P1;int16_t dig_P2;int16_t dig_P3;int16_t dig_P4;int16_t dig_P5;int16_t dig_P6;int16_t dig_P7;int16_t dig_P8;int16_t dig_P9;
} BMP280_CalibData;float process_bmp280_data(unsigned char *buf) {// 1. 解析温度和气压原始数据uint32_t press_raw = ((uint32_t)buf[0] << 12) | ((uint32_t)buf[1] << 4) | ((uint32_t)buf[2] >> 4);uint32_t temp_raw = ((uint32_t)buf[3] << 12) | ((uint32_t)buf[4] << 4) | ((uint32_t)buf[5] >> 4);// 2. 解析校准参数BMP280_CalibData calib;calib.dig_T1 = (uint16_t)(buf[6] | (buf[7] << 8));calib.dig_T2 = (int16_t)(buf[8] | (buf[9] << 8));calib.dig_T3 = (int16_t)(buf[10] | (buf[11] << 8));calib.dig_P1 = (uint16_t)(buf[12] | (buf[13] << 8));calib.dig_P2 = (int16_t)(buf[14] | (buf[15] << 8));calib.dig_P3 = (int16_t)(buf[16] | (buf[17] << 8));calib.dig_P4 = (int16_t)(buf[18] | (buf[19] << 8));calib.dig_P5 = (int16_t)(buf[20] | (buf[21] << 8));calib.dig_P6 = (int16_t)(buf[22] | (buf[23] << 8));calib.dig_P7 = (int16_t)(buf[24] | (buf[25] << 8));calib.dig_P8 = (int16_t)(buf[26] | (buf[27] << 8));calib.dig_P9 = (int16_t)(buf[28] | (buf[29] << 8));// 3. 计算温度int32_t var1, var2, t_fine;var1 = ((((temp_raw >> 3) - ((int32_t)calib.dig_T1 << 1))) * ((int32_t)calib.dig_T2)) >> 11;var2 = (((((temp_raw >> 4) - ((int32_t)calib.dig_T1)) * ((temp_raw >> 4) - ((int32_t)calib.dig_T1))) >> 12) * ((int32_t)calib.dig_T3)) >> 14;t_fine = var1 + var2;float temperature = (float)((t_fine * 5 + 128) >> 8) / 100.0f;// 4. 计算气压int64_t var1_p, var2_p, p;var1_p = ((int64_t)t_fine) - 128000;var2_p = var1_p * var1_p * (int64_t)calib.dig_P6;var2_p = var2_p + ((var1_p * (int64_t)calib.dig_P5) << 17);var2_p = var2_p + (((int64_t)calib.dig_P4) << 35);var1_p = ((var1_p * var1_p * (int64_t)calib.dig_P3) >> 8) + ((var1_p * (int64_t)calib.dig_P2) << 12);var1_p = ((((int64_t)1) << 47) + var1_p) * ((int64_t)calib.dig_P1) >> 33;if (var1_p == 0) {return 1; // 避免除以0}p = 1048576 - press_raw;p = (((p << 31) - var2_p) * 3125) / var1_p;var1_p = (((int64_t)calib.dig_P9) * (p >> 13) * (p >> 13)) >> 25;var2_p = (((int64_t)calib.dig_P8) * p) >> 19;p = ((p + var1_p + var2_p) >> 8) + (((int64_t)calib.dig_P7) << 4);float pressure = (float)p / 25600.0f;// 5. 输出结果return pressure;
}int main(void)
{int fd = 0;unsigned char buf[32];//存放原始数据int temp,press,i;short cal[12];fd = open("/dev/misc_bmp280", O_RDWR);if (-1 == fd){perror("fail to open");return -1;}while (1){read(fd, &buf, sizeof(buf));//接收传来的温度压力buf[0]-buf[5],校准参数buf[6]-buf[31];printf("press:%fhpa\n",process_bmp280_data(buf));sleep(1);}close(fd);return 0;
}

http://www.dtcms.com/a/300167.html

相关文章:

  • ACO-OFDM 的**频带利用率**(单位:bit/s/Hz)计算公式
  • 建筑施工场景下漏检率↓76%!陌讯多模态融合算法在工程安全监控的落地实践
  • OpHReda精准预测酶最佳PH
  • 进制间的映射关系
  • 2025牛客暑期多校第4场——G
  • Polyhedral Approaches in Combinatorial Optimization组合优化中的多面体方法(下)
  • Java实现大根堆与小根堆详解
  • 每日面试题15:如何解决堆溢出?
  • 如何检查服务器数据盘是否挂载成功?
  • Android-三种持久化方式详解
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-32,(知识点:模数转换器,信噪比,计算公式,)
  • 深入理解C语言快速排序与自省排序(Introsort)
  • 【每天一个知识点】GAN(生成对抗网络,Generative Adversarial Network)
  • Compose笔记(三十八)--CompositionLocal
  • 安卓学习记录1——持续更新ing
  • React组件中的this指向问题
  • 三防平板支持DMR对讲有什么用?实现高效集群调度
  • 如何理解“测试场景”与“测试要点”的区别和联系?
  • Linux系统架构核心全景详解
  • 从0到1学Pandas(六):Pandas 与数据库交互
  • KiCad 与 CircuitMaker 使用方法分享:从零开始学电子设计
  • JavaWeb(苍穹外卖)--学习笔记11(Filter(过滤器) 和 Interceptor(拦截器))
  • Windows开发,制作开发软件安装程序(一)
  • MySQL的底层原理--InnoDB数据页结构
  • 关于GateWay网关
  • 基于HMM的词性标注方法详解(HMM+Viterbi,例题分析)
  • 【专业扫盲】电压/电流反馈和串联/并联反馈
  • CSP2025模拟赛2(2025.7.26)
  • 机器人仿真(2)Ubuntu24.04下RTX5090配置IsaacSim与IsaacLab
  • Jenkins持续集成工具