首页 / 资讯中心 / 文章详情

STM32驱动BMP180气压计:I2C通信与海拔换算仿真

STM32驱动BMP180气压计:I2C通信与海拔换算仿真 ★ FEATURED ARTICLE
简介这份资源面向学习嵌入式开发与传感器应用的初学者及进阶者围绕STM32单片机驱动BMP180气压计展开解决气压、海拔高度与温度测量的一体化实现问题。包内共406个文件以113个h头文件、75个c源文件为核心辅以56个o目标文件、55个crf编译记录及uvprojx工程文件另含hex烧录文件、Proteus仿真工程与设计文档压缩包约30.52MB源码、仿真与资料齐备。已有357人学习下载具备一定参考热度。读者可从中获得完整的I2C通信驱动、BMP180校准与数据读取流程、气压转海拔的算法实现以及Proteus虚拟仿真验证方案配合原理图与说明文档便于对照理解各功能模块的代码组织与调试思路适合课程设计、毕业设计及嵌入式入门实践参考。1. 从一块气压计芯片说起这个仿真项目到底能跑出什么很多人第一次接触气压传感器都是从一块 BMP180 开始的。它便宜、常见、I2C 两线就能读数据但真要把“气压值”换算成“海拔高度”中间那套补偿算法能把人绕晕。这个基于 STM32 单片机的 BMP180 气压计仿真项目做的就是把这条链路完整跑通单片机通过 I2C 读取原始气压和温度寄存器套用芯片手册里的补偿公式算出真实气压、温度再进一步推算出海拔高度最后在 Proteus 里用虚拟终端或 LCD 把结果显示出来。它适合两类人一类是刚学完 I2C 协议、想找个完整外设案例练手的学生另一类是手头没有实物传感器、想先在仿真里把驱动和算法调通再上硬件的开发者。整套资料包含源码、Proteus 仿真工程和配套文档拿到手就能在电脑上把“读传感器—算数据—显示结果”这条链路走一遍不用等快递、不用焊板子。2. 项目架构与 Proteus 仿真环境搭建先把虚拟硬件跑起来2.1 整体结构三层分工各管一段这个项目的代码结构不复杂但分层是清晰的理解了这个分层后面改起来才不会乱。整体可以拆成三层最底层是 I2C 通信层负责和 BMP180 打交道包括起始信号、字节读写、应答检测中间层是 BMP180 驱动层负责初始化芯片、读取校准系数芯片出厂时烧在 EEPROM 里的 11 个参数、读取原始温度和气压寄存器最上层是应用层负责调用驱动拿到原始值套补偿公式算出真实物理量再驱动显示。对应的文件大致是这样组织的项目根目录/ ├── Core/ │ ├── Inc/ │ │ ├── bmp180.h // 驱动层头文件声明初始化和读取接口 │ │ ├── i2c_soft.h // 软件 I2C 头文件 │ │ └── main.h │ └── Src/ │ ├── bmp180.c // 补偿算法和寄存器操作都在这里 │ ├── i2c_soft.c // GPIO 模拟 I2C 时序 │ └── main.c // 主循环串口/LCD 输出 ├── Proteus/ │ └── BMP180_STM32.pdsprj // 仿真工程文件 └── Doc/ └── 接线说明与调试记录这里有个设计选择值得说一句很多教程直接用 STM32 的硬件 I2C 外设配置起来一堆寄存器仿真里还容易因为时序问题卡死。这个项目用的是 GPIO 模拟 I2C软件 I2C好处是时序完全可控Proteus 里跑起来更稳移植到不同型号的 STM32 上也几乎不用改。常见做法是把 SCL 和 SDA 各占一个普通 GPIO用延时函数控制高低电平翻转来凑时序代价是速率比硬件 I2C 慢但对 BMP180 这种低速传感器完全够用。2.2 Proteus 里怎么把虚拟电路搭起来仿真工程打开后核心元件就三个STM32 主控一般是 F103 系列、BMP180 传感器模型、显示或串口终端。接线逻辑和实物一致BMP180 的 VCC 接 3.3VGND 接地SCL 和 SDA 分别接到单片机选定的两个 GPIO 上SDA 还需要一个上拉电阻Proteus 里可以用 RES 元件补上阻值 4.7k 到 10k 都行。具体操作步骤打开.pdsprj工程确认 STM32 型号和源码里stm32f10x.h的宏定义一致不一致就改工程里的器件或者改代码里的型号宏。双击 STM32 元件在 “Program File” 一栏指向编译出来的.hex文件这是仿真能跑起来的关键一步很多人忘了加载 hex点运行没反应就是这里漏了。检查 BMP180 模型的 SCL/SDA 引脚编号和代码里i2c_soft.h定义的引脚宏对上。对不上不会报错但读出来全是 0 或者 0xFF。如果用的是虚拟终端显示把单片机的 TX 引脚接到 COMPIM 或 Virtual Terminal 的 RXD 上波特率在代码里和终端属性里设成一致常见是 9600。提示Proteus 的 BMP180 模型对 I2C 时序有一定容忍度但软件 I2C 的延时不能设得太短否则仿真里会出现应答丢失。延时函数用简单的 for 循环空转就行别用 SysTick 精确延时仿真环境下时钟模型和实物有偏差。2.3 编译工具链与 hex 生成源码是标准 Keil MDK 工程.uvprojx打开后直接 Build 就能生成 hex。如果手头没有 Keil也可以用 STM32CubeIDE 或 arm-none-eabi-gcc 重新建工程把Core/Src和Core/Inc下的文件加进去注意补上启动文件和链接脚本。编译前确认两件事一是芯片型号宏比如STM32F10X_MD和实际器件匹配二是输出格式勾选了 “Create HEX File”。这两步任何一步漏了Proteus 那边都加载不了程序现象就是电路通着但屏幕一片空白。3. BMP180 驱动与补偿算法拆解从原始寄存器到海拔高度3.1 初始化与校准系数读取BMP180 上电后不能直接读温度气压必须先读它 EEPROM 里的 11 个校准系数AC1 到 AC6、B1、B2、MB、MC、MD这些系数参与后面的补偿计算读错一个算出来的气压能偏出几十 hPa。初始化流程是发芯片地址写方向 0xEE、发寄存器地址 0xAA、重启后发读方向 0xEF连续读 22 个字节按大端拼成 16 位有符号数存进结构体。// bmp180.c 片段读取校准系数 uint8_t bmp180_init(void) { uint8_t buf[22]; // 从 0xAA 开始连续读 22 字节校准数据 if (i2c_read_bytes(BMP180_ADDR, 0xAA, buf, 22) ! 0) return 1; // 读失败返回错误码 // 大端拼接高字节在前低字节在后 bmp180.ac1 (int16_t)((buf[0] 8) | buf[1]); bmp180.ac2 (int16_t)((buf[2] 8) | buf[3]); bmp180.ac3 (int16_t)((buf[4] 8) | buf[5]); bmp180.ac4 (uint16_t)((buf[6] 8) | buf[7]); bmp180.ac5 (uint16_t)((buf[8] 8) | buf[9]); bmp180.ac6 (uint16_t)((buf[10] 8) | buf[11]); bmp180.b1 (int16_t)((buf[12] 8) | buf[13]); bmp180.b2 (int16_t)((buf[14] 8) | buf[15]); bmp180.mb (int16_t)((buf[16] 8) | buf[17]); bmp180.mc (int16_t)((buf[18] 8) | buf[19]); bmp180.md (int16_t)((buf[20] 8) | buf[21]); return 0; }这段代码的关键在类型AC4、AC5、AC6 是无符号的其余是有符号的拼错了符号位后面补偿公式里的除法会算出离谱的值。i2c_read_bytes是软件 I2C 的连续读封装内部先写寄存器地址再重启读数据这是 I2C 读寄存器的标准动作少一步重启信号读回来的就是错的。3.2 温度补偿先算 B5它是气压补偿的输入BMP180 的补偿算法是分步的温度算出来的中间变量 B5 要喂给气压计算所以顺序不能反。读原始温度写 0xF4 寄存器值为 0x2E延时至少 4.5ms再读 0xF6 和 0xF7 两个字节拼成 UT。// 读取原始温度并做一阶补偿 int32_t bmp180_read_temperature(void) { uint8_t buf[2]; i2c_write_byte(BMP180_ADDR, 0xF4, 0x2E); // 触发温度转换 delay_ms(5); // 手册要求 4.5ms i2c_read_bytes(BMP180_ADDR, 0xF6, buf, 2); int32_t ut (buf[0] 8) | buf[1]; int32_t x1 ((ut - (int32_t)bmp180.ac6) * bmp180.ac5) 15; int32_t x2 ((int32_t)bmp180.mc 11) / (x1 bmp180.md); bmp180.b5 x1 x2; // B5 留给气压计算用 return (bmp180.b5 8) 4; // 返回 0.1℃ 单位 } 15和 11这些移位不是随便写的是芯片手册给的定点运算写法目的是在整数运算下保持精度。返回值的(b5 8) 4里加 8 是四舍五入单位是 0.1 摄氏度显示的时候除以 10 就行。这里最容易翻车的地方是x1 bmp180.md可能为 0 导致除零虽然实际芯片不会出现但仿真模型如果校准系数没加载对就会触发加个判断更稳妥。3.3 气压补偿与海拔换算气压读取比温度多一个过采样设置0xF4 写 0x34一次过采样延时 4.5ms读 0xF6 起三个字节拼成 19 位原始值 UP。然后套手册里那串 B3、B4、B6、B7 的公式最后除以 256 得到 Pa 为单位的气压。// 气压补偿核心省略部分中间变量声明 int32_t bmp180_read_pressure(void) { uint8_t buf[3]; i2c_write_byte(BMP180_ADDR, 0xF4, 0x34); // 一次过采样 delay_ms(5); i2c_read_bytes(BMP180_ADDR, 0xF6, buf, 3); int32_t up (((int32_t)buf[0] 16) | (buf[1] 8) | buf[2]) 8; int32_t b6 bmp180.b5 - 4000; int32_t x1 (bmp180.b2 * ((b6 * b6) 12)) 11; int32_t x2 (bmp180.ac2 * b6) 11; int32_t x3 x1 x2; int32_t b3 ((((int32_t)bmp180.ac1 * 4 x3) 0) 2) 2; // ... B4、B7 计算略完整版在 bmp180.c 中 int32_t p ((b7 * 1000) 8); // 单位 Pa return p; }海拔换算用的是国际气压高度公式简化版就一行altitude 44330 * (1 - pow(p / 101325, 0.1903))其中 p 是当前气压Pa101325 是海平面标准气压。这个公式在代码里需要引入math.h的pow函数注意浮点运算在 STM32 上开销不小如果主循环频率高可以改成查表或者只在显示刷新时算一次。算出来的海拔单位是米精度受当地实际海平面气压影响仿真里用标准值就行实物上想准就得拿当地气象数据校准基准。4. 主循环与显示输出把数据送到串口和屏幕4.1 主循环的组织方式main.c的结构很直白初始化时钟、GPIO、串口或 LCD、I2C 引脚调用bmp180_init()读校准系数然后进 while(1) 循环每轮读一次温度、一次气压、算海拔格式化输出延时一秒。这个一秒的延时不是随便定的BMP180 连续转换本身有耗时加上串口打印太快了数据刷屏看不清太慢了又显得卡。int main(void) { SystemInit(); uart_init(9600); i2c_soft_init(); if (bmp180_init() ! 0) { printf(BMP180 init failed\r\n); while (1); // 初始化失败就停在这里方便排查 } while (1) { int32_t temp bmp180_read_temperature(); // 0.1℃ int32_t press bmp180_read_pressure(); // Pa float altitude 44330.0f * (1.0f - powf((float)press / 101325.0f, 0.1903f)); printf(Temp: %d.%d C\r\n, temp / 10, temp % 10); printf(Press: %d Pa\r\n, press); printf(Altitude: %.1f m\r\n, altitude); delay_ms(1000); } }printf重定向到串口是 STM32 的常规操作需要重写fputc函数把字符一个个塞进 USART 数据寄存器。如果仿真里终端没输出先查这个重定向有没有写、串口引脚有没有在 Proteus 里接对。初始化失败直接死循环是个实用技巧比默默跑下去输出一堆 0 强至少你知道问题出在传感器通信上。4.2 显示方案的两种选择资料里给了串口输出和 LCD 显示两套方案。串口方案调试最方便Proteus 的 Virtual Terminal 直接看数字改代码也快LCD 方案一般是 1602 或 OLED更接近成品形态但仿真里 LCD 模型刷新慢接线也多几根。我的建议是先用串口把数据跑通、确认补偿算法没问题再切到 LCD 做展示这样出问题时能快速定位是算法还是显示驱动。切换的时候注意 LCD 的驱动时序和 I2C 引脚别和 BMP180 冲突共用 I2C 总线的话要处理地址区分BMP180 是 0xEE/0xEFOLED 常见是 0x78不冲突但初始化顺序要注意。5. 避坑与常见问题排查仿真跑不通时先看这几条5.1 现象串口一直输出 0 或固定值原因通常是 I2C 通信根本没建立。先查 Proteus 里 SDA 有没有上拉电阻软件 I2C 配置的引脚和电路图是否一致再查bmp180_init的返回值有没有被忽略。解决方法是把初始化失败的分支加上打印确认是读校准系数就失败了还是读成功了但补偿算错。如果校准系数读出来全是 0xFF基本就是地址或时序问题。5.2 现象温度正常但气压离谱十有八九是 B5 没传对。气压补偿依赖温度计算出的bmp180.b5如果先调气压再调温度或者 b5 被覆盖气压就会算成负数或者几万 Pa。检查主循环里的调用顺序确保read_temperature在read_pressure之前且 b5 是全局或结构体成员别用局部变量。5.3 现象Proteus 点运行没反应最常见的是 hex 文件没加载或者加载了旧版本。改完代码重新编译后Proteus 不会自动更新 hex得手动重新指向新文件或者干脆把工程关掉重开。另一个原因是时钟配置和仿真模型不匹配比如代码里配了 72MHz 而 Proteus 里晶振属性没改表现就是程序跑飞或者延时完全不对。5.4 现象海拔数值跳变厉害仿真模型的气压值本身可能有小幅波动加上整数补偿的量化误差海拔跳个几米正常。想稳一点可以对连续几次采样做滑动平均或者在显示前做一阶低通滤波。别指望仿真里的海拔和真实地理位置对上它用的是标准海平面气压没有当地校准看趋势就行。6. 二次开发改造把这套驱动接到自己的项目里拿到这份源码多数人不会只满足于在仿真里看数字真正有价值的是把 BMP180 驱动抽出来复用到自己的板子上。我一般会做三件事第一把bmp180.c/h和i2c_soft.c/h单独拎出来去掉和 main 的耦合做成一个可移植的传感器模块接口只留init、read_temperature、read_pressure三个函数第二把软件 I2C 的引脚宏和延时函数做成可配置换芯片时只改头文件第三把海拔计算从主循环里挪出来封装成bmp180_calc_altitude(pressure, sea_level_pressure)基准气压做成参数实物部署时传当地气象站的数据精度能提升不少。改造时有个细节容易忽略软件 I2C 的延时在仿真和实物上差别很大。仿真里 for 循环空转几十次可能刚好烧到真实板子上因为主频和编译器优化不同时序可能过快导致通信失败。我的习惯是把延时做成宏实物调试时先用示波器或者逻辑分析仪看 SCL 频率确认在 100kHz 以内再往下走。另外如果目标板已经有硬件 I2C 在用可以把驱动层的读写函数换成 HAL 库的HAL_I2C_Mem_Read/Write补偿算法部分完全不用动这也是分层带来的好处。验证改造是否成功不用一上来就接屏幕先通过串口把温度和气压打出来和手机上的天气应用对比气压趋势趋势一致就说明驱动和算法都对了剩下的只是标定问题。从那以后我每次移植这类传感器驱动都强制先在串口上把原始值和补偿值并排打出来看一遍确认每一步都对得上手册再往下做显示和业务逻辑省得后面出了问题分不清是算法还是界面。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站