ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

BMP180气压传感器补偿算法与Proteus仿真实践

BMP180气压传感器补偿算法与Proteus仿真实践

简介:这份资源面向学习STM32嵌入式开发与传感器应用的初学者及进阶者,围绕BMP180气压计的气压、海拔与温度测量展开,提供从源码到仿真的完整实践方案。压缩包共406个文件,约30.52MB,以C语言源文件与头文件为主体,配合Keil工程文件、HEX固件、Proteus仿真工程及设计文档,覆盖驱动编写、I2C通信、数据校准与海拔换算等关键环节。已有357人学习下载,说明其在同类项目中具备一定参考价值。读者可从中获得可直接编译运行的工程源码、可复现的Proteus硬件仿真电路,以及BMP180数据手册与设计报告等全套资料,便于对照理解传感器初始化、原始数据读取与巴尔末公式换算的实现细节,也能借助仿真验证软硬件逻辑,适合课程设计、毕业设计或自学嵌入式数据采集的读者按模块拆解学习。

1. 拆开这个压缩包:BMP180 气压海拔温度三合一,仿真先跑通再谈实物

BMP180 这颗料在气压计圈子里算是老熟人了,I2C 两线、3.3V 供电、能同时给出气压、温度和换算海拔,价格便宜到学生项目随便用。但真正让人翻车的从来不是接线,而是补偿算法——原始寄存器读出来的是 ADC 码,不套那套官方补偿公式,气压能偏出几十 hPa,海拔直接飘几百米。这个资源包把 STM32 驱动、BMP180 补偿计算、Proteus 仿真工程和全套资料打包在一起,价值就在于让你在没焊一块板子的情况下,先把 I2C 时序、校准系数读取、温压补偿这条链路完整跑一遍。适合正在做课设、想验证气压计方案、或者手里有 Proteus 但不知道怎么把传感器模型接进 STM32 的人。下面按“先看懂数据流、再动手复现、最后排坑”的顺序拆。

2. BMP180 的数据链路:从 I2C 寄存器到海拔数字

2.1 为什么不能直接读气压值

BMP180 内部有 EEPROM 存了 11 个校准系数(AC1 到 AC6、B1、B2、MB、MC、MD),每次上电必须先读出来。传感器输出的 UT(温度原始值)和 UP(气压原始值)都是未补偿的 ADC 码,必须代入 Bosch 官方那套分段公式才能得到真实温度和气压。很多人图省事直接拿 UP 当气压用,结果串口打出来是 30000 多,看着像气压其实是 raw 值。这个资源包的驱动里把校准系数读取和补偿计算都封装好了,但你要能看懂每一步在算什么,改参数时才不会抓瞎。

温度补偿的核心是 B5 这个中间量,它同时参与温度计算和气压计算,所以顺序不能反:先算 UT 得 B5,再用 B5 去算气压。气压计算还分 oss(过采样设置)四档,0 到 3 对应不同的采样次数和精度,oss 越高转换越慢但噪声越小。仿真里用 oss=0 就够看趋势,实物上想稳一点用 oss=2 或 3。

2.2 仿真工程里的接线与地址

Proteus 里 BMP180 模型一般挂在 I2C1 上,STM32 这边用 PB6(SCL)和 PB7(SDA),上拉电阻 4.7k 到 3.3V。BMP180 的 7 位地址是 0x77,左移一位写操作是 0xEE,读操作是 0xEF。仿真模型对时序比实物宽容,但如果你在代码里把地址写错成 0x76,仿真里可能直接读回全 0xFF,串口一片 255,这时候别怀疑传感器坏了,先查地址。

/* BMP180 地址与关键寄存器定义,仿真和实物通用 */ #define BMP180_ADDR_W 0xEE /* 0x77 << 1,写 */ #define BMP180_ADDR_R 0xEF /* 0x77 << 1 | 1,读 */ #define BMP180_REG_CAL 0xAA /* 校准系数起始地址,共 22 字节 */ #define BMP180_REG_CTRL 0xF4 /* 控制寄存器:写命令 */ #define BMP180_REG_DATA 0xF6 /* 数据寄存器:读结果 */ #define BMP180_CMD_TEMP 0x2E /* 温度转换命令 */ #define BMP180_CMD_PRES 0x34 /* 气压转换命令,oss=0 */

上面这段是驱动的地基,地址和寄存器地址错一个字节,后面全白搭。注意BMP180_CMD_PRES在 oss=1/2/3 时要分别改成 0x74、0xB4、0xF4,资源包里如果只给了 0x34,你改精度的时候要自己补上。校准系数是 22 字节连续读,从 0xAA 开始,读的时候别用单字节循环,直接连续读效率高,也避免中间被其他 I2C 设备打断。

2.3 补偿计算的代码骨架

补偿公式看着长,拆开就是几个乘加和移位。下面这段是温度补偿的核心,B5 算出来之后气压补偿要用到它,所以函数返回值里必须把 B5 带出来。

/* 温度补偿:输入 UT 和校准系数,输出 0.1℃ 单位和 B5 */ int32_t bmp180_calc_temp(int32_t ut, int16_t ac5, int16_t ac6, int16_t mc, int16_t md, int32_t *b5_out) { int32_t x1 = ((ut - (int32_t)ac6) * (int32_t)ac5) >> 15; int32_t x2 = ((int32_t)mc << 11) / (x1 + md); int32_t b5 = x1 + x2; *b5_out = b5; return (b5 + 8) >> 4; /* 单位 0.1℃,除以 10 得摄氏度 */ }

x1那一步是拿原始温度和 AC6 做差再乘 AC5,右移 15 位相当于除以 32768,这是定点数运算的常规操作。x2里mc << 11是放大系数,除以x1 + md得到修正量。最后(b5 + 8) >> 4里的 +8 是四舍五入,别省,省了低温段会差 0.1℃。气压补偿更绕,里面有个(b6 * b6) >> 12的平方项,还有b3的三次方项,仿真里跑起来没感觉,实物上如果用软件浮点会慢,资源包里用的是整型运算,这点做得对。

3. 在 Proteus 里把仿真跑起来:从加载 hex 到串口出数

3.1 工程文件怎么摆

压缩包解压后一般分三块:Keil 工程目录、Proteus 工程文件(.pdsprj)、资料文档。Keil 那边打开 .uvprojx,编译前先确认器件型号选的是 STM32F103 系列(资源包常见的是 C8T6),输出勾选 Create HEX File。Proteus 那边双击 STM32 模型,在 Program File 里指向刚编译出来的 hex,晶振频率设成 8MHz 还是 72MHz 要跟代码里的 SystemInit 对上,对不上串口波特率会偏,打出来全是乱码。

# 编译后确认 hex 生成位置,通常在 Objects 目录 ls ./Objects/*.hex # Proteus 里加载时路径不要带中文,仿真模型对中文路径兼容性差

路径带中文是仿真翻车的经典原因,Proteus 加载 hex 失败时不报错,只是不运行,你盯着屏幕等半天串口没动静,其实是文件根本没读进去。我一般把整个工程拷到纯英文短路径下再开,比如D:\sim\bmp180,省得排查。

3.2 串口虚拟终端怎么看数

Proteus 里用 COMPIM 或者 Virtual Terminal 接 USART1 的 TX(PA9),波特率设 9600,和代码里USART_Init的配置一致。仿真启动后如果终端里能看到周期性的温度气压行,说明 I2C 读、补偿算、串口发这条链路通了。如果只看到第一行然后卡住,大概率是转换等待没做,BMP180 温度转换要等 4.5ms,气压 oss=0 要等 4.5ms,oss=3 要等 25.5ms,代码里用 delay 或者查 EOC 引脚都行,资源包里用的是固定延时,简单但够用。

/* 读一次完整温压的流程,注意每一步的等待 */ int32_t ut, up, b5, temp; bmp180_read_cal(); /* 上电先读 22 字节校准系数 */ bmp180_write(BMP180_REG_CTRL, BMP180_CMD_TEMP); delay_ms(5); /* 温度转换等待,留余量 */ ut = bmp180_read16(BMP180_REG_DATA); temp = bmp180_calc_temp(ut, ac5, ac6, mc, md, &b5); bmp180_write(BMP180_REG_CTRL, BMP180_CMD_PRES); delay_ms(5); /* oss=0 时 4.5ms,取 5ms */ up = bmp180_read16(BMP180_REG_DATA); /* 气压补偿用 up 和 b5,这里省略具体公式,资源包内有完整实现 */

延时给 5ms 是留了余量,实物上如果发现气压值偶尔跳变,先把延时加到 6ms 试试,很多时候是转换没完成就读了。海拔换算用国际气压公式,alt = 44330 * (1 - pow(p/101325, 0.1903)),仿真里 pow 用浮点没问题,实物上如果 MCU 没 FPU,建议查表或者用定点近似,资源包里给了浮点版本,够课设用。

3.3 校准系数读出来先打印一遍

这一步很多人跳过,但它是排错的分水岭。上电后先把 11 个系数通过串口打出来,对照数据手册的典型值范围看一眼:AC1 大概在 7000 到 8000 之间,AC5 在 20000 多,AC6 在 15000 到 20000 之间。如果读出来全是 0 或者全是 0xFFFF,说明 I2C 根本没通,别往下算了。仿真里系数是模型内置的固定值,实物上每颗片子不一样,但量级不会差太远。

提示:校准系数读取失败时,先量 SCL/SDA 有没有上拉,再用逻辑分析仪抓一帧起始信号,确认从机地址应答位是 0 还是 1。仿真里没有硬件可量,就检查 Proteus 模型属性里的地址设置。

4. 避坑与排查:仿真能跑不等于实物能用

4.1 串口打出 255 或 0:I2C 没应答

现象是校准系数全 0xFF 或者全 0,温度气压算出来是垃圾值。原因通常是地址写错、上拉缺失、或者 Proteus 模型没接电源。仿真里 BMP180 模型有 VCC 和 GND 引脚,忘了接 VCC 它照样在图上,但不工作。实物上先查 0x77 左移后的 0xEE 有没有写对,再用万用表量 SDA 空闲时是不是高电平,不是高电平就是上拉没接或者被拉死。

4.2 温度对但气压离谱:B5 没传对

温度显示 25℃ 正常,气压却是 300 多或者 90000 多。原因是气压补偿函数里用的 B5 是局部变量,温度算完就丢了,气压那边拿了个未初始化的值。解决是把 B5 作为输出参数从温度函数带出来,或者用全局变量存。资源包里如果两个函数分开写,检查一下参数传递,这是血泪经验,我见过不止一个人在这卡一晚上。

4.3 海拔一直漂:基准气压没设对

海拔公式里的 101325 是标准海平面气压,但实际天气每天气压都在变,拿固定值算海拔,一天之内漂十几米很正常。课设里这么用没问题,真要准就得输入当地实时基准气压。仿真里没有真实天气,这个坑不明显,实物上如果发现海拔早上和下午差很多,别怀疑传感器,先查基准值。

4.4 oss 改高了数据反而跳:转换时间不够

把 oss 从 0 改成 3 想提高精度,结果气压值跳得更厉害。原因是 oss=3 转换要 25.5ms,代码里延时还是 5ms,读到的还是上一次的旧数据或者半成品。改 oss 必须同步改延时,oss=1 等 7.5ms,oss=2 等 13.5ms,oss=3 等 25.5ms,取整加 1ms 余量。这个参数联动关系资源包的注释里如果没写,自己记牢。

4.5 仿真正常实物白屏:供电和电平

Proteus 里 3.3V 随便标,实物上 BMP180 是 3.3V 器件,SDA/SCL 上拉到 3.3V,如果主控是 5V 的 STM32 板子(有些最小系统板 IO 是 5V tolerant 但上拉到了 5V),长期跑会伤传感器。现象是刚开始能用,跑一会儿数据乱跳或者彻底没响应。解决是上拉统一到 3.3V,或者加电平转换。仿真不体现这个,实物上必须查。

5. 进阶:把仿真数据导出来做趋势验证

仿真跑通之后,别只盯着串口看几个数就完事。我一般会把串口输出重定向到文件,跑一段时间的温度气压序列,用脚本画个曲线,确认数据没有阶跃跳变、没有周期性归零。Proteus 的 Virtual Terminal 支持把内容保存到文件,或者用 COMPIM 配对虚拟串口,外面用 Python 的 pyserial 收。

import serial, time ser = serial.Serial('COM3', 9600, timeout=1) with open('bmp180_log.csv', 'w') as f: f.write('temp_c,pressure_pa,alt_m\n') t0 = time.time() while time.time() - t0 < 60: # 采 60 秒 line = ser.readline().decode('ascii', errors='ignore').strip() if line.count(',') == 2: # 简单校验格式 f.write(line + '\n')

这段脚本的关键在格式校验,line.count(',') == 2能过滤掉启动时的乱码行,保证写进 CSV 的都是完整数据。采完之后用 pandas 读进来,看气压列的差分,如果出现单点跳变超过 50Pa,回去查是不是延时不够或者 I2C 被干扰。海拔列做滑动平均,窗口取 10 个点,能看出趋势是否单调,仿真里气压是恒定的,海拔应该是一条直线,如果画出来是锯齿,说明补偿计算里有整型溢出或者移位方向错了。

验证补偿公式还有一个笨办法但很有效:拿数据手册里的典型 UT/UP 和对应系数手算一遍,跟代码输出对。比如 UT=27898、AC5=24275、AC6=16576、MC=-8711、MD=2868,手算温度应该是 15.0℃ 左右,代码跑出来差 0.1 以内算对。这一步做完,你对这套公式的信任度会完全不一样,后面改 oss 或者换传感器心里有底。

从那以后我每次拿到带补偿算法的传感器代码,都先把校准系数和一组已知输入手算对一遍,再上仿真,最后才接实物。这个习惯帮我省掉了大量“到底是硬件坏了还是算法错了”的来回猜。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进