BMP180 是博世(Bosch)公司推出的一款高精度数字气压与温度传感器,属于低功耗、小体积的环境监测传感器。它通过测量大气压间接计算海拔高度,并同时提供温度数据,广泛应用于气象站、无人机、移动设备、室内导航等领域。
我们只需要关注这一部分内容就可以了




本次实验我们选择i2c2接口与BMP180相连。实物连接图如下:


一,BMP180设备树插件
设备树插件(Device Tree Overlay,简称DTO)是Linux内核(通常在4.4版本及以上引入)中一种强大的动态设备树扩展机制。可以把它理解成能给正在运行的系统“打补丁”,允许在系统运行时动态地添加、修改或删除设备树中的节点和属性,而无需重新编译和烧写整个设备树。
1.设备树插件源码
/dts-v1/;
/plugin/;
#include <dt-bindings/leds/common.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/*
* Helper to show loaded overlays under: /proc/device-tree/chosen/overlays/
*/
&{/chosen} {
overlays {
kone-pocketbeagle2.kernel = __TIMESTAMP__;
};
};
&main_i2c2 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
mpu6050@68{
status = "okay";
compatible = "kone,i2c_mpu6050";
reg = <0x68>;
};
BMP180@77{
status = "okay";
compatible = "kone,BMP180";
reg = <0x77>;
};
AHT30@38{
status = "okay";
compatible = "kone,AHT30";
reg=<0x38>;
};
};
2.编译设备树插件
设置正确的环境变量
export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH"
# 设置架构和交叉编译前缀
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-
makefile中添加新增的设备树
nano /home/mxz/pocketbeagle_2/sdk/linux/arch/arm64/boot/dts/ti/Makefile
dtb-$(CONFIG_ARCH_K3) += kone-pocketbeagle2.dtbo
编译
# 进入内核源码目录
cd /home/mxz/pocketbeagle_2/sdk/linux
# 编译所有设备树
make dtbs
3.加载设备树插件
将生成的 kone-pocketbeagle2.dtbo copy到/boot/firmware/overlays/下
更新引导配置
sudo nano /boot/firmware/extlinux/extlinux.conf
label Linux
kernel /Image.gz
fdtdir /
append console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02860000 root=/dev/mmcblk1p2 ro rootfstype=ext4 rootwait net.ifnames=0
fdtoverlays /overlays/kone-pocketbeagle2.dtbo验证
# 重启
sudo reboot确认设备树覆盖层加载成功
# 查看覆盖层时间戳(确认编译时间)
cat /sys/firmware/devicetree/base/chosen/overlays/kone-pocketbeagle2.kernel
# 查看所有加载的覆盖层
ls /sys/firmware/devicetree/base/chosen/overlays/查看 I2C2 总线
ls /sys/bus/i2c2/devices/
二,BMP180驱动
驱动源码
#include <linux/init.h> #include <linux/module.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/types.h> #include <linux/errno.h> // 错误码定义 #include <linux/of.h> #include <asm/io.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/slab.h> // 内存分配 #include <linux/uaccess.h> // copy_to_user头文件 // ======================================define========================================= #define DEV_NAME "bmp180" #define BMP180_ADDR 0x77 // BMP180寄存器定义 #define BMP180_REG_CALIB_START 0xAA // 校准数据起始地址 #define BMP180_REG_CTRL_MEAS 0xF4 // 控制测量寄存器 #define BMP180_RRG_OUT_MSB 0xF6 // 输出数据MSB寄存器 // 控制测量命令 #define BMP180_CMD_TEMP 0x2E // 温度测量命令 #define BMP180_CMD_PRESS_OSS0 0x34 // 压力测量命令(OSS=0) // 海拔计算相关常量 #define SEA_LEVEL_PRESSURE 101325 // 海平面标准大气压 (Pa) #define ALTITUDE_FACTOR 44330000 // 用于海拔计算的常数 // ============================== 全局变量 ============================= static dev_t bmp180_dev_num; // 设备号 static struct cdev bmp180_cdev; // 字符设备结构 struct device *bmp180_device; // 设备结构 struct class *bmp180_class; // 设备类 static struct i2c_client *bmp180_client = NULL; // I2C客户端 // BMP180校准数据结构 struct bmp180_calib_data { short AC1; short AC2; short AC3; unsigned short AC4; unsigned short AC5; unsigned short AC6; short B1; short B2; short MB; short MC; short MD; }; struct bmp180_result { long temperature; // 温度(0.1°C) long pressure; // 压力(Pa) } ; static struct bmp180_result result ; static struct bmp180_calib_data calib; // 存储校准数据 static uint8_t OSS = 0; // ============================== I2C通信函数 ========================== /** * i2c_write_bmp180 - 向BMP180写入数据 * @client: I2C客户端 * @data: 要写入的数据 * @len: 数据长度 * * 返回值: 0成功,负数表示错误 */ static int i2c_write_bmp180(struct i2c_client*client,uint8_t *data,uint8_t len) { int ret; struct i2c_msg msg; if(!client) { printk(KERN_ERR "BMP180: NULL client pointer\n"); return -EINVAL; } msg.addr = client->addr; msg.flags = 0; //write msg.buf = data; msg.len = len; ret = i2c_transfer(client->adapter,&msg,1); if(ret != 1) { printk(KERN_ERR "bmp180 write is error\n"); return -EIO; } return 0; } /** * i2c_read_bmp180 - 从BMP180读取数据 * @client: I2C客户端 * @reg: 要读取的寄存器地址 * @data: 存储读取数据的缓冲区 * @len: 要读取的数据长度 * * 返回值: 0成功,负数表示错误 */ static int i2c_read_bmp180(struct i2c_client *client,uint8_t reg,uint8_t *data,uint8_t len) { int ret; struct i2c_msg msg[2]; if(!client) { printk(KERN_ERR "BMP180 : NULL client pointer\n"); //无效参数 return -EINVAL; } msg[0].addr = client->addr; msg[0].flags = 0; msg[0].buf = ® msg[0].len = 1; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = data; msg[1].len = len; ret = i2c_transfer(client->adapter,msg,2); if(ret != 2) { printk(KERN_ERR "bmp180 read is error\n"); return -EIO; //(输入输出错误) } return 0; } // ============================== 设备操作函数 ========================= /** * bmp180_open - 打开设备 * @inode: inode结构 * @file: file结构 * * 返回值: 0成功,负数表示错误 */ static int bmp180_open(struct inode*inode,struct file*file) { printk(KERN_INFO"BMP180 device is open\n"); return 0; } /** * bmp180_read - 读取设备数据 * @file: file结构 * @buf: 用户空间缓冲区 * @size: 请求读取的大小 * @off: 文件偏移 * * 返回值: 实际读取的字节数,负数表示错误 */ static ssize_t bmp180_read(struct file*file,char __user*buf,size_t size,loff_t*off) { uint8_t cmd[2]; uint8_t data[3]; long ut = 0; // 未补偿的温度值 long up = 0; // 未补偿的压力值 long x1, x2, b5; long p; int ret; // ====== 1. 读取温度 ====== cmd[0] = BMP180_REG_CTRL_MEAS; cmd[1] = BMP180_CMD_TEMP; ret = i2c_write_bmp180(bmp180_client,cmd,2); if(ret < 0) { printk(KERN_ERR "BMP180: Failed to start temperature measurement\n"); return ret; } // 等待温度测量完成(最大4.5ms) msleep(5); // 读取温度数据(2字节) ret = i2c_read_bmp180(bmp180_client,BMP180_RRG_OUT_MSB,data,2); if(ret < 0) { printk(KERN_ERR "BMP180: Failed to read temperature data\n"); return ret; } ut = (data[0] << 8) | data[1]; // ====== 2. 读取压力 ====== cmd[0] = BMP180_REG_CTRL_MEAS; cmd[1] = BMP180_CMD_PRESS_OSS0 + (OSS << 6); ret = i2c_write_bmp180(bmp180_client,cmd,2); if (ret < 0) { printk(KERN_ERR "BMP180: Failed to start pressure measurement\n"); return ret; } msleep(5); ret = i2c_read_bmp180(bmp180_client,BMP180_RRG_OUT_MSB,data,3); if (ret < 0) { printk(KERN_ERR "BMP180: Failed to read pressure data\n"); return ret; } up = ((data[0] << 16) | (data[1] << 8) | data[2]) >> (8 - OSS); // ====== 3. 温度补偿计算 ====== x1 = ((ut - calib.AC6) * calib.AC5) >> 15; x2 = (calib.MC << 11) / (x1 + calib.MD); b5 = x1 + x2; long temperature = (b5 + 8) >> 4; // ====== 4. 气压补偿计算 ====== long b6 = b5 - 4000; x1 = (calib.B2 * ((b6 * b6)>>12)) >> 11; x2 = (calib.AC2 * b6) >> 11; long x3 = x1 + x2; long b3 = (((calib.AC1 * 4 + x3) << OSS) + 2) / 4; x1 = (calib.AC3 * b6) >> 13; x2 = (calib.B1 * ((b6 * b6) >> 12)) >> 16; x3 = ((x1 + x2) + 2) >> 2; unsigned long b4 = (calib.AC4 * (unsigned long)(x3 + 32768)) >> 15; unsigned long b7 = ((unsigned long)up - b3) * (50000 >> OSS); if(b7 < 0x80000000){ p = (b7 * 2) / b4; }else{ p = (b7 / b4) * 2; } x1 = (p >> 8) * (p >> 8); x1 = (x1 * 3038) >> 16; x2 = (-7357 * p) >> 16; long pressure = p + ((x1 + x2 + 3791) >> 4); // ====== 5. 准备返回数据 ====== // 创建包含温度、气压和海拔高度的数据结构 result.temperature = temperature; result.pressure = pressure; printk(KERN_DEBUG "BMP180: Raw T=%ld, P=%ld Calculated T=%ld.%ld°C, P=%ldPa, \n", ut, up, temperature/10, temperature%10, pressure ); // ====== 6. 复制到用户空间 ====== if(copy_to_user(buf,&result,sizeof(result))){ printk(KERN_ERR "BMP180:copy_to_user is error \n"); return -EFAULT; } return sizeof(result); } /** * bmp180_release - 关闭设备 * @inode: inode结构 * @file: file结构 * * 返回值: 0 */ static int bmp180_release(struct inode*inode,struct file*file) { printk(KERN_INFO"bmp180 device is error\n"); return 0; } // 文件操作结构体 static struct file_operations fops = { .owner = THIS_MODULE, .open = bmp180_open, .read = bmp180_read, .release = bmp180_release , }; // ============================== 校准数据读取 ========================= /** * read_calibration_data - 读取校准数据 * * 返回值: 0成功,负数表示错误 */ int read_calibration_data(void) { uint8_t calib_data[22]; int ret; ret = i2c_read_bmp180(bmp180_client,BMP180_REG_CALIB_START,calib_data,sizeof(calib_data)); if(ret < 0) { printk(KERN_ERR "BMP180 read calibration data is error\n"); return ret; } calib.AC1 = (calib_data[0] << 8) | calib_data[1]; calib.AC2 = (calib_data[2] << 8) | calib_data[3]; calib.AC3 = (calib_data[4] << 8) | calib_data[5]; calib.AC4 = (calib_data[6] << 8) | calib_data[7]; calib.AC5 = (calib_data[8] << 8) | calib_data[9]; calib.AC6 = (calib_data[10] << 8) | calib_data[11]; calib.B1 = (calib_data[12] << 8) | calib_data[13]; calib.B2 = (calib_data[14] << 8) | calib_data[15]; calib.MB = (calib_data[16] << 8) | calib_data[17]; calib.MC = (calib_data[18] << 8) | calib_data[19]; calib.MD = (calib_data[20] << 8) | calib_data[21]; // 打印校准数据用于调试 printk(KERN_INFO "BMP180 Calibration Data:\n"); printk(KERN_INFO "AC1=%d, AC2=%d, AC3=%d\n", calib.AC1, calib.AC2, calib.AC3); printk(KERN_INFO "AC4=%u, AC5=%u, AC6=%u\n", calib.AC4, calib.AC5, calib.AC6); printk(KERN_INFO "B1=%d, B2=%d, MB=%d\n", calib.B1, calib.B2, calib.MB); printk(KERN_INFO "MC=%d, MD=%d\n", calib.MC, calib.MD); ret = i2c_read_bmp180(bmp180_client,BMP180_REG_CTRL_MEAS,&OSS,1); if(ret < 0) { printk(KERN_ERR "BMP180 read OSS data is error\n"); return ret; } OSS >>= 6; printk(KERN_INFO "OSS=%d\n",OSS); return 0; } // ============================== 驱动探测和移除 ======================= /** * bmp180_probe - 驱动探测函数 * @client: I2C客户端 * @id: 设备ID * * 返回值: 0成功,负数表示错误 */ static int bmp180_probe(struct i2c_client*client) { int ret; printk(KERN_INFO "BMP180 probe started for device at 0x%02X\n", client->addr); // 1. 分配设备号 ret = alloc_chrdev_region(&bmp180_dev_num,0,1,DEV_NAME); if(ret < 0) { printk(KERN_ERR "BMP180 : alloc_chrdev_region is errot\n"); return ret; } // 2. 初始化字符设备 bmp180_cdev.owner = THIS_MODULE; cdev_init(&bmp180_cdev,&fops); // 3. 添加字符设备 ret = cdev_add(&bmp180_cdev,bmp180_dev_num,1); if(ret < 0) { printk(KERN_ERR"BMP180:cdev_add is error\n"); goto free_dev_num; } // 4. 创建设备类 bmp180_class = class_create(DEV_NAME); // 5. 创建设备节点 bmp180_device = device_create(bmp180_class,NULL,bmp180_dev_num,NULL,DEV_NAME); // 6. 保存I2C客户端 bmp180_client = client; // 7. 读取校准数据 ret = read_calibration_data(); if(ret < 0) { printk(KERN_ERR "BMP180: Failed to read calibration data\n"); goto destroy_device; } printk(KERN_INFO "BMP180 driver probed successfully\n"); return 0; destroy_device: device_destroy(bmp180_class,bmp180_dev_num); class_destroy(bmp180_class); cdev_del(&bmp180_cdev); free_dev_num: unregister_chrdev_region(bmp180_dev_num,1); return ret; } /** * bmp180_remove - 驱动移除函数 * @client: I2C客户端 */ static void bmp180_remove(struct i2c_client*client) { device_destroy(bmp180_class,bmp180_dev_num); class_destroy(bmp180_class); cdev_del(&bmp180_cdev); unregister_chrdev_region(bmp180_dev_num,1); printk(KERN_INFO"bmp180 is remove\n"); } // ============================== 设备ID和设备树匹配表 ================= static const struct i2c_device_id bmp180_device_id[] = { {"kone,bmp180"}, {}, }; static const struct of_device_id bmp180_of_match_table[] = { {.compatible = "kone,bmp180"}, {}, }; // ============================== I2C驱动结构 ========================= static struct i2c_driver bmp180_driver = { .probe = bmp180_probe, .remove = bmp180_remove, .id_table = bmp180_device_id, .driver = { .name = "bmp180", .owner = THIS_MODULE, .of_match_table = bmp180_of_match_table, }, }; static int __init bmp180_init(void) { i2c_add_driver(&bmp180_driver); printk(KERN_INFO"bmp180 init \n"); return 0; } static void __exit bmp180_exit(void) { i2c_del_driver(&bmp180_driver); printk(KERN_INFO"bnp180 exit \n"); } module_init(bmp180_init); module_exit(bmp180_exit); MODULE_AUTHOR("kone"); MODULE_LICENSE("GPL v2");Makefile
#export PATH="/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH" # 确保系统工具目录在 PATH 的最前面 export PATH="/bin:/usr/bin:/home/mxz/pocketbeagle_2/sdk/gcc-11.5.0-nolibc/aarch64-linux/bin:$PATH" # 设置架构和交叉编译前缀 export ARCH=arm64 export CROSS_COMPILE=aarch64-linux- obj-m:=bmp180.o KDIR:=/home/mxz/pocketbeagle_2/sdk/linux PWD:=$(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean
测试app
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <math.h> // ICAO 标准大气参数 #define ICAO_SEA_LEVEL_PRESSURE 101325.0 // 海平面标准气压 (Pa) #define ICAO_SCALE_HEIGHT 44330.0 // 尺度高度 (米) #define ICAO_EXPONENT 0.190263 // 指数系数 (1/5.255 ≈ 0.190263) // 与驱动中定义相同的结果结构 struct bmp180_result { long temperature; // 温度(0.1°C) long pressure; // 压力(Pa) }; /** * @brief 使用ICAO简化公式计算海拔高度 altitude = 44330 * [1 - (P/P0)^(1/5.255)] * @param pressure 当前气压 (Pa) * @param sea_level_pressure 海平面气压 (Pa),如果为0则使用标准值 * @return 海拔高度 (米) */ double calculate_altitude(double pressure, double sea_level_pressure) { if(pressure < 0) return -1; // 确保气压值合理 if (pressure > sea_level_pressure) { pressure = sea_level_pressure; // 气压不可能高于海平面气压 } // 计算气压比 double pressure_ratio = pressure / sea_level_pressure; // 计算海拔 double altitude = ICAO_SCALE_HEIGHT * (1.0 - pow(pressure_ratio,ICAO_EXPONENT) ); return altitude; } int main() { int fd; struct bmp180_result result; // 打开设备 fd = open("/dev/bmp180", O_RDONLY); if (fd < 0) { perror("Failed to open device"); return -1; } // 读取数据 if (read(fd, &result, sizeof(result)) != sizeof(result)) { perror("Failed to read data"); close(fd); return -1; } // 计算真实值 float temperature = result.temperature / 10.0; float pressure = result.pressure / 100.0; // 转换为hPa double altitude = calculate_altitude((double)pressure,ICAO_SEA_LEVEL_PRESSURE); // 打印结果 printf("Temperature: %.1f°C\n", temperature); printf("Pressure: %.2f hPa\n", pressure); printf("Altitude: %.2f meters\n", altitude); // 关闭设备 close(fd); return 0; }
三,演示
将生成的bmp180.ko和app.c copy到开发板上
insmod bmp180.ko gcc app.c -lm ./a.out /dev/bmp180


			
			
			
						
			
 我要赚赏金
