init: Air780EPM 官方 LuatOS 项目代码基线

- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
wangfq
2026-08-31 08:53:49 +08:00
commit 173dd6874f
813 changed files with 125484 additions and 0 deletions
@@ -0,0 +1,253 @@
--[[
@module bmi270_app
@summary BMI270 六轴惯性传感器演示模块(3 种通信模式 + 中断触发)
@version 1.1
@date 2026.08.05
@author 江访
@usage
=== 演示流程: 初始化 → 数据读取 → 量程切换 → 姿态解算 → 中断触发 → 休眠唤醒 ===
通过 MODE 变量选择通信模式(改 MODE 的值即可,无需注释/取消注释代码):
MODE = 1 软件 I2C(默认推荐,仅需 scl/sda 引脚,任意 GPIO 均可,接线最灵活)
MODE = 2 硬件 I2C(指定 i2c_id,占用 CPU 少,Air780EPM 用 I2C1
MODE = 3 SPI(高速模式,需指定 spi_id + cs,适合高速采样)
本文件通过 exs_bmi270 扩展库的 API 逐一演示 BMI270 的以下功能:
1. 初始化和基本信息查询 -- setup() 芯片 ID 校验 + 微程序自动加载 + 中断注册
2. 加速度/角速度/温度读取 -- get_accel()/get_gyro()/get_temp(), 单位 g/°/s/°C
3. 量程切换演示 -- set_acc_range()/set_gyro_range(), 动态调整测量范围
4. 姿态解算 -- calibrate_gyro() 零偏标定 + get_attitude() Mahony 互补滤波
5. 中断触发演示 -- any_motion 事件中断,topic 方式回调
6. 休眠唤醒与资源释放 -- sleep()/wakeup()/close(), 低功耗管理
]]
-- 加载 exs_bmi270 扩展库
-- 该库提供 BMI270 的全部驱动功能: 初始化/数据读取/量程配置/姿态解算/中断/电源管理
local exs_bmi270 = require "exs_bmi270"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(默认) 2 = 硬件 I2C 3 = SPI
-- 各模式所需引脚/参数见下方 init_config,接线说明见 readme.md
local MODE = 1
-- 中断 GPIOBMI270 的 INT1 引脚接到核心板的这个 GPIO(三种模式通用,按实际接线修改)
local INT1_GPIO = 32
local init_mode = "I2C"
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIOAir780EPM 接线示例用 GPIO31/GPIO30
-- 不传 addr:扩展库自动探测 0x68/0x69SDO 引脚决定),chip id 校验命中即锁定
init_config = {scl = 31, sda = 30, acc_range = "4g", gyro_range = "500", acc_odr = 200, gyro_odr = 200}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1Pin66=SDA, Pin67=SCL
init_config = {i2c_id = 1, acc_range = "4g", gyro_range = "500", acc_odr = 200, gyro_odr = 200}
elseif MODE == 3 then
-- SPI:需接线 SPI0_CS=GPIO8, SPI0_CLK=GPIO11, SPI0_MOSI=GPIO9, SPI0_MISO=GPIO10
-- 注意:BMI270 最高支持 10MHz,杜邦线长时建议 1MHz 起步,稳定后再提速
init_mode = "SPI"
init_config = {spi_id = 0, cs = 8, speed = 1000000, acc_range = "4g", gyro_range = "500", acc_odr = 200, gyro_odr = 200}
else
log.error("TEST", "MODE 取值错误,应为 1/2/3")
return
end
-- 中断消息 topic:GPIO 中断回调发布,主任务等待后读取
local INT_TOPIC = "BMI270_INT"
--------------------------------------------------------------
-- [1/6] 初始化
-- - 自动完成芯片 ID 校验(期望 0x24)
-- - 自动加载 8192 字节 Bosch 出厂微程序(首次使用必须,否则无法测量)
-- - 自动配置量程、采样率,并注册 INT1 中断(data_ready + any_motion
--------------------------------------------------------------
local function init_func()
log.info("TEST", "=== [1/6] 初始化 ===")
log.info("TEST", string.format("通信模式: %s (MODE=%d)", MODE == 3 and "SPI" or (MODE == 2 and "硬件I2C" or "软件I2C"), MODE))
local result = exs_bmi270.setup(init_mode, {
scl = init_config.scl, sda = init_config.sda,
i2c_id = init_config.i2c_id,
spi_id = init_config.spi_id, cs = init_config.cs, speed = init_config.speed,
acc_range = init_config.acc_range, gyro_range = init_config.gyro_range,
acc_odr = init_config.acc_odr, gyro_odr = init_config.gyro_odr,
int1 = {
int_gpio = INT1_GPIO, -- BMI270 INT1 引脚接的 GPIO
-- data_ready = true, -- 数据就绪中断:每个采样周期触发一次
any_motion = true, -- 任意运动中断:检测到晃动触发
any_motion_thresh_mg = 100, -- 阈值 100mg(默认 83mg,越小越灵敏)
topic = INT_TOPIC, -- 中断回调发布该 topic,主任务等待
},
})
if not result then
log.error("TEST", "初始化失败! 检查接线: VCC=3.3V GND SCL SDA")
return false -- 初始化失败必须返回 false
end
log.info("TEST", "初始化成功,扩展库版本:", exs_bmi270.version())
return true
end
--------------------------------------------------------------
-- [2/6] 加速度/角速度/温度读取
-- - get_accel() 单位 g,水平静止时 Z≈1.0gX/Y≈0
-- - get_gyro() 单位 °/s,静止时应接近 0
-- - get_temp() 单位 °C,芯片温度会高于环境温度
--------------------------------------------------------------
local function read_data_func()
log.info("TEST", "=== [2/6] 数据读取 (单位 g / °/s / °C) ===")
local acc = exs_bmi270.get_accel()
local gyro = exs_bmi270.get_gyro()
local temp = exs_bmi270.get_temp()
if not acc or not gyro or not temp then
log.error("TEST", "读取数据失败!")
return
end
log.info("TEST", string.format("accel x=%.3f y=%.3f z=%.3f", acc.x, acc.y, acc.z))
log.info("TEST", string.format("gyro x=%.1f y=%.1f z=%.1f", gyro.x, gyro.y, gyro.z))
log.info("TEST", string.format("temp %.1f°C", temp))
-- 静止水平时 Z 轴应接近 1g;若设备竖直/倾斜安装,重力会分布到 X/Y/Z 上,
-- 属正常现象(此时姿态解算会自动以真实安装方向为基准)。
local acc_mag = math.sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z)
if math.abs(acc_mag - 1.0) > 0.15 then
log.warn("TEST", "加速度合矢量偏差较大(期望≈1.0g),检查传感器是否受振动或供电异常")
end
end
--------------------------------------------------------------
-- [3/6] 量程切换演示
-- - set_acc_range("8g") 将加速度量程从 ±4g 切换到 ±8g(分辨率下降但可测更大加速度)
-- - set_gyro_range("1000") 将陀螺仪量程从 ±500°/s 切换到 ±1000°/s
-- - 切换后 get_accel()/get_gyro() 自动按新量程换算
--------------------------------------------------------------
local function range_switch_func()
log.info("TEST", "=== [3/6] 量程切换演示 ===")
if not exs_bmi270.set_acc_range("8g") then
log.error("TEST", "加速度量程切换失败!")
return
end
log.info("TEST", "加速度量程已切换为 ±8g")
local acc = exs_bmi270.get_accel()
if acc then
log.info("TEST", string.format("accel x=%.3f y=%.3f z=%.3f", acc.x, acc.y, acc.z))
end
if not exs_bmi270.set_gyro_range("1000") then
log.error("TEST", "陀螺仪量程切换失败!")
return
end
log.info("TEST", "陀螺仪量程已切换为 ±1000°/s")
local gyro = exs_bmi270.get_gyro()
if gyro then
log.info("TEST", string.format("gyro x=%.1f y=%.1f z=%.1f", gyro.x, gyro.y, gyro.z))
end
end
--------------------------------------------------------------
-- [4/6] 姿态解算(Mahony 互补滤波)
-- - get_attitude() 融合陀螺仪积分与加速度计修正,输出 roll/pitch/yaw
-- - yaw 角连续叠加,无 ±180° 跳变;无磁力计会随时间缓慢漂移
-- - 必须在固定间隔循环中持续调用,保持陀螺积分连续
-- - 首次使用建议先调用 calibrate_gyro() 做陀螺零偏标定(设备保持静止),
-- 可显著抑制静止时 roll/pitch/yaw 的缓慢漂移
--------------------------------------------------------------
local function attitude_func()
log.info("TEST", "=== [4/6] 姿态解算 (5秒) ===")
-- 陀螺零偏标定:设备必须保持静止,采集 50 次陀螺读数求平均
-- 标定结果在库内部自动生效,后续 get_attitude() 积分前会扣除零偏
log.info("TEST", "请保持设备静止,正在进行陀螺零偏标定...")
local bias = exs_bmi270.calibrate_gyro(50)
if bias then
log.info("TEST", string.format("零偏标定完成: (%.3f, %.3f, %.3f)°/s", bias.x, bias.y, bias.z))
else
log.warn("TEST", "零偏标定失败,姿态解算将不带零偏补偿")
end
local start = os.clock()
while os.clock() - start < 5 do
local roll, pitch, yaw = exs_bmi270.get_attitude()
if roll then
log.info("TEST", string.format("roll=%.1f° pitch=%.1f° yaw=%.1f°", roll, pitch, yaw))
end
sys.wait(20) -- 20ms 调用一次(50Hz),间隔过大会影响姿态平滑度
end
log.info("TEST", "姿态解算演示结束")
end
--------------------------------------------------------------
-- [5/6] 中断触发演示
-- - 已通过 setup 注册 any_motion 事件中断(未启用 data_ready,避免高频触发)
-- - GPIO 中断回调只发布消息(不读 I2C),主任务 sys.waitUntil 等待
-- - 收到中断后 get_int_status() 查询具体事件,get_data() 读取数据
-- 测试方法:晃动设备 → any_motion 触发
--------------------------------------------------------------
local function int_func()
log.info("TEST", "=== [5/6] 中断触发演示 (10秒) ===")
log.info("TEST", "测试方法:晃动设备触发 any_motion 中断")
local start = os.clock()
while os.clock() - start < 10 do
sys.waitUntil(INT_TOPIC, 2000) -- 等待中断消息,超时 2 秒
-- 读取并清除中断状态(区分具体是哪个事件触发)
local events = exs_bmi270.get_int_status()
if events and #events > 0 then
log.info("TEST", "中断事件:", table.concat(events, ","))
end
-- 读取一次数据验证(在主任务协程中,I2C 时序完整)
local data = exs_bmi270.get_data()
if data then
log.info("TEST", string.format("acc(%.2f,%.2f,%.2f)g gyr(%.1f,%.1f,%.1f)°/s",
data.x, data.y, data.z, data.gx, data.gy, data.gz))
end
end
log.info("TEST", "中断触发演示结束")
end
--------------------------------------------------------------
-- [6/6] 休眠唤醒与资源释放
-- - sleep() 进入休眠,功耗最低;wakeup() 快速恢复,无需重新 setup()
-- - close() 关闭传感器并释放通信资源,之后需重新 setup() 才能使用
--------------------------------------------------------------
local function sleep_wakeup_func()
log.info("TEST", "=== [6/6] 休眠唤醒演示 ===")
exs_bmi270.sleep()
log.info("TEST", "已进入休眠,3 秒后唤醒")
sys.wait(3000) -- 休眠演示 3 秒
exs_bmi270.wakeup()
log.info("TEST", "已唤醒,读取一次数据验证")
local acc = exs_bmi270.get_accel()
if acc then
log.info("TEST", string.format("accel x=%.3f y=%.3f z=%.3f", acc.x, acc.y, acc.z))
end
-- 释放资源(演示用,实际项目按需调用)
exs_bmi270.close()
log.info("TEST", "传感器已关闭")
end
--------------------------------------------------------------
-- 主任务:按 [1/6]~[6/6] 顺序执行全部演示
--------------------------------------------------------------
local function bmi270_task()
sys.wait(300) -- 等待系统启动稳定,300ms
if not init_func() then return end -- 初始化失败则结束,按返回值判断
read_data_func()
sys.wait(500) -- 两个演示之间留 500ms,便于观察日志
range_switch_func()
sys.wait(500) -- 量程切换后等待配置生效,500ms
attitude_func()
sys.wait(500) -- 姿态解算演示后间隔 500ms
int_func()
sleep_wakeup_func()
log.info("TEST", "========== 所有演示完成 ==========")
end
-- 启动演示任务协程
sys.taskInit(bmi270_task)
@@ -0,0 +1,76 @@
--[[
@module main
@summary BMI270 六轴惯性传感器 Demo 入口
@version 1.1
@date 2026.08.05
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_bmi270 扩展库的完整功能,顺序为:
HELLO→[1/6]→[2/6]→[3/6]→[4/6]→[5/6]→[6/6]→End
1、初始化和基本信息查询([1/6])- 芯片识别/版本/中断注册
2、加速度/角速度/温度读取([2/6])- 三轴物理量数据
3、量程切换演示([3/6])- 加速度/陀螺仪量程动态切换
4、姿态解算([4/6]- Mahony 互补滤波,roll/pitch/yaw
5、中断触发演示([5/6]- data_ready + any_motion 特征中断
6、休眠唤醒与资源释放([6/6])- 低功耗演示
通信模式通过 bmi270_app.lua 中的 MODE 变量选择:
1 = 软件 I2C 2 = 硬件 I2C 3 = SPI
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "BMI270_Demo" -- 项目命名
VERSION = "001.000.000" -- 项目版本号
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 设置日志输出风格为样式2(建议调试时开启)
-- log.style(2)
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)
-- 加载 bmi270_app.lua 演示模块
-- 通信模式通过 bmi270_app.lua 中的 MODE 变量选择(1=软件I2C / 2=硬件I2C / 3=SPI
require "bmi270_app"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,235 @@
# BMI270 六轴惯性传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **bmi270_app.lua** - BMI270 功能演示模块,包含初始化、数据读取、量程切换、姿态解算、中断触发、休眠唤醒共 6 项功能的演示用例
### 1.2 扩展库模块
1. **exs_bmi270** - BMI270 扩展库,提供 I2C/SPI 双接口初始化、三轴加速度/角速度/温度读取、量程与采样率配置、Mahony 姿态解算、中断功能、计步功能、休眠唤醒等 API(8192 字节微程序已内嵌于扩展库,无需单独加载)
### 1.3 通信模式选择(MODE 变量)
`bmi270_app.lua` 顶部通过 **MODE 变量**选择通信模式,改一个变量即可切换,无需注释/取消注释代码:
| MODE 值 | 通信模式 | 所需参数 | 适用场景 |
|---------|---------|---------|---------|
| 1 | 软件 I2C(默认) | `scl`/`sda`(任意 GPIO | 接线最灵活,Air780EPM 接线示例用 GPIO31/GPIO30 |
| 2 | 硬件 I2C | `i2c_id`Air780EPM 用 1Pin66=SDA、Pin67=SCL | 占用 CPU 少,实时性要求高 |
| 3 | SPI | `spi_id`/`cs`/`speed`Air780EPM 用 SPI0 | 高速采样场景 |
### 1.4 中断功能说明
BMI270 支持硬件中断输出(INT1/INT2 引脚),扩展库提供:
- **data_ready 数据就绪中断**:开箱即用,每个采样周期触发一次,适合低功耗事件驱动
- **特征中断**any_motion 任意运动 / no_motion 无运动 / sig_motion 显著运动 / step 步进检测):开箱即用,`setup()`/`int_config()` 配置后自动使能并映射到 INT 引脚;支持阈值与持续时间参数调节(`any_motion_thresh_mg`/`any_motion_duration_ms``no_motion_thresh_mg`/`no_motion_duration_ms``sig_motion_block_ms``step_counter``step_watermark_steps`
- **计步说明**`step` 中断每检测到一步触发一次;`get_step_count()` 读取累计步数(32 位,0~4294967295),步数按 `step_watermark_steps` 的间隔刷新(默认每 20 步);`reset_step_count()` 可清零重新计步
中断使用示例见 `bmi270_app.lua``[5/6] 中断触发演示`,演示 data_ready + any_motion 的配置与触发;接线需将 BMI270 的 INT1 引脚接到一个空闲 GPIO(默认低有效,GPIO 用 FALLING 触发)。
## 二、演示流程介绍
本 demo 按顺序演示 exs_bmi270 扩展库的 6 项功能:HELLO → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
### 2.1 功能演示项说明
1. **[1/6] 初始化和基本信息查询** - 初始化 BMI270 传感器,自动完成芯片 ID 校验(期望 0x24)与 8192 字节微程序加载,注册 INT1 中断,打印扩展库版本号
2. **[2/6] 加速度/角速度/温度读取** - 读取三轴加速度(单位 g)、三轴角速度(单位 °/s)、芯片温度(单位 °C)
3. **[3/6] 量程切换演示** - 动态切换加速度量程(±4g→±8g)和陀螺仪量程(±500→±1000 °/s),验证切换后数据自动换算
4. **[4/6] 姿态解算** - 通过 Mahony 互补滤波输出 roll/pitch/yaw 姿态角,持续演示 5 秒
5. **[5/6] 中断触发演示** - data_ready + any_motion 特征中断,topic 方式回调,持续 10 秒
6. **[6/6] 休眠唤醒与资源释放** - 进入休眠、3 秒后唤醒验证数据,最后关闭传感器释放资源
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- BMI270 六轴惯性传感器模块 × 1
demo所演示的BMI270 惯性传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=1041004170261&mi_id=00004EzeZNi57_QjBNIlFbQ5VJZffnITA6qTwK2xBTP-baw&ns=1&skuId=6062545293708&spm=a21n57.1.hoverItem.2&utparam=%7B%22aplus_abtest%22%3A%227b34fb2d453729db70345acf83f5f9e2%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 BMI270 模块接线(软件 I2CMODE=1,默认推荐)
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>BMI270 模块<br/></td></tr>
<tr>
<td>GPIO31PIN32<br/></td><td>SCL<br/></td></tr>
<tr>
<td>GPIO30PIN31<br/></td><td>SDA<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
<tr>
<td>GPIO32PIN33<br/></td><td>INT1(中断输出,可选)<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。BMI270 模块需 3.3V 供电,SCL/SDA 需上拉电阻(常见模块已内置)。SDO 引脚接 GND 时 I2C 地址为 0x68,接 VCC 时为 0x69,如读取不到数据可切换试试。
![BMI270 六轴惯性传感器接线示意图](https://docs.openluat.com/cdn/image/Air780EHM_exs_bmi270.png)
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 4.3 脚本文件
- **main.lua** - 程序入口
- **bmi270_app.lua** - 演示模块
- **exs_bmi270** - 扩展库(含 8192 字节微程序)
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 BMI270 模块连接到核心板(选择 MODE 对应的接线)
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 首次上电请将设备水平静止放置,便于观察加速度数据
4. 检查所有接线无误,避免短路
### 5.2 软件配置
1. 打开 `bmi270_app.lua`,根据实际接线设置 **MODE 变量**1=软件I2C / 2=硬件I2C / 3=SPI
2. 确认 INT1_GPIO 与你的中断接线一致
3. `main.lua` 保持 `require "bmi270_app"` 即可
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件(含 exs_bmi270 扩展库)
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化和基本信息查询演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/6] 初始化
2. 观察日志输出,确认初始化成功、芯片 ID 校验通过、中断注册
3. 初始化失败时检查接线:VCC=3.3V、GND、SCL、SDA
#### 5.4.2 数据读取演示
1. 自动进入 [2/6] 数据读取
2. 观察三轴加速度(单位 g)、角速度(单位 °/s)、温度(单位 °C)输出
3. 静止水平放置时,加速度 Z 轴应接近 1.0g,X/Y 接近 0;角速度三轴接近 0
#### 5.4.3 量程切换演示
1. 自动进入 [3/6] 量程切换
2. 观察加速度量程从 ±4g 切换为 ±8g、陀螺仪量程从 ±500°/s 切换为 ±1000°/s
3. 切换后数据自动按新量程换算
> 说明:
> 扩展库的量程切换会自动执行软复位 + 完整重新初始化(约 150ms),
> 日志中会再次出现"微程序已分块写入",属正常现象。
#### 5.4.4 姿态解算演示
1. 自动进入 [4/6] 姿态解算
2. 演示会先自动执行陀螺零偏标定(约 50 次采样,需保持设备静止)
3. 标定完成后,观察 roll/pitch 反映设备当前真实静态姿态(水平放置约 0°,竖直/倾斜安装则显示对应角度)
4. 缓慢转动设备,观察三个姿态角实时跟踪变化
5. yaw 角连续叠加无 ±180° 跳变;无磁力计会随时间缓慢漂移
#### 5.4.5 中断触发演示
1. 自动进入 [5/6] 中断触发演示(持续 10 秒)
2. 晃动设备 → 触发 `any_motion` 事件;静置 → `data_ready` 周期性触发
3. 观察日志打印触发事件名和三轴数据
4. 若无中断触发,检查 INT1 接线与 INT1_GPIO 配置
#### 5.4.6 休眠唤醒演示
1. 自动进入 [6/6] 休眠唤醒
2. 观察 "已进入休眠" 日志,3 秒后自动唤醒
3. 唤醒后数据恢复读取,最后打印 "传感器已关闭"
### 5.5 预期效果
- **初始化**:BMI270 初始化成功,微程序加载完成,打印扩展库版本号
- **数据读取**:静止水平时 Z 轴加速度接近 1.0g,角速度接近 0,温度 30~45°C
- **量程切换**:量程切换成功(自动软复位重初始化),切换后数据正常输出
- **姿态解算**:先零偏标定,后姿态角平滑跟踪,Mahony 互补滤波有效抑制噪声
- **中断触发**:晃动触发 any_motion,静置时 data_ready 周期性触发,事件名打印正常
- **休眠唤醒**:休眠日志正常,唤醒后数据恢复
- **日志如下:**
```lua
[2026-08-05 12:15:08.669][000000000.200] I/user.main BMI270_Demo 001.000.000
[2026-08-05 12:15:08.728][000000000.522] I/user.TEST === [1/6] ===
[2026-08-05 12:15:08.790][000000000.525] I/user.exs_bmi270 BMI270 @ I2C ID=0x24
[2026-08-05 12:15:10.464][000000002.784] I/user.exs_bmi270 8192 128
[2026-08-05 12:15:10.635][000000002.953] I/user.exs_bmi270 init verify: PWR_CTRL=0x0E STATUS=0xD0 ACC_RANGE=0x01 GYR_RANGE=0x02
[2026-08-05 12:15:10.654][000000002.957] I/user.exs_bmi270 acc_range=4g gyro_range=500 acc_odr=200Hz gyro_odr=200Hz
[2026-08-05 12:15:10.673][000000002.967] I/user.exs_bmi270 any_motion 使
[2026-08-05 12:15:10.689][000000002.974] I/user.exs_bmi270 INT1 , int_gpio=32
[2026-08-05 12:15:10.706][000000002.975] I/user.TEST : 202608051200
[2026-08-05 12:15:10.727][000000002.975] I/user.TEST === [2/6] ( g / °/s / °C) ===
[2026-08-05 12:15:10.743][000000002.981] I/user.exs_bmi270 ACC_RAW: 01 F1 BB 00 64 F3
[2026-08-05 12:15:10.761][000000002.981] I/user.exs_bmi270 GYR_RAW: 00 1E 03 18 C2 FE
[2026-08-05 12:15:10.778][000000002.982] I/user.exs_bmi270 TMP_RAW: 97 0C
[2026-08-05 12:15:10.795][000000002.993] I/user.TEST accel x=-0.469 y=0.023 z=-0.394
[2026-08-05 12:15:10.811][000000002.994] I/user.TEST gyro x=126.0 y=81.2 z=-11.3
[2026-08-05 12:15:10.829][000000002.994] I/user.TEST temp 29.3°C
[2026-08-05 12:15:11.178][000000003.495] I/user.TEST === [3/6] ===
[2026-08-05 12:15:13.439][000000005.755] I/user.exs_bmi270 8192 128
[2026-08-05 12:15:13.662][000000005.946] I/user.TEST ±8g
[2026-08-05 12:15:16.114][000000008.403] I/user.TEST ±1000°/s
[2026-08-05 12:15:16.596][000000008.909] I/user.TEST === [4/6] 姿 (5) ===
[2026-08-05 12:15:16.845][000000009.165] I/user.exs_bmi270 : (1.976, -7.871, -10.207)°/s
[2026-08-05 12:15:16.877][000000009.172] I/user.TEST roll=168.6° pitch=41.8° yaw=150.8°
[2026-08-05 12:15:16.892][000000009.198] I/user.TEST roll=168.9° pitch=41.7° yaw=150.9°
[2026-08-05 12:15:16.908][000000009.224] I/user.TEST roll=169.2° pitch=41.5° yaw=151.0°
姿 20ms
[2026-08-05 12:15:21.690][000000014.011] I/user.TEST 姿
[2026-08-05 12:15:22.201][000000014.511] I/user.TEST === [5/6] (10) ===
[2026-08-05 12:15:22.435][000000014.754] I/user.TEST : any_motion
[2026-08-05 12:15:22.453][000000014.759] I/user.TEST acc(-0.17,-0.03,1.02)g gyr(-89.8,70.2,-5.8)°/s
[2026-08-05 12:15:33.119][000000025.408] I/user.TEST
[2026-08-05 12:15:33.135][000000025.408] I/user.TEST === [6/6] ===
[2026-08-05 12:15:33.156][000000025.409] I/user.exs_bmi270
[2026-08-05 12:15:36.139][000000028.432] I/user.TEST
[2026-08-05 12:15:36.157][000000028.437] I/user.TEST accel x=-0.678 y=0.011 z=-0.725
[2026-08-05 12:15:36.174][000000028.439] I/user.exs_bmi270
[2026-08-05 12:15:36.213][000000028.439] I/user.TEST ========== ==========
```
### 5.6 故障排除
| 现象 | 可能原因 | 解决方法 |
| ---- | ---- | ---- |
| 初始化失败,日志提示芯片识别失败 | 接线错误或供电不足 | 检查 VCC/GND/SCL/SDA 接线,确保 3.3V 供电 |
| 初始化失败,日志提示配置加载失败 | 传感器损坏 | 更换传感器模块 |
| 读取不到数据 | 接线错误或地址探测无命中 | 扩展库已自动探测 0x68/0x69(SDO 决定),仍失败检查 SDO 接线、供电与总线冲突 |
| 加速度 Z 轴偏差大 | 传感器未水平放置 | 水平静止放置后重新观察;竖直/倾斜安装属正常 |
| 数据不稳定 | 杜邦线过长/干扰 | 缩短杜邦线,降低 SPI 速率或改软件 I2C |
| 中断不触发 | INT1 接线错误或 INT1_GPIO 配置不对 | 检查 INT1 引脚接线,确认 bmi270_app.lua 中 INT1_GPIO 与实际一致 |
| SPI 模式芯片 ID 读回 0xFF | ① SDO/MISO 未接 ② 上电时 CSB 非低电平 ③ 模块 I2C 地址电阻钳住 SDO ④ SPI 引脚复用被占用 | ① 检查 4 根信号线 ② 确认 CS 上电为低 ③ 移除/确认 SDO 电阻 ④ 通过 LuatIO 检查 SPI0 复用是否被其他外设占用 |
### 5.7 扩展功能建议
1. **多圈偏航累计**get_attitude() 的 yaw 角为连续叠加值,可用于旋转圈数计数场景
2. **跌倒检测**:将加速度量程设为 "16g" 并监测合加速度阈值,可识别剧烈冲击
3. **SPI 高速采样**:对采样率要求高的场景可改用 SPI 模式(MODE=3,接线见 3.2.3,速率最高 1MHz 以上)
4. **低功耗应用**:间歇采样场景可读取一次后调用 sleep(),需要时再 wakeup() 降低功耗
5. **零偏标定**:产品首次上电静止 1~2 秒调用 calibrate_gyro(),可显著降低静止姿态漂移
6. **计步功能**:配置 `step = true` + `step_counter = true` + `step_watermark_steps`,配合 `get_step_count()`/`reset_step_count()` 实现计步器
7. exs_bmi270 更多接口的使用可以查看 [exs_bmi270 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_bmi270/)