04程序分析
基于 STM32F103C8T6 + HAL 库,软件模拟 I2C 通信,500ms 周期读取传感器数据
4.1 系统初始化
main() — 主函数入口
系统启动后依次执行:HAL 初始化 → 时钟配置(HSE 8MHz × PLL9 = 72MHz)→ GPIO 初始化 → OLED 初始化 → BMP180 初始化(I2C 初始化 + 读取校准系数)。
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
OLED_Init();
BMP180_Init();
while (1)
{
if(HAL_GetTick() - Time_Now >= 500)
{
BMP180_GetData(BMP180_OSS0);
OLED_Clear();
OLED_CenterPrintf(0, "芯片温度:%.1f℃", bmp180.Temp/10.0);
OLED_CenterPrintf(24, "气压:%.2fhPa", bmp180.Press/100.0);
OLED_CenterPrintf(48, "海拔:%.1fm", bmp180.Altitude);
OLED_Update();
Time_Now = HAL_GetTick();
}
}
}
4.2 BMP180 I2C 通信初始化
BMP180_IIC_init() — 软件 I2C GPIO 配置
使用 PB1 作为 SCL(推挽输出),PB0 作为 SDA(开漏输出),均上拉,高速模式。通过宏定义直接操作 GPIO 引脚电平,实现软件模拟 I2C 协议。
// 引脚定义
#define BMP180_SCL_GPIO_PORT GPIOB
#define BMP180_SCL_GPIO_PIN GPIO_PIN_1
#define BMP180_SDA_GPIO_PORT GPIOB
#define BMP180_SDA_GPIO_PIN GPIO_PIN_0
// I2C 地址:0xEE(写) / 0xEF(读) → 7位地址 0x77
static void BMP180_IIC_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
BMP180_SCL_GPIO_CLK_ENABLE();
BMP180_SDA_GPIO_CLK_ENABLE();
// SCL: 推挽输出,上拉
gpio_init_struct.Pin = BMP180_SCL_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BMP180_SCL_GPIO_PORT, &gpio_init_struct);
// SDA: 开漏输出,上拉(支持双向通信)
gpio_init_struct.Pin = BMP180_SDA_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;
HAL_GPIO_Init(BMP180_SDA_GPIO_PORT, &gpio_init_struct);
BMP180_stop();
}
4.3 校准系数读取
BMP180_ReadCalibrationData() — 读取 EEPROM 校准数据
BMP180 出厂时在内部 EEPROM 中烧录了 11 个校准系数,地址范围 0xAA ~ 0xBE。其中 AC1/AC2/AC3/B1/B2/MB/MC/MD 为有符号 16 位,AC4/AC5/AC6 为无符号 16 位。这些系数是后续温度和气压补偿计算的基础。
static void BMP180_ReadCalibrationData(void)
{
bmp180.AC1 = BMP180_ReadTwoByte_S(0xAA);
bmp180.AC2 = BMP180_ReadTwoByte_S(0xAC);
bmp180.AC3 = BMP180_ReadTwoByte_S(0xAE);
bmp180.AC4 = BMP180_ReadTwoByte_U(0xB0);
bmp180.AC5 = BMP180_ReadTwoByte_U(0xB2);
bmp180.AC6 = BMP180_ReadTwoByte_U(0xB4);
bmp180.B1 = BMP180_ReadTwoByte_S(0xB6);
bmp180.B2 = BMP180_ReadTwoByte_S(0xB8);
bmp180.MB = BMP180_ReadTwoByte_S(0xBA);
bmp180.MC = BMP180_ReadTwoByte_S(0xBC);
bmp180.MD = BMP180_ReadTwoByte_S(0xBE);
}
| 寄存器地址 | 系数 | 类型 | 用途 |
| 0xAA / 0xAB | AC1 | int16_t | 气压补偿 |
| 0xAC / 0xAD | AC2 | int16_t | 气压补偿 |
| 0xAE / 0xAF | AC3 | int16_t | 气压补偿 |
| 0xB0 / 0xB1 | AC4 | uint16_t | 气压补偿 |
| 0xB2 / 0xB3 | AC5 | uint16_t | 温度补偿 |
| 0xB4 / 0xB5 | AC6 | uint16_t | 温度补偿 |
| 0xB6 / 0xB7 | B1 | int16_t | 气压补偿 |
| 0xB8 / 0xB9 | B2 | int16_t | 气压补偿 |
| 0xBA / 0xBB | MB | int16_t | 保留 |
| 0xBC / 0xBD | MC | int16_t | 温度补偿 |
| 0xBE / 0xBF | MD | int16_t | 温度补偿 |
4.4 温度原始数据读取
BMP180_Read_UT() — 读取未补偿温度值
向控制寄存器 0xF4 写入 0x2E 启动温度转换,等待 5ms 后从数据寄存器 0xF6 读取 2 字节原始温度值(UT)。
static int32_t BMP180_Read_UT(void)
{
BMP180_WriteOneByte(0xF4, 0x2E); // 启动温度转换
HAL_Delay(5); // 等待转换完成
return (int32_t)BMP180_ReadTwoByte_S(0xF6);
}
4.5 气压原始数据读取
BMP180_Read_UP() — 读取未补偿气压值
向控制寄存器 0xF4 写入 0x34 + (oss << 6) 启动气压转换。不同过采样等级对应不同等待时间:OSS0=5ms, OSS1=8ms, OSS2=14ms, OSS3=26ms。读取 3 字节数据(MSB, LSB, XLSB),右移 (8-oss) 位得到原始气压值(UP)。
static int32_t BMP180_Read_UP(BMP180_OSS_t oss)
{
uint8_t ctrl;
uint32_t delay_tbl[] = {5, 8, 14, 26};
ctrl = 0x34 + (oss << 6);
BMP180_WriteOneByte(0xF4, ctrl);
HAL_Delay(delay_tbl[oss]);
// 读取 3 字节: MSB, LSB, XLSB
msb = BMP180_read_byte(1);
lsb = BMP180_read_byte(1);
xlsb = BMP180_read_byte(0);
int32_t raw = (msb << 16) | (lsb << 8) | xlsb;
raw >>= (8 - oss);
return raw;
}
| OSS 等级 | 模式 | 内部采样数 | 转换时间 | 分辨率 |
| OSS0 | Ultra Low Power | 1 | 4.5ms | 0.06 hPa |
| OSS1 | Standard | 2 | 7.5ms | 0.05 hPa |
| OSS2 | High | 4 | 13.5ms | 0.04 hPa |
| OSS3 | Ultra High | 8 | 25.5ms | 0.03 hPa |
4.6 温度补偿计算
温度补偿算法
利用校准系数 AC5, AC6, MC, MD 对原始温度 UT 进行补偿计算,得到真实温度值(单位 0.1°C)。最终 bmp180.Temp 除以 10 即为摄氏度。
// 温度补偿计算
bmp180.X1 = ((bmp180.UT - bmp180.AC6) * bmp180.AC5) >> 15;
bmp180.X2 = (((int32_t)bmp180.MC << 11)) / (bmp180.X1 + bmp180.MD);
bmp180.B5 = bmp180.X1 + bmp180.X2;
bmp180.Temp = (bmp180.B5 + 8) >> 4; // 单位 0.1℃
/* 计算公式解析:
X1 = (UT - AC6) × AC5 / 2^15
X2 = MC × 2^11 / (X1 + MD)
B5 = X1 + X2
T = (B5 + 8) / 2^4 → 单位 0.1℃
实际温度(℃) = T / 10
*/
4.7 气压补偿计算
气压补偿算法
气压补偿是最复杂的部分,涉及多个中间变量(B3, B4, B6, B7, X1, X2, X3)。计算过程分为:先由 B5 计算 B6,再通过 AC1/AC2/AC3/B1/B2 计算 X1/X2/X3,然后计算 B3 和 B4,接着用 B7/B4 得到初步气压值,最后进行多项式修正得到最终气压(单位 Pa)。
// 气压补偿计算
bmp180.B6 = bmp180.B5 - 4000;
bmp180.X1 = ((int32_t)bmp180.B2 * ((bmp180.B6 * bmp180.B6) >> 12)) >> 11;
bmp180.X2 = ((int32_t)bmp180.AC2 * bmp180.B6) >> 11;
bmp180.X3 = bmp180.X1 + bmp180.X2;
bmp180.B3 = ((((int32_t)bmp180.AC1 << 2) + bmp180.X3) + 2) >> 2;
bmp180.X1 = ((int32_t)bmp180.AC3 * bmp180.B6) >> 13;
bmp180.X2 = (((int32_t)bmp180.B1 * ((bmp180.B6 * bmp180.B6) >> 12))) >> 16;
bmp180.X3 = ((bmp180.X1 + bmp180.X2) + 2) >> 2;
bmp180.B4 = ((uint32_t)bmp180.AC4 * (bmp180.X3 + 32768UL)) >> 15;
bmp180.B7 = ((uint32_t)(bmp180.UP - bmp180.B3)) * 50000UL;
if(bmp180.B7 < 0x80000000UL)
bmp180.Press = (bmp180.B7 * 2UL) / bmp180.B4;
else
bmp180.Press = (bmp180.B7 / bmp180.B4) * 2UL;
// 多项式修正
bmp180.X1 = (bmp180.Press >> 8) * (bmp180.Press >> 8);
bmp180.X1 = ((int32_t)bmp180.X1 * 3038) >> 16;
bmp180.X2 = (-7357 * bmp180.Press) >> 16;
bmp180.Press = bmp180.Press + ((bmp180.X1 + bmp180.X2 + 3791) >> 4);
4.8 海拔计算
海拔计算 — 国际标准大气压公式
利用当前气压和海平面参考气压(SEA_LEVEL_PRESSURE),通过气压-高度指数关系计算海拔。代码中使用 100400.0 Pa(约 1004.0 hPa)作为当地海平面气压参考值。海拔 = 44330 × (1 - (P/P₀)^(1/5.255))。
// 用户配置:当地海平面气压 (Pa)
#define SEA_LEVEL_PRESSURE 100400.0f
// 海拔计算
bmp180.Altitude = 44330.0f * (1.0f - powf(
((float)bmp180.Press / SEA_LEVEL_PRESSURE),
(1.0f/5.255f)
));
/* 公式解析:
h = 44330 × (1 - (P / P0)^(1/5.255))
P : 当前实测气压 (Pa)
P0 : 海平面参考气压 (Pa),需根据当地天气预报设置
h : 海拔高度 (m)
示例:P=100400Pa → h≈0m
P=90000Pa → h≈914m
*/
4.9 主循环与 OLED 显示
while(1) — 主循环数据刷新
主循环以 500ms 为周期,通过 HAL_GetTick() 计时。每次更新调用 BMP180_GetData(OSS0) 读取并计算数据,然后清屏并在 OLED 上居中显示三行信息:芯片温度、气压、海拔。OLED 使用独立的软件 I2C(PB14=SDA, PB15=SCL)。
while (1)
{
if(HAL_GetTick() - Time_Now >= 500) // 500ms 更新一次
{
BMP180_GetData(BMP180_OSS0); // 读取温压数据
OLED_Clear(); // 清屏
OLED_CenterPrintf(0, "芯片温度:%.1f℃", bmp180.Temp/10.0);
OLED_CenterPrintf(24, "气压:%.2fhPa", bmp180.Press/100.0);
OLED_CenterPrintf(48, "海拔:%.1fm", bmp180.Altitude);
OLED_Update(); // 刷新显示
Time_Now = HAL_GetTick(); // 更新时间戳
}
}
4.10 数据值对照表
| 参数 | 变量 | 单位 | 显示转换 | 典型值 |
| 芯片温度 | bmp180.Temp | 0.1°C | ÷10 → °C | 25.0°C |
| 气压 | bmp180.Press | Pa | ÷100 → hPa | 1004.00 hPa |
| 海拔 | bmp180.Altitude | m | 直接显示 | 0.0m |