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;
}