在智能硬件项目里,传感器负责把真实环境转换成程序能够处理的数据。温度、气压、湿度、光照、距离、姿态这些信息,本质上都是外部世界进入代码系统的入口。对于 Python 硬件编程而言,BMP280 气压传感器是一个很适合入门的实验对象,因为它通过 I2C 总线通信,既能练习传感器接线,也能理解设备地址、寄存器读取、数据补偿和循环采集这些真实工程中常见的开发逻辑。
本实验使用 CanMV K210 开发板连接 BMP280 气压传感器。程序通过软件 I2C 将 CanMV 的 IO7 作为 SCL、IO6 作为 SDA,与 BMP280 建立通信连接。运行后,程序会扫描 I2C 总线,自动判断 BMP280 常见地址 0x76 或 0x77,再持续读取温度和气压数据,并在终端中每隔 0.5 秒打印一次结果。
| 学习目标 | 说明 |
|---|---|
| 理解 I2C 传感器通信 | 掌握 SCL、SDA、设备地址和总线扫描之间的关系 |
| 认识 BMP280 数据读取流程 | 理解温度、气压数据并不是直接读取变量,而是来自芯片寄存器 |
| 掌握传感器地址判断 | 通过 i2c.scan() 判断模块实际地址是 0x76 还是 0x77 |
| 理解校准补偿 | 认识 BMP280 需要结合内部校准参数计算温度和气压 |
| 建立环境采集思路 | 为后续 LCD 显示、气压曲线、物联网上传和环境监测实验打基础 |
这段代码的重点不只是打印温度和气压,而是展示一个完整的 I2C 传感器读取流程:创建总线、扫描设备、读取校准参数、配置传感器工作模式、读取原始数据、完成补偿计算、循环输出结果。后续接入更多 I2C 传感器时,也可以沿用类似的开发思路。
文章目录
理论基础
BMP280 是一类常见的温度与气压传感器模块。它内部会采集环境温度和大气压数据,但这些数据不会直接以"摄氏度"和"帕斯卡"的形式送到程序中,而是先保存在芯片内部寄存器里。主控板需要通过 I2C 总线读取寄存器,再结合 BMP280 内部存储的校准参数进行补偿计算,最终得到可读的温度和气压结果。
I2C 总线只需要两根核心通信线,SCL 负责时钟节奏,SDA 负责数据传输。多个 I2C 设备可以挂在同一条总线上,设备地址用于区分不同模块。BMP280 常见地址为 0x76 或 0x77,不同模块由于焊盘、电平或模块设计不同,实际地址可能不一样。因此本实验在创建总线后先执行 scan(),根据扫描结果自动选择可用地址。
BMP280 的数据读取还涉及补偿计算。传感器芯片内部存储了一组出厂校准参数,程序会从 0x88 到 0x9E 这些寄存器中读取温度和气压补偿参数。后续读取到原始温度和气压 ADC 数据后,驱动类会通过补偿公式计算出 temperature 和 pressure。这也是传感器实验中非常重要的一点:硬件返回的原始数据,很多时候需要经过驱动转换后才能成为业务层可直接使用的数据。
外部环境
温度与气压变化
BMP280 传感器
采集原始数据
内部寄存器
原始温度 / 原始气压 / 校准参数
I2C 总线
SCL = IO7 / SDA = IO6
CanMV K210
软件 I2C 读取寄存器
BMP280 驱动类
校准参数解析与补偿计算
Python 结果输出
temperature / pressure
串口终端
周期打印温度与气压
模块供电
VCC
公共地线
GND
这张流程图展示的是 BMP280 实验中的电路和数据链路。外部环境变化先被 BMP280 感知,传感器把原始测量值和校准参数保存在寄存器中,CanMV K210 通过 IO7、IO6 组成的软件 I2C 总线读取寄存器数据,驱动类再把原始数据转换成温度和气压结果。串口中看到的数值,已经不是芯片原始字节,而是经过驱动补偿后的计算结果。
从工程角度看,这类实验比普通 GPIO 亮灯更接近真实传感器开发。GPIO 实验通常只关心高低电平,而 BMP280 实验需要理解设备地址、寄存器、字节解析、校准参数、数据补偿和循环采样。后续学习温湿度传感器、姿态传感器、光照传感器或其他 I2C 模块时,这些概念会反复出现。
硬件设施
本实验围绕 CanMV K210 与 BMP280 气压传感器展开。代码没有使用 LCD、摄像头、按键、蜂鸣器、电机等模块,因此这些内容不作为当前实验重点。BMP280 通过 I2C 总线与开发板通信,程序侧主要依赖 machine.I2C 完成总线创建,依赖 fpioa_manager.fm 完成软件 I2C 引脚功能配置,依赖 ustruct.unpack 解析传感器内部校准参数。
接线关系可以先通过下面这张图建立整体印象。BMP280 的 SCL 连接 CanMV IO7,SDA 连接 CanMV IO6,VCC 和 GND 分别连接电源与地线。实际供电电压需要结合模块丝印和模块板载电路判断,部分 BMP280 模块支持 3.3V / 5V,裸芯片类模块通常更偏向 3.3V 供电。

| 硬件 / 软件 | 作用 | 说明 |
|---|---|---|
| CanMV K210 开发板 | 实验运行平台 | 负责执行 MicroPython 程序,并通过 IO6、IO7 与 BMP280 通信 |
| BMP280 气压传感器 | 环境数据采集模块 | 用于读取温度和气压数据,常见 I2C 地址为 0x76 或 0x77 |
| IO7 | I2C 时钟线 SCL | 根据代码和注释推导,连接 BMP280 的 SCL 引脚 |
| IO6 | I2C 数据线 SDA | 根据代码和注释推导,连接 BMP280 的 SDA 引脚 |
machine.I2C |
I2C 通信模块 | 创建软件 I2C 总线,并完成设备扫描、寄存器读写 |
fpioa_manager.fm |
引脚功能映射模块 | 为软件 I2C 提供 GPIOHS 功能绑定 |
micropython.const |
常量定义工具 | 用于定义 BMP280 工作模式、过采样、滤波和寄存器地址 |
ustruct.unpack |
二进制数据解析工具 | 将 BMP280 内部寄存器中的字节数据解析成校准参数 |
time |
延时控制模块 | 控制传感器数据读取间隔 |
实验中用到的核心模块如下。BMP280 负责采集温度和气压,CanMV K210 负责运行程序和读取数据,连接线负责建立 VCC、GND、SCL、SDA 这四类基础连接。调试时不要只看模块是否插上,还要确认 SCL 和 SDA 没有接反。

| BMP280 引脚 | CanMV 引脚 | 通信功能 | 代码配置 | 说明 |
|---|---|---|---|---|
| VCC | 3.3V / 5V | 电源输入 | 注释说明 | 为 BMP280 模块供电,实际电压以模块标识为准 |
| GND | GND | 公共地 | 注释说明 | 保证开发板与传感器具有相同电平参考 |
| SCL | IO7 | I2C 时钟线 | scl=7 |
由开发板输出 I2C 时钟信号 |
| SDA | IO6 | I2C 数据线 | sda=6 |
用于 I2C 数据读写 |
| I2C 地址 | 0x76 / 0x77 |
设备地址 | i2c_bus.scan() |
程序自动扫描常见地址,优先使用扫描到的有效地址 |
| GPIOHS1 | 软件 I2C SCL 映射 | gscl=fm.fpioa.GPIOHS1 |
辅助软件 I2C 工作 | 为软件 I2C 时钟线绑定 GPIOHS 功能 |
| GPIOHS2 | 软件 I2C SDA 映射 | gsda=fm.fpioa.GPIOHS2 |
辅助软件 I2C 工作 | 为软件 I2C 数据线绑定 GPIOHS 功能 |
完成接线后的整体效果如下。程序运行后,串口会先输出 BMP280 测试启动信息,再打印 I2C 扫描结果和实际使用的设备地址。传感器对象初始化成功后,终端会持续输出温度和气压数据。

| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口输出 BMP280 barometer test start |
没有输出时检查程序是否运行、串口是否连接 |
| I2C 扫描 | 终端显示 I2C scan: [...] |
扫描为空时重点检查 SCL、SDA、VCC、GND |
| 地址识别 | 扫描到 118 或 119,对应 0x76 或 0x77 |
十进制 118 / 119 等价于十六进制 0x76 / 0x77 |
| 温度输出 | 终端周期打印 Makerobo Temp |
温度异常时检查传感器通信和补偿参数读取 |
| 气压输出 | 终端周期打印 Makerobo Pressure |
气压单位为 Pa,数值通常在十万 Pa 左右 |
| 连续采集 | 每隔约 0.5 秒刷新一次数据 | 没有连续输出时检查主循环是否进入 |
I2C 可以理解成开发板和传感器之间的"数据通道"。SCL 负责提供通信节奏,SDA 负责传输数据,设备地址负责区分总线上的不同模块。当前代码创建的是软件 I2C,总线频率设置为 400000,也就是 400kHz。程序启动后会先扫描总线,如果发现 0x76 或 0x77,就使用扫描结果创建 BMP280 对象;如果扫描失败,则默认使用 0x76。
软件代码
本实验代码采用单文件合并方式,把 BMP280 驱动类、I2C 总线创建、地址扫描和主循环采集都放在同一个 Python 文件中。程序结构可以分成常量定义、驱动类封装、I2C 创建、地址检测和循环读取几个部分。这样的写法方便初学阶段直接运行,也能完整看到传感器从寄存器读取到数据输出的全过程。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能正常运行基础 print() 测试 |
| CanMV 固件 | 提供 machine.I2C、fpioa_manager 等模块 |
固件环境需要支持当前软件 I2C 与 FPIOA 写法 |
| USB 串口驱动 | 让电脑识别开发板串口 | 串口工具中能看到对应端口 |
micropython.const |
定义驱动常量 | 用于保存 BMP280 模式、寄存器和采样配置 |
ustruct.unpack |
解析校准参数 | 用于把寄存器中的字节数据转换成有符号或无符号整数 |
| 串口终端 | 查看温度、气压和扫描结果 | 能看到 I2C 地址和周期性采样数据 |
python
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
CanMV K210 BMP280 气压传感器实验 - 单文件合并版
接线说明:
BMP280 VCC -> 3.3V / 5V
BMP280 GND -> GND
BMP280 SCL -> CanMV IO7
BMP280 SDA -> CanMV IO6
常见 I2C 地址:
0x76 = 118
0x77 = 119
"""
import time
from micropython import const
from ustruct import unpack as unp
from machine import I2C
from fpioa_manager import fm
# =========================
# BMP280 驱动常量
# =========================
BMP280_POWER_SLEEP = const(0)
BMP280_POWER_FORCED = const(1)
BMP280_POWER_NORMAL = const(3)
BMP280_TEMP_OS_1 = const(1)
BMP280_TEMP_OS_2 = const(2)
BMP280_PRES_OS_1 = const(1)
BMP280_PRES_OS_2 = const(2)
BMP280_PRES_OS_4 = const(3)
BMP280_PRES_OS_8 = const(4)
BMP280_PRES_OS_16 = const(5)
BMP280_STANDBY_0_5 = const(0)
BMP280_STANDBY_62_5 = const(1)
BMP280_STANDBY_125 = const(2)
BMP280_IIR_FILTER_OFF = const(0)
BMP280_IIR_FILTER_4 = const(2)
BMP280_IIR_FILTER_16 = const(4)
BMP280_OS_ULTRALOW = const(0)
BMP280_OS_LOW = const(1)
BMP280_OS_STANDARD = const(2)
BMP280_OS_ULTRAHIGH = const(4)
_BMP280_OS_MATRIX = [
[BMP280_PRES_OS_1, BMP280_TEMP_OS_1, 7],
[BMP280_PRES_OS_2, BMP280_TEMP_OS_1, 9],
[BMP280_PRES_OS_4, BMP280_TEMP_OS_1, 14],
[BMP280_PRES_OS_8, BMP280_TEMP_OS_1, 23],
[BMP280_PRES_OS_16, BMP280_TEMP_OS_2, 44]
]
BMP280_CASE_HANDHELD_DYN = const(1)
_BMP280_CASE_MATRIX = [
[BMP280_POWER_NORMAL, BMP280_OS_ULTRAHIGH, BMP280_IIR_FILTER_4, BMP280_STANDBY_62_5],
[BMP280_POWER_NORMAL, BMP280_OS_STANDARD, BMP280_IIR_FILTER_16, BMP280_STANDBY_0_5],
[BMP280_POWER_FORCED, BMP280_OS_ULTRALOW, BMP280_IIR_FILTER_OFF, BMP280_STANDBY_0_5],
[BMP280_POWER_NORMAL, BMP280_OS_STANDARD, BMP280_IIR_FILTER_4, BMP280_STANDBY_125],
[BMP280_POWER_NORMAL, BMP280_OS_LOW, BMP280_IIR_FILTER_OFF, BMP280_STANDBY_0_5],
[BMP280_POWER_NORMAL, BMP280_OS_ULTRAHIGH, BMP280_IIR_FILTER_16, BMP280_STANDBY_0_5]
]
_BMP280_REGISTER_ID = const(0xD0)
_BMP280_REGISTER_RESET = const(0xE0)
_BMP280_REGISTER_STATUS = const(0xF3)
_BMP280_REGISTER_CONTROL = const(0xF4)
_BMP280_REGISTER_CONFIG = const(0xF5)
_BMP280_REGISTER_DATA = const(0xF7)
# =========================
# BMP280 驱动类
# =========================
class BMP280:
def __init__(self, i2c_bus, addr=0x76, use_case=BMP280_CASE_HANDHELD_DYN):
self._bmp_i2c = i2c_bus
self._i2c_addr = addr
# 读取温度和气压校准参数
self._T1 = unp("<H", self._read(0x88, 2))[0]
self._T2 = unp("<h", self._read(0x8A, 2))[0]
self._T3 = unp("<h", self._read(0x8C, 2))[0]
self._P1 = unp("<H", self._read(0x8E, 2))[0]
self._P2 = unp("<h", self._read(0x90, 2))[0]
self._P3 = unp("<h", self._read(0x92, 2))[0]
self._P4 = unp("<h", self._read(0x94, 2))[0]
self._P5 = unp("<h", self._read(0x96, 2))[0]
self._P6 = unp("<h", self._read(0x98, 2))[0]
self._P7 = unp("<h", self._read(0x9A, 2))[0]
self._P8 = unp("<h", self._read(0x9C, 2))[0]
self._P9 = unp("<h", self._read(0x9E, 2))[0]
self._t_raw = 0
self._t_fine = 0
self._t = 0
self._p_raw = 0
self._p = 0
self.read_wait_ms = 0
if use_case is not None:
self.use_case(use_case)
def _read(self, addr, size=1):
return self._bmp_i2c.readfrom_mem(self._i2c_addr, addr, size)
def _write(self, addr, b_arr):
if not type(b_arr) is bytearray:
b_arr = bytearray([b_arr])
return self._bmp_i2c.writeto_mem(self._i2c_addr, addr, b_arr)
def _gauge(self):
data = self._read(_BMP280_REGISTER_DATA, 6)
self._p_raw = (data[0] << 12) + (data[1] << 4) + (data[2] >> 4)
self._t_raw = (data[3] << 12) + (data[4] << 4) + (data[5] >> 4)
self._t_fine = 0
self._t = 0
self._p = 0
def reset(self):
self._write(_BMP280_REGISTER_RESET, 0xB6)
def _calc_t_fine(self):
self._gauge()
if self._t_fine == 0:
var1 = (((self._t_raw >> 3) - (self._T1 << 1)) * self._T2) >> 11
var2 = (
(((self._t_raw >> 4) - self._T1) *
((self._t_raw >> 4) - self._T1) >> 12) *
self._T3
) >> 14
self._t_fine = var1 + var2
@property
def temperature(self):
self._calc_t_fine()
if self._t == 0:
self._t = ((self._t_fine * 5 + 128) >> 8) / 100.0
return self._t
@property
def pressure(self):
self._calc_t_fine()
if self._p == 0:
var1 = self._t_fine - 128000
var2 = var1 * var1 * self._P6
var2 = var2 + ((var1 * self._P5) << 17)
var2 = var2 + (self._P4 << 35)
var1 = ((var1 * var1 * self._P3) >> 8) + ((var1 * self._P2) << 12)
var1 = (((1 << 47) + var1) * self._P1) >> 33
if var1 == 0:
return 0
p = 1048576 - self._p_raw
p = int((((p << 31) - var2) * 3125) / var1)
var1 = (self._P9 * (p >> 13) * (p >> 13)) >> 25
var2 = (self._P8 * p) >> 19
p = ((p + var1 + var2) >> 8) + (self._P7 << 4)
self._p = p / 256.0
return self._p
def _write_bits(self, address, value, length, shift=0):
data = self._read(address)[0]
mask = int("1" * length, 2) << shift
data &= ~mask
data |= mask & (value << shift)
self._write(address, data)
def _read_bits(self, address, length, shift=0):
data = self._read(address)[0]
return (data >> shift) & int("1" * length, 2)
@property
def standby(self):
return self._read_bits(_BMP280_REGISTER_CONFIG, 3, 5)
@standby.setter
def standby(self, value):
assert 0 <= value <= 7
self._write_bits(_BMP280_REGISTER_CONFIG, value, 3, 5)
@property
def iir(self):
return self._read_bits(_BMP280_REGISTER_CONFIG, 3, 2)
@iir.setter
def iir(self, value):
assert 0 <= value <= 4
self._write_bits(_BMP280_REGISTER_CONFIG, value, 3, 2)
@property
def temp_os(self):
return self._read_bits(_BMP280_REGISTER_CONTROL, 3, 5)
@temp_os.setter
def temp_os(self, value):
assert 0 <= value <= 5
self._write_bits(_BMP280_REGISTER_CONTROL, value, 3, 5)
@property
def press_os(self):
return self._read_bits(_BMP280_REGISTER_CONTROL, 3, 2)
@press_os.setter
def press_os(self, value):
assert 0 <= value <= 5
self._write_bits(_BMP280_REGISTER_CONTROL, value, 3, 2)
@property
def power_mode(self):
return self._read_bits(_BMP280_REGISTER_CONTROL, 2)
@power_mode.setter
def power_mode(self, value):
assert 0 <= value <= 3
self._write_bits(_BMP280_REGISTER_CONTROL, value, 2)
@property
def chip_id(self):
return self._read(_BMP280_REGISTER_ID, 2)
def force_measure(self):
self.power_mode = BMP280_POWER_FORCED
def normal_measure(self):
self.power_mode = BMP280_POWER_NORMAL
def sleep(self):
self.power_mode = BMP280_POWER_SLEEP
def use_case(self, use_case):
assert 0 <= use_case <= 5
power_mode, oversample_setting, iir, standby = _BMP280_CASE_MATRIX[use_case]
press_os, temp_os, self.read_wait_ms = _BMP280_OS_MATRIX[oversample_setting]
self._write(_BMP280_REGISTER_CONFIG, (iir << 2) + (standby << 5))
self._write(_BMP280_REGISTER_CONTROL, power_mode + (press_os << 2) + (temp_os << 5))
# =========================
# I2C 与主程序
# =========================
BMP280_ADDR = 0x76
def create_i2c_bus():
# SCL -> IO7,SDA -> IO6
return I2C(
I2C.I2C_SOFT,
freq=400000,
scl=7,
sda=6,
gscl=fm.fpioa.GPIOHS1,
gsda=fm.fpioa.GPIOHS2
)
def find_bmp280_addr(i2c_bus):
try:
addr_list = i2c_bus.scan()
print("I2C scan:", addr_list)
if 0x76 in addr_list:
return 0x76
if 0x77 in addr_list:
return 0x77
except Exception as e:
print("I2C scan failed:", e)
return BMP280_ADDR
def loop(sensor):
while True:
print("Makerobo Temp = {0:0.2f} *C".format(sensor.temperature))
print("Makerobo Pressure = {0:0.2f} Pa".format(sensor.pressure))
time.sleep(0.5)
if __name__ == "__main__":
print("BMP280 barometer test start")
bus = create_i2c_bus()
sensor_addr = find_bmp280_addr(bus)
print("Use BMP280 address:", sensor_addr)
makerobo_sensor = BMP280(bus, addr=sensor_addr)
loop(makerobo_sensor)
代码中的常量区域主要用于描述 BMP280 的工作模式。BMP280_POWER_SLEEP、BMP280_POWER_FORCED、BMP280_POWER_NORMAL 对应睡眠、强制测量和正常测量模式;温度过采样、气压过采样、待机时间和 IIR 滤波常量,则用于配置传感器的采样精度、响应速度和数据稳定性。初学阶段可以先把这些常量理解成"传感器工作参数",它们会被 use_case() 组合成一套实际运行配置。
BMP280 类是本实验的核心。实例化对象时,构造函数会保存 I2C 总线对象和传感器地址,再从 0x88 到 0x9E 这些寄存器中读取温度和气压校准参数。BMP280 输出的并不是可以直接使用的温度和气压值,而是原始 ADC 数据。程序必须结合这些校准参数进行补偿计算,才能得到更有参考价值的环境数据。
| 函数名 | 功能 | 对应现象 |
|---|---|---|
__init__() |
初始化 BMP280 对象并读取校准参数 | 程序能够根据传感器内部参数计算温度和气压 |
_read() |
从指定寄存器读取数据 | CanMV 通过 I2C 获取 BMP280 内部数据 |
_write() |
向指定寄存器写入配置 | 程序可以设置 BMP280 的工作模式和采样参数 |
_gauge() |
读取原始温度和气压数据 | 传感器完成一次数据采样 |
reset() |
复位 BMP280 | 将传感器恢复到初始状态 |
_calc_t_fine() |
计算温度补偿中间值 | 为温度和气压补偿提供基础数据 |
temperature |
返回摄氏温度 | 终端打印 Makerobo Temp 数值 |
pressure |
返回气压值 | 终端打印 Makerobo Pressure 数值 |
_write_bits() |
修改寄存器中的指定 bit 位 | 精确配置传感器模式、滤波和过采样参数 |
_read_bits() |
读取寄存器中的指定 bit 位 | 查询传感器当前配置状态 |
force_measure() |
设置强制测量模式 | 适合按需触发一次测量的场景 |
normal_measure() |
设置正常测量模式 | 适合持续采集温度和气压 |
sleep() |
设置睡眠模式 | 可降低传感器功耗 |
use_case() |
应用预设采样方案 | 按配置矩阵设置功耗、滤波、过采样和待机时间 |
create_i2c_bus() |
创建软件 I2C 总线 | IO7 和 IO6 被用于 BMP280 通信 |
find_bmp280_addr() |
扫描并选择 BMP280 地址 | 终端打印 I2C 扫描结果和实际使用地址 |
loop() |
循环读取并打印数据 | 终端持续输出温度和气压 |
_read() 和 _write() 封装了 I2C 寄存器读写。_gauge() 从数据寄存器 0xF7 开始读取 6 个字节,并拆解出原始气压值和原始温度值。temperature 和 pressure 使用 @property 写法封装成属性,主程序读取 sensor.temperature 和 sensor.pressure 时,看起来像访问普通变量,内部实际会触发数据采样和补偿计算。
create_i2c_bus() 负责创建软件 I2C 总线。代码中 I2C.I2C_SOFT 表示使用软件模拟 I2C,freq=400000 表示通信频率为 400kHz,scl=7 和 sda=6 对应 CanMV 的 IO7 和 IO6。find_bmp280_addr() 通过 scan() 扫描总线设备地址,如果发现 0x76 或 0x77,就返回扫描到的地址,减少不同 BMP280 模块地址不一致带来的调试问题。
主程序从打印 BMP280 barometer test start 开始,接着创建 I2C 总线并扫描设备地址。传感器对象创建成功后,程序进入 loop() 循环,每隔 0.5 秒读取一次温度和气压。这个流程接近真实物联网设备的数据采集逻辑:建立通信、确认设备、初始化驱动、持续采样、输出数据。
扩展应用
BMP280 实验的调试重点主要集中在接线、I2C 地址、供电电压、总线通信和数据读取上。终端没有数据并不一定代表传感器损坏,很多时候是 SCL、SDA 接反、地址不匹配、供电不稳定或 I2C 总线没有扫描到设备。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| I2C 扫描结果为空 | SCL / SDA 接线错误、传感器未供电、GND 未共地 | 核对 BMP280 的 SCL 是否连接 IO7,SDA 是否连接 IO6,并确认 VCC、GND 正常连接 |
程序提示 I2C scan failed |
I2C 总线创建失败或引脚配置异常 | 检查 create_i2c_bus() 中的 scl=7、sda=6 是否符合实际接线 |
| 扫描结果显示 118 或 119 | I2C 扫描结果用十进制显示 | 118 对应 0x76,119 对应 0x77,属于正常现象 |
| 读取地址不正确 | BMP280 模块地址可能为 0x76 或 0x77 |
查看终端 I2C scan 输出,确认实际扫描到的设备地址 |
| 温度或气压显示为 0 | 校准参数读取异常或原始数据异常 | 重新检查 I2C 通信,必要时单独打印 chip_id 或扫描地址确认传感器响应 |
| 数据波动明显 | 采样环境变化、传感器未固定、滤波配置不合适 | 保持模块稳定,适当调整 IIR 滤波和采样模式 |
| 程序能启动但没有连续输出 | 主循环未进入或对象初始化失败 | 观察是否打印 Use BMP280 address,如果没有,问题多半出现在 I2C 创建或地址扫描阶段 |
| 气压单位看起来过大 | BMP280 输出单位为 Pa | 标准大气压约为十万 Pa,显示为 Pa 时数值较大属于正常现象 |
| 供电后模块发热或异常 | 供电电压与模块规格不匹配 | 优先查看模块丝印和资料,不能确定时使用 3.3V 供电更稳妥 |
BMP280 实验的价值不只是读取一个温度和气压数值,而是建立传感器数据采集的完整思路。程序通过 I2C 总线读取寄存器数据,再经过驱动类封装、校准参数补偿和循环输出,把真实环境变化转换成可以被代码处理的数字。这个模式可以迁移到很多环境监测、物联网采集和智能硬件项目中。
| 应用场景 | 实现思路 | 可扩展能力 |
|---|---|---|
| 环境气压监测 | 定时读取 BMP280 气压数据,并在终端或上位机中记录变化 | 后续可扩展数据保存、曲线绘制和异常提醒 |
| 室内温度参考 | 使用 sensor.temperature 周期性读取温度 |
可结合 LCD 显示模块作为后续课程扩展方向 |
| 海拔变化估算 | 根据气压变化推算高度变化趋势 | 可扩展为简易高度计或户外环境记录器 |
| 气象教学实验 | 观察不同环境下气压和温度数值变化 | 可用于讲解传感器采样、单位换算和数据补偿 |
| 物联网采集节点 | 将 BMP280 作为环境采集端,定时输出数据 | 后续可扩展 Wi-Fi、MQTT 或 Web 数据上传 |
| 硬件通信调试 | 通过 I2C 扫描理解设备地址和总线通信 | 可迁移到其他 I2C 传感器,如光照、温湿度、姿态模块 |
| 数据分析案例 | 把连续采集的温度和气压保存为时间序列 | 可扩展 Python 数据可视化和趋势分析课程 |
从工程角度看,当前代码已经具备较好的扩展基础。BMP280 驱动被封装成类,I2C 创建和地址扫描被拆成独立函数,主循环只负责读取和打印数据。后续增加屏幕显示、数据上传、阈值报警或文件记录时,不需要重写底层传感器驱动,只需要围绕 sensor.temperature 和 sensor.pressure 继续扩展上层业务逻辑。
总结
本实验通过 CanMV K210 开发板完成了 BMP280 气压传感器的数据读取,核心能力包括 I2C 总线通信、传感器地址扫描、寄存器读写、校准参数解析、温度补偿、气压补偿、类封装和循环采集。代码从创建软件 I2C 总线开始,把 IO7 和 IO6 变成传感器通信接口,再通过 BMP280 驱动类读取内部寄存器,将原始数据转换成温度和气压结果。
这类实验适合作为传感器课程的基础案例。代码中的 readfrom_mem() 对应真实芯片寄存器读取,scan() 对应 I2C 设备发现,@property 对应更简洁的数据访问方式,补偿公式则体现了硬件数据并不总是可以直接使用。后续课程可以继续扩展到 LCD 实时显示、蜂鸣器阈值报警、Wi-Fi 数据上传、气压曲线可视化、更多 I2C 传感器接入和 AI 硬件环境感知等方向。