自学内容网 自学内容网

linux 下调试 bmp280 气压传感器

供自己备忘;

linux 下有自带的 bmp280 驱动,实际测试数据抖动不理想;

于是自己重写一个 bmp280 驱动,实际测试数据依旧抖动,不理想;

考虑使用 SPL06 来测试看看效果;

1. 参考资料:

BMP280气压传感器详解(STM32)_哔哩哔哩_bilibili

2. 简单调试

计划是每秒读取 100 次,实际是按下图框出的配置配置的;

内核版本为:4.9.84

2.1 阅读手册

通过阅读数据手册,可以获取如下信息:

  • 存在 3 种模式

    • 休眠模式:不进行采样

    • 正常模式:循环采样

    • 强制模式:采样一次后进入休眠模式

  • 上电后,自动进入休眠模式

  • 推荐数据连续读取

  • 使用补偿参数计算实际压力和温度

  • 连续读取时,数据不会错误;非连续读取时数据会有混淆情况(读取的时候更新采样数据)

  • MSB:高字节(高 8 位),表示寄存器地址的前 8 位。

  • LSB:低字节(低 8 位),表示寄存器地址的后 8 位。

  • 可以写的寄存器一共只有三个

    • BMP280_REG_CONFIG 0xF5

    • BMP280_REG_CTRL_MEAS 0xF4

    • BMP280_REG_RESET 0xE0

2.2 dts 配置如下

0x76 是 7 位 i2c 地址,对应芯片 SDO 脚是接地的;

2.3 Makefile 文件如下

内核路径修改为自己的,交叉编译器也是;前提是内核编译过,配置好交叉编译器。

KERN_DIR = /opt/liangtao/sigmastar/js230-IKAYAKI_DLM00V017_SSD222D/kernel

all:
make -C $(KERN_DIR) M=`pwd` modules
arm-linux-gnueabihf-gcc bmp280_read.c -o bmp280_read -lm
#cp i2c_bmp280.ko bmp280_read /opt/liangtao/output/

clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm bmp280_read

obj-m += i2c_bmp280.o
2.4 驱动代码修改如下

从之前的博客 mpu6050 拷贝而来;

主要的点如下:

  • bmp280_init 初始化 bmp280

    • 复位后延时 2ms

    • 温度 2 倍过采样

    • 气压 16 倍过采样

    • 正常模式

    • STANDBY TIME 为 0.5ms

    • IIR 为 16 倍

    • 读取校验数据

  • 定时器 30ms 触发工作队列进行数据读取

代码如下:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>

// 宏定义
/* BMP280 specific registers */
#define BMP280_REG_TEMP_XLSB        0xFC
#define BMP280_REG_TEMP_LSB         0xFB
#define BMP280_REG_TEMP_MSB         0xFA
#define BMP280_REG_PRESS_XLSB       0xF9
#define BMP280_REG_PRESS_LSB        0xF8
#define BMP280_REG_PRESS_MSB        0xF7

/* Helper mask to truncate excess 4 bits on pressure and temp readings */
#define BMP280_MEAS_TRIM_MASK       GENMASK(24, 4)

#define BMP280_REG_CONFIG           0xF5
#define BMP280_REG_CTRL_MEAS        0xF4
#define BMP280_REG_STATUS           0xF3
#define BMP280_REG_RESET            0xE0
#define BMP280_REG_ID               0xD0

#define BMP280_REG_COMP_TEMP_START  0x88
#define BMP280_COMP_TEMP_REG_COUNT  6

#define BMP280_REG_COMP_PRESS_START 0x8E
#define BMP280_COMP_PRESS_REG_COUNT 18

#define BMP280_COMP_H5_MASK     GENMASK(15, 4)

#define BMP280_CONTIGUOUS_CALIB_REGS    (BMP280_COMP_TEMP_REG_COUNT + \
                     BMP280_COMP_PRESS_REG_COUNT)

#define BMP280_STANDBY_T_MASK       GENMASK(7, 5)
#define BMP280_STANDBY_T_500US      0
#define BMP280_STANDBY_T_62500US    1
#define BMP280_STANDBY_T_125MS      2
#define BMP280_STANDBY_T_250MS      3
#define BMP280_STANDBY_T_500MS      4
#define BMP280_STANDBY_T_1000MS     5
#define BMP280_STANDBY_T_2000MS     6
#define BMP280_STANDBY_T_4000MS     7

#define BMP280_FILTER_MASK          GENMASK(4, 2)
#define BMP280_FILTER_OFF           0
#define BMP280_FILTER_2X            1
#define BMP280_FILTER_4X            2
#define BMP280_FILTER_8X            3
#define BMP280_FILTER_16X           4

#define BMP280_OSRS_TEMP_MASK       GENMASK(7, 5)
#define BMP280_OSRS_TEMP_SKIP       0
#define BMP280_OSRS_TEMP_1X         1
#define BMP280_OSRS_TEMP_2X         2
#define BMP280_OSRS_TEMP_4X         3
#define BMP280_OSRS_TEMP_8X         4
#define BMP280_OSRS_TEMP_16X        5

#define BMP280_OSRS_PRESS_MASK      GENMASK(4, 2)
#define BMP280_OSRS_PRESS_SKIP      0
#define BMP280_OSRS_PRESS_1X        1
#define BMP280_OSRS_PRESS_2X        2
#define BMP280_OSRS_PRESS_4X        3
#define BMP280_OSRS_PRESS_8X        4
#define BMP280_OSRS_PRESS_16X       5

#define BMP280_MODE_MASK            GENMASK(1, 0)
#define BMP280_MODE_SLEEP           0
#define BMP280_MODE_FORCED          1
#define BMP280_MODE_NORMAL          3

/* BMP280 register skipped special values */
#define BMP280_TEMP_SKIPPED         0x80000
#define BMP280_PRESS_SKIPPED        0x80000


#define DEV_NAME "i2c_bmp280"
#define DEV_CNT (1)

struct bmp280_calib
{
    u16 T1;         /* calibration T1 data */
    s16 T2;         /* calibration T2 data */
    s16 T3;         /* calibration T3 data */
    u16 P1;         /* calibration P1 data */
    s16 P2;         /* calibration P2 data */
    s16 P3;         /* calibration P3 data */
    s16 P4;         /* calibration P4 data */
    s16 P5;         /* calibration P5 data */
    s16 P6;         /* calibration P6 data */
    s16 P7;         /* calibration P7 data */
    s16 P8;         /* calibration P8 data */
    s16 P9;         /* calibration P9 data */
    s32 t_fine;     /* calibration t_fine data */
};
static struct bmp280_calib bmp280_calib_data;


static dev_t bmp280_devno;                  // 定义字符设备的设备号
static struct cdev bmp280_chr_dev;          // 定义字符设备结构体 chr_dev
struct class *class_bmp280;                 // 保存创建的类
struct device *device_bmp280;               // 保存创建的设备
struct device_node *bmp280_device_node;     // bmp280 的设备树节点结构体
struct i2c_client *bmp280_client = NULL;    // 保存 bmp280 设备对应的 i2c_client 结构体,匹配成功后由 .prob 函数带回。

// 定时器,定时读取 bmp280 的数据
static struct timer_list t;
// 用于定时器是否使能
static char is_timer_active;
static unsigned char bmp280_result[8];


// 工作队列,用于执行耗时的 i2c 操作
static struct workqueue_struct *bmp280_wq;
static struct work_struct bmp280_work;

// 一个互斥锁,对读取数据进行保护
static DEFINE_MUTEX(bmp280_mutex);


static u32 __get_unaligned_be24(const u8 *p)
{
    return p[0] << 16 | p[1] << 8 | p[2];
}

static u32 get_unaligned_be24(const void *p)
{
    return __get_unaligned_be24(p);
}

/* 通过 i2c 向 bmp280 写入数据
 * bmp280_client:bmp280 的 i2c_client 结构体。
 * address, 数据要写入的地址,
 * data, 要写入的数据
 * 返回值,错误,-1。成功,0  
 */
static int i2c_write_bmp280(struct i2c_client *bmp280_client, u8 address, u8 data)
{
    int error = 0;
    u8 write_data[2];
    struct i2c_msg send_msg; // 要发送的数据结构体

    /* 设置要发送的数据 */
    write_data[0] = address;
    write_data[1] = data;

    /* 发送 iic 要写入的地址 reg */
    send_msg.addr = bmp280_client->addr; // bmp280 在 iic 总线上的地址
    send_msg.flags = 0;                   // 标记为发送数据
    send_msg.buf = write_data;            // 写入的首地址
    send_msg.len = 2;                     // reg 长度

    /* 执行发送 */
    error = i2c_transfer(bmp280_client->adapter, &send_msg, 1);
    if (error != 1) {
        printk("i2c_write_bmp280 error %d\n", error);
        return -1;
    }

    return 0;
}

/* 通过 i2c 向 bmp280 写入数据
 * bmp280_client:bmp280 的 i2c_client 结构体。
 * address, 要读取的地址,
 * data,保存读取得到的数据
 * length,读长度
 * 返回值,错误,-1。成功,0
 */
static int i2c_read_bmp280(struct i2c_client *bmp280_client, u8 address, void *data, u32 length)
{
    int error = 0;
    u8 address_data = address;
    struct i2c_msg bmp280_msg[2];

    /* 设置读取位置 msg */
    bmp280_msg[0].addr = bmp280_client->addr;   // bmp280 在 iic 总线上的地址
    bmp280_msg[0].flags = 0;                    // 标记为发送数据
    bmp280_msg[0].buf = &address_data;          // 写入的首地址
    bmp280_msg[0].len = 1;                      // 写入长度

    /* 设置读取位置 msg */
    bmp280_msg[1].addr = bmp280_client->addr;   // bmp280 在 iic 总线上的地址
    bmp280_msg[1].flags = I2C_M_RD;             // 标记为读取数据
    bmp280_msg[1].buf = data;                   // 读取得到的数据保存位置
    bmp280_msg[1].len = length;                 // 读取长度

    error = i2c_transfer(bmp280_client->adapter, bmp280_msg, 2);

    if (error != 2) {
        printk("i2c_read_bmp280 error %d\n", error);
        return -1;
    }
    return 0;
}

/*
 * 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.
 *
 * Taken from datasheet, Section 3.11.3, "Compensation formula".
 */
static s32 bmp280_compensate_temp(s32 adc_temp)
{
    struct bmp280_calib *calib = &bmp280_calib_data;
    s32 var1, var2;

    var1 = (((adc_temp >> 3) - ((s32)calib->T1 << 1)) *
        ((s32)calib->T2)) >> 11;
    var2 = (((((adc_temp >> 4) - ((s32)calib->T1)) *
          ((adc_temp >> 4) - ((s32)calib->T1))) >> 12) *
        ((s32)calib->T3)) >> 14;
    calib->t_fine = var1 + var2;

    return (calib->t_fine * 5 + 128) >> 8;
}

/*
 * 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
 *
 * Taken from datasheet, Section 3.11.3, "Compensation formula".
 */
static u32 bmp280_compensate_press(s32 adc_press)
{
    struct bmp280_calib *calib = &bmp280_calib_data;
    s64 var1, var2, p;

    var1 = ((s64)calib->t_fine) - 128000;
    var2 = var1 * var1 * (s64)calib->P6;
    var2 += (var1 * (s64)calib->P5) << 17;
    var2 += ((s64)calib->P4) << 35;
    var1 = ((var1 * var1 * (s64)calib->P3) >> 8) +
        ((var1 * (s64)calib->P2) << 12);
    var1 = ((((s64)1) << 47) + var1) * ((s64)calib->P1) >> 33;

    if (var1 == 0)
        return 0;

    p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125;
    p = div64_s64(p, var1);
    var1 = (((s64)calib->P9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = ((s64)(calib->P8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((s64)calib->P7) << 4);

    return (u32)p;
}

static void bmp280_work_func(struct work_struct *work)
{
    u8 buf[6];
    int ret;
    s32 adc, comp_temp;
    u32 comp_press;

    // 读取原始的气压、温度数据
    ret = i2c_read_bmp280(bmp280_client, BMP280_REG_PRESS_MSB, buf, sizeof(buf));
    if (ret < 0) {
        printk("i2c_read_bmp280 error\n");
        return;
    }

    adc = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&buf[3]));
    if (adc == BMP280_TEMP_SKIPPED) {
        /* reading was skipped */
        printk("reading temperature skipped\n");
        return;
    }
    comp_temp = bmp280_compensate_temp(adc);

    adc = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&buf[0]));
    if (adc == BMP280_PRESS_SKIPPED) {
        /* reading was skipped */
        printk("reading pressure skipped\n");
        return;
    }
    comp_press = bmp280_compensate_press(adc);

    mutex_lock(&bmp280_mutex);
    memcpy(bmp280_result, &comp_temp, sizeof(comp_temp));
    memcpy(&bmp280_result[4], &comp_press, sizeof(comp_press));
    mutex_unlock(&bmp280_mutex);

    //comp_press = comp_press / 256;
    //printk("temperature = %d, pressure = %d\n", comp_temp, comp_press);
}

static void bmp280_timer_func(unsigned long data)
{
    if (is_timer_active) {
        mod_timer(&t, jiffies + msecs_to_jiffies(30));
    }

    // 只在任务未在队列中时才添加新任务
    if (!work_pending(&bmp280_work)) {
        queue_work(bmp280_wq, &bmp280_work);
    }
}

/* 初始化 bmp280
 * 返回值,成功,返回 0。失败,返回 -1
 */
static int bmp280_init(void)
{
    int err = 0;
    unsigned char buf[1];

    /* 配置 bmp280 */

    // 设置 BMP280_RST_REG 寄存器值为 0xB6,触发复位
    err += i2c_write_bmp280(bmp280_client, BMP280_REG_RESET, 0xB6);
    // 等待复位完成
    msleep(2);

    // 读取 BMP280_ID_REG 寄存器,判断是否连接成功
    err += i2c_read_bmp280(bmp280_client, BMP280_REG_ID, &buf[0], 1);
    if (buf[0] != 0x58) {
        printk("bmp280_init id error, id 0x%x, err %d\n", buf[0], err);
        return -1;
    }

    // 配置 BMP280_REG_CTRL_MEAS 寄存器; 配置温度过采样,配置气压过采样,配置供电模式
    buf[0] = FIELD_PREP(BMP280_OSRS_TEMP_MASK, BMP280_OSRS_TEMP_2X) | 
        FIELD_PREP(BMP280_OSRS_PRESS_MASK, BMP280_OSRS_PRESS_16X) | 
        FIELD_PREP(BMP280_MODE_MASK, BMP280_MODE_NORMAL);

    err += i2c_write_bmp280(bmp280_client, BMP280_REG_CTRL_MEAS, buf[0]);

    // 配置 IIR 滤波 BMP280_STANDBY_T_MASK
    buf[0] = FIELD_PREP(BMP280_STANDBY_T_MASK, BMP280_STANDBY_T_500US) |
        FIELD_PREP(BMP280_FILTER_MASK, BMP280_FILTER_16X);
    err += i2c_write_bmp280(bmp280_client, BMP280_REG_CONFIG, buf[0]);

    /*
     * Some chips have calibration parameters "programmed into the devices'
     * non-volatile memory during production". Let's read them out at probe
     * time once. They will not change.
     */
    // 读取校准参数
    err += i2c_read_bmp280(bmp280_client, BMP280_REG_COMP_TEMP_START, (void *)&bmp280_calib_data, BMP280_CONTIGUOUS_CALIB_REGS);

    if (err < 0) {
        printk("bmp280_init error\n");
        return -1;
    }

    return 0;
}

static int bmp280_open(struct inode *inode, struct file *filp)
{
    is_timer_active = 1;

    mod_timer(&t, jiffies + msecs_to_jiffies(1));
    return 0;
}

static ssize_t bmp280_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    int error;
    unsigned char buffer[8]; // 保存 bmp280 转换得到的原始数据

    mutex_lock(&bmp280_mutex);
    memcpy(buffer, bmp280_result, sizeof(bmp280_result));
    mutex_unlock(&bmp280_mutex);

    error = copy_to_user(buf, buffer, sizeof(buffer));
    if (error != 0) {
        printk("copy_to_user error!");
        return -1;
    }

    return sizeof(buffer);
}

static int bmp280_release(struct inode *inode, struct file *filp)
{
    is_timer_active = 0;
    return 0;
}

static struct file_operations bmp280_chr_dev_fops =
{
    .owner = THIS_MODULE,
    .open = bmp280_open,
    .read = bmp280_read,
    .release = bmp280_release,
};

static int bmp280_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret;

    printk("bmp280_probe, HZ = %d\n", HZ);

    bmp280_client = client;

    /* 初始化 bmp280 */
    ret = bmp280_init();
    if (ret < 0) {
        printk("fail to init bmp280\n");
        goto alloc_err;
    }

    // 采用动态分配的方式,获取设备编号,次设备号为 0,
    // 设备名称为 i2c_bmp280,可通过命令 cat /proc/devices 查看
    // DEV_CNT 为 1,当前只申请一个设备编号
    ret = alloc_chrdev_region(&bmp280_devno, 0, DEV_CNT, DEV_NAME);
    if (ret < 0) {
        printk("fail to alloc bmp280_devno\n");
        goto alloc_err;
    }

    // 关联字符设备结构体 cdev 与文件操作结构体 file_operations
    bmp280_chr_dev.owner = THIS_MODULE;
    cdev_init(&bmp280_chr_dev, &bmp280_chr_dev_fops);

    // 添加设备至 cdev_map 散列表中
    ret = cdev_add(&bmp280_chr_dev, bmp280_devno, DEV_CNT);
    if (ret < 0) {
        printk("fail to add cdev\n");
        goto add_err;
    }

    /* 创建类 */
    class_bmp280 = class_create(THIS_MODULE, DEV_NAME);

    /* 创建设备 DEV_NAME 指定设备名 */
    device_bmp280 = device_create(class_bmp280, NULL, bmp280_devno, NULL, DEV_NAME);

    /* 初始化工作队列 */
    bmp280_wq = create_singlethread_workqueue("bmp280_wq");
    INIT_WORK(&bmp280_work, bmp280_work_func);

    /* 创建一个定时器,开始以 100hz 的频率来采样 */
    setup_timer(&t, bmp280_timer_func, 0);

    return 0;

add_err:
    // 添加设备失败时,需要注销设备号
    unregister_chrdev_region(bmp280_devno, DEV_CNT);
    printk("bmp280_probe error! \n");
alloc_err:
    return -1;
}

static int bmp280_remove(struct i2c_client *client)
{
    device_destroy(class_bmp280, bmp280_devno);         // 清除设备
    class_destroy(class_bmp280);                        // 清除类
    cdev_del(&bmp280_chr_dev);                          // 清除设备号
    unregister_chrdev_region(bmp280_devno, DEV_CNT);    // 取消注册字符设备
    del_timer_sync(&t);                                 // 删除定时器
    flush_workqueue(bmp280_wq);                         // 确保所有任务完成
    destroy_workqueue(bmp280_wq);                       // 销毁工作队列
    return 0;
}

static const struct i2c_device_id bmp280_device_id[] = {
    {"bosch,bmp280", 0},
    {/* sentinel */}
};

static const struct of_device_id bmp280_of_match_table[] = {
    {.compatible = "bosch,bmp280"},
    {/* sentinel */}
};

struct i2c_driver bmp280_driver = {
    .probe = bmp280_probe,
    .remove = bmp280_remove,
    .id_table = bmp280_device_id,
    .driver = {
        .name = "invensense,bmp280",
        .owner = THIS_MODULE,
        .of_match_table = bmp280_of_match_table,
    },
};

static int __init bmp280_driver_init(void)
{
    int ret;

    printk("bmp280_driver_init\n");
    ret = i2c_add_driver(&bmp280_driver);
    return ret;
}

static void __exit bmp280_driver_exit(void)
{
    printk("bmp280_driver_exit\n");
    i2c_del_driver(&bmp280_driver);
}

module_init(bmp280_driver_init);
module_exit(bmp280_driver_exit);

MODULE_LICENSE("GPL");
2.5 应用代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>

void print_timestamp() 
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("[%ld.%06ld] ", tv.tv_sec, tv.tv_usec);
}

int main(int argc, char *argv[])
{
int error;
unsigned char receive_data[8];  //保存收到的 bmp280 转换结果数据,先为温度,后为气压
int temp;
unsigned int press;
float real_temp;
float real_press;

/*打开文件*/
int fd = open("/dev/i2c_bmp280", O_RDWR);
if (fd < 0) {
printf("open file : %s failed !\n", argv[0]);
return -1;
}
usleep(200000);

while (1) {
/* 读取数据 */
print_timestamp();
error = read(fd, receive_data, sizeof(receive_data));
if (error != 8) {
printf("read file error! \n");
close(fd);
break;
}
print_timestamp();
/* 打印数据 */
memcpy(&temp, receive_data, sizeof(temp));
memcpy(&press, &receive_data[4], sizeof(press));
real_temp = temp / 100.0;
real_press = press / 256.0;
printf("temp %f, press %f\n", real_temp, real_press);
usleep(10000);
}

/*关闭文件*/
error = close(fd);
if (error < 0) {
printf("close file error! \n");
}

return 0;
}


原文地址:https://blog.csdn.net/liangtao_1996/article/details/143776282

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!