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,245 @@
--[[
@module ads1115_demo
@summary ADS1115 16位ADC传感器演示模块,包含所有功能的演示用例
@version 2.0
@date 2026.08.24
@author 王城钧
@usage
本文件包含 ADS1115 的逐项功能演示。
通过 exs_ads1115 扩展库的 API 逐一演示数据读取、多通道扫描、比较器中断、PGA 增益切换等功能。
]]
-- 加载 exs_ads1115 扩展库
local exs_ads1115 = require "exs_ads1115"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(任意 GPIO,接线最灵活)
-- 2 = 硬件 I2C(占用 CPU 少,稳定可靠,推荐)
local MODE = 2
-- ALERT/RDY 中断引脚:ADS1115 模块 ALERT/RDY 接到核心板 GPIO22(按实际接线修改)
local ALERT_GPIO = 22
-- 中断消息 topic:GPIO 中断回调发布,主任务等待后读取数据清除锁存报警
local ALERT_TOPIC = "ADS1115_ALERT"
-- 按 MODE 组装初始化配置
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIO(示例 GPIO31/GPIO30
-- 不传 addr:扩展库自动探测 0x48~0x4B,读 Config 校验命中即锁定
init_config = {scl = 31, sda = 30, channel = 0, pga = 1, sps = 128, mode = 1, alert_pin = ALERT_GPIO}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1PIN66=SDA, PIN67=SCL
init_config = {i2c_id = 1, channel = 0, pga = 1, sps = 128, mode = 1, alert_pin = ALERT_GPIO}
else
log.error("ads1115_demo", "MODE 取值错误,应为 1/2")
return
end
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("ads1115_demo", "===== [1/4] 初始化与数据读取 =====")
local result = exs_ads1115.setup(init_config)
if not result then
log.error("ads1115_demo", "ADS1115 初始化失败,请检查接线")
return false
end
log.info("ads1115_demo", "ADS1115 初始化成功,版本:", exs_ads1115.version())
sys.wait(200)
local data = exs_ads1115.get_data()
if data then
log.info("ads1115_demo", string.format("AIN0 = %.4f V (raw=%d)", data.voltage, data.raw))
else
log.error("ads1115_demo", "读取数据失败")
end
sys.wait(1000)
for i = 1, 3 do
sys.wait(500)
local d = exs_ads1115.get_data()
if d then
log.info("ads1115_demo", string.format("第%d次读取: AIN0 = %.4f V (raw=%d)", i, d.voltage, d.raw))
end
end
log.info("ads1115_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] 多通道扫描
local function demo_channel_scan()
log.info("ads1115_demo", "===== [2/4] 多通道扫描 =====")
for ch = 0, 3 do
local ok = exs_ads1115.set_config({channel = ch})
if not ok then
log.warn("ads1115_demo", "通道", ch, "切换失败")
return false
end
local data = exs_ads1115.get_data()
if data then
log.info("ads1115_demo", string.format("AIN%d = %.4f V (raw=%d)", ch, data.voltage, data.raw))
end
sys.wait(50)
end
-- 扫描结束显式切回 AIN0 通道,避免通道停留在 AIN3 影响后续步骤
exs_ads1115.set_config({channel = 0})
log.info("ads1115_demo", "---- [2/4] 完成 ----")
return true
end
-- ALERT 中断回调:只发布消息,不直接做 I2C 操作(避免打断通信时序)
local function alert_cb_func()
sys.publish(ALERT_TOPIC)
end
-- [3/4] 比较器中断报警
local function demo_comparator_int()
log.info("ads1115_demo", "===== [3/4] 比较器中断报警 =====")
-- 切回 AIN0 通道并切换到连续模式(多通道扫描后通道停在 AIN3,必须先切回)
local ok = exs_ads1115.set_config({channel = 0, mode = 0})
if not ok then
return false
end
-- 窗口比较器(高于 3.0V 或低于 2.0V 都报警),1 次越界报警,锁存
ok = exs_ads1115.set_comparator(1, 0, true)
if not ok then
return false
end
-- 阈值:低于 2.0V 或高于 3.0V 触发报警
ok = exs_ads1115.set_threshold(2.0, 3.0)
if not ok then
return false
end
-- 注册 ALERT 引脚下降沿中断(覆盖 setup 时的输入模式)
gpio.setup(ALERT_GPIO, alert_cb_func, gpio.PULLUP, gpio.FALLING)
-- 读取当前电压并提示(帮助判断电压是否已在阈值外)
local cur = exs_ads1115.get_data()
if cur then
log.info("ads1115_demo", string.format("当前 channel=%d, 电压 %.3f V(阈值 2.0V~3.0V", cur.channel, cur.voltage))
if cur.voltage > 3.0 or cur.voltage < 2.0 then
log.info("ads1115_demo", "当前电压已在阈值外!请先调回 2.5V 左右,再越过阈值触发下降沿中断")
end
end
log.info("ads1115_demo", "请调节输入电压越过 3.0V 或低于 2.0V,等待 ALERT 中断(30 秒内)...")
-- 等待中断消息,同时每 500ms 轮询电压与 ALERT 电平辅助检测(总时长约 30 秒)
-- 轮询检测不依赖 ALERT 下降沿:即使操作方式不产生下降沿,也能验证比较器逻辑是否正常
local got = false
for i = 1, 60 do
-- 每 500ms 轮询一次电压与 ALERT 电平,实时观察比较器状态
cur = exs_ads1115.get_data()
if cur then
local alert = exs_ads1115.get_alert()
log.info("ads1115_demo", string.format("电压 %.3f VALERT=%s", cur.voltage, tostring(alert)))
if cur.voltage > 3.0 or cur.voltage < 2.0 then
-- 先检查 ALERT 中断消息,优先验证硬件中断链路
if sys.waitUntil(ALERT_TOPIC, 200) then
log.warn("ads1115_demo", "收到 ALERT 中断:电压超阈值报警!")
else
log.warn("ads1115_demo", string.format("轮询检测到电压越界!当前 AIN0 = %.3f V", cur.voltage))
end
got = true
break
end
end
-- 等待中断消息(500ms 超时,超时后继续轮询)
if sys.waitUntil(ALERT_TOPIC, 500) then
log.warn("ads1115_demo", "收到 ALERT 中断:电压超阈值报警!")
got = true
break
end
end
if got then
-- 中断刚结束,延迟 100ms 再读数据(避开 GPIO 中断打断 I2C 传输后的总线敏感期)
sys.wait(100)
-- 锁存模式下读取数据可清除报警(扩展库内置失败重试与总线重建)
cur = exs_ads1115.get_data()
if cur then
log.info("ads1115_demo", string.format("当前电压 = %.3f V (raw=%d)", cur.voltage, cur.raw))
else
log.warn("ads1115_demo", "读取当前电压失败(扩展库已自动重试并重建 I2C 总线)")
end
else
log.info("ads1115_demo", "30 秒内未触发报警(可调节输入电压验证中断功能)")
end
log.info("ads1115_demo", "---- [3/4] 完成 ----")
return true
end
-- [4/4] PGA 增益切换演示
local function demo_pga_switch()
log.info("ads1115_demo", "===== [4/4] PGA 增益切换演示 =====")
-- 切回 AIN0 通道、单次模式(比较器演示后处于连续模式,此处恢复)
exs_ads1115.set_config({channel = 0, mode = 1})
log.info("ads1115_demo", "切换 PGA 为 1(满量程 ±4.096V,分辨率 125μV")
exs_ads1115.set_config({channel = 0, pga = 1})
sys.wait(300)
local data = exs_ads1115.get_data()
if data then
log.info("ads1115_demo", string.format("PGA=1 下: AIN0 = %.4f V (raw=%d)", data.voltage, data.raw))
end
sys.wait(1000)
log.info("ads1115_demo", "切换 PGA 为 2(满量程 ±2.048V,分辨率 62.5μV,输入超过 ±2.048V 会钳位)")
exs_ads1115.set_config({channel = 0, pga = 2})
sys.wait(300)
data = exs_ads1115.get_data()
if data then
log.info("ads1115_demo", string.format("PGA=2 下: AIN0 = %.4f V (raw=%d)", data.voltage, data.raw))
end
sys.wait(1000)
-- 恢复默认 PGA=1
log.info("ads1115_demo", "恢复 PGA 为 1(满量程 ±4.096V")
exs_ads1115.set_config({channel = 0, pga = 1})
sys.wait(300)
log.info("ads1115_demo", "---- [4/4] 完成 ----")
end
local function ads1115_demo_task_func()
log.info("ads1115_demo", "HELLO")
sys.wait(1000)
local ok = demo_init_and_read()
if not ok then
log.error("ads1115_demo", "初始化失败,演示终止")
return
end
sys.wait(500)
demo_channel_scan()
sys.wait(500)
demo_comparator_int()
sys.wait(500)
demo_pga_switch()
sys.wait(500)
log.info("ads1115_demo", "===== [演示完毕] =====")
exs_ads1115.close()
log.info("ads1115_demo", "End")
end
sys.taskInit(ads1115_demo_task_func)
@@ -0,0 +1,60 @@
--[[
@module main
@summary ADS1115 16位ADC传感器 Demo 入口
@version 1.0
@date 2026.08.24
@author 王城钧
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_ads1115 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化与数据读取([1/4]
2、多通道扫描([2/4]
3、比较器中断报警([3/4]
4、PGA 增益切换([4/4]
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己的项目的需求,自定义格式即可
]]
PROJECT = "ADS1115_Demo" -- 项目命名
VERSION = "001.000.000" -- 项目版本号
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 设置日志输出风格为样式2(建议调试时开启)
-- log.style(2)
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 加载 ads1115_demo.lua 演示模块
-- 通信模式通过 ads1115_demo.lua 中的 MODE 变量选择(1=软件I2C / 2=硬件I2C
require "ads1115_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,220 @@
# ADS1115 16位ADC传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **ads1115_demo.lua** - ADS1115 功能演示模块,包含数据读取、多通道扫描、比较器中断、PGA 增益切换等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_ads1115** - ADS1115 扩展库,提供初始化、电压读取、通道切换、PGA 增益配置、数据速率配置、比较器中断、校准等 API
### 1.3 I2C 模式说明
**推荐使用硬件 I2C 模式**Air780EPM 使用 I2C1PIN66=SDA, PIN67=SCL),占用 CPU 少、通信稳定。本 demo 默认使用硬件 I2C 模式演示,也支持软件 I2C 模式(任意 GPIO 接线,通过 `ads1115_demo.lua``MODE` 变量切换)。
## 二、演示流程介绍
本 demo 按顺序演示 exs_ads1115 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 ADS1115 传感器并读取 AIN0 电压数据(单位 V)
2. **[2/4] 多通道扫描** - 依次切换 AIN0~AIN3 四路单端通道读取电压
3. **[3/4] 比较器中断报警** - 窗口比较器(阈值 2.0V~3.0V),电压越界时 ALERT/RDY 引脚下降沿中断触发
4. **[4/4] PGA 增益切换** - 在 PGA=1(±4.096V)和 PGA=2(±2.048V)之间切换,观察分辨率与满量程变化
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- ADS1115 16 位 I2C ADC 传感器模块 × 1
- 母对母杜邦线 × 7根
- TYPE-C 数据线 × 1
- 直流稳压源 × 1
![](https://docs.openluat.com/cdn/image/Air780EPM/780EPM_ads1115.jpg)
### 3.2 接线配置
#### 3.2.1 ADS1115 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>ADS1115 模块<br/></td></tr>
<tr>
<td>3.3VVDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
<tr>
<td>PIN67I2C1_SCL<br/></td><td>SCL<br/></td></tr>
<tr>
<td>PIN66I2C1_SDA<br/></td><td>SDA<br/></td></tr>
<tr>
<td>GND<br/></td><td>ADDR(设备地址 0x48<br/></td></tr>
<tr>
<td>GPIO22<br/></td><td>ALERT/RDY(比较器中断,[3/4] 使用)<br/></td></tr>
<tr>
<td>被测电压(0~3.3V<br/></td><td>AIN0<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。被测电压超过 3.3V 时必须电阻分压后接入 AIN 引脚(芯片模拟输入上限为 VDD+0.3V)。
#### 3.2.2 外部电源接线(AIN0 测试电压源)
使用可调直流电源给 AIN0 提供测试电压时,**外部电源的负极(-)必须接到核心板 GND(共地)**,否则 AIN0 与 ADS1115 之间没有参考电位,读数会乱飘、无意义。
完整接线图(含外部电源):
```
外部电源(+) ────────────────→ ADS1115 模块: AIN0
外部电源(-) ────────────────→ Air780EPM 核心板: GND ← 共地!
核心板 3.3V (VDD_EXT) ──────→ ADS1115 模块: VCC
核心板 GND ─────────────────→ ADS1115 模块: GND
核心板 PIN67 (I2C1_SCL) ────→ ADS1115 模块: SCL
核心板 PIN66 (I2C1_SDA) ────→ ADS1115 模块: SDA
核心板 GND ─────────────────→ ADS1115 模块: ADDR(地址 0x48
核心板 GPIO22 ──────────────→ ADS1115 模块: ALERT/RDY(比较器中断)
```
| 接线对象 | 接到哪里 | 说明 |
|---|---|---|
| 外部电源正极(+ | ADS1115 模块 AIN0 | 测试电压 0~3.3V(超出需分压) |
| 外部电源负极(-) | 核心板 GND | **必须共地**,与模块 GND 同一点 |
> 共地关系:外部电源负极 → 核心板 GND → 模块 GND,三者是同一电位。若使用电池做测试源,电池负极同样要接到核心板 GND。
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/)demo所使用的是 LuatOS@Air780EPM base 26.04 bsp V2050
### 4.3 脚本文件
1. **main.lua** - 程序入口
2. **ads1115_demo.lua** - ADS1115 功能演示模块
3. **exs_ads1115** - ADS1115 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 ADS1115 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 ads1115_demo.lua 演示模块
require "ads1115_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察 AIN0 电压数据(单位 V)的连续读取
#### 5.4.2 多通道扫描演示
1. 自动进入 [2/4] 多通道扫描
2. 依次观察 AIN0~AIN3 四路通道的电压数据
3. 未接信号的通道读数接近 0V(悬空漂移属正常现象)
#### 5.4.3 比较器中断报警演示
1. 自动进入 [3/4] 比较器中断报警
2. 先将输入电压调到 2.5V(阈值窗口内),观察日志提示
3. 保持电源开启,调节电压越过 3.0V 或低于 2.0V,观察 ALERT 中断触发
#### 5.4.4 PGA 增益切换演示
1. 自动进入 [4/4] PGA 增益切换
2. 观察 PGA=1(满量程 ±4.096V)下的电压读数
3. 观察 PGA=2(满量程 ±2.048V)下的电压读数(若输入电压超过 ±2.048V,读数会钳位到满量程)
### 5.5 预期效果
- **初始化与数据读取**:ADS1115 初始化成功,AIN0 电压数据正常输出
- **多通道扫描**:4 路通道均能正常读取电压
- **比较器中断报警**:电压越过阈值(>3.0V 或 <2.0V)时 ALERT 中断触发
- **PGA 增益切换**:PGA 切换后分辨率与满量程随之变化
- **日志如下:**
```lua
[2026-08-24 15:39:43.507][000000000.216] I/user.main ADS1115_Demo 001.000.000
[2026-08-24 15:39:48.659][000000006.640] I/user.ads1115_demo ===== [1/4] =====
[2026-08-24 15:39:48.689][000000006.661] I/user.ads1115_demo ADS1115 : 202608241540
[2026-08-24 15:39:48.705][000000006.679] I/user.ads1115_demo AIN0 = 2.7818 V (raw=22254)
[2026-08-24 15:39:50.255][000000008.230] I/user.ads1115_demo 1: AIN0 = 2.7828 V (raw=22262)
[2026-08-24 15:39:50.749][000000008.731] I/user.ads1115_demo 2: AIN0 = 2.7819 V (raw=22255)
[2026-08-24 15:39:51.274][000000009.248] I/user.ads1115_demo 3: AIN0 = 2.7818 V (raw=22254)
[2026-08-24 15:39:51.290][000000009.264] I/user.ads1115_demo ---- [1/4] 完成 ----
[2026-08-24 15:39:51.433][000000009.416] I/user.ads1115_demo ===== [2/4] =====
[2026-08-24 15:39:51.546][000000009.516] I/user.ads1115_demo AIN0 = 2.7828 V (raw=22262)
[2026-08-24 15:39:51.561][000000009.533] I/user.ads1115_demo AIN1 = 0.5946 V (raw=4757)
[2026-08-24 15:39:51.563][000000009.534] I/user.ads1115_demo AIN2 = 0.6086 V (raw=4869)
[2026-08-24 15:39:51.565][000000009.534] I/user.ads1115_demo AIN3 = 0.6180 V (raw=4944)
[2026-08-24 15:39:51.567][000000009.534] I/user.ads1115_demo ---- [2/4] 完成 ----
[2026-08-24 15:39:51.274][000000009.248] I/user.ads1115_demo ===== [3/4] =====
[2026-08-24 15:39:51.290][000000009.264] I/user.ads1115_demo 2.782 VALERT=1
[2026-08-24 15:39:51.433][000000009.416] I/user.ads1115_demo ALERT
[2026-08-24 15:39:51.546][000000009.516] I/user.ads1115_demo = 3.077 V (raw=24618)
[2026-08-24 15:39:51.561][000000009.533] I/user.ads1115_demo ---- [3/4] 完成 ----
[2026-08-24 15:39:51.563][000000009.534] I/user.ads1115_demo ===== [4/4] PGA =====
[2026-08-24 15:39:51.565][000000009.534] I/user.ads1115_demo PGA 1 ±4.096V 125μV
[2026-08-24 15:39:51.567][000000009.534] I/user.ads1115_demo PGA=1 : AIN0 = 2.7818 V (raw=22254)
[2026-08-24 15:39:51.574][000000009.534] I/user.ads1115_demo PGA 2 ±2.048V 62.5μV ±2.048V
[2026-08-24 15:39:51.575][000000009.534] I/user.ads1115_demo PGA=2 : AIN0 = 2.0479 V (raw=32767)
[2026-08-24 15:39:51.577][000000009.534] I/user.ads1115_demo PGA 1 ±4.096V
[2026-08-24 15:39:51.579][000000009.534] I/user.ads1115_demo ---- [4/4] 完成 ----
[2026-08-24 15:39:51.581][000000009.534] I/user.ads1115_demo ===== [] =====
[2026-08-24 15:39:51.583][000000009.534] I/user.exs_ads1115
[2026-08-24 15:39:51.585][000000009.534] I/user.ads1115_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 ADS1115 接线是否正确(SCL、SDA、VCC、GND
- 确认 ADDR 引脚接 GND(地址 0x48),不能悬空
- 确认 SCL/SDA 上拉电阻已接(4.7kΩ 到 3.3V)
- 检查电源电压是否稳定(3.3V
2. **读取数据始终为零**
- 检查输入信号是否接入(AIN 悬空时读数漂移,属正常现象)
- 检查传感器供电是否正常
- 确认接线无松动
3. **比较器中断不触发**
- 确认 ALERT/RDY 引脚已接 GPIO22,并核对 `ALERT_GPIO` 常量
- 确认电压真正越过阈值(先用万用表确认 AIN0 实际电压)
- 若进入 [3/4] 时电压已在阈值外,先调回 2.5V 左右再越过阈值(下降沿触发)
### 5.7 扩展功能建议
exs_ads1115 更多接口的使用可以查看 [exs_ads1115 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_ads1115/)
@@ -0,0 +1,146 @@
--[[
@module adxl34x_demo
@summary ADXL345/ADXL346 三轴加速度传感器演示模块,包含所有功能的演示用例
@version 2.0
@date 2026.07.17
@author 江访
@usage
本文件包含 ADXL345/ADXL346 的逐项功能演示。
通过 exs_adxl34x 扩展库的 API 逐一演示数据读取、量程切换、输出速率切换、休眠唤醒等功能。
]]
local exs_adxl34x = require "exs_adxl34x"
local function adxl34x_int1_cb(data)
log.info("adxl34x_demo", string.format("int1: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("adxl34x_demo", "===== [1/4] 初始化与数据读取 =====")
local result = exs_adxl34x.setup("I2C", {
scl = 31,
sda = 30,
int1 = { int_gpio = 29, tap = true, cb = adxl34x_int1_cb },
})
if not result then
log.error("adxl34x_demo", "ADXL345/ADXL346 初始化失败,请检查接线")
return false
end
log.info("adxl34x_demo", "ADXL345/ADXL346 初始化成功,版本:", exs_adxl34x.version())
sys.wait(200)
local data = exs_adxl34x.get_data()
if data then
log.info("adxl34x_demo", string.format("X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
else
log.error("adxl34x_demo", "读取数据失败")
end
sys.wait(1000)
for i = 1, 3 do
sys.wait(500)
local d = exs_adxl34x.get_data()
if d then
log.info("adxl34x_demo", string.format("第%d次读取: X=%.3f Y=%.3f Z=%.3f g", i, d.x, d.y, d.z))
end
end
log.info("adxl34x_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] 量程切换演示
local function demo_range_switch()
log.info("adxl34x_demo", "===== [2/4] 量程切换演示 =====")
log.info("adxl34x_demo", "切换量程为 4g")
exs_adxl34x.set_range("4g")
sys.wait(300)
local data = exs_adxl34x.get_data()
if data then
log.info("adxl34x_demo", string.format("4g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("adxl34x_demo", "切换量程为 2g(高精度)")
exs_adxl34x.set_range("2g")
sys.wait(300)
data = exs_adxl34x.get_data()
if data then
log.info("adxl34x_demo", string.format("2g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("adxl34x_demo", "---- [2/4] 完成 ----")
end
-- [3/4] 输出速率切换
local function demo_odr_switch()
log.info("adxl34x_demo", "===== [3/4] 输出速率切换 =====")
local odr_list = { 25, 50, 100, 200 }
for _, odr in ipairs(odr_list) do
log.info("adxl34x_demo", string.format("设置输出速率为 %dHz", odr))
exs_adxl34x.set_odr(odr)
sys.wait(500)
local data = exs_adxl34x.get_data()
if data then
log.info("adxl34x_demo", string.format("%dHz 下: X=%.3f Y=%.3f Z=%.3f g", odr, data.x, data.y, data.z))
end
sys.wait(500)
end
exs_adxl34x.set_odr(100)
log.info("adxl34x_demo", "---- [3/4] 完成 ----")
end
-- [4/4] 休眠与唤醒演示
local function demo_sleep_wakeup()
log.info("adxl34x_demo", "===== [4/4] 休眠与唤醒演示 =====")
log.info("adxl34x_demo", "进入待机模式(低功耗)")
exs_adxl34x.sleep(); sys.wait(10000)
sys.wait(500)
log.info("adxl34x_demo", "从待机模式唤醒")
exs_adxl34x.wakeup()
sys.wait(200)
local data = exs_adxl34x.get_data()
if data then
log.info("adxl34x_demo", string.format("唤醒后数据: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
log.info("adxl34x_demo", "---- [4/4] 完成 ----")
end
local function adxl34x_demo_task_func()
log.info("adxl34x_demo", "HELLO")
sys.wait(1000)
local ok = demo_init_and_read()
if not ok then
log.error("adxl34x_demo", "初始化失败,演示终止")
return
end
sys.wait(500)
demo_range_switch()
sys.wait(500)
demo_odr_switch()
sys.wait(500)
demo_sleep_wakeup()
sys.wait(500)
log.info("adxl34x_demo", "===== [演示完毕] =====")
exs_adxl34x.close()
log.info("adxl34x_demo", "End")
end
sys.taskInit(adxl34x_demo_task_func)
@@ -0,0 +1,69 @@
--[[
@module main
@summary ADXL345/ADXL346 三轴加速度传感器 Demo 入口
@version 1.0
@date 2026.07.14
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_adxl34x 扩展库的完整功能,顺序为:
HELLO→[1/3]→[2/3]→[3/3]→End
1、初始化和数据读取([1/3]
2、量程切换演示([2/3]
3、输出速率切换([3/3]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "ADXL34X_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 adxl34x_demo.lua 演示模块
require "adxl34x_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,243 @@
# ADXL345/ADXL346 三轴加速度传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **adxl34x_demo.lua** - ADXL345/ADXL346 功能演示模块,包含数据读取、量程切换、输出速率切换等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_adxl34x** - ADXL345/ADXL346 扩展库,提供初始化、三轴加速度数据读取、量程切换、输出速率切换、睡眠/唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**ADXL345/ADXL346 在异常 I2C 通信后可能锁死 SDA 总线。本库已内置 I2C 总线卡死自动检测与恢复功能。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_adxl34x 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 ADXL345/ADXL346 传感器并读取三轴加速度数据(X/Y/Z,单位 g)
2. **[2/4] 量程切换演示** - 在 ±2g(高精度)和 ±4g(宽范围)量程之间切换,观察数据变化
3. **[3/4] 输出速率切换** - 依次切换 25Hz / 50Hz / 100Hz / 200Hz 输出速率
4. **[4/4] 休眠与唤醒演示** - 演示 sleep()/wakeup() 待机与唤醒
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- ADXL345/ADXL346 三轴加速度传感器模块 × 1
demo所演示的ADXL346 三轴加速度传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=674652805487&mi_id=0000HelRiVcJXLoq8Fq6jNLiArUvUR3J2pup2XI5jGtyLC8&ns=1&priceTId=2147849c17839172022585398e11c7&spm=a21n57.1.hoverItem.12&utparam=%7B%22aplus_abtest%22%3A%22418b653a98a5ab0b5f0ba7b791f3d489%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 5
- 母对母杜邦线 × 5
- TYPE-C 数据线 × 1
![ADXL345/ADXL346 模块接线实物图](https://docs.openluat.com/cdn/image/Air780EHM_ADXL36X.png)
### 3.2 接线配置
#### 3.2.1 ADXL345/ADXL346 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>ADXL345/ADXL346 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>SDA<br/></td></tr>
<tr>
<td>30/GPIO29<br/></td><td>INT1<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 四、演示软件环境
### 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 脚本文件
1. **main.lua** - 程序入口
2. **adxl34x_demo.lua** - ADXL345/ADXL346 功能演示模块
3. **exs_adxl34x** - ADXL345/ADXL346 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 ADXL345/ADXL346 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 adxl34x_demo.lua 演示模块
require "adxl34x_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴加速度数据(X/Y/Z,单位 g)的连续读取
#### 5.4.2 量程切换演示
1. 自动进入 [2/4] 量程切换演示
2. 观察 ±4g 宽范围量程下的数据
3. 观察 ±2g 高精度量程下的数据
#### 5.4.3 输出速率切换演示
1. 自动进入 [3/4] 输出速率切换演示
2. 依次观察 25Hz / 50Hz / 100Hz / 200Hz 下的数据读取
3. 最终恢复为 100Hz
#### 5.4.4 休眠与唤醒演示
1. 自动进入 [4/4] 休眠与唤醒演示
2. 观察 sleep() 进入待机模式
3. 观察 wakeup() 唤醒后数据恢复
### 5.5 预期效果
- **初始化与数据读取**ADXL345/ADXL346 初始化成功,三轴加速度数据正常输出
- **量程切换**:量程切换后数据范围随之变化,灵敏度不同
- **输出速率切换**:不同速率下均能正常读取数据
- **休眠与唤醒**sleep 后进入低功耗,wakeup 后数据读取正常
- **日志如下:**
```lua
[2026-07-21 10:26:41.222][000000000.177] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:26:41.225][000000000.177] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:26:41.228][000000000.179] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:26:41.231][000000000.181] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:26:41.233][000000000.181] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:26:41.234][000000000.182] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:26:41.237][000000000.199] I/user.main ADXL34X_Demo 001.999.000
[2026-07-21 10:26:41.240][000000000.213] D/user.exs_adxl34x version -> 202607170900
[2026-07-21 10:26:41.242][000000000.214] I/user.adxl34x_demo HELLO
[2026-07-21 10:26:41.524][000000001.214] I/user.adxl34x_demo ===== [1/4] =====
[2026-07-21 10:26:41.527][000000001.235] I/user.exs_adxl34x ADXL346 @ 0x53
[2026-07-21 10:26:41.530][000000001.302] I/user.exs_adxl34x INT1 , int_gpio=29
[2026-07-21 10:26:41.532][000000001.303] I/user.exs_adxl34x mode=I2C range=2g odr=100Hz
[2026-07-21 10:26:41.535][000000001.303] I/user.adxl34x_demo ADXL345/ADXL346 : 202607170900
[2026-07-21 10:26:41.701][000000001.506] I/user.adxl34x_demo X=0.984 Y=0.098 Z=0.156 g
[2026-07-21 10:26:41.718][000000001.626] I/user.adxl34x_demo int1: X=1.086 Y=-0.156 Z=0.223 g
[2026-07-21 10:26:41.727][000000001.768] I/user.adxl34x_demo int1: X=0.988 Y=-0.039 Z=0.184 g
[2026-07-21 10:26:41.734][000000002.030] I/user.adxl34x_demo int1: X=1.004 Y=-0.043 Z=0.172 g
[2026-07-21 10:26:42.468][000000002.789] I/user.adxl34x_demo int1: X=0.457 Y=0.152 Z=0.566 g
[2026-07-21 10:26:42.575][000000002.910] I/user.adxl34x_demo int1: X=0.363 Y=0.535 Z=0.832 g
[2026-07-21 10:26:42.685][000000003.009] I/user.adxl34x_demo 1: X=0.895 Y=0.297 Z=-0.352 g
[2026-07-21 10:26:42.690][000000003.020] I/user.adxl34x_demo int1: X=0.422 Y=0.336 Z=0.020 g
[2026-07-21 10:26:42.905][000000003.231] I/user.adxl34x_demo int1: X=0.617 Y=0.156 Z=0.133 g
[2026-07-21 10:26:42.982][000000003.303] I/user.adxl34x_demo int1: X=0.434 Y=0.133 Z=1.035 g
[2026-07-21 10:26:43.182][000000003.397] I/mobile sim0 sms ready
[2026-07-21 10:26:43.191][000000003.398] D/mobile cid1, state0
[2026-07-21 10:26:43.197][000000003.399] D/mobile bearer act 0, result 0
[2026-07-21 10:26:43.203][000000003.399] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:26:43.209][000000003.449] D/mobile TIME_SYNC 0 tm 1784600806
[2026-07-21 10:26:43.214][000000003.498] I/user.adxl34x_demo int1: X=0.430 Y=0.320 Z=1.234 g
[2026-07-21 10:26:43.219][000000003.512] I/user.adxl34x_demo 2: X=0.742 Y=0.230 Z=0.738 g
[2026-07-21 10:26:43.312][000000003.632] I/user.adxl34x_demo int1: X=0.012 Y=0.043 Z=0.840 g
[2026-07-21 10:26:43.360][000000003.679] I/user.adxl34x_demo int1: X=0.387 Y=0.246 Z=0.844 g
[2026-07-21 10:26:43.501][000000003.825] I/user.adxl34x_demo int1: X=-0.008 Y=-0.039 Z=1.012 g
[2026-07-21 10:26:43.687][000000004.015] I/user.adxl34x_demo 3: X=-0.035 Y=-0.020 Z=0.973 g
[2026-07-21 10:26:43.694][000000004.015] I/user.adxl34x_demo ---- [1/4] 完成 ----
[2026-07-21 10:26:44.110][000000004.438] I/user.adxl34x_demo int1: X=0.152 Y=0.496 Z=0.930 g
[2026-07-21 10:26:44.172][000000004.499] I/user.adxl34x_demo int1: X=0.113 Y=0.414 Z=0.652 g
[2026-07-21 10:26:44.188][000000004.515] I/user.adxl34x_demo ===== [2/4] =====
[2026-07-21 10:26:44.193][000000004.515] I/user.adxl34x_demo 4g
[2026-07-21 10:26:44.199][000000004.518] I/user.exs_adxl34x 4g
[2026-07-21 10:26:44.267][000000004.589] I/user.adxl34x_demo int1: X=-0.281 Y=0.711 Z=0.906 g
[2026-07-21 10:26:44.501][000000004.821] I/user.adxl34x_demo int1: X=0.867 Y=1.242 Z=0.547 g
[2026-07-21 10:26:44.505][000000004.824] I/user.adxl34x_demo 4g : X=0.867 Y=1.242 Z=0.547 g
[2026-07-21 10:26:44.564][000000004.898] I/user.adxl34x_demo int1: X=-0.078 Y=2.055 Z=0.359 g
[2026-07-21 10:26:44.611][000000004.942] I/user.adxl34x_demo int1: X=-0.508 Y=1.648 Z=1.047 g
[2026-07-21 10:26:44.658][000000004.991] I/user.adxl34x_demo int1: X=-0.227 Y=1.477 Z=0.352 g
[2026-07-21 10:26:44.706][000000005.041] I/user.adxl34x_demo int1: X=0.297 Y=1.891 Z=0.914 g
[2026-07-21 10:26:44.815][000000005.139] I/user.adxl34x_demo int1: X=-0.445 Y=2.031 Z=0.180 g
[2026-07-21 10:26:44.863][000000005.191] I/user.adxl34x_demo int1: X=0.281 Y=1.812 Z=0.688 g
[2026-07-21 10:26:44.925][000000005.254] I/user.adxl34x_demo int1: X=0.219 Y=1.953 Z=0.336 g
[2026-07-21 10:26:44.987][000000005.319] I/user.adxl34x_demo int1: X=-0.375 Y=1.953 Z=0.391 g
[2026-07-21 10:26:45.491][000000005.824] I/user.adxl34x_demo 2g
[2026-07-21 10:26:45.507][000000005.827] I/user.exs_adxl34x 2g
[2026-07-21 10:26:45.804][000000006.131] I/user.adxl34x_demo 2g : X=0.027 Y=0.977 Z=0.078 g
[2026-07-21 10:26:46.807][000000007.131] I/user.adxl34x_demo ---- [2/4] 完成 ----
[2026-07-21 10:26:47.305][000000007.631] I/user.adxl34x_demo ===== [3/4] =====
[2026-07-21 10:26:47.312][000000007.632] I/user.adxl34x_demo 25Hz
[2026-07-21 10:26:47.316][000000007.633] I/user.exs_adxl34x 25Hz
[2026-07-21 10:26:47.816][000000008.137] I/user.adxl34x_demo 25Hz : X=0.035 Y=0.969 Z=0.074 g
[2026-07-21 10:26:48.311][000000008.637] I/user.adxl34x_demo 50Hz
[2026-07-21 10:26:48.314][000000008.639] I/user.exs_adxl34x 50Hz
[2026-07-21 10:26:48.811][000000009.142] I/user.adxl34x_demo 50Hz : X=0.031 Y=0.973 Z=0.074 g
[2026-07-21 10:26:49.309][000000009.642] I/user.adxl34x_demo 100Hz
[2026-07-21 10:26:49.314][000000009.644] I/user.exs_adxl34x 100Hz
[2026-07-21 10:26:49.824][000000010.147] I/user.adxl34x_demo 100Hz : X=0.031 Y=0.984 Z=0.070 g
[2026-07-21 10:26:50.320][000000010.647] I/user.adxl34x_demo 200Hz
[2026-07-21 10:26:50.324][000000010.649] I/user.exs_adxl34x 200Hz
[2026-07-21 10:26:50.817][000000011.152] I/user.adxl34x_demo 200Hz : X=0.031 Y=0.977 Z=0.074 g
[2026-07-21 10:26:51.332][000000011.653] I/user.exs_adxl34x 100Hz
[2026-07-21 10:26:51.345][000000011.654] I/user.adxl34x_demo ---- [3/4] 完成 ----
[2026-07-21 10:26:51.834][000000012.154] I/user.adxl34x_demo ===== [4/4] =====
[2026-07-21 10:26:51.842][000000012.155] I/user.adxl34x_demo
[2026-07-21 10:26:51.848][000000012.156] I/user.exs_adxl34x
[2026-07-21 10:27:02.329][000000022.656] I/user.adxl34x_demo
[2026-07-21 10:27:02.335][000000022.657] I/user.exs_adxl34x
[2026-07-21 10:27:02.532][000000022.860] I/user.adxl34x_demo : X=0.027 Y=0.969 Z=0.070 g
[2026-07-21 10:27:02.536][000000022.861] I/user.adxl34x_demo ---- [4/4] 完成 ----
[2026-07-21 10:27:03.035][000000023.361] I/user.adxl34x_demo ===== [] =====
[2026-07-21 10:27:03.082][000000023.363] I/user.exs_adxl34x
[2026-07-21 10:27:03.139][000000023.363] I/user.adxl34x_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 ADXL345/ADXL346 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_adxl34x 扩展库已正常加载
2. **读取数据始终为零**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(SDO=GND 时地址 0x53SDO=VCC 时地址 0x1D
- 确认接线无松动
3. **量程切换无效果**
- 确认 `set_range()` 参数为 "2g"、"4g"、"8g" 或 "16g"
- 切换后需等待传感器稳定再读取数据
### 5.7 扩展功能建议
exs_adxl34x 更多接口的使用可以查看 [exs_adxl34x 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_adxl34x/)
@@ -0,0 +1,252 @@
--[[
@module aip650e_demo
@summary AiP650E 驱动芯片演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.13
@author 江访
@usage
本文件包含 AiP650E 的逐项功能演示和综合展示。
通过 exs_aip650e 扩展库的 API 逐一演示数码管显示、按键检测、亮度调节、
7段/8段模式切换、睡眠唤醒等功能。
]]
-- 加载 exs_aip650e 扩展库
local exs_aip650e = require "exs_aip650e"
-- 演示字符串显示
local function demo_string()
log.info("aip650e_demo", "===== [1/6] 字符串显示 =====")
log.info("aip650e_demo", "显示 1234")
exs_aip650e.clear()
exs_aip650e.set_display("1234")
sys.wait(1500)
log.info("aip650e_demo", "显示 88.8")
exs_aip650e.clear()
exs_aip650e.set_display("88.8")
sys.wait(1500)
log.info("aip650e_demo", "显示 AbCd")
exs_aip650e.clear()
exs_aip650e.set_display("AbCd")
sys.wait(1500)
log.info("aip650e_demo", "从第 3 位开始显示 12")
exs_aip650e.clear()
exs_aip650e.set_display("12", 3)
sys.wait(1500)
log.info("aip650e_demo", "显示 HELO")
exs_aip650e.clear()
exs_aip650e.set_display("HELO")
sys.wait(1500)
exs_aip650e.clear()
sys.wait(500)
end
-- 演示按键轮询
local function demo_keys()
log.info("aip650e_demo", "===== [2/6] 按键轮询 =====")
log.info("aip650e_demo", "请在 10 秒内按下连接到 AiP650E 的按键...")
exs_aip650e.clear()
exs_aip650e.set_display("KEYS")
local timeout = 10000
local step = 100
local elapsed = 0
while elapsed < timeout do
local key = exs_aip650e.get_key()
if key then
log.info("aip650e_demo", "检测到按键, 编码:", key)
local key_str = string.format("%02d", key)
exs_aip650e.set_display(key_str)
sys.wait(1000)
return
end
sys.wait(step)
elapsed = elapsed + step
end
log.info("aip650e_demo", "按键检测超时,跳过")
end
-- 按键回调函数
local function on_key(key_code)
log.info("aip650e_demo", "按键回调触发, 编码:", key_code)
local display = string.format("%02d", key_code)
exs_aip650e.set_display(display)
end
-- 演示按键回调
local function demo_key_callback()
log.info("aip650e_demo", "===== [3/6] 按键回调 =====")
log.info("aip650e_demo", "接下来 15 秒内按按键查看回调效果")
exs_aip650e.clear()
exs_aip650e.set_display("CALL")
exs_aip650e.set_key_callback(on_key)
sys.wait(15000)
exs_aip650e.set_key_callback(nil)
log.info("aip650e_demo", "按键回调演示结束")
end
-- 演示亮度调节
local function demo_brightness()
log.info("aip650e_demo", "===== [4/6] 亮度调节 =====")
exs_aip650e.clear()
exs_aip650e.set_display("8888")
sys.wait(500)
for bright = 0, 7 do
log.info("aip650e_demo", "设置亮度:", bright)
exs_aip650e.set_brightness(bright)
sys.wait(800)
end
-- 恢复默认亮度
exs_aip650e.set_brightness(4)
exs_aip650e.clear()
sys.wait(500)
end
-- 演示 7段/8段 模式切换
local function demo_segment_mode()
log.info("aip650e_demo", "===== [5/6] 7段/8段模式切换 =====")
-- 8 段模式(含 DP 段),显示数字
exs_aip650e.clear()
exs_aip650e.set_mode(0)
exs_aip650e.set_display("8SEG")
sys.wait(2000)
-- 显示带小数点的内容(8 段模式特性)
exs_aip650e.clear()
exs_aip650e.set_display("88.8")
sys.wait(2000)
-- 切换到 7 段模式
exs_aip650e.clear()
exs_aip650e.set_mode(1)
exs_aip650e.set_display("7SEG")
sys.wait(2000)
exs_aip650e.clear()
sys.wait(500)
-- 切回 8 段模式
exs_aip650e.set_mode(0)
end
-- 综合演示
local function demo_final()
log.info("aip650e_demo", "===== [6/6] 综合演示 =====")
-- 倒计时
for i = 5, 0, -1 do
exs_aip650e.clear()
exs_aip650e.set_display(string.format("%04d", i))
sys.wait(500)
end
-- HELLO 闪烁
for i = 0, 3 do
exs_aip650e.clear()
exs_aip650e.set_display("HELO")
sys.wait(500)
exs_aip650e.clear()
sys.wait(300)
end
-- 全 8 显示 + 亮度渐变
exs_aip650e.clear()
exs_aip650e.set_display("8888")
for i = 0, 7 do
exs_aip650e.set_brightness(i)
sys.wait(200)
end
sys.wait(1000)
exs_aip650e.set_brightness(4)
exs_aip650e.clear()
-- 日期展示
log.info("aip650e_demo", "日期展示: 26-07-13")
exs_aip650e.set_display("26-07-13")
sys.wait(3000)
-- 温度展示(摄氏度)
exs_aip650e.clear()
sys.wait(200)
local DEGREE = "\xB0"
for temp = 25, 32 do
local cel = string.format("%02d", temp) .. DEGREE .. "C"
exs_aip650e.set_display(cel)
local bright = math.floor((temp - 25) / 7 * 7 + 0.5)
if bright < 0 then bright = 0 end
if bright > 7 then bright = 7 end
exs_aip650e.set_brightness(bright)
sys.wait(800)
end
-- 温度展示(华氏度)
for temp = 77, 90, 2 do
local fah = string.format("%02d", temp) .. DEGREE .. "F"
exs_aip650e.set_display(fah)
sys.wait(600)
end
exs_aip650e.set_brightness(5)
sys.wait(2000)
exs_aip650e.clear()
-- 演示开启/关闭显示
log.info("aip650e_demo", "显示关闭演示")
exs_aip650e.set_display("OFF_")
sys.wait(2000)
exs_aip650e.display_off()
sys.wait(2000)
exs_aip650e.display_on()
sys.wait(2000)
exs_aip650e.clear()
log.info("aip650e_demo", "---- 综合演示结束 ----")
end
-- 演示主函数(运行在协程中)
local function aip650e_demo_task_func()
-- 初始化 (Air780EPM: CLK=31, DIO=30)
local result = exs_aip650e.setup({
clk = 31,
dio = 30,
bright = 5,
})
if not result then
log.error("aip650e_demo", "AiP650E 初始化失败,请检查接线")
return
end
log.info("aip650e_demo", "AiP650E 初始化成功,版本:", exs_aip650e.version())
-- HELLO 开始 → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
exs_aip650e.set_display("HELO")
sys.wait(1000)
demo_string()
demo_keys()
demo_key_callback()
demo_brightness()
demo_segment_mode()
demo_final()
exs_aip650e.set_display("End-")
sys.wait(3000)
log.info("aip650e_demo", "===== [演示完毕] =====")
end
sys.taskInit(aip650e_demo_task_func)
@@ -0,0 +1,71 @@
--[[
@module main
@summary AiP650E 数码管驱动芯片测试 Demo 入口
@version 1.0
@date 2026.07.13
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_aip650e 扩展库的完整功能,顺序为:
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]
5、7段/8段模式切换([5/6]
6、综合演示-温度/时钟/睡眠([6/6])
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "AiP650E_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 aip650e_demo.lua 演示模块
require "aip650e_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,180 @@
# AiP650E 数码管驱动芯片演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **aip650e_demo.lua** - AiP650E 功能演示模块,包含数码管显示、按键检测、亮度调节、模式切换、综合展示等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_aip650e** - AiP650E 扩展库,提供数码管段码显示、按键检测、亮度调节、模式切换等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_aip650e 扩展库的 6 项功能:HELLO → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
### 2.1 功能演示项说明
1. **[1/6] 字符串显示** - 数码管逐项显示数字、带小数点数字、字母组合、指定位置显示
2. **[2/6] 按键轮询** - 通过 `get_key()` API 轮询检测按键,10 秒超时
3. **[3/6] 亮度调节** - 展示 0~7 级亮度调节效果
4. **[4/6] 按键回调** - 通过 `set_key_callback()` API 设置按键回调函数,15 秒演示窗口
5. **[5/6] 模式切换** - 8 段与 7 段模式切换演示
6. **[6/6] 综合演示** - 倒计时、HELLO 闪烁、全 8 显示、亮度渐变、日期展示、温度展示
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- AiP650E 数码管模块(4 位数码管 + 按键) × 1
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 AiP650E 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>AiP650E 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>CLK<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>DIO<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 四、演示软件环境
### 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 脚本文件
1. **main.lua** - 主程序入口
2. **aip650e_demo.lua** - AiP650E 功能演示模块
3. **exs_aip650e** - AiP650E 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 AiP650E 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 aip650e_demo.lua 演示模块
require "aip650e_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 字符串显示演示
1. 设备启动后显示 "HELLO",随后自动进入 [1/6] 字符串显示
2. 观察数码管依次显示:`1234``88.8``AbCd` → 从第 3 位显示 `1234`
3. 检查字符显示是否正确,带小数点功能是否正常
#### 5.4.2 按键检测演示
1. 自动进入 [2/6] 按键轮询演示,数码管显示 "KEY-"
2. 在 10 秒内按下 AiP650E 模块上的任意按键
3. 观察数码管显示按键编码(如 "K01"、"K02" 等)
4. 若 10 秒内无按键按下,自动跳过
#### 5.4.3 亮度调节演示
1. 自动进入 [3/6] 亮度调节演示,数码管显示 "8888"
2. 观察数码管亮度从 0 级到 7 级逐级变化
3. 最终恢复到 4 级亮度
#### 5.4.4 按键回调演示
1. 自动进入 [4/6] 按键回调演示,数码管显示 "Cb-Key"
2. 在 15 秒内按下任意按键,观察数码管显示对应按键编码
3. 观察日志输出,确认回调函数 `on_key` 被正常触发
4. 15 秒后自动结束回调演示
#### 5.4.5 模式切换演示
1. 自动进入 [5/6] 模式切换演示
2. 观察数码管从 8 段模式切换到 7 段模式
3. 再切回 8 段模式,观察显示效果变化
4. 确认小数点(DP)在 7 段模式下不显示
#### 5.4.6 综合演示
1. 自动进入 [6/6] 综合演示
2. 观察倒计时效果:5 → 4 → 3 → 2 → 1 → 0
3. 观察 "HELO" 闪烁效果,重复 4 次
4. 观察全 8 显示 + 亮度渐变
5. 日期展示:显示格式 "YY-MM-DD"(如 "26-07-13"),停留 3 秒
6. 温度展示:
- 摄氏度:25°C → 32°C 逐度变化,亮度随温度升高而增加
- 华氏度:77°F → 90°F 逐度变化
7. 演示结束后显示 "End" 并停留 3 秒
### 5.5 预期效果
- **字符串显示**:数字、字母、小数点均能正确显示,支持指定起始位置
- **按键检测**:轮询模式和回调模式均能正确检测按键,编码显示正常
- **亮度调节**:0~7 级亮度逐级变化,效果明显
- **模式切换**:8 段和 7 段模式切换正常,小数点显示状态正确
- **日期/温度**:格式显示正确,°C 和 °F 符号正常显示
### 5.6 故障排除
1. **数码管无显示**
- 检查 AiP650E 接线是否正确(CLK、DIO、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_aip650e 扩展库已正常加载
2. **按键无响应**
- 检查按键电路是否正常
- 确认扩展库初始化成功
- 查看日志确认 `setup()` 返回值为 true
3. **亮度无法调节**
- 确认调用 `set_brightness()` 时参数为 0~7
4. **显示内容错乱**
- 检查杜邦线是否过长导致通信不稳定
5. **通信不稳定**
- 检查 CLK 和 DIO 接线是否牢固
### 5.7 扩展功能建议
exs_aip650e 更多接口的使用可以查看 [exs_aip650e 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_aip650e/)
@@ -0,0 +1,123 @@
--[[
@module bl0939_app
@summary BL0939 双路免校准电能计量数据读取业务模块
@version 1.0
@date 2026.08.20
@author 李明
]]
local exs_bl0939 = require "exs_bl0939"
-- 通信模式切换:1=SPI / 2=UART
-- 根据实际硬件接线选择
-- SPI 模式:BL0939 SEL 接 Air780EPM GPIO28(高电平)
-- BL0939 SCLK -> Air780EPM SPI0_SCK
-- BL0939 RX/SDI -> Air780EPM SPI0_MOSI
-- BL0939 TX/SDO -> Air780EPM SPI0_MISO(需外部上拉电阻)
-- UART 模式:BL0939 SEL 接 Air780EPM GPIO28(低电平)
-- BL0939 RX/SDI -> Air780EPM UART1_TX (GPIO18)
-- BL0939 TX/SDO -> Air780EPM UART1_RX (GPIO17)(需外部上拉电阻)
-- 注意:BL0939 的 SEL 引脚悬空默认为 UART 模式(内部下拉)
local MODE = 2
-- UART 器件地址(仅 UART 模式生效)
-- SOP16L 封装固定为 5SSOP20L 封装由 A4~A1 引脚电平决定 0~15
local UART_ADDR = 5
-- 外部采样电路参数,需根据实际互感器和分压电阻修改
-- 电流有效值换算:I(A) = IA/B_RMS * Vref / 324004(规格书 2.6
-- 电压有效值换算:V(V) = V_RMS * Vref / 79931(规格书 2.6
-- 有功功率换算:P(W) = WATT * Vref^2 / 4046(规格书 2.2
-- Vref 为内置基准电压,典型值 1.218V
-- 以下 ratio 为简化系数,实际需根据互感器变比和分压比标定
local voltage_ratio = 1000 -- 电压分压比,例如 1000:1
local current_ratio = 1000 -- 电流互感器变比,例如 1000:1
-- 按 MODE 组装 setup 参数
local function build_config_func()
if MODE == 1 then
-- SPI 模式:BL0939 SEL 接高电平
return {
mode = "spi",
spi_id = 0,
cs = 20, -- 占位 GPIOBL0939 SPI 无 CS,不接 BL0939
sel_pin = 28, -- 接 BL0939 SEL,拉高选 SPI 模式
ac_freq = 50,
rms_update = 400,
fast_rms_threshold = 0x7FFF,
fast_rms_cycle = exs_bl0939.FAST_RMS_CYCLE_FULL,
cf_func = exs_bl0939.CF_FUNC_ENERGY
}
else
-- UART 模式:BL0939 SEL 接低电平
return {
mode = "uart",
uart_id = 1,
addr = UART_ADDR, -- SOP16L 固定 5
sel_pin = 28, -- 接 BL0939 SEL,拉低选 UART 模式
ac_freq = 50,
rms_update = 400,
fast_rms_threshold = 0x7FFF,
fast_rms_cycle = exs_bl0939.FAST_RMS_CYCLE_FULL,
cf_func = exs_bl0939.CF_FUNC_ENERGY
}
end
end
-- 传感器初始化函数
-- 重要:必须返回 boolean,失败时 return false
local function init_func()
local result = exs_bl0939.setup(build_config_func())
if not result then
log.error("bl0939", "init failed")
log.error("bl0939", "检查接线: VCC=3.3V GND, SEL=GPIO28, SPI 模式接 SCK/MOSI/MISO, UART 模式接 TX/RX")
return false -- 返回 false,不能只 return
end
log.info("bl0939", "init success, version:", exs_bl0939.version())
return true
end
-- 读取并打印双路电能数据
local function read_data_func()
local data = exs_bl0939.get_data()
if not data then
log.error("bl0939", "read data failed")
return
end
-- 电压和温度
log.info("bl0939", string.format("电压=%.2fV 温度=%.1f°C",
data.v_rms / voltage_ratio, data.temp))
-- A 通道
log.info("bl0939", string.format("A路 电流=%.3fA 有功=%.2fW 相角=%.1f°",
data.ia_rms / current_ratio,
data.a_watt / voltage_ratio / current_ratio,
data.a_angle))
-- B 通道
log.info("bl0939", string.format("B路 电流=%.3fA 有功=%.2fW 相角=%.1f°",
data.ib_rms / current_ratio,
data.b_watt / voltage_ratio / current_ratio,
data.b_angle))
-- 快速有效值(用于漏电/过流监控参考)
log.info("bl0939", string.format("快速RMS A=%d B=%d 电能脉冲A=%d B=%d",
data.ia_fast_rms, data.ib_fast_rms, data.cfa_cnt, data.cfb_cnt))
end
-- 传感器读取任务协程
local function sensor_task_func()
sys.wait(100) -- 等待系统稳定,100ms
local ok = init_func()
if not ok then return end -- 按返回值判断是否继续
for i = 1, 10 do
read_data_func()
sys.wait(1000) -- 每隔 1 秒读取一次
end
exs_bl0939.close()
log.info("bl0939", "demo finished")
end
sys.taskInit(sensor_task_func)
@@ -0,0 +1,65 @@
--[[
@module main
@summary LuatOS 用户应用脚本文件入口
@version 1.0
@date 2026.08.20
@author 蒋骞
@usage
本 demo 演示 BL0939 双路免校准电能计量芯片的数据读取功能,业务逻辑见 bl0939_app.lua
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_bl0939 扩展库的完整功能,顺序为:
1、初始化([1/2]
2、循环读取双路电能数据([2/2])
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "780EPM_bl0939"
VERSION = "001.999.000"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 设置日志输出风格为样式2(建议调试时开启)
-- log.style(2)
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时/已使用/历史最高内存情况
-- 方便分析内存使用是否有异常
-- local function mem_check_func()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end
-- sys.timerLoopStart(mem_check_func, 3000)
-- 加载业务模块
require "bl0939_app"
sys.run()
-- sys.run() 之后不要加任何语句
@@ -0,0 +1,194 @@
# Air780EPM + BL0939 双路免校准电能计量 Demo
> <font color="red"><b>声明:
>
>严禁带电操作! 进行任何连接或拆卸前,务必断开电源。
>高压危险! 测试220V时,请确保所有接线可靠,并使用隔离变压器及漏电保护器。
>本介绍为技术支持页面,操作人员需具备电气安全知识,否则请勿尝试,操作风险由使用者自行承担</b></font>
## 项目简介
**Air780EPM**:通过 UART1MAIN_UART)或 SPI0 读取上海贝岭 BL0939 双路电能计量芯片,持续循环读取 + 失联自动重连,内置实机校准系数。
## 文件结构
- `user/main.lua`:脚本入口
- `user/bl0939_app.lua`:业务逻辑(循环读取/换算/断线重连;`MODE` 变量切换 uart/spi
- `lib/exs_bl0939.lua`BL0939 驱动扩展库(SPI/UART 双模式)
## 一、功能模块介绍
### 1.1 核心主程序模块
[main.lua](main.lua) 为 LuatOS 脚本入口,仅负责加载业务模块并启动调度器。
业务逻辑全部在 `bl0939_app.lua` 中实现。
### 1.2 扩展库模块
- `exs_bl0939.lua`:BL0939 驱动扩展库,封装 SPI/UART 通信、SEL 模式切换、寄存器配置、数据读取、温度与相角换算。
- `exs_bl0939.md`:扩展库 API 文档,含六章结构说明。
### 1.3 通信模式说明
本 Demo 支持 SPI 和 UART 两种通信模式,通过 `bl0939_app.lua` 顶部的 `MODE` 变量切换:
- `MODE = 1`:SPI 模式(推荐),默认使用 SPI0、SEL=GPIO28(拉高)。
- `MODE = 2`:UART 模式,默认使用 UART1、4800bps、器件地址 5SOP16L)。
BL0939 的 SEL 引脚决定通信模式:SEL=1(高电平)为 SPI,SEL=0(低电平或悬空)为 UART。
请根据实际硬件接线选择对应模式。
---
## 二、演示流程介绍
1. 系统上电后等待 100ms 稳定。
2. 初始化 BL0939,配置 SEL 引脚、通信接口、交流频率、RMS 刷新、快速有效值参数。
3. 循环读取 10 次双路电能数据,每次间隔 1 秒。
4. 打印电压、温度、A/B 双路电流、有功功率、相角、快速有效值、电能脉冲计数。
5. 演示结束后关闭传感器。
注意:UART 模式下 `get_data()` 优先使用全电参数数据包(35 字节一次返回),响应更快。
---
## 三、演示硬件环境
### 3.1 硬件清单
- 支持 LuatOS 的模块(如 Air780EPM
- BL0939 芯片或已集成模块(SOP16L 或 SSOP20L 封装)
- 电流互感器(2 路,用于 A/B 电流通道 IP1/IN1、IP2/IN2
- 电压分压电路(用于 VP 电压通道)
- 外部上拉电阻(接 TX/SDO 引脚,规格书要求)
- 杜邦线或 PCB 转接板
### 3.2 接线配置
#### SPI 模式(MODE = 1SEL 接高电平)
```text
Air780EPM BL0939
SPI0_SCK ------> SCLK
SPI0_MOSI ------> RX/SDI
SPI0_MISO <------ TX/SDO(需外部上拉电阻)
GPIO28 ------> SEL (高电平选 SPI 模式)
3.3V ------> VDD
GND ------> GND
IP1/IN1 <------ 电流 A 通道采样
IP2/IN2 <------ 电流 B 通道采样
VP <------ 电压采样
```
注意:BL0939 SPI 不支持片选(CS),`bl0939_app.lua``cs=20` 仅为
`spi.deviceSetup` 所需占位 GPIO,不接 BL0939。
#### UART 模式(MODE = 2SEL 接低电平或悬空)
```text
Air780EPM BL0939
UART1_TX ------> RX/SDI GPIO18
UART1_RX <------ TX/SDO GPIO17,需外部上拉电阻)
GPIO28 ------> SEL (低电平选 UART 模式)
3.3V ------> VDD
GND ------> GND
```
注意:
- UART 固定 4800bpsN/8/1.5,不可通过软件修改。
- SOP16L 封装器件地址固定为 5SSOP20L 由 A4~A1 引脚电平决定 0~15。
- UART 模式仅需两个低速光耦即可实现隔离通信。
---
## 四、演示软件环境
### 4.1 开发工具
- Luatools 或 LuatOS 配套 IDE
- USB 数据线用于烧录和日志查看
### 4.2 内核固件
请使用支持 SPI/UART 扩展库模块的最新 LuatOS 固件。
### 4.3 脚本文件
- `main.lua`:脚本入口
- `bl0939_app.lua`:业务逻辑
- `exs_bl0939.lua`:扩展库(放到脚本同级目录)
- `exs_bl0939.md`API 文档
---
## 五、演示核心步骤
### 5.1 硬件准备
按 3.2 节接线,确保 BL0939 供电为 3.3VGND 共地。
TX/SDO 引脚需接外部上拉电阻(规格书要求)。
电流互感器和电压分压电路参数需根据实际应用场景计算。
![](https://docs.openluat.com/cdn/image/780EPM_BL0939.jpg)
### 5.2 软件配置
1. 打开 `bl0939_app.lua`
2. 根据接线方式设置 `MODE = 1`SPI)或 `MODE = 2`UART)。
3. UART 模式下,根据封装设置 `UART_ADDR`SOP16L 固定 5SSOP20L 由引脚决定)。
4. 修改 `voltage_ratio``current_ratio` 为实际采样电路参数。
注意:
- `SEL=GPIO28` 用于切换通信模式,SPI 拉高、UART 拉低;
- BL0939 内部下拉,SEL 悬空默认为 UART 模式;
- UART 固定 4800bps,无法通过 `baud` 参数修改。
### 5.3 软件烧录
`main.lua``bl0939_app.lua``exs_bl0939.lua` 一起烧录到模块中,重启运行。
### 5.4 功能测试
观察日志输出,确认初始化成功、数据读取正常。
### 5.5 预期效果(含实测日志)
当前测试只接了一路负载,是由一个100W 500Ω和一个200W 200Ω的功率电阻串联,测试电流为 0.318A,电压为 220V,温度为 29.2°C,部分日志如下:
```text
[2026-08-21 15:14:44.838][000000073.994] D/user.exs_bl0939 UART PKT RX 55 BD F6 00 A2 56 01 00 00 00 BD 36 38 82 02 00 38 C5 00 00 00 00 02 00 00 00 00 00 C7 01 00 FE 03 00 37
[2026-08-21 15:14:44.853][000000074.016] D/user.exs_bl0939 UART RD RX 07 00 00 9C
[2026-08-21 15:14:44.885][000000074.037] D/user.exs_bl0939 UART RD RX FF 00 00 A3
[2026-08-21 15:14:44.893][000000074.038] I/user.bl0939 RAW V=3684029 IA=87714 IB=0 PA=50488 PB=0 W1=2 W2=0 TPS1=455
[2026-08-21 15:14:44.909][000000074.038] I/user.bl0939 电压=222.47V 温度=29.2C
[2026-08-21 15:14:44.924][000000074.039] I/user.bl0939 A路 电流=0.318A 有功=70.84W 相角=0.1 电能=0.0003kWh
[2026-08-21 15:14:44.939][000000074.039] I/user.bl0939 B路 电流=0.000A 有功=0.00W 相角=4.6 电能=0.0000kWh
```
### 5.6 故障排除
| 现象 | 排查方法 |
|---|---|
| 初始化失败 | 检查 VCC/GND 接线;确认 SEL 引脚电平与 MODE 一致;检查 SPI 或 UART 接线 |
| 数据全为 0 | 检查电压/电流采样电路是否接入;确认 IP1/IN1、IP2/IN2、VP 接线正确 |
| 温度异常 | 温度寄存器需芯片工作一段时间,上电后立即读取可能不准确 |
| UART 读取失败 | 确认 UART 固定 4800bps;检查 TX/RX 是否交叉;确认器件地址与封装一致 |
| SPI checksum 错误 | 检查 SPI0_SCK/MOSI/MISO 接线;确认 SPI 模式 1CPOL=0/CPHA=1 |
| 相角为 0 | 确认电压通道有交流信号输入;A/B_CORNER 寄存器需过零更新 |
### 5.7 扩展功能建议
- 连接 CF 引脚到 GPIO,使用 GPIO 中断统计电能脉冲。
- 配置快速有效值阈值,用于 A/B 通道漏电/过流保护。
- 设置 CF 为温度报警输出,配合 temp_alert_th 实现过温保护。
- 接入校准源,写入校准系数(RMSOS/WATTOS)提高计量精度。
- 使用 SSOP20L 封装多片级联,实现多路计量。
- 通过 MQTT 或 OneNET 上传电能数据到云端。
---
## 六、参考文档
- [exs_bl0939.md](../exs_bl0939.md)
- BL0939 规格书
@@ -0,0 +1,315 @@
--[[
@module bme680_demo
@summary BME680 四合一环境传感器演示模块,包含所有功能的演示用例
@version 1.1
@date 2026.08.20
@author 江访
@usage
本文件包含 BME680 的逐项功能演示。
通过 exs_bme680 扩展库的 API 逐一演示数据读取、IIR 滤波切换、过采样率切换、
气体加热器切换、空气质量判断、海拔计算等功能。
通过 MODE 变量选择通信模式(改 MODE 的值即可,无需注释/取消注释代码):
MODE = 1 软件 I2C(默认推荐,仅需 scl/sda 引脚,任意 GPIO 均可,接线最灵活)
MODE = 2 硬件 I2C(指定 i2c_id,占用 CPU 少)
Air780EPM 接线(软件 I2C):
SCL = GPIO31
SDA = GPIO30
]]
-- 加载 exs_bme680 扩展库
-- 该库提供 BME680 的全部驱动功能: 初始化/温度/气压/湿度/气体电阻读取/过采样/滤波/加热器/海拔
local exs_bme680 = require "exs_bme680"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(默认) 2 = 硬件 I2C
-- 各模式所需引脚/参数见下方 init_config,接线说明见 readme.md
local MODE = 1
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIOAir780EPM 接线示例用 GPIO31/GPIO30
init_config = {scl = 31, sda = 30}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1),不传 scl/sda 时无总线恢复能力
init_config = {i2c_id = 1}
else
log.error("bme680_demo", "MODE 取值错误,应为 1/2")
return
end
--------------------------------------------------------------
-- [1/6] 初始化与数据读取
-- - setup() 自动完成芯片 ID 校验、校准数据读取、采样/滤波/加热器参数配置
-- - get_data() 返回温度(°C)、气压(hPa)、湿度(%RH)、气体电阻(Ω)
--------------------------------------------------------------
local function demo_init_and_read()
log.info("bme680_demo", "===== [1/6] 初始化与数据读取 =====")
-- BME680 默认配置:温度 4X 过采样,气压 4X 过采样,湿度 2X 过采样
-- IIR 滤波 8 次平均,气体加热器 320°C / 150ms
local result = exs_bme680.setup(init_config)
if not result then
log.error("bme680_demo", "BME680 初始化失败")
log.error("bme680_demo", "检查接线: VCC=3.3V GND SCL SDAI2C 地址是否正确(如 0x76/0x77")
return false -- 初始化失败必须返回 false
end
log.info("bme680_demo", "BME680 初始化成功,版本:", exs_bme680.version())
-- 气体预热说明:MOX 气体传感器上电后电阻会从低值缓慢爬升,
-- 新板首次上电需预热 20~30 分钟才稳定,稳定前气体电阻读值无参考意义
log.info("bme680_demo", "预热提示: MOX 气体传感器需预热 20~30 分钟,")
log.info("bme680_demo", "预热期间电阻从低值爬升属正常,稳定前气体电阻读值不可信")
-- 等待传感器稳定,气体传感器需要预热
-- 首次读取气体电阻可能为 0(加热器未稳定),这是正常现象
sys.wait(500) -- 等待传感器稳定,500ms
local data = exs_bme680.get_data()
if data then
local gas_kohm = data.gas_resistance / 1000
log.info("bme680_demo", string.format("温度=%.2f°C 气压=%.2fhPa 湿度=%.1f%%RH 气体=%.1fkΩ",
data.temperature, data.pressure, data.humidity, gas_kohm))
else
log.error("bme680_demo", "读取数据失败")
end
sys.wait(1000) -- 稳定后等待 1 秒进入连续读取
-- 连续读取 3 次,观察数据变化
for i = 1, 3 do
sys.wait(2000) -- 每次读取间隔 2 秒
local d = exs_bme680.get_data()
if d then
local gas_kohm = d.gas_resistance / 1000
log.info("bme680_demo", string.format("第%d次读取: 温度=%.2f°C 气压=%.1fhPa 湿度=%.1f%%RH 气体=%.1fkΩ",
i, d.temperature, d.pressure, d.humidity, gas_kohm))
end
end
log.info("bme680_demo", "---- [1/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- [2/6] IIR 滤波器切换演示
-- - set_filter(0~7) 设置 IIR 滤波器系数,数值越大数据越平滑
--------------------------------------------------------------
local function demo_filter_switch()
log.info("bme680_demo", "===== [2/6] IIR 滤波器切换演示 =====")
-- 依次切换 filter=0/1/2/3/4/5/6/7,观察数据变化
local filter_list = { 0, 1, 2, 3, 4, 5, 6, 7 }
for _, f in ipairs(filter_list) do
log.info("bme680_demo", string.format("设置滤波器 filter=%d", f))
exs_bme680.set_filter(f)
sys.wait(100) -- 等待滤波器生效,100ms
local data = exs_bme680.get_data()
if data then
log.info("bme680_demo", string.format("filter=%d: 温度=%.2f°C 气压=%.2fhPa 湿度=%.1f%%RH",
f, data.temperature, data.pressure, data.humidity))
end
sys.wait(1000) -- 每档滤波器显示间隔 1 秒
end
log.info("bme680_demo", "---- [2/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- [3/6] 过采样率切换演示
-- - set_temp/press/hum_oversampling(0~5) 设置过采样率
-- - 低功耗(1X)与高精度(16X)对比
--------------------------------------------------------------
local function demo_oversampling_switch()
log.info("bme680_demo", "===== [3/6] 过采样率切换演示 =====")
-- 低功耗模式:全部 1X 过采样
log.info("bme680_demo", "切换到低功耗模式(1X过采样)")
exs_bme680.set_temp_oversampling(1)
exs_bme680.set_press_oversampling(1)
exs_bme680.set_hum_oversampling(1)
sys.wait(100) -- 等待配置生效,100ms
local data = exs_bme680.get_data()
if data then
log.info("bme680_demo", string.format("低功耗: 温度=%.2f°C 气压=%.2fhPa 湿度=%.1f%%RH",
data.temperature, data.pressure, data.humidity))
end
sys.wait(1000) -- 展示间隔 1 秒
-- 高精度模式:全部 16X 过采样
log.info("bme680_demo", "切换到高精度模式(16X过采样)")
exs_bme680.set_temp_oversampling(5)
exs_bme680.set_press_oversampling(5)
exs_bme680.set_hum_oversampling(5)
sys.wait(100) -- 等待配置生效,100ms
data = exs_bme680.get_data()
if data then
log.info("bme680_demo", string.format("高精度: 温度=%.2f°C 气压=%.2fhPa 湿度=%.1f%%RH",
data.temperature, data.pressure, data.humidity))
end
log.info("bme680_demo", "---- [3/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- [4/6] 气体加热器切换演示
-- - set_gas_heater(temp, time) 设置加热器温度(200~400°C)与时长(1~4032ms)
-- - 不同温度下气体电阻值会变化
--------------------------------------------------------------
local function demo_gas_heater_switch()
log.info("bme680_demo", "===== [4/6] 气体加热器切换演示 =====")
-- 不同加热器温度组合
local heater_configs = {
{ temp = 250, time = 100, desc = "低温短时(250°C/100ms" },
{ temp = 320, time = 150, desc = "默认配置(320°C/150ms" },
{ temp = 350, time = 200, desc = "高温长时(350°C/200ms" },
}
for _, cfg in ipairs(heater_configs) do
log.info("bme680_demo", cfg.desc)
exs_bme680.set_gas_heater(cfg.temp, cfg.time)
sys.wait(200) -- 等待加热器参数生效,200ms
local data = exs_bme680.get_data()
if data then
local gas_kohm = data.gas_resistance / 1000
log.info("bme680_demo", string.format("气体=%.1fkΩ 温度=%.2f°C 湿度=%.1f%%RH",
gas_kohm, data.temperature, data.humidity))
end
sys.wait(1000) -- 展示间隔 1 秒
end
log.info("bme680_demo", "---- [4/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- 空气质量判断辅助函数
-- 说明:不能直接看电阻绝对值判断空气好坏(不同芯片/环境基线差异大),
-- 必须以干净空气稳定基线 R0 为基准,看实时电阻 R 的相对下降幅度:
-- 污染度 = (1 - R/R0) * 100%,下降越多污染越高
-- 参数:R 实时气体电阻(Ω)
-- R0 干净空气稳定基线电阻(Ω)
-- 返回:等级字符串, 污染度百分比
--------------------------------------------------------------
local function calc_air_quality(R, R0)
if not R0 or R0 <= 0 or not R or R <= 0 then
return "未就绪", 0
end
local ratio = R / R0
local pollution = (1 - ratio) * 100
local level
if ratio >= 0.9 then
level = ""
elseif ratio >= 0.7 then
level = ""
elseif ratio >= 0.4 then
level = ""
else
level = ""
end
return level, pollution
end
--------------------------------------------------------------
-- [5/6] 空气质量判断演示
-- - 预热说明:MOX 传感器上电后电阻从低值爬升,新板需预热 20~30 分钟才稳定
-- - 判断方法:先记录干净空气稳定基线 R0,实时电阻相对 R0 下降越多空气越差
--------------------------------------------------------------
local function demo_air_quality()
log.info("bme680_demo", "===== [5/6] 空气质量判断演示 =====")
log.info("bme680_demo", "预热说明: 新板首次上电 MOX 气体传感器需预热 20~30 分钟")
log.info("bme680_demo", "电阻会从低值爬升直至稳定,稳定前气体电阻读值无参考意义")
log.info("bme680_demo", "判断方法: 以干净空气稳定电阻为基线R0,污染度=(1-R/R0)*100%")
local data = exs_bme680.get_data()
if not data then
log.error("bme680_demo", "读取数据失败,空气质量演示终止")
return true
end
-- 记录基线 R0:真实使用必须在干净空气中预热 20~30 分钟后记录稳定值,
-- 此处为便于演示,直接以当前读数为基线(若为 0 说明加热器未稳定)
local R0 = data.gas_resistance
if R0 <= 0 then
log.warn("bme680_demo", "当前气体电阻为 0(加热器未稳定),请先预热 20~30 分钟再演示")
log.warn("bme680_demo", "本次演示基线无效,后续空气等级将显示为未就绪")
else
log.info("bme680_demo", string.format("记录基线 R0=%.1fkΩ(应取干净空气预热稳定后的值)", R0 / 1000))
end
-- 连续读取 3 次,实时计算污染度与空气等级
for i = 1, 3 do
sys.wait(3000) -- 每次间隔 3 秒
local d = exs_bme680.get_data()
if d then
local level, pollution = calc_air_quality(d.gas_resistance, R0)
log.info("bme680_demo", string.format("第%d次: 气体=%.1fkΩ 污染度=%.1f%% 空气等级=%s",
i, d.gas_resistance / 1000, pollution, level))
end
end
log.info("bme680_demo", "提示: 空气等级阈值(优≥0.9R0/良≥0.7R0/中≥0.4R0/差<0.4R0)为经验值")
log.info("bme680_demo", "---- [5/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- [6/6] 海拔高度测量与关闭
-- - get_altitude(pressure) 依据气压计算海拔高度
-- - close() 关闭传感器并释放 I2C 资源
--------------------------------------------------------------
local function demo_altitude_and_close()
log.info("bme680_demo", "===== [6/6] 海拔高度测量与关闭 =====")
local data = exs_bme680.get_data()
if data then
local alt = exs_bme680.get_altitude(data.pressure)
local gas_kohm = data.gas_resistance / 1000
log.info("bme680_demo", string.format("温度=%.2f°C 气压=%.1fhPa 湿度=%.1f%%RH 气体=%.1fkΩ 海拔=%.1f米",
data.temperature, data.pressure, data.humidity, gas_kohm, alt))
end
-- 关闭传感器
exs_bme680.close()
log.info("bme680_demo", "---- [6/6] 完成 ----")
return true
end
--------------------------------------------------------------
-- 主任务:按 [1/6]~[6/6] 顺序执行全部演示
--------------------------------------------------------------
local function bme680_demo_task_func()
log.info("bme680_demo", "HELLO")
sys.wait(1000) -- 等待系统启动稳定,1 秒
local ok = demo_init_and_read()
if not ok then
log.error("bme680_demo", "初始化失败,演示终止")
return
end
sys.wait(500) -- 演示间隔,500ms
demo_filter_switch()
sys.wait(500) -- 演示间隔,500ms
demo_oversampling_switch()
sys.wait(500) -- 演示间隔,500ms
demo_gas_heater_switch()
sys.wait(500) -- 演示间隔,500ms
demo_air_quality()
sys.wait(500) -- 演示间隔,500ms
demo_altitude_and_close()
sys.wait(500) -- 演示间隔,500ms
log.info("bme680_demo", "===== [演示完毕] =====")
log.info("bme680_demo", "End")
end
-- 启动演示任务协程
sys.taskInit(bme680_demo_task_func)
@@ -0,0 +1,70 @@
--[[
@module main
@summary BME680 四合一环境传感器 Demo 入口
@version 1.1
@date 2026.08.20
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_bme680 扩展库的完整功能,顺序为:
HELLO→[1/6]→[2/6]→[3/6]→[4/6]→[5/6]→[6/6]→End
1、初始化和数据读取([1/6]
2、IIR 滤波器切换演示([2/6])
3、过采样率切换演示([3/6]
4、气体加热器切换演示([4/6]
5、空气质量判断演示([5/6],含气体预热说明)
6、海拔高度测量与关闭([6/6]
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "BME680_Demo" -- 项目命名
VERSION = "001.999.000" -- 项目版本号
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 设置日志输出风格为样式2(建议调试时开启)
-- log.style(2)
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时/已使用/历史最高内存情况
-- 方便分析内存使用是否有异常
-- local function mem_check_func()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end
-- sys.timerLoopStart(mem_check_func, 3000)
-- 加载 bme680_demo.lua 演示模块(完整功能演示)
require "bme680_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run() 之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,282 @@
# BME680 四合一环境传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口
2. **bme680_demo.lua** - BME680 功能演示模块
### 1.2 扩展库模块
1. **exs_bme680** - BME680 扩展库,提供初始化、温度/气压/湿度/气体电阻读取、过采样率配置、IIR 滤波配置、气体加热器配置、海拔高度计算等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**BME680 使用标准 I2C 接口通信,软件 I2C 模式无需指定 I2C 控制器,任意 GPIO 均可模拟 I2C 时序,灵活性更高。本 demo 默认使用软件 I2C 模式演示,也可通过 `bme680_demo.lua` 中的 `MODE` 变量切换为硬件 I2C 模式。
## 二、演示流程介绍
本 demo 按顺序演示 exs_bme680 扩展库的 6 项功能:HELLO → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
### 2.1 功能演示项说明
1. **[1/6] 初始化与数据读取** - 初始化 BME680 传感器并读取温度(°C)、气压(hPa)、湿度(%RH)和气体电阻(Ω)数据,包含气体预热说明
2. **[2/6] IIR 滤波器切换演示** - 依次切换 filter=0~7,观察数据平滑效果
3. **[3/6] 过采样率切换演示** - 对比低功耗(1X)和高精度(16X)模式下的测量效果
4. **[4/6] 气体加热器切换演示** - 依次测试三种加热器温度/时间组合(250°C、320°C、350°C
5. **[5/6] 空气质量判断演示** - 演示如何判断空气好坏:先记录干净空气稳定基线 R0,实时电阻相对 R0 下降越多空气越差(污染度 = (1 - R/R0) × 100%
6. **[6/6] 海拔高度测量与关闭** - 通过气压值计算海拔高度后关闭传感器
> **关于气体传感器预热与空气好坏判断**(详见下文 5.4.5):
> - BME680 的 MOX 气体传感器上电后电阻会从低值(如几 kΩ)缓慢爬升至几十 kΩ,**新板首次上电需预热 20~30 分钟**电阻才稳定,这是正常现象(预热/烧机效应),稳定前读取的气体电阻没有参考意义
> - **不能直接看电阻绝对值判断空气好坏**,必须以干净空气中预热稳定后的电阻为基线 R0,实时电阻 R 相对 R0 **下降越多说明 VOC 浓度越高、空气越差**
> - 经验等级:污染度 ≤10% 优、10%~30% 良、30%~60% 中、>60% 差(阈值需结合场景标定)
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- BME680 四合一环境传感器模块 × 1
demo所演示的 BME680 环境传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=585977696909&mi_id=00005GuQmhs21YgvmlIoT24gzgF3ZneshBSDiRStMyOfDd0&ns=1&priceTId=213e00d417839209172374576e1068&spm=a21n57.1.hoverItem.5&utparam=%7B%22aplus_abtest%22%3A%22e1d0645c1ad2540833bb794c3c13d5e1%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 BME680 模块接线
| Air780EPM 核心板 | BME680 模块 |
|-----------------|------------|
| 32/GPIO31 | SCL |
| 31/GPIO30 | SDA |
| VDD_EXT | VCC |
| GND | GND |
> 说明:
> 1. 接线时注意杜邦线不宜过长,以免通信不稳定
> 2. SCL 和 SDA 需外接 4.7kΩ~10kΩ 上拉电阻到 VCC(部分模块已自带)
> 3. BME680 的 I2C 地址:SDO=GND 时 0x76SDO=VCC 时 0x77
![](https://docs.openluat.com/cdn/image/Air780EHM_BME680.png)
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 4.3 脚本文件
1. **main.lua** - 程序入口
2. **bme680_demo.lua** - BME680 功能演示模块
3. **exs_bme680** - BME680 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 BME680 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
require "bme680_demo"
```
如需切换通信模式,修改 `bme680_demo.lua` 中的 `MODE` 变量(1=软件 I2C2=硬件 I2C)。
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/6] 初始化与数据读取
2. 观察日志输出,确认初始化成功(打印校准数据、预热提示)
3. 观察温度(°C)、气压(hPa)、湿度(%RH)和气体电阻(kΩ)的连续读取
4. 注意:首次上电气体电阻可能为 0,MOX 气体传感器需预热 20~30 分钟才稳定(详见 5.4.5
#### 5.4.2 IIR 滤波器切换演示
1. 自动进入 [2/6] IIR 滤波器切换演示
2. 依次观察 filter=0(关闭)~ filter=7128 次平均)下的数据
3. filter 越大数据跳动越小
#### 5.4.3 过采样率切换演示
1. 自动进入 [3/6] 过采样率切换演示
2. 先切换到低功耗模式(1X),观察数据
3. 再切换到高精度模式(16X),对比数据精度变化
#### 5.4.4 气体加热器切换演示
1. 自动进入 [4/6] 气体加热器切换演示
2. 依次测试 250°C/100ms、320°C/150ms、350°C/200ms 三种参数
3. 观察不同温度下气体电阻值的变化
#### 5.4.5 空气质量判断演示
> 这是理解 BME680 气体传感器最关键的一步,演示**预热时长**和**如何判断空气好坏**。
1. 自动进入 [5/6] 空气质量判断演示
2. 日志会先打印预热说明:**新板首次上电 MOX 气体传感器需预热 20~30 分钟**,电阻从低值缓慢爬升属正常,稳定前气体电阻读值无参考意义
3. 记录当前读数作为基线 R0(真实使用时必须在干净空气中预热 20~30 分钟、电阻稳定后再记录 R0)
4. 连续读取 3 次,每次计算污染度 `(1 - R/R0) × 100%` 并输出空气等级
**如何判断空气好坏(核心)**
- 不能直接看电阻**绝对值**(不同芯片、不同温湿度下基线差异大)
- 在**干净空气**中预热 20~30 分钟后,记录稳定电阻为基线 R0
- 运行时实时电阻 R 相对 R0 **下降越多,说明 VOC 浓度越高、空气越差**
- 判断指标:`ratio = R/R0`(越接近 1 越干净),`污染度 = (1 - R/R0) × 100%`
- 经验等级(阈值需结合场景标定):污染度 ≤10% **优**、10%~30% **良**、30%~60% **中**、>60% **差**
```lua
-- 判断空气质量等级,R 为实时气体电阻,R0 为干净空气稳定基线
local function judge_air(R, R0)
if R0 <= 0 or R <= 0 then return "未就绪" end
local pollution = (1 - R / R0) * 100
if pollution <= 10 then return "" end
if pollution <= 30 then return "" end
if pollution <= 60 then return "" end
return ""
end
```
#### 5.4.6 海拔高度测量与关闭
1. 自动进入 [6/6] 海拔高度测量与关闭
2. 显示温度、气压、湿度、气体电阻和海拔高度
3. 关闭传感器后打印 "End"
### 5.5 预期效果
- **初始化与数据读取**:BME680 初始化成功,四项环境数据正常输出
- **IIR 滤波器切换**:不同滤波器下数据平滑度不同
- **过采样率切换**:高精度模式数据更稳定
- **气体加热器切换**:不同温度下气体电阻值有变化
- **海拔高度测量**:根据气压计算出当前海拔高度
- **日志如下:**
```lua
[2026-08-20 21:30:06.039][000000000.246] I/user.main BME680_Demo 001.999.000
[2026-08-20 21:30:06.044][000000000.263] D/user.exs_bme680 version -> 202608080000
[2026-08-20 21:30:06.048][000000000.265] I/user.bme680_demo HELLO
[2026-08-20 21:30:06.172][000000001.265] I/user.bme680_demo ===== [1/5] =====
[2026-08-20 21:30:06.176][000000001.271] I/user.exs_bme680 I2C总线恢复完成
[2026-08-20 21:30:06.181][000000001.334] I/user.exs_bme680 BME680 @ I2C 0x77, CHIP_ID=0x61
[2026-08-20 21:30:06.194][000000001.347] I/user.exs_bme680 raw_T1=25923 raw_P1=36006 raw_T2=26603 raw_P2=-10347
[2026-08-20 21:30:06.215][000000001.347] I/user.exs_bme680 : T1=25923 T2=26603 T3=3 P1=36006 P2=-10347 H1=667 H2=1031 GH1=-34 GH2=-6023 GH3=18
[2026-08-20 21:30:06.233][000000001.355] I/user.exs_bme680 BME680 , os_temp=4 os_pres=3 os_hum=2 filter=3 heater_temp=320°C heater_dur=150ms
[2026-08-20 21:30:06.241][000000001.355] I/user.exs_bme680 : res_heat_range=1 res_heat_val=40 range_sw_err=1 heatr_res=0x76
[2026-08-20 21:30:06.250][000000001.355] I/user.exs_bme680 : MOX 20~30
[2026-08-20 21:30:06.258][000000001.355] I/user.exs_bme680 gas_resistance 0
[2026-08-20 21:30:06.270][000000001.355] I/user.bme680_demo BME680 : 202608080000
[2026-08-20 21:30:06.390][000000002.048] I/user.bme680_demo =24.12°C =1008.40hPa 湿=66.6%RH =2.4
[2026-08-20 21:30:09.534][000000005.241] I/user.bme680_demo 1: =24.12°C =1008.4hPa 湿=66.4%RH =3.4
[2026-08-20 21:30:11.735][000000007.434] I/user.bme680_demo 2: =24.14°C =1008.4hPa 湿=66.3%RH =4.6
[2026-08-20 21:30:13.922][000000009.627] I/user.bme680_demo 3: =24.16°C =1008.4hPa 湿=66.1%RH =5.9
[2026-08-20 21:30:13.929][000000009.628] I/user.bme680_demo ---- [1/5] 完成 ----
[2026-08-20 21:30:14.421][000000010.128] I/user.bme680_demo ===== [2/5] IIR =====
[2026-08-20 21:30:14.427][000000010.128] I/user.bme680_demo filter=0
[2026-08-20 21:30:14.437][000000010.136] I/user.exs_bme680 IIR 0 (0)
[2026-08-20 21:30:14.716][000000010.428] I/user.bme680_demo filter=0: =24.58°C =1008.42hPa 湿=66.1%RH
[2026-08-20 21:30:15.723][000000011.429] I/user.bme680_demo filter=1
[2026-08-20 21:30:15.729][000000011.436] I/user.exs_bme680 IIR 1 (1)
[2026-08-20 21:30:16.019][000000011.729] I/user.bme680_demo filter=1: =24.45°C =1008.44hPa 湿=65.8%RH
[2026-08-20 21:30:17.026][000000012.730] I/user.bme680_demo filter=2
[2026-08-20 21:30:17.033][000000012.737] I/user.exs_bme680 IIR 2 (3)
[2026-08-20 21:30:17.322][000000013.030] I/user.bme680_demo filter=2: =24.45°C =1008.40hPa 湿=65.5%RH
[2026-08-20 21:30:18.330][000000014.031] I/user.bme680_demo filter=3
[2026-08-20 21:30:18.336][000000014.038] I/user.exs_bme680 IIR 3 (7)
[2026-08-20 21:30:18.628][000000014.331] I/user.bme680_demo filter=3: =24.43°C =1008.40hPa 湿=65.3%RH
[2026-08-20 21:30:19.620][000000015.332] I/user.bme680_demo filter=4
[2026-08-20 21:30:19.634][000000015.339] I/user.exs_bme680 IIR 4 (15)
[2026-08-20 21:30:19.930][000000015.632] I/user.bme680_demo filter=4: =24.42°C =1008.36hPa 湿=65.0%RH
[2026-08-20 21:30:20.921][000000016.633] I/user.bme680_demo filter=5
[2026-08-20 21:30:20.937][000000016.640] I/user.exs_bme680 IIR 5 (31)
[2026-08-20 21:30:21.231][000000016.933] I/user.bme680_demo filter=5: =24.43°C =1008.40hPa 湿=64.9%RH
[2026-08-20 21:30:22.234][000000017.934] I/user.bme680_demo filter=6
[2026-08-20 21:30:22.240][000000017.941] I/user.exs_bme680 IIR 6 (63)
[2026-08-20 21:30:22.527][000000018.234] I/user.bme680_demo filter=6: =24.44°C =1008.42hPa 湿=64.7%RH
[2026-08-20 21:30:23.534][000000019.235] I/user.bme680_demo filter=7
[2026-08-20 21:30:23.540][000000019.242] I/user.exs_bme680 IIR 7 (127)
[2026-08-20 21:30:23.829][000000019.535] I/user.bme680_demo filter=7: =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:24.831][000000020.536] I/user.bme680_demo ---- [2/5] 完成 ----
[2026-08-20 21:30:25.326][000000021.036] I/user.bme680_demo ===== [3/5] =====
[2026-08-20 21:30:25.331][000000021.036] I/user.bme680_demo 1X过采样
[2026-08-20 21:30:25.342][000000021.043] I/user.exs_bme680 1
[2026-08-20 21:30:25.349][000000021.051] I/user.exs_bme680 1
[2026-08-20 21:30:25.357][000000021.058] I/user.exs_bme680 湿 1
[2026-08-20 21:30:25.621][000000021.326] I/user.bme680_demo : =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:26.621][000000022.327] I/user.bme680_demo 16X过采样
[2026-08-20 21:30:26.626][000000022.334] I/user.exs_bme680 5
[2026-08-20 21:30:26.636][000000022.342] I/user.exs_bme680 5
[2026-08-20 21:30:26.642][000000022.349] I/user.exs_bme680 湿 5
[2026-08-20 21:30:26.993][000000022.705] I/user.bme680_demo : =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:26.998][000000022.706] I/user.bme680_demo ---- [3/5] 完成 ----
[2026-08-20 21:30:27.504][000000023.206] I/user.bme680_demo ===== [4/5] =====
[2026-08-20 21:30:27.512][000000023.206] I/user.bme680_demo 250°C/100ms
[2026-08-20 21:30:27.518][000000023.214] I/user.exs_bme680 250°C, 100ms
[2026-08-20 21:30:27.919][000000023.620] I/user.bme680_demo =32.2 =24.46°C 湿=64.7%RH
[2026-08-20 21:30:28.909][000000024.621] I/user.bme680_demo 320°C/150ms
[2026-08-20 21:30:28.925][000000024.628] I/user.exs_bme680 320°C, 150ms
[2026-08-20 21:30:29.374][000000025.085] I/user.bme680_demo =16.6 =24.46°C 湿=64.6%RH
[2026-08-20 21:30:30.382][000000026.086] I/user.bme680_demo 350°C/200ms
[2026-08-20 21:30:30.389][000000026.093] I/user.exs_bme680 350°C, 200ms
[2026-08-20 21:30:30.894][000000026.600] I/user.bme680_demo =14.4 =24.46°C 湿=64.6%RH
[2026-08-20 21:30:31.888][000000027.601] I/user.bme680_demo ---- [4/5] 完成 ----
[2026-08-20 21:30:32.387][000000028.101] I/user.bme680_demo ===== [5/5] =====
[2026-08-20 21:30:32.700][000000028.409] I/user.bme680_demo =24.46°C =1008.4hPa 湿=64.6%RH =16.8 =40.5
[2026-08-20 21:30:32.706][000000028.409] I/user.exs_bme680
[2026-08-20 21:30:32.712][000000028.410] I/user.bme680_demo ---- [5/5] 完成 ----
[2026-08-20 21:30:33.210][000000028.910] I/user.bme680_demo ===== [] =====
[2026-08-20 21:30:33.215][000000028.910] I/user.bme680_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 BME680 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 SCL/SDA 上拉电阻是否连接
- 确认 exs_bme680 扩展库已正常加载
2. **读取数据异常**
- 检查传感器供电是否正常
- 自动地址检测会尝试 0x76 和 0x77,确认 SDO 引脚电平
- 检查接线无松动
3. **气体电阻一直为 0**
- 新板首次上电需要预热 20~30 分钟,MOX 加热器稳定前 gas_resistance 返回 0,属正常现象(详见 5.4.5)
- 预热期间电阻从低值缓慢爬升属正常,待电阻稳定后再记录基线 R0 判断空气质量
- 确认加热器温度和时间设置合理(默认 320°C/150ms),加热器未被禁用
- 若长时间预热后仍为 0,检查供电是否稳定、`set_gas_heater` 参数是否合法
4. **海拔高度显示异常**
- 海拔计算基于海平面标准气压 1013.25hPa,天气变化会影响计算精度
- 可通过 `exs_bme680.set_sea_level_pressure(pressure)` 传入实际海平面气压校正
### 5.7 扩展功能建议
exs_bme680 更多接口的使用可以查看 [exs_bme680 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_bme680/)
@@ -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/)
@@ -0,0 +1,111 @@
--[[
@module bmp180_demo
@summary BMP180 数字气压传感器演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.23
@author 江访
@usage
本文件包含 BMP180 的逐项功能演示。
通过 exs_bmp180 扩展库的 API 逐一演示数据读取、过采样率切换、海拔高度计算、关闭传感器等功能。
]]
local exs_bmp180 = require "exs_bmp180"
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("bmp180_demo", "===== [1/4] 初始化与数据读取 =====")
local result = exs_bmp180.setup({
scl = 31,
sda = 30,
})
if not result then
log.error("bmp180_demo", "BMP180 初始化失败,请检查接线")
return false
end
log.info("bmp180_demo", "BMP180 初始化成功,版本:", exs_bmp180.version())
-- 等待传感器稳定,数据手册推荐值
sys.wait(200)
local data = exs_bmp180.get_data()
if data then
log.info("bmp180_demo", string.format("温度=%.1f°C 气压=%.2fhPa", data.temperature, data.pressure / 100))
else
log.error("bmp180_demo", "读取数据失败")
end
sys.wait(1000) -- 稳定后等待 1 秒进入连续读取
for i = 1, 3 do
sys.wait(2000) -- 每次读取间隔 2 秒
local d = exs_bmp180.get_data()
if d then
log.info("bmp180_demo", string.format("第%d次读取: 温度=%.1f°C 气压=%.1fPa", i, d.temperature, d.pressure))
end
end
log.info("bmp180_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] 过采样率切换演示
local function demo_oss_switch()
log.info("bmp180_demo", "===== [2/4] 过采样率切换演示 =====")
local oss_list = { 0, 1, 2, 3 }
for _, oss in ipairs(oss_list) do
log.info("bmp180_demo", string.format("设置过采样率为 oss=%d", oss))
exs_bmp180.set_oss(oss)
sys.wait(100) -- 等待 OSS 切换生效
local data = exs_bmp180.get_data()
if data then
log.info("bmp180_demo", string.format("oss=%d: 温度=%.1f°C 气压=%.2fhPa", oss, data.temperature, data.pressure / 100))
end
sys.wait(1000) -- 每档 OSS 显示间隔 1 秒
end
log.info("bmp180_demo", "---- [2/4] 完成 ----")
end
-- [3/4] 海拔高度测量
local function demo_altitude()
log.info("bmp180_demo", "===== [3/4] 海拔高度测量 =====")
local data = exs_bmp180.get_data()
if data then
local alt = exs_bmp180.get_altitude(data.pressure)
log.info("bmp180_demo", string.format("温度=%.1f°C 气压=%.1fPa 海拔=%.1f米", data.temperature, data.pressure, alt))
end
log.info("bmp180_demo", "---- [3/4] 完成 ----")
end
-- [4/4] 关闭传感器
local function demo_close()
log.info("bmp180_demo", "===== [4/4] 演示完毕 =====")
log.info("bmp180_demo", "---- [4/4] 完成 ----")
end
local function bmp180_demo_task_func()
log.info("bmp180_demo", "HELLO")
sys.wait(1000) -- 启动后等待 1 秒再开始演示
local ok = demo_init_and_read()
if not ok then
log.error("bmp180_demo", "初始化失败,演示终止")
return
end
sys.wait(500) -- 每项演示间隔 500ms
demo_oss_switch()
sys.wait(500) -- 每项演示间隔 500ms
demo_altitude()
sys.wait(500) -- 每项演示间隔 500ms
demo_close()
sys.wait(500) -- 关闭后等待 500ms 再结束
log.info("bmp180_demo", "===== [演示完毕] =====")
log.info("bmp180_demo", "End")
end
sys.taskInit(bmp180_demo_task_func)
@@ -0,0 +1,70 @@
--[[
@module main
@summary BMP180 数字气压传感器 Demo 入口
@version 1.0
@date 2026.07.23
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_bmp180 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化和数据读取([1/4]
2、过采样率切换演示([2/4]
3、海拔高度测量([3/4]
4、关闭传感器([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "BMP180_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)
-- 加载 bmp180_demo.lua 演示模块
require "bmp180_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,194 @@
# BMP180 数字气压传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **bmp180_demo.lua** - BMP180 功能演示模块,包含数据读取、过采样率切换、海拔高度计算等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_bmp180** - BMP180 扩展库,提供初始化、气压/温度读取、过采样率切换、海拔高度计算、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**BMP180 使用标准 I2C 接口通信,软件 I2C 模式无需指定 I2C 控制器,任意 GPIO 均可模拟 I2C 时序,灵活性更高。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_bmp180 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 BMP180 传感器并读取温度(°C)和气压(Pa/hPa)数据
2. **[2/4] 过采样率切换演示** - 依次切换 OSS=0/1/2/3,观察不同精度下的温度与气压数据
3. **[3/4] 海拔高度测量** - 通过气压值计算当前海拔高度(米)
4. **[4/4] 关闭传感器** - 演示正确的传感器关闭流程
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- BMP180 数字气压传感器模块 × 1
demo所演示的BMP180 气压传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=944404504388&mi_id=0000ZuOrQ4eZ6pWb2BYc1iPpBMJ6TrMasd1C9m_mvfsV1tk&ns=1&priceTId=213e00d417839207947873080e1068&skuId=5844683444104&spm=a21n57.1.hoverItem.2&utparam=%7B%22aplus_abtest%22%3A%224cad96d9d94a499da42340cb4d160b98%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 BMP180 模块接线
![](https://docs.openluat.com/cdn/image/Air780EHM_exs_bmp180.png)
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>BMP180 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<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>
</table>
> 说明:
> 1. 接线时注意杜邦线不宜过长,以免通信不稳定
> 2. SCL 和 SDA 需外接 4.7kΩ~10kΩ 上拉电阻到 VCC(部分模块已自带)
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 4.3 脚本文件
1. **main.lua** - 程序入口
2. **bmp180_demo.lua** - BMP180 功能演示模块
3. **exs_bmp180** - BMP180 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 BMP180 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 bmp180_demo.lua 演示模块
require "bmp180_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察温度(°C)和气压(Pa/hPa)的连续读取
#### 5.4.2 过采样率切换演示
1. 自动进入 [2/4] 过采样率切换演示
2. 依次观察 OSS=0(最快)、OSS=1、OSS=2、OSS=3(最高精度)下的数据
3. 注意 OSS 越高,数据跳动越小,但转换时间越长
#### 5.4.3 海拔高度测量
1. 自动进入 [3/4] 海拔高度测量
2. 观察基于当前气压值和海平面标准气压(101325Pa)计算的海拔高度
#### 5.4.4 关闭传感器
1. 自动进入 [4/4] 关闭传感器
2. 演示完毕后打印 "End"
### 5.5 预期效果
- **初始化与数据读取**:BMP180 初始化成功,温度与气压数据正常输出
- **过采样率切换**:不同 OSS 下精度不同,OSS 越高数据越稳定
- **海拔高度测量**:根据气压计算出当前海拔高度
- **关闭传感器**:传感器正常关闭
- **日志如下:**
```lua
[2026-07-23 15:30:32.854][000000000.247] I/user.main BMP180_Demo 001.000.000
[2026-07-23 15:30:32.868][000000000.259] D/user.exs_bmp180 version -> 202607220900
[2026-07-23 15:30:32.882][000000000.260] I/user.bmp180_demo HELLO
[2026-07-23 15:30:33.032][000000001.261] I/user.bmp180_demo ===== [1/4] =====
[2026-07-23 15:30:33.042][000000001.269] I/user.exs_bmp180 BMP180 @ I2C 0x77, CHIP_ID=0x55
[2026-07-23 15:30:33.055][000000001.286] I/user.exs_bmp180 : AC1=6464 AC2=-1308 AC3=-14829 AC4=33096 AC5=24574 AC6=21778
[2026-07-23 15:30:33.069][000000001.286] I/user.exs_bmp180 , oss=0
[2026-07-23 15:30:33.081][000000001.286] I/user.bmp180_demo BMP180 : 202607220900
[2026-07-23 15:30:33.092][000000001.502] I/user.bmp180_demo =24.1°C =1004.56hPa
[2026-07-23 15:30:35.560][000000004.517] I/user.bmp180_demo 1: =24.1°C =100450.0Pa
[2026-07-23 15:30:37.582][000000006.533] I/user.bmp180_demo 2: =24.1°C =100450.0Pa
[2026-07-23 15:30:39.588][000000008.548] I/user.bmp180_demo 3: =24.1°C =100453.0Pa
[2026-07-23 15:30:39.605][000000008.548] I/user.bmp180_demo ---- [1/4] 完成 ----
[2026-07-23 15:30:40.099][000000009.049] I/user.bmp180_demo ===== [2/4] =====
[2026-07-23 15:30:40.121][000000009.049] I/user.bmp180_demo oss=0
[2026-07-23 15:30:40.136][000000009.050] I/user.exs_bmp180 oss=0
[2026-07-23 15:30:40.208][000000009.165] I/user.bmp180_demo oss=0: =24.1°C =1004.59hPa
[2026-07-23 15:30:41.203][000000010.165] I/user.bmp180_demo oss=1
[2026-07-23 15:30:41.216][000000010.165] I/user.exs_bmp180 oss=1
[2026-07-23 15:30:41.328][000000010.284] I/user.bmp180_demo oss=1: =24.1°C =1004.57hPa
[2026-07-23 15:30:42.320][000000011.284] I/user.bmp180_demo oss=2
[2026-07-23 15:30:42.336][000000011.284] I/user.exs_bmp180 oss=2
[2026-07-23 15:30:42.444][000000011.409] I/user.bmp180_demo oss=2: =24.1°C =1004.56hPa
[2026-07-23 15:30:43.456][000000012.409] I/user.bmp180_demo oss=3
[2026-07-23 15:30:43.469][000000012.409] I/user.exs_bmp180 oss=3
[2026-07-23 15:30:43.595][000000012.546] I/user.bmp180_demo oss=3: =24.1°C =1004.60hPa
[2026-07-23 15:30:44.589][000000013.546] I/user.bmp180_demo ---- [2/4] 完成 ----
[2026-07-23 15:30:45.086][000000014.046] I/user.bmp180_demo ===== [3/4] =====
[2026-07-23 15:30:45.118][000000014.082] I/user.bmp180_demo =24.1°C =100457.0Pa =72.5
[2026-07-23 15:30:45.135][000000014.082] I/user.bmp180_demo ---- [3/4] 完成 ----
[2026-07-23 15:30:45.631][000000014.583] I/user.bmp180_demo ===== [4/4] =====
[2026-07-23 15:30:45.646][000000014.583] I/user.bmp180_demo ---- [4/4] 完成 ----
[2026-07-23 15:30:46.126][000000015.083] I/user.bmp180_demo ===== [] =====
[2026-07-23 15:30:46.139][000000015.083] I/user.bmp180_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 BMP180 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 SCL/SDA 上拉电阻是否连接
- 确认 exs_bmp180 扩展库已正常加载
2. **读取数据始终为零或异常**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(BMP180 固定地址 0x77
- 确认接线无松动
- 检查 SCL/SDA 上拉电阻
3. **海拔高度显示异常**
- 海拔计算基于海平面标准气压 101325Pa,天气变化会影响计算精度
- 可通过 `get_altitude(pressure, sea_level_pressure)` 传入实际海平面气压校正
### 5.7 扩展功能建议
exs_bmp180 更多接口的使用可以查看 [exs_bmp180 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_bmp180/)
@@ -0,0 +1,121 @@
--[[
@module bmp280_demo
@summary BMP280 数字气压传感器演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.24
@author 江访
@usage
本文件包含 BMP280 的逐项功能演示。
通过 exs_bmx280 扩展库的 API 逐一演示数据读取、IIR 滤波切换、海拔计算等功能。
Air780EPM 接线(软件 I2C):
SCL = GPIO31
SDA = GPIO30
]]
local exs_bmx280 = require "exs_bmx280"
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("bmp280_demo", "===== [1/4] 初始化与数据读取 =====")
-- BMP280 使用软件 I2C 模式初始化,开启 IIR 滤波
-- 注意:BMP180 返回气压单位为 Pa,BMP280 返回气压单位为 hPa,使用时不需换算
local result = exs_bmx280.setup("I2C", {
scl = 31,
sda = 30,
filter = 4, -- 16 次采样平均,数据更平滑
})
if not result then
log.error("bmp280_demo", "BMP280 初始化失败,请检查接线")
return false
end
log.info("bmp280_demo", "BMP280 初始化成功,版本:", exs_bmx280.version())
-- 等待传感器稳定,数据手册推荐 200ms
sys.wait(200)
local data = exs_bmx280.get_data()
if data then
log.info("bmp280_demo", string.format("温度=%.2f°C 气压=%.2fhPa", data.temperature, data.pressure))
else
log.error("bmp280_demo", "读取数据失败")
end
sys.wait(1000) -- 稳定后等待 1 秒进入连续读取
-- 连续读取 3 次,观察数据变化
for i = 1, 3 do
sys.wait(2000) -- 每次读取间隔 2 秒
local d = exs_bmx280.get_data()
if d then
log.info("bmp280_demo", string.format("第%d次读取: 温度=%.2f°C 气压=%.1fhPa", i, d.temperature, d.pressure))
end
end
log.info("bmp280_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] IIR 滤波器切换演示
local function demo_filter_switch()
log.info("bmp280_demo", "===== [2/4] IIR 滤波器切换演示 =====")
-- 依次切换 filter=0/1/2/3/4,观察数据变化
local filter_list = { 0, 1, 2, 3, 4 }
for _, f in ipairs(filter_list) do
log.info("bmp280_demo", string.format("设置滤波器 filter=%d", f))
exs_bmx280.set_filter(f)
sys.wait(100) -- 等待滤波器生效
local data = exs_bmx280.get_data()
if data then
log.info("bmp280_demo", string.format("filter=%d: 温度=%.2f°C 气压=%.2fhPa", f, data.temperature, data.pressure))
end
sys.wait(1000) -- 每档滤波器显示间隔 1 秒
end
log.info("bmp280_demo", "---- [2/4] 完成 ----")
end
-- [3/4] 海拔高度测量
local function demo_altitude()
log.info("bmp280_demo", "===== [3/4] 海拔高度测量 =====")
local data = exs_bmx280.get_data()
if data then
local alt = exs_bmx280.get_altitude(data.pressure)
log.info("bmp280_demo", string.format("温度=%.2f°C 气压=%.1fhPa 海拔=%.1f米", data.temperature, data.pressure, alt))
end
log.info("bmp280_demo", "---- [3/4] 完成 ----")
end
-- [4/4] 关闭传感器
local function demo_close()
log.info("bmp280_demo", "===== [4/4] 关闭传感器 =====")
exs_bmx280.close()
log.info("bmp280_demo", "---- [4/4] 完成 ----")
end
local function bmp280_demo_task_func()
log.info("bmp280_demo", "HELLO")
sys.wait(1000) -- 启动后等待 1 秒再开始演示
local ok = demo_init_and_read()
if not ok then
log.error("bmp280_demo", "初始化失败,演示终止")
return
end
sys.wait(500) -- 每项演示间隔 500ms
demo_filter_switch()
sys.wait(500) -- 每项演示间隔 500ms
demo_altitude()
sys.wait(500) -- 每项演示间隔 500ms
demo_close()
sys.wait(500) -- 关闭后等待 500ms 再结束
log.info("bmp280_demo", "===== [演示完毕] =====")
log.info("bmp280_demo", "End")
end
sys.taskInit(bmp280_demo_task_func)
@@ -0,0 +1,70 @@
--[[
@module main
@summary BMP280 数字气压传感器 Demo 入口
@version 1.0
@date 2026.07.24
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_bmx280 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化和数据读取([1/4]
2、IIR 滤波器切换演示([2/4])
3、海拔高度测量([3/4]
4、关闭传感器([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "BMP280_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)
-- 加载 bmx280_demo.lua 演示模块
require "bmx280_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,208 @@
# BMP280 数字气压传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口
2. **bmp280_demo.lua** - BMP280 功能演示模块
### 1.2 扩展库模块
1. **exs_bmx280** - BMP280/BME280 扩展库,提供初始化、气压/温度/湿度读取、IIR 滤波配置、海拔高度计算等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**BMP280 使用标准 I2C 接口通信,软件 I2C 模式无需指定 I2C 控制器,任意 GPIO 均可模拟 I2C 时序,灵活性更高。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_bmx280 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 BMP280 传感器并读取温度(°C)和气压(hPa)数据
2. **[2/4] IIR 滤波器切换演示** - 依次切换 filter=0/1/2/3/4,观察数据平滑效果
3. **[3/4] 海拔高度测量** - 通过气压值计算当前海拔高度(米)
4. **[4/4] 关闭传感器** - 关闭传感器,结束演示
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- BMP280 数字气压传感器模块 × 1
demo所演示的BMP280 气压传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=536588151627&mi_id=0000rIYIyPLv_DpmZ6ZcJUWP9s0vMMalxqy2nPnWDpHQZ48&ns=1&spm=a21n57.1.hoverItem.3&utparam=%7B%22aplus_abtest%22%3A%2296002c296ee298a12225edacf84fa11b%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 BMP280 模块接线
![](https://docs.openluat.com/cdn/image/Air780EHM_exs_bmp280.png)
| Air780EPM 核心板 | BMP280 模块 |
|-----------------|------------|
| 32/GPIO31 | SCL |
| 31/GPIO30 | SDA |
| VDD_EXT | VCC |
| GND | GND |
> 说明:
> 1. 接线时注意杜邦线不宜过长,以免通信不稳定
> 2. SCL 和 SDA 需外接 4.7kΩ~10kΩ 上拉电阻到 VCC(部分模块已自带)
> 3. BMP280 的 I2C 地址:SDO=GND 时 0x76SDO=VCC 时 0x77
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 4.3 脚本文件
1. **main.lua** - 程序入口
2. **bmp280_demo.lua** - BMP280 功能演示模块
3. **exs_bmx280** - BMP280/BME280 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 BMP280 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
require "bmp280_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察温度(°C)和气压(hPa)的连续读取
#### 5.4.2 IIR 滤波器切换演示
1. 自动进入 [2/4] IIR 滤波器切换演示
2. 依次观察 filter=0(关闭)~ filter=416 次平均)下的数据
3. filter 越大数据跳动越小
#### 5.4.3 海拔高度测量
1. 自动进入 [3/4] 海拔高度测量
2. 观察基于当前气压值和海平面标准气压(101325Pa)计算的海拔高度
#### 5.4.4 关闭传感器
1. 自动进入 [4/4] 关闭传感器
2. 演示完毕后打印 "End"
### 5.5 预期效果
- **初始化与数据读取**:BMP280 初始化成功,温度与气压数据正常输出
- **IIR 滤波器切换**:不同滤波器下数据平滑度不同
- **海拔高度测量**:根据气压计算出当前海拔高度
- **关闭传感器**:传感器正常关闭
- **日志如下:**
```lua
[2026-07-24 19:59:39.006][000000000.213] D/user.exs_bmx280 version -> 202607240000
[2026-07-24 19:59:39.028][000000000.214] I/user.bmp280_demo HELLO
[2026-07-24 19:59:39.586][000000001.214] I/user.bmp280_demo ===== [1/4] =====
[2026-07-24 19:59:39.605][000000001.224] I/user.exs_bmx280 BMP280 @ I2C 0x76
[2026-07-24 19:59:39.613][000000001.262] I/user.exs_bmx280 : T1=27251 T2=25882 T3=50 P1=39170 P2=-10689
[2026-07-24 19:59:39.622][000000001.264] I/user.exs_bmx280 BMP280 , filter=4
[2026-07-24 19:59:39.630][000000001.265] I/user.bmp280_demo BMP280 : 202607240000
[2026-07-24 19:59:39.640][000000001.469] I/user.bmp280_demo =25.55°C =1008.00hPa
[2026-07-24 19:59:42.440][000000004.473] I/user.bmp280_demo 1: =26.02°C =1008.0hPa
[2026-07-24 19:59:43.789][000000005.748] I/mobile sim0 sms ready
[2026-07-24 19:59:43.809][000000005.749] D/mobile cid1, state0
[2026-07-24 19:59:43.822][000000005.749] D/mobile bearer act 0, result 0
[2026-07-24 19:59:43.837][000000005.750] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-24 19:59:43.852][000000005.786] D/mobile TIME_SYNC 0 tm 1784894382
[2026-07-24 19:59:44.448][000000006.477] I/user.bmp280_demo 2: =26.06°C =1008.0hPa
[2026-07-24 19:59:46.450][000000008.481] I/user.bmp280_demo 3: =26.08°C =1008.0hPa
[2026-07-24 19:59:46.476][000000008.481] I/user.bmp280_demo ---- [1/4] 完成 ----
[2026-07-24 19:59:46.949][000000008.982] I/user.bmp280_demo ===== [2/4] IIR =====
[2026-07-24 19:59:46.967][000000008.983] I/user.bmp280_demo filter=0
[2026-07-24 19:59:46.983][000000008.986] I/user.exs_bmx280 IIR 0
[2026-07-24 19:59:47.059][000000009.090] I/user.bmp280_demo filter=0: =26.09°C =1008.00hPa
[2026-07-24 19:59:48.071][000000010.090] I/user.bmp280_demo filter=1
[2026-07-24 19:59:48.091][000000010.093] I/user.exs_bmx280 IIR 1
[2026-07-24 19:59:48.164][000000010.197] I/user.bmp280_demo filter=1: =26.10°C =1008.00hPa
[2026-07-24 19:59:49.175][000000011.197] I/user.bmp280_demo filter=2
[2026-07-24 19:59:49.196][000000011.200] I/user.exs_bmx280 IIR 2
[2026-07-24 19:59:49.284][000000011.305] I/user.bmp280_demo filter=2: =26.11°C =1008.00hPa
[2026-07-24 19:59:50.281][000000012.305] I/user.bmp280_demo filter=3
[2026-07-24 19:59:50.301][000000012.308] I/user.exs_bmx280 IIR 3
[2026-07-24 19:59:50.390][000000012.412] I/user.bmp280_demo filter=3: =26.12°C =1008.00hPa
[2026-07-24 19:59:51.387][000000013.412] I/user.bmp280_demo filter=4
[2026-07-24 19:59:51.409][000000013.415] I/user.exs_bmx280 IIR 4
[2026-07-24 19:59:51.497][000000013.520] I/user.bmp280_demo filter=4: =26.13°C =1008.00hPa
[2026-07-24 19:59:52.493][000000014.520] I/user.bmp280_demo ---- [2/4] 完成 ----
[2026-07-24 19:59:52.992][000000015.020] I/user.bmp280_demo ===== [3/4] =====
[2026-07-24 19:59:53.011][000000015.024] I/user.bmp280_demo =26.14°C =1008.0hPa =43.8
[2026-07-24 19:59:53.025][000000015.025] I/user.bmp280_demo ---- [3/4] 完成 ----
[2026-07-24 19:59:53.492][000000015.525] I/user.bmp280_demo ===== [4/4] =====
[2026-07-24 19:59:53.511][000000015.527] I/user.exs_bmx280
[2026-07-24 19:59:53.528][000000015.527] I/user.bmp280_demo ---- [4/4] 完成 ----
[2026-07-24 19:59:54.003][000000016.027] I/user.bmp280_demo ===== [] =====
[2026-07-24 19:59:54.025][000000016.027] I/user.bmp280_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 BMP280 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 SCL/SDA 上拉电阻是否连接
- 确认 exs_bmx280 扩展库已正常加载
2. **读取数据异常**
- 检查传感器供电是否正常
- 自动地址检测会尝试 0x76 和 0x77,确认 SDO 引脚电平
- 检查接线无松动
3. **海拔高度显示异常**
- 海拔计算基于海平面标准气压 101325Pa,天气变化会影响计算精度
- 可通过 `exs_bmx280.set_sea_level_pressure(pressure)` 传入实际海平面气压校正
### 5.7 扩展功能建议
exs_bmx280 更多接口的使用可以查看 [exs_bmx280 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_bmx280/)
## 六、BMP180 与 BMP280 对比
| 特性 | BMP180 | BMP280 |
|------|--------|--------|
| 扩展库 | exs_bmp180 | exs_bmx280 |
| 初始化方式 | `exs_bmp180.setup({scl, sda})` | `exs_bmx280.setup("I2C", {scl, sda, ...})` |
| 气压单位 | Pa(帕斯卡) | hPa(百帕斯卡) |
| 温度精度 | 0.1°C | 0.01°C |
| IIR 滤波 | 不支持(过采样 OSS 方式) | 支持 filter=0~4 |
| 工作电压 | 1.8V~3.6V | 1.71V~3.6V |
| 气压范围 | 300~1100hPa | 300~1100hPa |
| 湿度测量 | 不支持 | BME280 型号支持 |
@@ -0,0 +1,152 @@
--[[
@module lis2dh12_demo
@summary LIS2DH12 三轴加速度传感器演示模块,包含所有功能的演示用例
@version 2.0
@date 2026.07.17
@author 江访
@usage
本文件包含 LIS2DH12 的逐项功能演示。
通过 exs_lis2dh12 扩展库的 API 逐一演示数据读取、量程切换、输出速率切换、温度读取、休眠唤醒等功能。
]]
local exs_lis2dh12 = require "exs_lis2dh12"
local function lis2dh12_int1_cb(data)
if data.dir then
log.info("lis2dh12_demo", string.format("int1: 朝向=%s X=%.3f Y=%.3f Z=%.3f g", data.dir, data.x, data.y, data.z))
else
log.info("lis2dh12_demo", string.format("int1: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
end
-- [1/5] 初始化与数据读取
local function demo_init_and_read()
log.info("lis2dh12_demo", "===== [1/5] 初始化与数据读取 =====")
local result = exs_lis2dh12.setup("I2C", {
scl = 31,
sda = 30,
powermode = "highres",
enable_direction = "6d",
int1 = { int_gpio = 29, tap = true, direction = true, cb = lis2dh12_int1_cb },
})
if not result then
log.error("lis2dh12_demo", "LIS2DH12 初始化失败,请检查接线")
return false
end
log.info("lis2dh12_demo", "LIS2DH12 初始化成功,版本:", exs_lis2dh12.version())
exs_lis2dh12.dump_regs()
sys.wait(200)
local data = exs_lis2dh12.get_data()
if data then
log.info("lis2dh12_demo", string.format("X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
else
log.error("lis2dh12_demo", "读取数据失败")
end
sys.wait(1000)
for i = 1, 3 do
sys.wait(500)
local d = exs_lis2dh12.get_data()
if d then
log.info("lis2dh12_demo", string.format("第%d次读取: X=%.3f Y=%.3f Z=%.3f g", i, d.x, d.y, d.z))
end
end
log.info("lis2dh12_demo", "---- [1/5] 完成 ----")
return true
end
-- [2/5] 量程切换演示
local function demo_range_switch()
log.info("lis2dh12_demo", "===== [2/5] 量程切换演示 =====")
log.info("lis2dh12_demo", "切换量程为 4g")
exs_lis2dh12.set_range("4g")
sys.wait(300)
local data = exs_lis2dh12.get_data()
if data then
log.info("lis2dh12_demo", string.format("4g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("lis2dh12_demo", "切换量程为 2g(高精度)")
exs_lis2dh12.set_range("2g")
sys.wait(300)
data = exs_lis2dh12.get_data()
if data then
log.info("lis2dh12_demo", string.format("2g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("lis2dh12_demo", "---- [2/5] 完成 ----")
end
-- [3/5] 输出速率切换
local function demo_odr_switch()
log.info("lis2dh12_demo", "===== [3/5] 输出速率切换 =====")
local odr_list = { 25, 50, 100, 200 }
for _, odr in ipairs(odr_list) do
log.info("lis2dh12_demo", string.format("设置输出速率为 %dHz", odr))
exs_lis2dh12.set_odr(odr)
sys.wait(500)
local data = exs_lis2dh12.get_data()
if data then
log.info("lis2dh12_demo", string.format("%dHz 下: X=%.3f Y=%.3f Z=%.3f g", odr, data.x, data.y, data.z))
end
sys.wait(500)
end
exs_lis2dh12.set_odr(100)
log.info("lis2dh12_demo", "---- [3/5] 完成 ----")
end
-- [4/5] 温度读取演示
local function demo_temp_read()
log.info("lis2dh12_demo", "===== [4/5] 温度读取演示 =====")
log.info("lis2dh12_demo", "使能温度传感器")
exs_lis2dh12.enable_temp(true)
sys.wait(200)
local temp = exs_lis2dh12.get_temp()
if temp then
log.info("lis2dh12_demo", string.format("芯片温度: %.1f °C", temp))
else
log.error("lis2dh12_demo", "读取温度失败")
end
exs_lis2dh12.enable_temp(false)
log.info("lis2dh12_demo", "---- [4/5] 完成 ----")
end
-- [5/5] 休眠与唤醒演示
local function demo_sleep_wakeup()
log.info("lis2dh12_demo", "===== [5/5] 休眠与唤醒演示 =====")
log.info("lis2dh12_demo", "进入 power-down 模式(低功耗)")
exs_lis2dh12.sleep(); sys.wait(10000)
sys.wait(500)
log.info("lis2dh12_demo", "从 power-down 模式唤醒")
exs_lis2dh12.wakeup()
sys.wait(200)
local data = exs_lis2dh12.get_data()
if data then
log.info("lis2dh12_demo", string.format("唤醒后数据: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
log.info("lis2dh12_demo", "---- [5/5] 完成 ----")
end
local function lis2dh12_demo_task_func()
log.info("lis2dh12_demo", "HELLO")
sys.wait(1000)
local ok = demo_init_and_read()
if not ok then return end
sys.wait(500)
demo_range_switch()
sys.wait(500)
demo_odr_switch()
sys.wait(500)
demo_temp_read()
sys.wait(500)
demo_sleep_wakeup()
sys.wait(500)
log.info("lis2dh12_demo", "===== [演示完毕] =====")
exs_lis2dh12.close()
log.info("lis2dh12_demo", "End")
end
sys.taskInit(lis2dh12_demo_task_func)
@@ -0,0 +1,70 @@
--[[
@module main
@summary LIS2DH12 三轴加速度传感器 Demo 入口
@version 1.0
@date 2026.07.16
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_lis2dh12 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化和数据读取([1/4]
2、量程切换演示([2/4]
3、输出速率切换([3/4]
4、温度读取演示([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "LIS2DH12_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 lis2dh12_demo.lua 演示模块
require "lis2dh12_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,243 @@
# LIS2DH12 三轴加速度传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **lis2dh12_demo.lua** - LIS2DH12 功能演示模块,包含数据读取、量程切换、输出速率切换、温度读取等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_lis2dh12** - LIS2DH12 扩展库,提供初始化、三轴加速度数据读取、量程切换、输出速率切换、功耗模式切换、温度读取、睡眠/唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**LIS2DH12 在异常 I2C 通信后可能锁死 SDA 总线。本库已内置 I2C 总线卡死自动检测与恢复功能。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_lis2dh12 扩展库的 5 项功能:HELLO → [1/5] → [2/5] → [3/5] → [4/5] → [5/5] → End
### 2.1 功能演示项说明
1. **[1/5] 初始化与数据读取** - 初始化 LIS2DH12 传感器并读取三轴加速度数据(X/Y/Z,单位 g),附带 data_ready 中断示例
2. **[2/5] 量程切换演示** - 在 ±2g(高精度)和 ±4g(宽范围)量程之间切换,观察数据变化
3. **[3/5] 输出速率切换** - 依次切换 25Hz / 50Hz / 100Hz / 200Hz 输出速率
4. **[4/5] 温度读取演示** - 使能温度传感器并读取芯片温度
5. **[5/5] 休眠与唤醒演示** - 演示 sleep()/wakeup() 待机与唤醒
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- CJMCU-LIS2DH12 三轴加速度传感器模块 × 1
demo所演示的LIS2DH12 三轴加速度传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=558250923931&mi_id=0000CnnCsR0FV7f0P7irmdBj13iLAdQybnwMchjjsbtxS9Q&ns=1&priceTId=215045e417839190178541937e1198&skuId=4867682785456&spm=a21n57.1.hoverItem.2&utparam=%7B%22aplus_abtest%22%3A%2259cc019145c3f9fde211bc6c0f71f883%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 6
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 I2C 模式接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>CJMCU-LIS2DH12 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>SDA<br/></td></tr>
<tr>
<td>30/GPIO29<br/></td><td>INT1<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
![](https://docs.openluat.com/cdn/image/Air780EHM_exs_lis2dh12.png)
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 4.3 脚本文件
1. **main.lua** - 程序入口
2. **lis2dh12_demo.lua** - LIS2DH12 功能演示模块
3. **exs_lis2dh12** - LIS2DH12 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 CJMCU-LIS2DH12 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 lis2dh12_demo.lua 演示模块
require "lis2dh12_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/5] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴加速度数据(X/Y/Z,单位 g)的连续读取
#### 5.4.2 量程切换演示
1. 自动进入 [2/5] 量程切换演示
2. 观察 ±4g 宽范围量程下的数据
3. 观察 ±2g 高精度量程下的数据
#### 5.4.3 输出速率切换演示
1. 自动进入 [3/5] 输出速率切换演示
2. 依次观察 25Hz / 50Hz / 100Hz / 200Hz 下的数据读取
3. 最终恢复为 100Hz
#### 5.4.4 温度读取演示
1. 自动进入 [4/5] 温度读取演示
2. 使能温度传感器并等待稳定
3. 读取并显示芯片温度(单位 °C
#### 5.4.5 休眠与唤醒演示
1. 自动进入 [5/5] 休眠与唤醒演示
2. 观察 sleep() 进入待机模式
3. 观察 wakeup() 唤醒后数据恢复
### 5.5 预期效果
- **初始化与数据读取**:LIS2DH12 初始化成功,三轴加速度数据正常输出
- **量程切换**:量程切换后数据范围随之变化,灵敏度不同
- **输出速率切换**:不同速率下均能正常读取数据
- **温度读取**:正常读取芯片温度(约 20-40°C)
- **休眠与唤醒**sleep 后进入低功耗,wakeup 后数据读取正常
- **日志如下:**
```lua
[2026-07-21 10:30:03.304][000000000.179] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:30:03.305][000000000.179] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:30:03.307][000000000.181] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:30:03.310][000000000.183] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:30:03.312][000000000.184] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:30:03.315][000000000.184] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:30:03.317][000000000.201] I/user.main LIS2DH12_Demo 001.999.000
[2026-07-21 10:30:03.319][000000000.217] D/user.exs_lis2dh12 version -> 202607170900
[2026-07-21 10:30:03.320][000000000.218] I/user.lis2dh12_demo HELLO
[2026-07-21 10:30:03.512][000000001.218] I/user.lis2dh12_demo ===== [1/5] =====
[2026-07-21 10:30:03.515][000000001.233] I/user.exs_lis2dh12 LIS2DH12 @ I2C 0x19
[2026-07-21 10:30:03.517][000000001.243] I/user.exs_lis2dh12 REG读回 CTRL1=0x57 CTRL4=0x88 STATUS=0x0F INT1_CFG=0x00
[2026-07-21 10:30:03.518][000000001.252] I/user.exs_lis2dh12 INT1 , gpio=29
[2026-07-21 10:30:03.520][000000001.252] I/user.exs_lis2dh12 mode=I2C range=2g odr=100Hz mode=highres
[2026-07-21 10:30:03.533][000000001.253] I/user.lis2dh12_demo LIS2DH12 : 202607170900
[2026-07-21 10:30:03.539][000000001.269] I/user.exs_lis2dh12 CTRL1=0x57 CTRL2=0x00 CTRL3=0xC0 CTRL4=0x88 CTRL5=0x0C CTRL6=0x00 STATUS=0xFF INT1_SRC=0x00 INT2_SRC=0x00 FIFO_SRC=0x20
[2026-07-21 10:30:03.544][000000001.478] I/user.lis2dh12_demo X=-0.965 Y=-0.008 Z=-0.029 g
[2026-07-21 10:30:04.086][000000002.295] I/mobile sim0 sms ready
[2026-07-21 10:30:04.091][000000002.296] D/mobile cid1, state0
[2026-07-21 10:30:04.096][000000002.296] D/mobile bearer act 0, result 0
[2026-07-21 10:30:04.100][000000002.297] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:30:04.104][000000002.353] D/mobile TIME_SYNC 0 tm 1784601007
[2026-07-21 10:30:04.724][000000002.986] I/user.lis2dh12_demo 1: X=-0.368 Y=-0.170 Z=-0.713 g
[2026-07-21 10:30:05.223][000000003.494] I/user.lis2dh12_demo 2: X=-0.083 Y=0.008 Z=-1.023 g
[2026-07-21 10:30:05.732][000000004.004] I/user.lis2dh12_demo 3: X=-0.119 Y=0.108 Z=0.237 g
[2026-07-21 10:30:05.735][000000004.004] I/user.lis2dh12_demo ---- [1/5] 完成 ----
[2026-07-21 10:30:06.013][000000004.271] I/user.lis2dh12_demo int1: =front X=-0.670 Y=-0.097 Z=-0.194 g
[2026-07-21 10:30:06.243][000000004.504] I/user.lis2dh12_demo ===== [2/5] =====
[2026-07-21 10:30:06.247][000000004.505] I/user.lis2dh12_demo 4g
[2026-07-21 10:30:06.252][000000004.508] I/user.exs_lis2dh12 4g
[2026-07-21 10:30:06.555][000000004.816] I/user.lis2dh12_demo 4g : X=0.102 Y=0.350 Z=-0.872 g
[2026-07-21 10:30:07.549][000000005.816] I/user.lis2dh12_demo 2g
[2026-07-21 10:30:07.553][000000005.819] I/user.exs_lis2dh12 2g
[2026-07-21 10:30:07.861][000000006.127] I/user.lis2dh12_demo 2g : X=-0.063 Y=0.379 Z=0.801 g
[2026-07-21 10:30:08.856][000000007.127] I/user.lis2dh12_demo ---- [2/5] 完成 ----
[2026-07-21 10:30:09.369][000000007.627] I/user.lis2dh12_demo ===== [3/5] =====
[2026-07-21 10:30:09.374][000000007.628] I/user.lis2dh12_demo 25Hz
[2026-07-21 10:30:09.378][000000007.631] I/user.exs_lis2dh12 25Hz
[2026-07-21 10:30:09.881][000000008.139] I/user.lis2dh12_demo 25Hz : X=0.033 Y=0.084 Z=0.999 g
[2026-07-21 10:30:10.377][000000008.639] I/user.lis2dh12_demo 50Hz
[2026-07-21 10:30:10.382][000000008.643] I/user.exs_lis2dh12 50Hz
[2026-07-21 10:30:10.890][000000009.151] I/user.lis2dh12_demo 50Hz : X=0.145 Y=0.163 Z=0.982 g
[2026-07-21 10:30:11.385][000000009.651] I/user.lis2dh12_demo 100Hz
[2026-07-21 10:30:11.388][000000009.654] I/user.exs_lis2dh12 100Hz
[2026-07-21 10:30:11.898][000000010.162] I/user.lis2dh12_demo 100Hz : X=0.754 Y=0.033 Z=0.060 g
[2026-07-21 10:30:12.395][000000010.662] I/user.lis2dh12_demo 200Hz
[2026-07-21 10:30:12.398][000000010.665] I/user.exs_lis2dh12 200Hz
[2026-07-21 10:30:12.905][000000011.173] I/user.lis2dh12_demo 200Hz : X=-0.589 Y=-0.397 Z=0.942 g
[2026-07-21 10:30:13.215][000000011.481] I/user.lis2dh12_demo int1: =down X=-0.341 Y=-0.250 Z=0.819 g
[2026-07-21 10:30:13.419][000000011.676] I/user.exs_lis2dh12 100Hz
[2026-07-21 10:30:13.423][000000011.676] I/user.lis2dh12_demo ---- [3/5] 完成 ----
[2026-07-21 10:30:13.915][000000012.176] I/user.lis2dh12_demo ===== [4/5] =====
[2026-07-21 10:30:13.919][000000012.177] I/user.lis2dh12_demo 使
[2026-07-21 10:30:13.923][000000012.178] I/user.exs_lis2dh12 使
[2026-07-21 10:30:14.087][000000012.353] I/user.lis2dh12_demo int1: =front X=-0.906 Y=0.708 Z=-0.263 g
[2026-07-21 10:30:14.118][000000012.381] I/user.lis2dh12_demo : 39.0 °C
[2026-07-21 10:30:14.122][000000012.382] I/user.exs_lis2dh12
[2026-07-21 10:30:14.125][000000012.382] I/user.lis2dh12_demo ---- [4/5] 完成 ----
[2026-07-21 10:30:14.616][000000012.883] I/user.lis2dh12_demo ===== [5/5] =====
[2026-07-21 10:30:14.619][000000012.884] I/user.lis2dh12_demo power-down
[2026-07-21 10:30:14.623][000000012.886] I/user.exs_lis2dh12 power-down
[2026-07-21 10:30:25.117][000000023.386] I/user.lis2dh12_demo power-down
[2026-07-21 10:30:25.120][000000023.389] I/user.exs_lis2dh12 power-down ODR=100Hz
[2026-07-21 10:30:25.336][000000023.597] I/user.lis2dh12_demo : X=-0.933 Y=0.196 Z=-0.178 g
[2026-07-21 10:30:25.340][000000023.597] I/user.lis2dh12_demo ---- [5/5] 完成 ----
[2026-07-21 10:30:25.833][000000024.098] I/user.lis2dh12_demo ===== [] =====
[2026-07-21 10:30:25.837][000000024.104] I/user.exs_lis2dh12
[2026-07-21 10:30:25.842][000000024.104] I/user.lis2dh12_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 LIS2DH12 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_lis2dh12 扩展库已正常加载
2. **读取数据始终为零**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(SA0=GND 时地址 0x18SA0=VCC 时地址 0x19
- 确认接线无松动
3. **量程切换无效果**
- 确认 `set_range()` 参数为 "2g"、"4g"、"8g" 或 "16g"
- 切换后需等待传感器稳定再读取数据
4. **温度读取失败**
- 确认已在 setup 时设置 `enable_temp = true` 或调用 `enable_temp(true)`
- 使能后需等待约 200ms 让 ADC 稳定
### 5.7 扩展功能建议
exs_lis2dh12 更多接口的使用可以查看 [exs_lis2dh12 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_lis2dh12/)
@@ -0,0 +1,70 @@
--[[
@module main
@summary MPU6050 六轴姿态传感器 Demo 入口
@version 1.0
@date 2026.07.23
@author 王城钧
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_mpu6050 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化和数据读取([1/4])- 加速度/角速度/温度
2、静态倾角测量([2/4])- 加速度计推算俯仰/横滚
3、动态姿态解算,互补滤波([3/4])- 陀螺+加速度融合
4、碰撞检测与自由落体([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "MPU6050_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 mpu6050_demo.lua 演示模块
require "mpu6050_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,212 @@
--[[
@module mpu6050_demo
@summary MPU6050 六轴姿态传感器演示模块
@version 2.0
@date 2026.07.23
@author 王城钧
@usage
=== 演示流程: 基础测试(10项) → 持续读取(10秒) → DMP测试(可选) ===
本文件通过 exs_mpu6050 扩展库的 API 逐一演示 MPU6050 的以下功能:
1. I2C 初始化与芯片自检 -- i2c.setup + mpu.setup, 自动检测地址 0x68/0x69, 自动加载校准
2. 芯片温度读取 -- mpu.get_data().temp, 单位 ℃
3. 三轴加速度读取 -- mpu.get_data().accel, 单位 g, 静止水平时 Z≈1.0g
4. 三轴角速度读取 -- mpu.get_data().gyro, 单位 °/s, 静止时应接近 0
5. 静态倾角测量 -- mpu.get_tilt(), 通过加速度计直接推算俯仰/横滚角
6. 碰撞/敲击检测 -- mpu.detect_tap(阈值, 去抖时间), 检测瞬时加速度冲击
7. 自由落体/失重检测 -- mpu.detect_freefall(阈值), 三轴合加速度低于阈值时触发
8. 校准状态查询 -- mpu.is_calibrated(), 检查加速度/陀螺零偏是否已校准
9. 手动重新校准(注释) -- mpu.calibrate(), 设备需水平静止, 200次采样取平均
10. 持续读取(加速度+互补滤波+碰撞) -- 循环 10 秒, 演示 get_attitude() 动态姿态融合
附加: DMP 数字运动处理器 -- mpu.dmp_init() + mpu.dmp_get_euler(), 固件已内置
]]
-- 加载 exs_mpu6050 扩展库
-- 该库提供 MPU6050 的全部驱动功能: 初始化/校准/数据读取/姿态解算/DMP
local mpu = require "exs_mpu6050"
-- 硬件 I2C 总线 ID
-- Air780EGH: 使用 I2C1 (Pin66=SDA, Pin67=SCL) → hwid=1
-- Air8000: 使用 I2C0 (GPIO2=SDA, GPIO1=SCL) → hwid=0
local i2c_hwid = 1
----------------------------------------------------------------
-- 基础功能测试 (10 项, 顺序执行)
-- 覆盖: 初始化 → 温度 → 加速度 → 陀螺仪 → 倾角 → 碰撞 → 失重 → 校准 → 持续读取
----------------------------------------------------------------
local function mpu6050_task()
------------------------------------------------------------
-- [1/10] 初始化 I2C + MPU6050
-- - i2c.setup 配置 I2C 总线为快速模式(400kHz)
-- - mpu.setup 自动检测芯片地址(先试0x68, 再试0x69), 验证 WHO_AM_I 寄存器(应为0x68)
-- - 无校准数据时建议调用 calibrate(): 设备需水平静止, 采样200次, 需要50次以上有效静止采样
-- - 校准数据会保存到文件 /mpu6050_calib.txt, 后续启动自动加载
------------------------------------------------------------
log.info("TEST", "=== [1/10] 初始化 ===")
i2c.setup(i2c_hwid, i2c.FAST)
if not mpu.setup({ i2c_id = i2c_hwid }) then
log.error("TEST", "初始化失败! 检查接线: VCC=3.3V GND SCL SDA")
while true do sys.wait(1000) end
end
------------------------------------------------------------
-- [2/10] 芯片温度读取
-- - MPU6050 内置温度传感器, 用于内部补偿陀螺零漂
-- - 正常工作时芯片温度约 30~45℃, 随运行时间缓慢上升
------------------------------------------------------------
log.info("TEST", "=== [2/10] 温度 ===")
local temp = mpu.get_data().temp
log.info("TEST", string.format("芯片温度: %.1f°C", temp))
------------------------------------------------------------
-- [3/10] 三轴加速度读取 (已校准)
-- - 返回 x, y, z 分量, 单位 g (1g ≈ 9.8m/s² 重力加速度)
-- - 静止水平放置时: Z≈+1.0g, X≈0g, Y≈0g
-- - Z 轴正向为芯片顶面朝上, X/Y 向由芯片丝印方向决定
-- - 量程默认 ±2g, 分辨率 16384 LSB/g
------------------------------------------------------------
log.info("TEST", "=== [3/10] 加速度 (单位g) ===")
local a = mpu.get_data().accel
log.info("TEST", string.format("accel x=%.3f y=%.3f z=%.3f", a.x, a.y, a.z))
-- 如果 Z 轴偏差超过 0.15g, 提示检查放置或校准
if math.abs(a.z - 1.0) > 0.15 then
log.warn("TEST", "Z轴偏差较大(期望≈1.0g), 检查传感器是否水平")
end
------------------------------------------------------------
-- [4/10] 三轴角速度读取 (已校准)
-- - 返回 x, y, z 分量, 单位 °/s (度每秒)
-- - 静止时应全部接近 0, 超过 ±5°/s 说明零偏校准不佳
-- - 量程默认 ±250°/s, 分辨率 131 LSB/(°/s)
------------------------------------------------------------
log.info("TEST", "=== [4/10] 陀螺仪 (单位°/s) ===")
local g = mpu.get_data().gyro
log.info("TEST", string.format("gyro x=%.1f y=%.1f z=%.1f", g.x, g.y, g.z))
if math.abs(g.x) > 5 or math.abs(g.y) > 5 or math.abs(g.z) > 5 then
log.warn("TEST", "陀螺零偏较大(期望≈0), 重新校准试试")
end
------------------------------------------------------------
-- [5/10] 静态倾角读取 (加速度计直接推算)
-- - pitch(俯仰): 设备前后倾斜角度, 前倾为正
-- - roll(横滚): 设备左右倾斜角度, 右倾为正
-- - 纯加速度推算, 静态精度高(受重力矢量影响)
-- - 动态时受运动加速度干扰, 噪声大, 不宜用于运动中的姿态估计
------------------------------------------------------------
log.info("TEST", "=== [5/10] 倾角 (单位°) ===")
local pitch, roll = mpu.get_tilt()
log.info("TEST", string.format("tilt pitch=%.1f° roll=%.1f°", pitch, roll))
------------------------------------------------------------
-- [6/10] 碰撞/敲击检测
-- - detect_tap(threshold, debounce_ms)
-- threshold: 加速度合矢量阈值(g), 默认 2.0g, 轻敲可设 1.5g
-- debounce_ms: 去抖时间(ms), 默认 500ms, 防止单次敲击被多次触发
-- - 原理: 三轴加速度模长 sqrt(x²+y²+z²) 超过阈值则触发
-- - 静止时模长约 1.0g, 敲击瞬间可达 3~5g
------------------------------------------------------------
log.info("TEST", "=== [6/10] 碰撞检测 ===")
if mpu.detect_tap(2.0) then
log.info("TEST", "碰撞检测: YES")
else
log.info("TEST", "碰撞检测: NO (正常, 静止时不应触发)")
end
------------------------------------------------------------
-- [7/10] 自由落体/失重检测
-- - detect_freefall(threshold)
-- threshold: 加速度合矢量阈值(g), 默认 0.3g
-- - 原理: 三轴合加速度 < 阈值时判定为失重/自由落体
-- - 自由落体时加速度趋近于 0, 正常静止时为 1.0g
------------------------------------------------------------
log.info("TEST", "=== [7/10] 自由落体检测 ===")
if mpu.detect_freefall() then
log.info("TEST", "自由落体: YES (你在自由落体?)")
else
log.info("TEST", "自由落体: NO (正常)")
end
------------------------------------------------------------
-- [8/10] 校准状态查询
-- - is_calibrated() 返回校准数据是否就绪
-- - 校准数据来自: 首次自动校准 或 手动 calibrate() 调用
-- - 数据保存在 /mpu6050_calib.txt (6个值: gx,gy,gz,ax,ay,az)
-- - 无校准时 get_data() 返回原始值, 精度降低
------------------------------------------------------------
log.info("TEST", "=== [8/10] 校准状态 ===")
log.info("TEST", "已校准:", mpu.is_calibrated())
------------------------------------------------------------
-- [9/10] 手动重新校准 (默认注释)
-- - 在运行过程中需要重新校准时取消注释
-- - 调用前确保设备水平静止, 否则校准结果不准确
-- - 校准完成会自动覆盖 /mpu6050_calib.txt
------------------------------------------------------------
-- log.info("TEST", "=== [9/10] 手动校准 ===")
-- local ok = mpu.calibrate()
-- log.info("TEST", "校准结果:", ok)
------------------------------------------------------------
-- [10/10] 持续读取 (10秒循环)
-- - 每 200ms 读取一次: 加速度 + 互补滤波姿态 + 碰撞检测
-- - get_attitude() 使用互补滤波: alpha=0.96
-- 陀螺积分权重 96% + 加速度推算权重 4%
-- 陀螺积分提供高频动态响应, 加速度修正低频漂移
-- - 必须持续循环调用以保持积分连续性, 中断后积分重置
-- - 在循环中同时监控碰撞事件并累计次数
------------------------------------------------------------
log.info("TEST", "=== [10/10] 持续读取 (10秒) ===")
local start = os.clock()
local tap_count = 0
while os.clock() - start < 10 do
local a = mpu.get_data().accel
local p, r = mpu.get_attitude()
if mpu.detect_tap(2.0, 500) then
tap_count = tap_count + 1
log.info("TEST", string.format("碰撞! 第%d次", tap_count))
end
log.info("TEST", string.format(
"a:(%5.2f,%5.2f,%5.2f)g p=%.1f° r=%.1f°",
a.x, a.y, a.z, p, r))
sys.wait(200)
end
log.info("TEST", string.format("持续读取完成, 碰撞%d次", tap_count))
------------------------------------------------------------
-- 基础测试完毕
------------------------------------------------------------
log.info("TEST", "========== 所有测试完成 ==========")
end
----------------------------------------------------------------
-- DMP 数字运动处理器测试 (可选)
-- - DMP 是 MPU6050 内置的运动处理引擎, 直接输出融合后的四元数/欧拉角
-- - 相比互补滤波, DMP 精度更高、CPU 负载更低
-- - DMP 固件已内置在 exs_mpu6050.lua 中 (MotionApps v20), 无需额外文件
-- 固件来源: GitHub i2cdevlib → Arduino/MPU6050/MPU6050_6Axis_MotionApps20.h
-- - DMP 输出: Roll(横滚), Pitch(俯仰), Yaw(偏航, 无磁力计会漂移)
----------------------------------------------------------------
local function mpu6050_dmp()
sys.wait(3000) -- 等待基础测试开始后再启动 DMP
if mpu.dmp_init() then
log.info("DMP_TEST", "DMP 就绪")
while true do
local r, p, y = mpu.dmp_get_euler()
if r then
log.info("DMP", string.format("R:%.1f P:%.1f Y:%.1f", r, p, y))
end
sys.wait(3000)
end
end
end
-- 启动基础测试任务 (协程运行, 通过命名函数指定)
sys.taskInit(mpu6050_task)
-- 启动 DMP 测试任务 (可选)
sys.taskInit(mpu6050_dmp)
@@ -0,0 +1,216 @@
# MPU6050 六轴姿态传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **mpu6050_demo.lua** - MPU6050 功能演示模块,包含数据读取、静态倾角、动态姿态解算、碰撞检测等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_mpu6050** - MPU6050 扩展库,提供 I2C 初始化、三轴加速度/角速度数据读取、温度、倾角、姿态解算(互补滤波)、碰撞/敲击检测、自由落体检测、自动校准等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_mpu6050 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 MPU6050 传感器并读取三轴加速度(X/Y/Z,单位 g)、三轴角速度(X/Y/Z,单位 °/s)、芯片温度
2. **[2/4] 静态倾角测量** - 通过加速度计推算俯仰角(Pitch)和横滚角(Roll),适用于静态姿态测量
3. **[3/4] 动态姿态解算(互补滤波)** - 融合陀螺仪积分与加速度计数据,通过互补滤波输出动态姿态角
4. **[4/4] 碰撞检测与自由落体** - 演示敲击检测(detect_tap)和失重/自由落体检测(detect_freefall
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板
- MPU6050 六轴姿态传感器模块 × 1
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
![](https://docs.openluat.com/air8000/luatos/app/image/mpu6050.jpg)
### 3.2 接线配置
#### 3.2.1 MPU6050 模块接线(硬件 I2C
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>MPU6050 模块(GY-521<br/></td></tr>
<tr>
<td>67 / I2C1_SCL<br/></td><td>SCL<br/></td></tr>
<tr>
<td>66 / I2C1_SDA<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>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。MPU6050 模块(GY-521)通常包含 3.3V 稳压器,可直接使用 VDD_EXT 供电。
## 四、演示软件环境
### 4.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/)
### 4.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)demo 使用 LuatOS-SoC_V2050_Air780EPM 1号固件
### 4.3 脚本文件
- **main.lua** - 程序入口
- **mpu6050_demo.lua** - 演示模块
- **exs_mpu6050** - 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 MPU6050 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 首次上电请将设备水平放置,以便自动校准
4. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 mpu6050_demo.lua 演示模块
require "mpu6050_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴加速度数据(X/Y/Z,单位 g)、角速度(°/s)、温度(℃)输出
4. 静止水平放置时,加速度 Z 轴应接近 1.0g,X/Y 接近 0
#### 5.4.2 静态倾角测量演示
1. 自动进入 [2/4] 静态倾角测量
2. 保持设备静止,观察俯仰角和横滚角
3. 水平放置时倾角应接近 0°
4. 可倾斜设备观察角度变化
#### 5.4.3 动态姿态解算演示
1. 自动进入 [3/4] 动态姿态解算(互补滤波)
2. 缓慢转动设备,观察俯仰角和横滚角的变化
3. 互补滤波融合了陀螺仪和加速度计数据,动态响应更好
#### 5.4.4 碰撞检测演示
1. 自动进入 [4/4] 碰撞检测与自由落体
2. 5 秒检测窗口内,用手指轻轻敲击传感器
3. 观察日志是否输出"检测到敲击!"
4. 快速抛接设备可触发"失重/自由落体"检测
### 5.5 预期效果
- **初始化与数据读取**:MPU6050 初始化成功,自动校准完成,三轴加速度/角速度/温度数据正常输出
- **静态倾角**:水平放置时俯仰/横滚接近 0°,倾斜设备时角度准确响应
- **动态姿态解算**:转动设备时姿态角平滑跟踪,互补滤波有效抑制噪声
- **碰撞检测**:敲击传感器可被 detect_tap 捕获,自由落体可被 detect_freefall 捕获
- **日志如下:**
```lua
[2026-07-23 18:22:49.845][000000000.253] I/user.TEST === 1. ===
[2026-07-23 18:22:49.846][000000000.253] I2C_MasterSetup 426:I2C1, Total 65 HCNT 22 LCNT 40
[2026-07-23 18:22:49.848][000000000.305] I/user.mpu6050 WHO_AM_I 0x68 addr 0x68
[2026-07-23 18:22:49.850][000000000.560] I/user.mpu6050 g:(-283,-72,239) a:(-1851,2252,-28738)
[2026-07-23 18:22:49.851][000000000.561] I/user.mpu6050 init
[2026-07-23 18:22:49.858][000000000.561] I/user.TEST === 2. ===
[2026-07-23 18:22:49.860][000000000.562] I/user.TEST : 29.6°C
[2026-07-23 18:22:49.863][000000000.562] I/user.TEST === 3. (g) ===
[2026-07-23 18:22:49.867][000000000.564] I/user.TEST accel x=0.127 y=-0.077 z=1.112
[2026-07-23 18:22:49.870][000000000.564] I/user.TEST === 4. (°/s) ===
[2026-07-23 18:22:49.873][000000000.566] I/user.TEST gyro x=1.9 y=9.1 z=3.6
[2026-07-23 18:22:49.876][000000000.566] W/user.TEST (0),
[2026-07-23 18:22:49.879][000000000.566] I/user.TEST === 5. (°) ===
[2026-07-23 18:22:49.883][000000000.568] I/user.TEST tilt pitch=-3.7° roll=6.4°
[2026-07-23 18:22:49.885][000000000.568] I/user.TEST === 6. ===
[2026-07-23 18:22:49.888][000000000.570] I/user.TEST : NO (, )
[2026-07-23 18:22:49.891][000000000.570] I/user.TEST === 7. ===
[2026-07-23 18:22:49.894][000000000.572] I/user.TEST : NO ()
[2026-07-23 18:22:49.897][000000000.572] I/user.TEST === 8. ===
[2026-07-23 18:22:49.900][000000000.572] I/user.TEST : true
[2026-07-23 18:22:49.903][000000000.572] I/user.TEST === 10. (10) ===
[2026-07-23 18:22:49.906][000000000.578] I/user.TEST a:( 0.12,-0.07, 1.09)g p=-0.1° r=0.3°
[2026-07-23 18:22:49.945][000000000.783] I/user.TEST a:( 0.14,-0.09, 0.98)g p=-0.3° r=0.7°
[2026-07-23 18:22:49.948][000000000.990] I/user.TEST a:( 0.14,-0.07, 0.98)g p=-0.6° r=0.9°
[2026-07-23 18:22:49.950][000000001.195] I/user.TEST a:( 0.13,-0.05, 0.97)g p=-1.9° r=2.7°
[2026-07-23 18:22:49.953][000000001.401] I/user.TEST a:( 0.13,-0.06, 0.97)g p=-2.0° r=2.9°
[2026-07-23 18:22:49.990][000000001.607] I/user.TEST a:( 0.14,-0.06, 0.97)g p=-2.0° r=3.1°
[2026-07-23 18:22:50.135][000000001.813] I/user.TEST a:( 0.13,-0.06, 0.98)g p=-2.1° r=3.3°
[2026-07-23 18:22:50.341][000000002.019] I/user.TEST a:( 0.14,-0.06, 0.97)g p=-3.4° r=1.9°
[2026-07-23 18:22:50.545][000000002.225] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-3.4° r=2.1°
[2026-07-23 18:22:50.752][000000002.431] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-3.4° r=2.3°
[2026-07-23 18:22:50.958][000000002.637] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-3.4° r=2.5°
[2026-07-23 18:22:51.162][000000002.843] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-3.3° r=2.7°
[2026-07-23 18:22:51.369][000000003.049] I/user.TEST a:( 0.13,-0.05, 0.97)g p=-2.3° r=1.0°
[2026-07-23 18:22:51.575][000000003.255] I/user.TEST a:( 0.13,-0.06, 0.98)g p=-2.4° r=1.3°
[2026-07-23 18:22:51.591][000000003.271] I/user.mpu6050 DMP固件加载中, 1929
[2026-07-23 18:22:51.783][000000003.461] I/user.TEST a:( 0.13,-0.05, 0.97)g p=-2.4° r=1.6°
[2026-07-23 18:22:51.909][000000003.589] I/user.mpu6050
[2026-07-23 18:22:51.989][000000003.667] I/user.TEST a:( 0.13,-0.06, 0.97)g p=-2.4° r=1.8°
[2026-07-23 18:22:52.115][000000003.791] I/user.mpu6050 DMP
[2026-07-23 18:22:52.119][000000003.791] I/user.DMP_TEST DMP
[2026-07-23 18:22:52.121][000000003.800] I/user.DMP R:-173.1 P:0.2 Y:0.3
[2026-07-23 18:22:52.196][000000003.873] I/user.TEST a:( 0.13,-0.06, 0.98)g p=-2.4° r=2.0°
[2026-07-23 18:22:52.400][000000004.079] I/user.TEST a:( 0.14,-0.05, 0.96)g p=-0.8° r=2.4°
[2026-07-23 18:22:52.606][000000004.285] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-0.8° r=2.6°
[2026-07-23 18:22:52.813][000000004.491] I/user.TEST a:( 0.13,-0.05, 0.97)g p=-0.9° r=2.8°
[2026-07-23 18:22:53.018][000000004.697] I/user.TEST a:( 0.13,-0.06, 0.98)g p=-1.0° r=3.0°
[2026-07-23 18:22:53.220][000000004.903] I/user.TEST a:( 0.13,-0.05, 0.98)g p=-1.0° r=3.2°
[2026-07-23 18:22:53.425][000000005.109] I/user.TEST a:( 0.13,-0.05, 0.98)g p=0.5° r=3.8°
[2026-07-23 18:22:53.644][000000005.315] I/user.TEST a:( 0.13,-0.05, 0.97)g p=0.4° r=3.9°
[2026-07-23 18:22:53.836][000000005.521] I/user.TEST a:( 0.13,-0.04, 0.98)g p=0.3° r=4.1°
[2026-07-23 18:22:54.042][000000005.727] I/user.TEST a:( 0.12,-0.06, 0.99)g p=0.2° r=4.2°
[2026-07-23 18:22:54.263][000000005.933] I/user.TEST a:( 0.13,-0.05, 0.98)g p=0.1° r=4.4°
[2026-07-23 18:22:54.454][000000006.139] I/user.TEST a:( 0.13,-0.05, 0.97)g p=1.6° r=5.0°
[2026-07-23 18:22:54.660][000000006.345] I/user.TEST a:( 0.13,-0.04, 0.98)g p=1.5° r=5.1°
[2026-07-23 18:22:54.865][000000006.551] I/user.TEST a:( 0.13,-0.04, 0.99)g p=1.3° r=5.2°
[2026-07-23 18:22:55.074][000000006.758] I/user.TEST a:( 0.13,-0.05, 0.98)g p=1.2° r=5.3°
[2026-07-23 18:22:55.152][000000006.828] I/user.DMP R:0.0 P:-0.1 Y:-1.4
[2026-07-23 18:22:55.280][000000006.965] I/user.TEST a:( 0.13,-0.06, 0.99)g p=1.0° r=5.4°
[2026-07-23 18:22:55.487][000000007.170] I/user.TEST a:( 0.13,-0.05, 0.98)g p=2.4° r=6.0°
[2026-07-23 18:22:55.695][000000007.376] I/user.TEST a:( 0.14,-0.04, 0.97)g p=2.3° r=6.1°
[2026-07-23 18:22:55.899][000000007.582] I/user.TEST a:( 0.14,-0.05, 0.99)g p=2.1° r=6.2°
[2026-07-23 18:22:56.104][000000007.788] I/user.TEST a:( 0.14,-0.04, 0.97)g p=1.9° r=6.2°
[2026-07-23 18:22:56.324][000000007.994] I/user.TEST a:( 0.13,-0.04, 0.98)g p=1.7° r=6.3°
[2026-07-23 18:22:56.516][000000008.200] I/user.TEST a:( 0.13,-0.04, 0.98)g p=3.5° r=6.7°
[2026-07-23 18:22:56.722][000000008.406] I/user.TEST a:( 0.13,-0.05, 0.97)g p=3.2° r=6.8°
[2026-07-23 18:22:56.928][000000008.612] I/user.TEST a:( 0.14,-0.05, 0.97)g p=3.0° r=6.8°
[2026-07-23 18:22:57.136][000000008.818] I/user.TEST a:( 0.14,-0.04, 0.97)g p=2.8° r=6.9°
[2026-07-23 18:22:57.341][000000009.024] I/user.TEST a:( 0.13,-0.04, 0.98)g p=4.7° r=7.4°
[2026-07-23 18:22:57.547][000000009.230] I/user.TEST a:( 0.14,-0.04, 0.96)g p=4.4° r=7.4°
[2026-07-23 18:22:57.751][000000009.436] I/user.TEST a:( 0.14,-0.05, 0.98)g p=4.2° r=7.4°
[2026-07-23 18:22:57.957][000000009.643] I/user.TEST a:( 0.14,-0.04, 0.97)g p=3.9° r=7.5°
[2026-07-23 18:22:58.179][000000009.855] I/user.DMP R:0.0 P:-0.0 Y:-1.5
[2026-07-23 18:22:58.182][000000009.862] I/user.TEST a:( 0.14,-0.05, 0.97)g p=3.7° r=7.5°
[2026-07-23 18:22:58.383][000000010.063] I/user.TEST , 0
[2026-07-23 18:22:58.385][000000010.063] I/user.TEST ========== ==========
```
@@ -0,0 +1,68 @@
--[[
@module main
@summary QMC5883L 三轴地磁传感器 Demo 入口
@version 1.0
@date 2026.07.14
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_qmc5883l 扩展库的完整功能,顺序为:
HELLO→[1/3]→[2/3]→[3/3]→End
1、初始化和数据读取([1/3]
2、量程切换演示([2/3]
3、输出速率切换([3/3]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "QMC5883L_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 qmc5883l_demo.lua 演示模块
require "qmc5883l_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!
@@ -0,0 +1,144 @@
--[[
@module qmc5883l_demo
@summary QMC5883L 三轴地磁传感器演示模块,包含所有功能的演示用例
@version 2.0
@date 2026.07.17
@author 江访
@usage
本文件包含 QMC5883L 的逐项功能演示。
通过 exs_qmc5883l 扩展库的 API 逐一演示数据读取、量程切换、输出速率切换、休眠唤醒等功能。
]]
local exs_qmc5883l = require "exs_qmc5883l"
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("qmc5883l_demo", "===== [1/4] 初始化与数据读取 =====")
local result = exs_qmc5883l.setup({
scl = 31,
sda = 30,
})
if not result then
log.error("qmc5883l_demo", "QMC5883L 初始化失败,请检查接线")
return false
end
log.info("qmc5883l_demo", "QMC5883L 初始化成功,版本:", exs_qmc5883l.version())
sys.wait(200)
local data = exs_qmc5883l.get_data()
if data then
log.info("qmc5883l_demo", string.format("X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
else
log.error("qmc5883l_demo", "读取数据失败")
end
sys.wait(1000)
for i = 1, 3 do
sys.wait(500)
local d = exs_qmc5883l.get_data()
if d then
log.info("qmc5883l_demo", string.format("第%d次读取: X=%.2f Y=%.2f Z=%.2f uT", i, d.x, d.y, d.z))
end
end
log.info("qmc5883l_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] 量程切换演示
local function demo_range_switch()
log.info("qmc5883l_demo", "===== [2/4] 量程切换演示 =====")
log.info("qmc5883l_demo", "切换量程为 2G(高精度)")
exs_qmc5883l.set_range("2G")
sys.wait(300)
local data = exs_qmc5883l.get_data()
if data then
log.info("qmc5883l_demo", string.format("2G 量程: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("qmc5883l_demo", "切换量程为 8G(宽范围)")
exs_qmc5883l.set_range("8G")
sys.wait(300)
data = exs_qmc5883l.get_data()
if data then
log.info("qmc5883l_demo", string.format("8G 量程: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("qmc5883l_demo", "---- [2/4] 完成 ----")
end
-- [3/4] 输出速率切换
local function demo_odr_switch()
log.info("qmc5883l_demo", "===== [3/4] 输出速率切换 =====")
local odr_list = {10, 50, 100, 200}
for _, odr in ipairs(odr_list) do
log.info("qmc5883l_demo", string.format("设置输出速率为 %dHz", odr))
exs_qmc5883l.set_odr(odr)
sys.wait(500)
local data = exs_qmc5883l.get_data()
if data then
log.info("qmc5883l_demo", string.format("%dHz 下: X=%.2f Y=%.2f Z=%.2f uT", odr, data.x, data.y, data.z))
end
sys.wait(500)
end
exs_qmc5883l.set_odr(10)
log.info("qmc5883l_demo", "---- [3/4] 完成 ----")
end
-- [4/4] 休眠与唤醒演示
local function demo_sleep_wakeup()
log.info("qmc5883l_demo", "===== [4/4] 休眠与唤醒演示 =====")
log.info("qmc5883l_demo", "进入待机模式(低功耗)")
exs_qmc5883l.sleep(); sys.wait(10000)
sys.wait(500)
log.info("qmc5883l_demo", "从待机模式唤醒")
exs_qmc5883l.wakeup()
sys.wait(200)
local data = exs_qmc5883l.get_data()
if data then
log.info("qmc5883l_demo", string.format("唤醒后数据: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
log.info("qmc5883l_demo", "---- [4/4] 完成 ----")
end
local function qmc5883l_demo_task_func()
log.info("qmc5883l_demo", "HELLO")
sys.wait(1000)
local ok = demo_init_and_read()
if not ok then
log.error("qmc5883l_demo", "初始化失败,演示终止")
return
end
sys.wait(500)
demo_range_switch()
sys.wait(500)
demo_odr_switch()
sys.wait(500)
demo_sleep_wakeup()
sys.wait(500)
log.info("qmc5883l_demo", "===== [演示完毕] =====")
exs_qmc5883l.close()
log.info("qmc5883l_demo", "End")
end
sys.taskInit(qmc5883l_demo_task_func)
@@ -0,0 +1,217 @@
# QMC5883L 三轴地磁传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **qmc5883l_demo.lua** - QMC5883L 功能演示模块,包含数据读取、量程切换、输出速率切换等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_qmc5883l** - QMC5883L 扩展库,提供初始化、三轴磁场数据读取、量程切换、输出速率切换、睡眠/唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**QMC5883L 在异常 I2C 通信后可能锁死 SDA 总线。本库已内置 I2C 总线卡死自动检测与恢复功能。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_qmc5883l 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 QMC5883L 传感器并读取三轴磁场数据(X/Y/Z,单位 μT)
2. **[2/4] 量程切换演示** - 在 ±2G(高精度)和 ±8G(宽范围)量程之间切换,观察数据变化
3. **[3/4] 输出速率切换** - 依次切换 10Hz / 50Hz / 100Hz / 200Hz 输出速率
4. **[4/4] 休眠与唤醒演示** - 演示 sleep()/wakeup() 待机与唤醒
## 三、显示效果
![](https://docs.openluat.com/cdn/image/Air780EHM_qmc5883l.png)
## 四、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- QMC5883L 三轴地磁传感器模块 × 1
demo所演示的QMC5883L 三轴地磁传感器模块[购买链接](https://detail.tmall.com/item.htm?abbucket=12&id=41286452886&mi_id=0000mL_358u9JOlcX4nXEexqz-sHmWlRSDzJVprt2nO-dBw&ns=1&priceTId=215045e417839191910527013e1198&skuId=5886090880527&spm=a21n57.1.hoverItem.10&utparam=%7B%22aplus_abtest%22%3A%22dc3bc40800adf636989efd96ef16c269%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 5
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 QMC5883L 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>QMC5883L 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<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>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 程序入口
2. **qmc5883l_demo.lua** - QMC5883L 功能演示模块
3. **exs_qmc5883l** - QMC5883L 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 QMC5883L 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 qmc5883l_demo.lua 演示模块
require "qmc5883l_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴磁场数据(X/Y/Z,单位 μT)的连续读取
#### 6.4.2 量程切换演示
1. 自动进入 [2/4] 量程切换演示
2. 观察 ±2G 高精度量程下的数据
3. 观察 ±8G 宽范围量程下的数据
#### 6.4.3 输出速率切换演示
1. 自动进入 [3/4] 输出速率切换演示
2. 依次观察 10Hz / 50Hz / 100Hz / 200Hz 下的数据读取
3. 最终恢复为 10Hz
#### 6.4.4 休眠与唤醒演示
1. 自动进入 [4/4] 休眠与唤醒演示
2. 观察 sleep() 进入待机模式
3. 观察 wakeup() 唤醒后数据恢复
### 6.5 预期效果
- **初始化与数据读取**:QMC5883L 初始化成功,三轴磁场数据正常输出
- **量程切换**:量程切换后数据范围随之变化,灵敏度不同
- **输出速率切换**:不同速率下均能正常读取数据
- **休眠与唤醒**sleep 后进入低功耗,wakeup 后数据读取正常
- **日志如下:**
```lua
[2026-07-21 10:31:33.902][000000000.180] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:31:33.908][000000000.180] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:31:33.914][000000000.182] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:31:33.921][000000000.184] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:31:33.930][000000000.184] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:31:33.940][000000000.185] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:31:33.945][000000000.202] I/user.main QMC5883L_Demo 001.999.000
[2026-07-21 10:31:33.953][000000000.215] D/user.exs_qmc5883l version -> 202607200639
[2026-07-21 10:31:33.958][000000000.216] I/user.qmc5883l_demo HELLO
[2026-07-21 10:31:34.544][000000001.216] I/user.qmc5883l_demo ===== [1/4] =====
[2026-07-21 10:31:34.551][000000001.224] I/user.exs_qmc5883l I2C总线恢复完成
[2026-07-21 10:31:34.561][000000001.230] I/user.exs_qmc5883l , range=8G odr=10Hz osr=512
[2026-07-21 10:31:34.571][000000001.231] I/user.qmc5883l_demo QMC5883L : 202607200639
[2026-07-21 10:31:34.586][000000001.434] I/user.qmc5883l_demo X=-25.90 Y=13.70 Z=13.33 uT
[2026-07-21 10:31:36.087][000000002.863] I/mobile sim0 sms ready
[2026-07-21 10:31:36.099][000000002.863] D/mobile cid1, state0
[2026-07-21 10:31:36.113][000000002.864] D/mobile bearer act 0, result 0
[2026-07-21 10:31:36.125][000000002.864] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:31:36.133][000000002.906] D/mobile TIME_SYNC 0 tm 1784601099
[2026-07-21 10:31:36.144][000000002.937] I/user.qmc5883l_demo 1: X=-30.07 Y=14.53 Z=7.33 uT
[2026-07-21 10:31:36.579][000000003.440] I/user.qmc5883l_demo 2: X=-29.90 Y=15.93 Z=3.53 uT
[2026-07-21 10:31:37.075][000000003.943] I/user.qmc5883l_demo 3: X=-19.70 Y=21.90 Z=-12.53 uT
[2026-07-21 10:31:37.088][000000003.943] I/user.qmc5883l_demo ---- [1/4] 完成 ----
[2026-07-21 10:31:37.572][000000004.443] I/user.qmc5883l_demo ===== [2/4] =====
[2026-07-21 10:31:37.587][000000004.444] I/user.qmc5883l_demo 2G
[2026-07-21 10:31:37.599][000000004.446] I/user.exs_qmc5883l 2G
[2026-07-21 10:31:37.884][000000004.749] I/user.qmc5883l_demo 2G : X=8.56 Y=16.00 Z=-10.85 uT
[2026-07-21 10:31:38.892][000000005.749] I/user.qmc5883l_demo 8G
[2026-07-21 10:31:38.906][000000005.752] I/user.exs_qmc5883l 8G
[2026-07-21 10:31:39.186][000000006.055] I/user.qmc5883l_demo 8G : X=-37.03 Y=10.07 Z=-3.60 uT
[2026-07-21 10:31:40.192][000000007.055] I/user.qmc5883l_demo ---- [2/4] 完成 ----
[2026-07-21 10:31:40.690][000000007.555] I/user.qmc5883l_demo ===== [3/4] =====
[2026-07-21 10:31:40.701][000000007.556] I/user.qmc5883l_demo 10Hz
[2026-07-21 10:31:40.711][000000007.558] I/user.exs_qmc5883l 10Hz
[2026-07-21 10:31:41.203][000000008.061] I/user.qmc5883l_demo 10Hz : X=-16.93 Y=-30.57 Z=-4.20 uT
[2026-07-21 10:31:41.696][000000008.561] I/user.qmc5883l_demo 50Hz
[2026-07-21 10:31:41.709][000000008.564] I/user.exs_qmc5883l 50Hz
[2026-07-21 10:31:42.208][000000009.067] I/user.qmc5883l_demo 50Hz : X=-32.77 Y=-15.93 Z=7.07 uT
[2026-07-21 10:31:42.704][000000009.567] I/user.qmc5883l_demo 100Hz
[2026-07-21 10:31:42.717][000000009.570] I/user.exs_qmc5883l 100Hz
[2026-07-21 10:31:43.201][000000010.073] I/user.qmc5883l_demo 100Hz : X=-33.20 Y=-13.87 Z=8.43 uT
[2026-07-21 10:31:43.715][000000010.573] I/user.qmc5883l_demo 200Hz
[2026-07-21 10:31:43.725][000000010.576] I/user.exs_qmc5883l 200Hz
[2026-07-21 10:31:44.213][000000011.079] I/user.qmc5883l_demo 200Hz : X=-32.93 Y=-13.70 Z=8.43 uT
[2026-07-21 10:31:44.711][000000011.581] I/user.exs_qmc5883l 10Hz
[2026-07-21 10:31:44.722][000000011.582] I/user.qmc5883l_demo ---- [3/4] 完成 ----
[2026-07-21 10:31:45.211][000000012.082] I/user.qmc5883l_demo ===== [4/4] =====
[2026-07-21 10:31:45.222][000000012.083] I/user.qmc5883l_demo
[2026-07-21 10:31:45.233][000000012.084] I/user.exs_qmc5883l
[2026-07-21 10:31:55.716][000000022.584] I/user.qmc5883l_demo
[2026-07-21 10:31:55.727][000000022.585] I/user.exs_qmc5883l
[2026-07-21 10:31:55.918][000000022.788] I/user.qmc5883l_demo : X=-32.70 Y=-13.93 Z=8.53 uT
[2026-07-21 10:31:55.930][000000022.788] I/user.qmc5883l_demo ---- [4/4] 完成 ----
[2026-07-21 10:31:56.431][000000023.289] I/user.qmc5883l_demo ===== [] =====
[2026-07-21 10:31:56.445][000000023.290] I/user.exs_qmc5883l
[2026-07-21 10:31:56.461][000000023.291] I/user.qmc5883l_demo End
```
### 6.6 故障排除
1. **传感器初始化失败**
- 检查 QMC5883L 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_qmc5883l 扩展库已正常加载
2. **读取数据始终为零**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(QMC5883L 7 位地址为 0x0D,写操作地址 0x1A)
- 确认接线无松动
3. **量程切换无效果**
- 确认 `set_range()` 参数为 "2G" 或 "8G"
- 切换后需等待传感器稳定再读取数据
### 6.7 扩展功能建议
exs_qmc5883l 更多接口的使用可以查看 [exs_qmc5883l 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_qmc5883l/)
@@ -0,0 +1,73 @@
--[[
@module main
@summary QMC5883P 三轴地磁传感器 Demo 入口
@version 1.0
@date 2026.08.05
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_qmc5883p 扩展库的完整功能,顺序为:
HELLO→[1/7]→[2/7]→[3/7]→[4/7]→[5/7]→[6/7]→[7/7]→End
1、初始化和数据读取([1/7]
2、量程切换演示([2/7]
3、输出速率切换([3/7]
4、过采样/降采样配置([4/7]
5、软复位演示([5/7]
6、芯片自检演示([6/7]
7、休眠唤醒与关闭([7/7]
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "QMC5883P_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 qmc5883p_app.lua 演示模块
require "qmc5883p_app"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!
@@ -0,0 +1,289 @@
--[[
@module qmc5883p_app
@summary QMC5883P 三轴地磁传感器演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.08.05
@author 江访
@usage
本文件包含 QMC5883P 的逐项功能演示。
通过 exs_qmc5883p 扩展库的 API 逐一演示数据读取、量程切换、输出速率切换、
过采样/降采样配置、软复位、芯片自检、休眠唤醒等功能。
通过 MODE 变量选择通信模式(改 MODE 的值即可,无需注释/取消注释代码):
MODE = 1 软件 I2C(默认推荐,仅需 scl/sda 引脚,任意 GPIO 均可,接线最灵活)
MODE = 2 硬件 I2C(指定 i2c_id,占用 CPU 少,Air780EPM 用 I2C1
]]
-- 加载 exs_qmc5883p 扩展库
-- 该库提供 QMC5883P 的全部驱动功能: 初始化/数据读取/量程配置/速率配置/自检/电源管理
local exs_qmc5883p = require "exs_qmc5883p"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(默认) 2 = 硬件 I2C
-- 各模式所需引脚/参数见下方 init_config,接线说明见 readme.md
local MODE = 1
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIOAir780EPM 接线示例用 GPIO31/GPIO30
init_config = {scl = 31, sda = 30}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1),不传 scl/sda 时无总线恢复能力
init_config = {i2c_id = 1}
else
log.error("qmc5883p_demo", "MODE 取值错误,应为 1/2")
return
end
--------------------------------------------------------------
-- [1/7] 初始化与数据读取
-- - setup() 自动完成芯片 ID 校验、I2C 总线恢复、采样参数配置
-- - get_data() 返回三轴磁场强度,单位 μT,并带溢出标志 overflow
--------------------------------------------------------------
local function demo_init_and_read()
log.info("qmc5883p_demo", "===== [1/7] 初始化与数据读取 =====")
local result = exs_qmc5883p.setup(init_config)
if not result then
log.error("qmc5883p_demo", "QMC5883P 初始化失败")
log.error("qmc5883p_demo", "检查接线: VCC=3.3V GND SCL SDA")
return false -- 初始化失败必须返回 false
end
log.info("qmc5883p_demo", "QMC5883P 初始化成功,版本:", exs_qmc5883p.version())
sys.wait(200) -- 等待首次测量数据就绪,200ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
else
log.error("qmc5883p_demo", "读取数据失败")
end
for i = 1, 3 do
sys.wait(500) -- 每 500ms 读取一次
local d = exs_qmc5883p.get_data()
if d then
log.info("qmc5883p_demo", string.format("第%d次读取: X=%.2f Y=%.2f Z=%.2f uT", i, d.x, d.y, d.z))
end
end
log.info("qmc5883p_demo", "---- [1/7] 完成 ----")
return true
end
--------------------------------------------------------------
-- [2/7] 量程切换演示
-- - set_range() 支持 "2G"/"8G"/"12G"/"30G" 四种量程
-- - 切换后 get_data() 自动按新灵敏度换算 μT 值,无需重新 setup()
--------------------------------------------------------------
local function demo_range_switch()
log.info("qmc5883p_demo", "===== [2/7] 量程切换演示 =====")
log.info("qmc5883p_demo", "切换量程为 2G(高精度,灵敏度 15000 LSB/G")
exs_qmc5883p.set_range("2G")
sys.wait(300) -- 等待量程切换生效,300ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("2G 量程: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
sys.wait(1000) -- 展示间隔,1 秒
log.info("qmc5883p_demo", "切换量程为 8G(宽范围,灵敏度 3750 LSB/G")
exs_qmc5883p.set_range("8G")
sys.wait(300) -- 等待量程切换生效,300ms
data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("8G 量程: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
sys.wait(1000) -- 展示间隔,1 秒
log.info("qmc5883p_demo", "切换量程为 30G(最宽量程,抗干扰最强)")
exs_qmc5883p.set_range("30G")
sys.wait(300) -- 等待量程切换生效,300ms
data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("30G 量程: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
-- 恢复默认量程
exs_qmc5883p.set_range("8G")
log.info("qmc5883p_demo", "---- [2/7] 完成 ----")
end
--------------------------------------------------------------
-- [3/7] 输出速率切换演示
-- - set_odr() 支持 10Hz / 50Hz / 100Hz / 200Hz
-- - ODR 越低功耗越低,越高响应越快
--------------------------------------------------------------
local function demo_odr_switch()
log.info("qmc5883p_demo", "===== [3/7] 输出速率切换 =====")
local odr_list = {10, 50, 100, 200}
for _, odr in ipairs(odr_list) do
log.info("qmc5883p_demo", string.format("设置输出速率为 %dHz", odr))
exs_qmc5883p.set_odr(odr)
sys.wait(500) -- 等待速率配置生效,500ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("%dHz 下: X=%.2f Y=%.2f Z=%.2f uT", odr, data.x, data.y, data.z))
end
sys.wait(500) -- 展示间隔,500ms
end
exs_qmc5883p.set_odr(10) -- 恢复默认 10Hz
log.info("qmc5883p_demo", "---- [3/7] 完成 ----")
end
--------------------------------------------------------------
-- [4/7] 过采样/降采样配置演示
-- - set_osr(osr, osr2)osr 为过采样率(8/4/2/1),osr2 为降采样率(1/2/4/8
-- - 两者乘积最大 64,值越大噪声越低、功耗越高
--------------------------------------------------------------
local function demo_osr_config()
log.info("qmc5883p_demo", "===== [4/7] 过采样/降采样配置 =====")
log.info("qmc5883p_demo", "配置为最高滤波:OSR1=8(过采样 8 次)+ OSR2=8(降采样 8 次)")
exs_qmc5883p.set_osr(8, 8)
sys.wait(300) -- 等待配置生效,300ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("最高滤波: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
sys.wait(1000) -- 展示间隔,1 秒
log.info("qmc5883p_demo", "配置为最低功耗:OSR1=1 + OSR2=1")
exs_qmc5883p.set_osr(1, 1)
sys.wait(300) -- 等待配置生效,300ms
data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("最低功耗: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
-- 恢复默认配置
exs_qmc5883p.set_osr(8, 8)
log.info("qmc5883p_demo", "---- [4/7] 完成 ----")
end
--------------------------------------------------------------
-- [5/7] 软复位演示
-- - soft_reset() 复位芯片后自动重新应用当前配置,无需重新 setup()
--------------------------------------------------------------
local function demo_soft_reset()
log.info("qmc5883p_demo", "===== [5/7] 软复位演示 =====")
local result = exs_qmc5883p.soft_reset()
if not result then
log.error("qmc5883p_demo", "软复位失败")
return false
end
log.info("qmc5883p_demo", "软复位完成,配置已恢复")
sys.wait(300) -- 等待复位后数据稳定,300ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("复位后数据: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
log.info("qmc5883p_demo", "---- [5/7] 完成 ----")
return true
end
--------------------------------------------------------------
-- [6/7] 芯片自检演示
-- - self_test() 利用芯片内置自检激励信号验证信号链路是否正常
-- - 返回自检前后三轴增量(单位 LSB),增量明显大于正常噪声
-- 表示信号链路正常
--------------------------------------------------------------
local function demo_self_test()
log.info("qmc5883p_demo", "===== [6/7] 芯片自检演示 =====")
local delta = exs_qmc5883p.self_test()
if not delta then
log.error("qmc5883p_demo", "芯片自检失败")
return false
end
log.info("qmc5883p_demo", string.format("自检增量 dx=%d dy=%d dz=%d", delta.dx, delta.dy, delta.dz))
log.info("qmc5883p_demo", "自检增量明显大于正常噪声表示信号链路正常")
log.info("qmc5883p_demo", "---- [6/7] 完成 ----")
return true
end
--------------------------------------------------------------
-- [7/7] 休眠唤醒与关闭
-- - sleep() 进入挂起模式(功耗约 22 μA),wakeup() 快速恢复,无需重新 setup()
-- - close() 关闭传感器,之后需重新 setup() 才能使用
--------------------------------------------------------------
local function demo_sleep_wakeup_close()
log.info("qmc5883p_demo", "===== [7/7] 休眠唤醒与关闭 =====")
log.info("qmc5883p_demo", "进入挂起模式(低功耗约 22 μA")
exs_qmc5883p.sleep()
sys.wait(3000) -- 挂起演示 3 秒
log.info("qmc5883p_demo", "从挂起模式唤醒")
exs_qmc5883p.wakeup()
sys.wait(200) -- 等待唤醒后数据稳定,200ms
local data = exs_qmc5883p.get_data()
if data then
log.info("qmc5883p_demo", string.format("唤醒后数据: X=%.2f Y=%.2f Z=%.2f uT", data.x, data.y, data.z))
end
exs_qmc5883p.close()
log.info("qmc5883p_demo", "---- [7/7] 完成 ----")
return true
end
--------------------------------------------------------------
-- 主任务:按 [1/7]~[7/7] 顺序执行全部演示
--------------------------------------------------------------
local function qmc5883p_demo_task_func()
log.info("qmc5883p_demo", "HELLO")
sys.wait(1000) -- 等待系统启动稳定,1 秒
local ok = demo_init_and_read()
if not ok then
log.error("qmc5883p_demo", "初始化失败,演示终止")
return
end
sys.wait(500) -- 演示间隔,500ms
demo_range_switch()
sys.wait(500) -- 演示间隔,500ms
demo_odr_switch()
sys.wait(500) -- 演示间隔,500ms
demo_osr_config()
sys.wait(500) -- 演示间隔,500ms
ok = demo_soft_reset()
if not ok then
log.error("qmc5883p_demo", "软复位失败,演示终止")
return
end
sys.wait(500) -- 演示间隔,500ms
ok = demo_self_test()
if not ok then
log.error("qmc5883p_demo", "自检失败,演示终止")
return
end
sys.wait(500) -- 演示间隔,500ms
demo_sleep_wakeup_close()
log.info("qmc5883p_demo", "===== [演示完毕] =====")
log.info("qmc5883p_demo", "End")
end
-- 启动演示任务协程
sys.taskInit(qmc5883p_demo_task_func)
@@ -0,0 +1,265 @@
# QMC5883P 三轴地磁传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **qmc5883p_app.lua** - QMC5883P 功能演示模块,包含数据读取、量程切换、输出速率切换、过采样/降采样配置、软复位、自检、休眠唤醒等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_qmc5883p** - QMC5883P 扩展库,提供初始化、三轴磁场数据读取、量程切换、输出速率切换、过采样/降采样配置、软复位、芯片自检、睡眠/唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**QMC5883P 在异常 I2C 通信后可能锁死 SDA 总线。本库已内置 I2C 总线卡死自动检测与恢复功能。本 demo 默认使用软件 I2C 模式演示,也可通过 `qmc5883p_app.lua` 中的 `MODE` 变量切换为硬件 I2C 模式。
## 二、演示流程介绍
本 demo 按顺序演示 exs_qmc5883p 扩展库的 7 项功能:HELLO → [1/7] → [2/7] → [3/7] → [4/7] → [5/7] → [6/7] → [7/7] → End
### 2.1 功能演示项说明
1. **[1/7] 初始化与数据读取** - 初始化 QMC5883P 传感器并读取三轴磁场数据(X/Y/Z,单位 μT)
2. **[2/7] 量程切换演示** - 在 ±2G(高精度)、±8G(宽范围)和 ±30G(最宽量程)之间切换,观察数据变化
3. **[3/7] 输出速率切换** - 依次切换 10Hz / 50Hz / 100Hz / 200Hz 输出速率
4. **[4/7] 过采样/降采样配置** - 演示过采样率(OSR1)与降采样率(OSR2)的组合配置
5. **[5/7] 软复位演示** - 软复位芯片并自动恢复当前配置
6. **[6/7] 芯片自检演示** - 利用芯片内置自检激励信号验证信号链路是否正常
7. **[7/7] 休眠唤醒与关闭** - 演示 sleep()/wakeup() 挂起与唤醒,以及 close() 关闭
## 三、显示效果
![](https://docs.openluat.com/cdn/image/Air780EHM_qmc5883p.png)
## 四、演示硬件环境
### 4.1 硬件清单
- Air780EPM 核心板 × 1
- QMC5883P 三轴地磁传感器模块 × 1
demo所演示的QMC5883P[购买链接](https://item.taobao.com/item.htm?id=40554139118&mi_id=000052zSrRJbsW6EV_ocgwEuuox1wD1Jox20itdXNm9CiTc&skuId=4534297737305&spm=tbpc.boughtlist.suborder_itemtitle.1.67002e8dIVsk7g)
- 母对母杜邦线 × 5
- TYPE-C 数据线 × 1
### 4.2 接线配置
#### 4.2.1 QMC5883P 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>QMC5883P 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<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>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 程序入口
2. **qmc5883p_app.lua** - QMC5883P 功能演示模块
3. **exs_qmc5883p** - QMC5883P 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 QMC5883P 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 qmc5883p_app.lua 演示模块
require "qmc5883p_app"
```
如需切换通信模式,修改 `qmc5883p_app.lua` 中的 `MODE` 变量(1=软件 I2C2=硬件 I2C)。
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/7] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴磁场数据(X/Y/Z,单位 μT)的连续读取
#### 6.4.2 量程切换演示
1. 自动进入 [2/7] 量程切换演示
2. 观察 ±2G 高精度量程下的数据
3. 观察 ±8G 宽范围量程下的数据
4. 观察 ±30G 最宽量程下的数据
#### 6.4.3 输出速率切换演示
1. 自动进入 [3/7] 输出速率切换演示
2. 依次观察 10Hz / 50Hz / 100Hz / 200Hz 下的数据读取
3. 最终恢复为 10Hz
#### 6.4.4 过采样/降采样配置演示
1. 自动进入 [4/7] 过采样/降采样配置演示
2. 观察最高滤波配置(OSR1=8 + OSR2=8)下的数据
3. 观察最低功耗配置(OSR1=1 + OSR2=1)下的数据
#### 6.4.5 软复位演示
1. 自动进入 [5/7] 软复位演示
2. 观察软复位完成后配置自动恢复
3. 观察复位后的数据读取
#### 6.4.6 芯片自检演示
1. 自动进入 [6/7] 芯片自检演示
2. 观察自检增量 dx/dy/dz 输出
3. 增量明显大于正常噪声表示信号链路正常
#### 6.4.7 休眠唤醒与关闭演示
1. 自动进入 [7/7] 休眠唤醒与关闭演示
2. 观察 sleep() 进入挂起模式
3. 观察 wakeup() 唤醒后数据恢复
4. 观察 close() 关闭传感器
### 6.5 预期效果
- **初始化与数据读取**:QMC5883P 初始化成功,三轴磁场数据正常输出
- **量程切换**:量程切换后数据范围随之变化,灵敏度不同
- **输出速率切换**:不同速率下均能正常读取数据
- **过采样/降采样配置**:配置切换后均能正常读取数据
- **软复位**:复位后配置自动恢复,数据读取正常
- **芯片自检**:自检增量明显大于噪声,信号链路正常
- **休眠唤醒与关闭**:sleep 后进入低功耗,wakeup 后数据读取正常
**日志如下:**
```lua
[2026-08-07 14:44:44.084][000000000.200] I/user.main QMC5883P_Demo 001.999.000
[2026-08-07 14:44:44.090][000000000.215] D/user.exs_qmc5883p version -> 202608050000
[2026-08-07 14:44:44.099][000000000.216] I/user.qmc5883p_demo HELLO
[2026-08-07 14:44:44.219][000000001.216] I/user.qmc5883p_demo ===== [1/7] =====
[2026-08-07 14:44:44.224][000000001.222] I/user.exs_qmc5883p I2C总线恢复完成
[2026-08-07 14:44:44.229][000000001.229] I/user.exs_qmc5883p , range=8G odr=10Hz osr=8 osr2=8 set_reset=on
[2026-08-07 14:44:44.237][000000001.229] I/user.qmc5883p_demo QMC5883P : 202608050000
[2026-08-07 14:44:44.242][000000001.431] I/user.exs_qmc5883p : F4 FC 23 FB 31 FF
[2026-08-07 14:44:44.251][000000001.433] I/user.qmc5883p_demo X=-20.80 Y=-33.20 Z=-5.52 uT
[2026-08-07 14:44:44.418][000000001.937] I/user.qmc5883p_demo 1: X=-20.83 Y=-33.39 Z=-5.71 uT
[2026-08-07 14:44:44.929][000000002.441] I/user.qmc5883p_demo 2: X=-20.80 Y=-33.36 Z=-5.81 uT
[2026-08-07 14:44:45.426][000000002.945] I/user.qmc5883p_demo 3: X=-22.00 Y=-29.60 Z=-6.40 uT
[2026-08-07 14:44:45.433][000000002.945] I/user.qmc5883p_demo ---- [1/7] 完成 ----
[2026-08-07 14:44:45.922][000000003.445] I/user.qmc5883p_demo ===== [2/7] =====
[2026-08-07 14:44:45.926][000000003.445] I/user.qmc5883p_demo 2G 15000 LSB/G
[2026-08-07 14:44:45.938][000000003.450] I/user.exs_qmc5883p 2G
[2026-08-07 14:44:46.234][000000003.754] I/user.qmc5883p_demo 2G : X=-16.60 Y=-6.70 Z=-29.81 uT
[2026-08-07 14:44:47.229][000000004.754] I/user.qmc5883p_demo 8G 3750 LSB/G
[2026-08-07 14:44:47.244][000000004.758] I/user.exs_qmc5883p 8G
[2026-08-07 14:44:47.541][000000005.062] I/user.qmc5883p_demo 8G : X=31.09 Y=5.89 Z=-17.23 uT
[2026-08-07 14:44:48.551][000000006.062] I/user.qmc5883p_demo 30G
[2026-08-07 14:44:48.558][000000006.066] I/user.exs_qmc5883p 30G
[2026-08-07 14:44:48.847][000000006.370] I/user.qmc5883p_demo 30G : X=-16.50 Y=5.40 Z=-19.00 uT
[2026-08-07 14:44:48.862][000000006.374] I/user.exs_qmc5883p 8G
[2026-08-07 14:44:48.868][000000006.374] I/user.qmc5883p_demo ---- [2/7] 完成 ----
[2026-08-07 14:44:49.361][000000006.874] I/user.qmc5883p_demo ===== [3/7] =====
[2026-08-07 14:44:49.368][000000006.875] I/user.qmc5883p_demo 10Hz
[2026-08-07 14:44:49.373][000000006.879] I/user.exs_qmc5883p 10Hz
[2026-08-07 14:44:49.859][000000007.383] I/user.qmc5883p_demo 10Hz : X=-23.39 Y=-9.60 Z=-20.96 uT
[2026-08-07 14:44:50.367][000000007.883] I/user.qmc5883p_demo 50Hz
[2026-08-07 14:44:50.371][000000007.887] I/user.exs_qmc5883p 50Hz
[2026-08-07 14:44:50.866][000000008.391] I/user.qmc5883p_demo 50Hz : X=11.52 Y=-20.48 Z=19.33 uT
[2026-08-07 14:44:51.366][000000008.891] I/user.qmc5883p_demo 100Hz
[2026-08-07 14:44:51.382][000000008.895] I/user.exs_qmc5883p 100Hz
[2026-08-07 14:44:51.881][000000009.399] I/user.qmc5883p_demo 100Hz : X=-14.75 Y=-15.76 Z=12.11 uT
[2026-08-07 14:44:52.378][000000009.899] I/user.qmc5883p_demo 200Hz
[2026-08-07 14:44:52.383][000000009.903] I/user.exs_qmc5883p 200Hz
[2026-08-07 14:44:52.891][000000010.407] I/user.qmc5883p_demo 200Hz : X=-19.97 Y=-13.39 Z=-27.09 uT
[2026-08-07 14:44:53.389][000000010.911] I/user.exs_qmc5883p 10Hz
[2026-08-07 14:44:53.400][000000010.911] I/user.qmc5883p_demo ---- [3/7] 完成 ----
[2026-08-07 14:44:53.889][000000011.411] I/user.qmc5883p_demo ===== [4/7] / =====
[2026-08-07 14:44:53.893][000000011.411] I/user.qmc5883p_demo OSR1=8 8 + OSR2=8 8
[2026-08-07 14:44:53.907][000000011.416] I/user.exs_qmc5883p / osr=8 osr2=8
[2026-08-07 14:44:54.199][000000011.720] I/user.qmc5883p_demo : X=-19.60 Y=-11.20 Z=-25.89 uT
[2026-08-07 14:44:55.206][000000012.720] I/user.qmc5883p_demo OSR1=1 + OSR2=1
[2026-08-07 14:44:55.212][000000012.724] I/user.exs_qmc5883p / osr=1 osr2=1
[2026-08-07 14:44:55.504][000000013.028] I/user.qmc5883p_demo : X=-25.89 Y=-8.88 Z=-5.39 uT
[2026-08-07 14:44:55.519][000000013.032] I/user.exs_qmc5883p / osr=8 osr2=8
[2026-08-07 14:44:55.526][000000013.032] I/user.qmc5883p_demo ---- [4/7] 完成 ----
[2026-08-07 14:44:56.015][000000013.532] I/user.qmc5883p_demo ===== [5/7] =====
[2026-08-07 14:44:56.030][000000013.544] I/user.exs_qmc5883p
[2026-08-07 14:44:56.033][000000013.544] I/user.qmc5883p_demo
[2026-08-07 14:44:56.327][000000013.848] I/user.qmc5883p_demo : X=3.01 Y=5.31 Z=-5.41 uT
[2026-08-07 14:44:56.334][000000013.848] I/user.qmc5883p_demo ---- [5/7] 完成 ----
[2026-08-07 14:44:56.823][000000014.348] I/user.qmc5883p_demo ===== [6/7] =====
[2026-08-07 14:44:57.043][000000014.559] I/user.exs_qmc5883p , dx=-154 dy=-130 dz=-84
[2026-08-07 14:44:57.049][000000014.559] I/user.qmc5883p_demo dx=-154 dy=-130 dz=-84
[2026-08-07 14:44:57.053][000000014.560] I/user.qmc5883p_demo >1000 LSB
[2026-08-07 14:44:57.058][000000014.560] I/user.qmc5883p_demo ---- [6/7] 完成 ----
[2026-08-07 14:44:57.540][000000015.060] I/user.qmc5883p_demo ===== [7/7] =====
[2026-08-07 14:44:57.546][000000015.060] I/user.qmc5883p_demo 22 μA
[2026-08-07 14:44:57.551][000000015.062] I/user.exs_qmc5883p
[2026-08-07 14:45:00.545][000000018.062] I/user.qmc5883p_demo
[2026-08-07 14:45:00.551][000000018.064] I/user.exs_qmc5883p
[2026-08-07 14:45:00.747][000000018.267] I/user.qmc5883p_demo : X=-4.85 Y=-0.27 Z=3.81 uT
[2026-08-07 14:45:00.752][000000018.268] I/user.exs_qmc5883p
[2026-08-07 14:45:00.754][000000018.268] I/user.qmc5883p_demo ---- [7/7] 完成 ----
[2026-08-07 14:45:00.757][000000018.269] I/user.qmc5883p_demo ===== [] =====
[2026-08-07 14:45:00.761][000000018.269] I/user.qmc5883p_demo End
```
### 6.6 故障排除
1. **传感器初始化失败**
- 检查 QMC5883P 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_qmc5883p 扩展库已正常加载
2. **读取数据始终为零**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(QMC5883P 7 位地址为 0x2C,写操作地址 0x58,读操作地址 0x59)
- 确认接线无松动
3. **量程切换无效果**
- 确认 `set_range()` 参数为 "2G"/"8G"/"12G"/"30G"
- 切换后需等待传感器稳定再读取数据
4. **自检增量过小**
- 确认传感器工作正常,供电稳定
- 自检增量明显大于正常噪声表示信号链路正常
### 6.7 扩展功能建议
exs_qmc5883p 更多接口的使用可以查看 [exs_qmc5883p 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_qmc5883p/)
@@ -0,0 +1,70 @@
--[[
@module main
@summary SC7A20H 三轴加速度传感器 Demo 入口
@version 1.0
@date 2026.07.23
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_sc7a20h 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、初始化和数据读取([1/4]
2、量程切换演示([2/4]
3、输出速率切换([3/4]
4、休眠与唤醒演示([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "SC7A20H_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 sc7a20h_demo.lua 演示模块
require "sc7a20h_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,216 @@
# SC7A20H 三轴加速度传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **sc7a20h_demo.lua** - SC7A20H 功能演示模块,包含数据读取、量程切换、输出速率切换、休眠唤醒等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_sc7a20h** - SC7A20H 扩展库,提供初始化、三轴加速度数据读取、量程切换、输出速率切换、功耗模式切换、睡眠/唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**SC7A20H 在异常 I2C 通信后可能锁死 SDA 总线。本库已内置 I2C 总线卡死自动检测与恢复功能。本 demo 使用软件 I2C 模式演示。
## 二、演示流程介绍
本 demo 按顺序演示 exs_sc7a20h 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 初始化与数据读取** - 初始化 SC7A20H 传感器并读取三轴加速度数据(X/Y/Z,单位 g)
2. **[2/4] 量程切换演示** - 在 ±2g(高精度)和 ±4g(宽范围)量程之间切换,观察数据变化
3. **[3/4] 输出速率切换** - 依次切换 25Hz / 50Hz / 100Hz / 200Hz 输出速率
4. **[4/4] 休眠与唤醒演示** - 演示 sleep()/wakeup() 待机与唤醒
## 三、显示效果
![](https://docs.openluat.com/cdn/image/Air780EHM_exs_sc7a20h.png)
## 四、演示硬件环境
### 4.1 硬件清单
- Air780EPM 核心板 × 1
- SC7A20H 三轴加速度传感器模块 × 1
demo所演示的SC7A20H 三轴加速度传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=952174888778&mi_id=0000k9PlrHnaylHxzFwS6fLcPWcOuAeIBtBNy8ctxSmKAXs&ns=1&priceTId=215045e417839190910407222e1198&spm=a21n57.1.hoverItem.3&utparam=%7B%22aplus_abtest%22%3A%22c1f47c9c4ac843532d170075a796f4a9%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 6
- TYPE-C 数据线 × 1
### 4.2 接线配置
#### 4.2.1 SC7A20H 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>SC7A20H 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>SDA<br/></td></tr>
<tr>
<td>30/GPIO29<br/></td><td>INT1<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 程序入口
2. **sc7a20h_demo.lua** - SC7A20H 功能演示模块
3. **exs_sc7a20h** - SC7A20H 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 SC7A20H 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 sc7a20h_demo.lua 演示模块
require "sc7a20h_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 初始化与数据读取演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/4] 初始化与数据读取
2. 观察日志输出,确认初始化成功
3. 观察三轴加速度数据(X/Y/Z,单位 g)的连续读取
#### 6.4.2 量程切换演示
1. 自动进入 [2/4] 量程切换演示
2. 观察 ±4g 宽范围量程下的数据
3. 观察 ±2g 高精度量程下的数据
#### 6.4.3 输出速率切换演示
1. 自动进入 [3/4] 输出速率切换演示
2. 依次观察 25Hz / 50Hz / 100Hz / 200Hz 下的数据读取
3. 最终恢复为 100Hz
#### 6.4.4 休眠与唤醒演示
1. 自动进入 [4/4] 休眠与唤醒演示
2. 观察 sleep() 进入待机模式
3. 观察 wakeup() 唤醒后数据恢复
### 6.5 预期效果
- **初始化与数据读取**:SC7A20H 初始化成功,三轴加速度数据正常输出
- **量程切换**:量程切换后数据范围随之变化,灵敏度不同
- **输出速率切换**:不同速率下均能正常读取数据
- **休眠与唤醒**sleep 后进入低功耗,wakeup 后数据读取正常
- **打印日志**
```lua
[2026-07-23 14:13:19.999][000000000.246] I/user.main SC7A20H_Demo 001.999.000
[2026-07-23 14:13:20.005][000000000.262] D/user.exs_sc7a20h version -> 202607180900
[2026-07-23 14:13:20.014][000000000.264] I/user.sc7a20h_demo HELLO
[2026-07-23 14:13:20.506][000000001.265] I/user.sc7a20h_demo ===== [1/4] =====
[2026-07-23 14:13:20.521][000000001.274] I/user.exs_sc7a20h SC7A20H @ I2C 0x18
[2026-07-23 14:13:20.537][000000001.295] I/user.exs_sc7a20h REG读回 CTRL1=0x57 CTRL4=0x88 STATUS=0xFF INT1_CFG=0x00
[2026-07-23 14:13:20.548][000000001.302] I/user.exs_sc7a20h INT1 , gpio=29
[2026-07-23 14:13:20.555][000000001.303] I/user.exs_sc7a20h mode=I2C range=2g odr=100Hz mode=highres
[2026-07-23 14:13:20.564][000000001.303] I/user.sc7a20h_demo SC7A20H : 202607180900
[2026-07-23 14:13:20.570][000000001.318] I/user.exs_sc7a20h CTRL1=0x57 CTRL2=0x01 CTRL3=0x40 CTRL4=0x88 CTRL5=0x08 CTRL6=0x00 STATUS=0xFF INT1_SRC=0x15 INT2_SRC=0x00 FIFO_SRC=0x20
[2026-07-23 14:13:20.757][000000001.521] I/user.sc7a20h_demo X=-0.660 Y=0.545 Z=0.551 g
[2026-07-23 14:13:22.264][000000003.024] I/user.sc7a20h_demo 1: X=-0.660 Y=0.549 Z=0.555 g
[2026-07-23 14:13:22.774][000000003.527] I/user.sc7a20h_demo 2: X=-0.666 Y=0.552 Z=0.552 g
[2026-07-23 14:13:23.269][000000004.030] I/user.sc7a20h_demo 3: X=-0.661 Y=0.545 Z=0.551 g
[2026-07-23 14:13:23.273][000000004.030] I/user.sc7a20h_demo ---- [1/4] 完成 ----
[2026-07-23 14:13:23.769][000000004.531] I/user.sc7a20h_demo ===== [2/4] =====
[2026-07-23 14:13:23.773][000000004.531] I/user.sc7a20h_demo 4g
[2026-07-23 14:13:23.776][000000004.534] I/user.exs_sc7a20h 4g
[2026-07-23 14:13:24.080][000000004.836] I/user.sc7a20h_demo 4g : X=-0.662 Y=0.545 Z=0.551 g
[2026-07-23 14:13:25.076][000000005.837] I/user.sc7a20h_demo 2g
[2026-07-23 14:13:25.081][000000005.839] I/user.exs_sc7a20h 2g
[2026-07-23 14:13:25.387][000000006.142] I/user.sc7a20h_demo 2g : X=-0.663 Y=0.546 Z=0.556 g
[2026-07-23 14:13:26.385][000000007.143] I/user.sc7a20h_demo ---- [2/4] 完成 ----
[2026-07-23 14:13:26.882][000000007.643] I/user.sc7a20h_demo ===== [3/4] =====
[2026-07-23 14:13:26.898][000000007.643] I/user.sc7a20h_demo 25Hz
[2026-07-23 14:13:26.913][000000007.646] I/user.exs_sc7a20h 25Hz
[2026-07-23 14:13:27.395][000000008.149] I/user.sc7a20h_demo 25Hz : X=-0.670 Y=0.551 Z=0.559 g
[2026-07-23 14:13:27.891][000000008.650] I/user.sc7a20h_demo 50Hz
[2026-07-23 14:13:27.903][000000008.653] I/user.exs_sc7a20h 50Hz
[2026-07-23 14:13:28.092][000000008.856] I/user.sc7a20h_demo int1: =down X=0.125 Y=0.323 Z=0.513 g
[2026-07-23 14:13:28.373][000000009.133] I/user.sc7a20h_demo int1: =back X=0.475 Y=0.291 Z=0.404 g
[2026-07-23 14:13:28.389][000000009.155] I/user.sc7a20h_demo 50Hz : X=0.208 Y=0.180 Z=0.390 g
[2026-07-23 14:13:28.656][000000009.410] I/user.sc7a20h_demo int1: =down X=0.075 Y=1.164 Z=1.317 g
[2026-07-23 14:13:28.876][000000009.628] I/user.sc7a20h_demo int1: =down X=0.281 Y=0.115 Z=0.308 g
[2026-07-23 14:13:28.891][000000009.656] I/user.sc7a20h_demo 100Hz
[2026-07-23 14:13:28.903][000000009.659] I/user.exs_sc7a20h 100Hz
[2026-07-23 14:13:28.937][000000009.693] I/user.sc7a20h_demo int1: =down X=-0.659 Y=1.035 Z=1.466 g
[2026-07-23 14:13:29.404][000000010.161] I/user.sc7a20h_demo 100Hz : X=-0.659 Y=0.357 Z=0.859 g
[2026-07-23 14:13:29.905][000000010.662] I/user.sc7a20h_demo 200Hz
[2026-07-23 14:13:29.921][000000010.665] I/user.exs_sc7a20h 200Hz
[2026-07-23 14:13:30.404][000000011.167] I/user.sc7a20h_demo 200Hz : X=-0.440 Y=0.722 Z=0.582 g
[2026-07-23 14:13:30.918][000000011.670] I/user.exs_sc7a20h 100Hz
[2026-07-23 14:13:30.933][000000011.670] I/user.sc7a20h_demo ---- [3/4] 完成 ----
[2026-07-23 14:13:31.416][000000012.171] I/user.sc7a20h_demo ===== [4/4] =====
[2026-07-23 14:13:31.432][000000012.171] I/user.sc7a20h_demo power-down
[2026-07-23 14:13:31.444][000000012.174] I/user.exs_sc7a20h power-down
[2026-07-23 14:13:41.913][000000022.674] I/user.sc7a20h_demo power-down
[2026-07-23 14:13:41.932][000000022.676] I/user.exs_sc7a20h power-down ODR=100Hz
[2026-07-23 14:13:42.116][000000022.879] I/user.sc7a20h_demo : X=-0.614 Y=0.165 Z=0.852 g
[2026-07-23 14:13:42.132][000000022.879] I/user.sc7a20h_demo ---- [4/4] 完成 ----
[2026-07-23 14:13:42.613][000000023.380] I/user.sc7a20h_demo ===== [] =====
[2026-07-23 14:13:42.628][000000023.386] I/user.exs_sc7a20h
[2026-07-23 14:13:42.640][000000023.386] I/user.sc7a20h_demo End
```
### 6.6 故障排除
1. **传感器初始化失败**
- 检查 SC7A20H 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_sc7a20h 扩展库已正常加载
2. **读取数据始终为零**
- 检查传感器供电是否正常
- 检查 I2C 地址是否正确(SA0=GND 时地址 0x18SA0=VCC 时地址 0x19
- 确认接线无松动
3. **量程切换无效果**
- 确认 `set_range()` 参数为 "2g"、"4g"、"8g" 或 "16g"
- 切换后需等待传感器稳定再读取数据
### 6.7 扩展功能建议
exs_sc7a20h 更多接口的使用可以查看 [exs_sc7a20h 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_sc7a20h/)
@@ -0,0 +1,154 @@
--[[
@module sc7a20h_demo
@summary SC7A20H 三轴加速度传感器演示模块,包含所有功能的演示用例
@version 1.1
@date 2026.07.23
@author 江访
@usage
本文件包含 SC7A20H 的逐项功能演示。
通过 exs_sc7a20h 扩展库的 API 逐一演示数据读取、量程切换、输出速率切换、休眠唤醒等功能。
]]
local exs_sc7a20h = require "exs_sc7a20h"
local function sc7a20h_int1_cb(data)
if data.dir then
log.info("sc7a20h_demo", string.format("int1: 朝向=%s X=%.3f Y=%.3f Z=%.3f g", data.dir, data.x, data.y, data.z))
else
log.info("sc7a20h_demo", string.format("int1: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
end
-- [1/4] 初始化与数据读取
local function demo_init_and_read()
log.info("sc7a20h_demo", "===== [1/4] 初始化与数据读取 =====")
local result = exs_sc7a20h.setup("I2C", {
scl = 31,
sda = 30,
powermode = "highres",
enable_direction = "6d",
int1 = { int_gpio = 29, activity = true, threshold_mg = 500, cb = sc7a20h_int1_cb },
})
if not result then
log.error("sc7a20h_demo", "SC7A20H 初始化失败,请检查接线")
return false
end
log.info("sc7a20h_demo", "SC7A20H 初始化成功,版本:", exs_sc7a20h.version())
exs_sc7a20h.dump_regs()
sys.wait(200)
local data = exs_sc7a20h.get_data()
if data then
log.info("sc7a20h_demo", string.format("X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
else
log.error("sc7a20h_demo", "读取数据失败")
end
sys.wait(1000)
for i = 1, 3 do
sys.wait(500)
local d = exs_sc7a20h.get_data()
if d then
log.info("sc7a20h_demo", string.format("第%d次读取: X=%.3f Y=%.3f Z=%.3f g", i, d.x, d.y, d.z))
end
end
log.info("sc7a20h_demo", "---- [1/4] 完成 ----")
return true
end
-- [2/4] 量程切换演示
local function demo_range_switch()
log.info("sc7a20h_demo", "===== [2/4] 量程切换演示 =====")
log.info("sc7a20h_demo", "切换量程为 4g")
exs_sc7a20h.set_range("4g")
sys.wait(300)
local data = exs_sc7a20h.get_data()
if data then
log.info("sc7a20h_demo", string.format("4g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("sc7a20h_demo", "切换量程为 2g(高精度)")
exs_sc7a20h.set_range("2g")
sys.wait(300)
data = exs_sc7a20h.get_data()
if data then
log.info("sc7a20h_demo", string.format("2g 量程: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
sys.wait(1000)
log.info("sc7a20h_demo", "---- [2/4] 完成 ----")
end
-- [3/4] 输出速率切换
local function demo_odr_switch()
log.info("sc7a20h_demo", "===== [3/4] 输出速率切换 =====")
local odr_list = { 25, 50, 100, 200 }
for _, odr in ipairs(odr_list) do
log.info("sc7a20h_demo", string.format("设置输出速率为 %dHz", odr))
exs_sc7a20h.set_odr(odr)
sys.wait(500)
local data = exs_sc7a20h.get_data()
if data then
log.info("sc7a20h_demo", string.format("%dHz 下: X=%.3f Y=%.3f Z=%.3f g", odr, data.x, data.y, data.z))
end
sys.wait(500)
end
exs_sc7a20h.set_odr(100)
log.info("sc7a20h_demo", "---- [3/4] 完成 ----")
end
-- [4/4] 休眠与唤醒演示
local function demo_sleep_wakeup()
log.info("sc7a20h_demo", "===== [4/4] 休眠与唤醒演示 =====")
log.info("sc7a20h_demo", "进入 power-down 模式(低功耗)")
exs_sc7a20h.sleep(); sys.wait(10000)
sys.wait(500)
log.info("sc7a20h_demo", "从 power-down 模式唤醒")
exs_sc7a20h.wakeup()
sys.wait(200)
local data = exs_sc7a20h.get_data()
if data then
log.info("sc7a20h_demo", string.format("唤醒后数据: X=%.3f Y=%.3f Z=%.3f g", data.x, data.y, data.z))
end
log.info("sc7a20h_demo", "---- [4/4] 完成 ----")
end
local function sc7a20h_demo_task_func()
log.info("sc7a20h_demo", "HELLO")
sys.wait(1000)
local ok = demo_init_and_read()
if not ok then
log.error("sc7a20h_demo", "初始化失败,演示终止")
return
end
sys.wait(500)
demo_range_switch()
sys.wait(500)
demo_odr_switch()
sys.wait(500)
demo_sleep_wakeup()
sys.wait(500)
log.info("sc7a20h_demo", "===== [演示完毕] =====")
exs_sc7a20h.close()
log.info("sc7a20h_demo", "End")
end
sys.taskInit(sc7a20h_demo_task_func)
@@ -0,0 +1,70 @@
--[[
@module main
@summary SPL06-001 数字气压传感器 Demo 入口
@version 1.0
@date 2026.08.21
@author 江访
@usage
本 demo 演示 exs_spl06 扩展库的完整功能,业务逻辑见 spl06_app.lua
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_spl06 扩展库的完整功能,顺序为:
HELLO→[1/5]→[2/5]→[3/5]→[4/5]→[5/5]→End
1、初始化([1/5]
2、数据读取([2/5]
3、海拔高度计算([3/5]
4、过采样率切换([4/5]
5、测量模式切换与休眠唤醒([5/5])
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "sensor_spl06" -- 项目命名
VERSION = "001.999.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秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- local function mem_check_func()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end
-- sys.timerLoopStart(mem_check_func, 3000)
-- 加载 spl06_app.lua 演示模块
require "spl06_app"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,232 @@
# SPL06-001 数字气压传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **spl06_app.lua** - SPL06-001 功能演示模块,包含初始化、数据读取、海拔计算、过采样切换、模式切换与休眠唤醒等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_spl06** - SPL06-001 扩展库,提供初始化(I2C 地址自动探测、芯片 ID 校验、校准系数读取)、气压/温度读取、过采样率切换、海拔高度计算、测量模式切换、休眠唤醒、关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**SPL06-001 使用标准 I2C 接口通信,软件 I2C 模式无需指定 I2C 控制器,任意 GPIO 均可模拟 I2C 时序,灵活性更高。本 demo 默认使用软件 I2C 模式(SCL=GPIO31, SDA=GPIO30)演示,也可通过 `spl06_app.lua` 中的 `MODE` 变量切换为硬件 I2C 模式。
> SPL06-001 是**双地址芯片**:SDO 引脚悬空或接高电平时地址为 0x77,SDO 接 GND 时地址为 0x76。`setup()` 不传 `addr` 时扩展库会自动探测两个地址并校验芯片 ID,无需手动配置。
## 二、演示流程介绍
本 demo 按顺序演示 exs_spl06 扩展库的 5 项功能:HELLO → [1/5] → [2/5] → [3/5] → [4/5] → [5/5] → End
### 2.1 功能演示项说明
1. **[1/5] 初始化** - 初始化传感器(I2C 地址自动探测、芯片 ID 校验、校准系数读取、过采样率配置、缩放基准建立)
2. **[2/5] 数据读取** - 连续读取 5 次气压(hPa)和温度(℃),并演示单次读取接口
3. **[3/5] 海拔高度计算** - 按国际气压测高公式计算海拔高度,支持自定义海平面气压
4. **[4/5] 过采样率切换** - 依次切换气压过采样率 1x/16x/64x(含缩放因子动态自校准),观察不同精度下的数据
5. **[5/5] 模式切换与休眠唤醒** - 切换待机/单次/连续测量模式,演示休眠唤醒与资源释放
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- SPL06-001 数字气压传感器模块 × 1
demo所演示的SPL06-001 数字气压传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=754082120547&mi_id=00002cEFf0br1iAbdFxAZR2wZA_t7gtaASLPpQM39JfcWTs&ns=1&priceTId=214783cd17873034704783015e0fdd&skuId=5372558067929&spm=a21n57.1.hoverItem.2&utparam=%7B%22aplus_abtest%22%3A%222783bdd13dde62ac2d7099c56862328f%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 I2C 模式接线
| Air780EPM 核心板 | SPL06-001 模块 |
|------------------|--------------|
| 32/GPIO31 | SCL |
| 31/GPIO30 | SDA |
| VDD_EXT | VCC3.3V |
| GND | GND |
> 说明:
> 1. 接线时注意杜邦线不宜过长,以免通信不稳定
> 2. SCL 和 SDA 需外接 4.7kΩ~10kΩ 上拉电阻到 VCC(部分模块已自带)
> 3. 模块 SDO 引脚悬空(或接高电平)时 I2C 地址为 0x77,接 GND 时地址为 0x76;扩展库会自动探测,无需关心
![](https://docs.openluat.com/cdn/image/Air780EHM_SPL06.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 脚本文件
1. **main.lua** - 程序入口
2. **spl06_app.lua** - SPL06-001 功能演示模块
3. **exs_spl06** - SPL06-001 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 SPL06-001 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 spl06_app.lua 演示模块
require "spl06_app"
```
如需切换通信模式,修改 `spl06_app.lua` 中的 `MODE` 变量(1=软件 I2C2=硬件 I2C)。
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 初始化演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/5] 初始化
2. 观察日志输出,确认初始化成功(应看到 `芯片地址自适应:0x76/0x77``初始化完成`
#### 5.4.2 数据读取演示
1. 自动进入 [2/5] 数据读取
2. 观察气压(hPa)和温度(℃)的连续读取,共 5 次,再观察单次读取接口结果
#### 5.4.3 海拔计算演示
1. 自动进入 [3/5] 海拔高度计算
2. 观察气压值与两种海平面气压基准(默认 1013.25hPa / 自定义 1005hPa)下的海拔高度
#### 5.4.4 过采样率切换演示
1. 自动进入 [4/5] 过采样率切换演示
2. 依次观察气压 OSR=01x 最快)、OSR=416x 默认)、OSR=664x 高精度)下的数据
3. 切换高过采样时观察缩放因子动态自校准日志(`缩放因子动态校准: kP ... kT ...`
4. 注意 OSR 越高,数据跳动越小,但单次测量耗时越长
#### 5.4.5 模式切换与休眠唤醒演示
1. 自动进入 [5/5] 模式切换与休眠唤醒
2. 依次观察待机(standby)/单次气压(pressure/连续(both)模式的切换日志
3. 观察睡眠 3 秒后唤醒、唤醒后数据恢复输出、最后关闭传感器
### 5.5 预期效果
- **初始化**:SPL06-001 初始化成功,芯片地址自动探测锁定、校准系数读取正常
- **数据读取**:气压与温度数据正常输出(气压约 1002.87hPa,温度约 23.6℃)
- **海拔计算**:基于实测气压输出海拔高度(默认/自定义海平面气压两种)
- **过采样率切换**:不同 OSR 下精度不同,OSR 越高数据越稳定;切换高过采样时自动动态校准
- **模式切换与休眠唤醒**:待机/单次/连续模式切换正常,睡眠后唤醒数据恢复输出
- **日志如下:**
```lua
[2026-08-21 17:28:45.888][000000000.200] I/user.main sensor_spl06 001.999.000
[2026-08-21 17:28:45.899][000000000.216] D/user.exs_spl06 version -> 202608211700
[2026-08-21 17:28:45.942][000000000.217] I/user.spl06_app HELLO
[2026-08-21 17:28:47.219][000000001.217] I/user.spl06_app ===== [1/5] =====
[2026-08-21 17:28:47.638][000000001.225] I/user.exs_spl06 0x76
[2026-08-21 17:28:47.681][000000001.294] I/user.exs_spl06 : P -112660 T 2295708
[2026-08-21 17:28:47.761][000000001.294] I/user.exs_spl06 , prs_osr=4 tmp_osr=3
[2026-08-21 17:28:47.833][000000001.295] I/user.spl06_app SPL06-001 : 202608211700
[2026-08-21 17:28:47.882][000000001.795] I/user.spl06_app ===== [2/5] =====
[2026-08-21 17:28:47.899][000000001.795] I/user.spl06_app 5 / hPa /
[2026-08-21 17:28:48.336][000000001.802] I/user.spl06_app 1: =1002.86hPa =23.8
[2026-08-21 17:28:48.366][000000002.808] I/user.spl06_app 2: =1002.85hPa =23.8
[2026-08-21 17:28:48.408][000000003.814] I/user.spl06_app 3: =1002.86hPa =23.8
[2026-08-21 17:28:48.588][000000004.820] I/user.spl06_app 4: =1002.86hPa =23.8
[2026-08-21 17:28:49.571][000000005.826] I/user.spl06_app 5: =1002.86hPa =23.8
[2026-08-21 17:28:50.691][000000006.928] I/user.spl06_app =1002.86hPa =23.8
[2026-08-21 17:28:51.194][000000007.428] I/user.spl06_app ===== [3/5] =====
[2026-08-21 17:28:51.240][000000007.440] I/user.spl06_app =1002.87hPa=86.81005hPa海拔=17.9
[2026-08-21 17:28:51.687][000000007.940] I/user.spl06_app ===== [4/5] =====
[2026-08-21 17:28:51.709][000000007.940] I/user.spl06_app 1x/16x/64x 8x
[2026-08-21 17:28:51.749][000000007.985] I/user.exs_spl06 : kP 524310 kT 7864779
[2026-08-21 17:28:51.765][000000007.986] I/user.exs_spl06 , prs_osr=0 tmp_osr=3
[2026-08-21 17:28:51.802][000000007.986] I/user.spl06_app OSR=0(1x)
[2026-08-21 17:28:52.186][000000008.442] I/user.spl06_app OSR=0 1: =1002.83hPa =23.8
[2026-08-21 17:28:53.202][000000009.448] I/user.spl06_app OSR=0 2: =1002.88hPa =23.8
[2026-08-21 17:28:54.196][000000010.454] I/user.spl06_app OSR=0 3: =1002.87hPa =23.8
[2026-08-21 17:28:55.210][000000011.460] I/user.spl06_app OSR=0 4: =1002.84hPa =23.8
[2026-08-21 17:28:56.213][000000012.466] I/user.spl06_app OSR=0 5: =1002.84hPa =23.8
[2026-08-21 17:28:57.208][000000013.466] I/user.spl06_app OSR=0 =1002.85hPa
[2026-08-21 17:28:57.286][000000013.536] I/user.exs_spl06 : kP 253984 kT 7868382
[2026-08-21 17:28:57.294][000000013.537] I/user.exs_spl06 , prs_osr=4 tmp_osr=3
[2026-08-21 17:28:57.304][000000013.537] I/user.spl06_app OSR=4(16x)
[2026-08-21 17:28:57.800][000000014.047] I/user.spl06_app OSR=4 1: =1002.85hPa =23.8
[2026-08-21 17:28:58.805][000000015.053] I/user.spl06_app OSR=4 2: =1002.85hPa =23.8
[2026-08-21 17:28:59.823][000000016.059] I/user.spl06_app OSR=4 3: =1002.85hPa =23.8
[2026-08-21 17:29:00.819][000000017.065] I/user.spl06_app OSR=4 4: =1002.85hPa =23.8
[2026-08-21 17:29:01.814][000000018.071] I/user.spl06_app OSR=4 5: =1002.85hPa =23.8
[2026-08-21 17:29:02.822][000000019.071] I/user.spl06_app OSR=4 =1002.85hPa
[2026-08-21 17:29:02.963][000000019.219] I/user.exs_spl06 : kP 1040493 kT 7864292
[2026-08-21 17:29:02.970][000000019.220] I/user.exs_spl06 , prs_osr=6 tmp_osr=3
[2026-08-21 17:29:02.976][000000019.220] I/user.spl06_app OSR=6(64x)
[2026-08-21 17:29:03.476][000000019.730] I/user.spl06_app OSR=6 1: =1002.85hPa =23.8
[2026-08-21 17:29:04.489][000000020.736] I/user.spl06_app OSR=6 2: =1002.85hPa =23.8
[2026-08-21 17:29:05.495][000000021.742] I/user.spl06_app OSR=6 3: =1002.85hPa =23.8
[2026-08-21 17:29:06.493][000000022.748] I/user.spl06_app OSR=6 4: =1002.84hPa =23.8
[2026-08-21 17:29:07.495][000000023.754] I/user.spl06_app OSR=6 5: =1002.84hPa =23.8
[2026-08-21 17:29:08.504][000000024.754] I/user.spl06_app OSR=6 =1002.85hPa
[2026-08-21 17:29:09.033][000000025.292] I/user.exs_spl06 : kP 253960 kT 7869000
[2026-08-21 17:29:09.044][000000025.293] I/user.exs_spl06 , prs_osr=4 tmp_osr=3
[2026-08-21 17:29:09.548][000000025.793] I/user.spl06_app ===== [5/5] =====
[2026-08-21 17:29:09.557][000000025.795] I/user.spl06_app standby
[2026-08-21 17:29:09.565][000000025.796] I/user.spl06_app pressure
[2026-08-21 17:29:09.573][000000025.797] I/user.spl06_app +both
[2026-08-21 17:29:10.046][000000026.303] I/user.spl06_app : =1002.85hPa =23.8
[2026-08-21 17:29:10.056][000000026.304] I/user.exs_spl06
[2026-08-21 17:29:10.066][000000026.305] I/user.spl06_app 3
[2026-08-21 17:29:13.063][000000029.306] I/user.exs_spl06
[2026-08-21 17:29:13.073][000000029.306] I/user.spl06_app
[2026-08-21 17:29:13.561][000000029.812] I/user.spl06_app : =1002.85hPa =23.8
[2026-08-21 17:29:13.573][000000029.813] I/user.exs_spl06
[2026-08-21 17:29:13.590][000000029.813] I/user.spl06_app
[2026-08-21 17:29:13.619][000000029.814] I/user.spl06_app ===== [] =====
[2026-08-21 17:29:13.631][000000029.814] I/user.spl06_app End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 SPL06-001 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 SCL/SDA 上拉电阻是否连接
- 确认 exs_spl06 扩展库已正常加载
2. **读取数据始终为 0 或异常**
- 检查传感器供电是否正常
- 确认 I2C 地址是否正确(SPL06-001 支持 0x76/0x77,由 SDO 引脚决定,扩展库会自动探测)
- 确认接线无松动
- 检查 SCL/SDA 上拉电阻
3. **气压数据跳动过大**
- 提高过采样率(如 `set_osr(6, 4)` 对应 64x),数据更稳定但耗时更长
- 传感器附近避免风吹、空调等气压扰动源
### 5.7 扩展功能建议
exs_spl06 更多接口的使用可以查看 [exs_spl06 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_spl06/)
@@ -0,0 +1,221 @@
--[[
@module spl06_app
@summary SPL06-001 数字气压传感器演示模块,包含初始化、数据读取、海拔计算、过采样切换、模式切换与休眠唤醒演示用例
@version 1.0
@date 2026.08.21
@author 江访
@usage
本文件包含 SPL06-001 的逐项功能演示。
通过 exs_spl06 扩展库的 API 逐一演示初始化、数据读取、海拔计算、
过采样率切换、测量模式切换与休眠唤醒等功能。
通过 MODE 变量选择通信模式(改 MODE 的值即可,无需注释/取消注释代码):
MODE = 1 软件 I2C(默认推荐,仅需 scl/sda 引脚,任意 GPIO 均可,接线最灵活)
MODE = 2 硬件 I2C(指定 i2c_id,占用 CPU 少)
Air780EPM 接线(软件 I2C):
SCL = GPIO31
SDA = GPIO30
]]
-- 加载 exs_spl06 扩展库
local exs_spl06 = require "exs_spl06"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(默认) 2 = 硬件 I2C
-- 各模式所需引脚/参数见下方 init_config,接线说明见 readme.md
local MODE = 1
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIOAir780EPM 接线示例用 GPIO31/GPIO30
-- SPL06-001 支持双地址 0x76/0x77(由 SDO 引脚决定),不传 addr 时扩展库自动探测
init_config = {scl = 31, sda = 30}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1),不传 scl/sda 时无总线恢复能力
init_config = {i2c_id = 1}
else
log.error("spl06_app", "MODE 取值错误,应为 1/2")
return
end
--------------------------------------------------------------
-- [1/5] 初始化
-- - setup() 自动完成 I2C 地址自动探测、芯片 ID 校验、校准系数读取、
-- 过采样率配置、缩放基准建立(16x 气压/8x 温度)
--------------------------------------------------------------
local function demo_init()
log.info("spl06_app", "===== [1/5] 初始化 =====")
local result = exs_spl06.setup(init_config)
if not result then
log.error("spl06_app", "初始化失败")
log.error("spl06_app", "检查接线: VCC=3.3V GND SCL SDAI2C 地址是否正确(如 0x76/0x77")
return false -- 初始化失败必须返回 false
end
log.info("spl06_app", "SPL06-001 初始化成功,版本:", exs_spl06.version())
return true
end
--------------------------------------------------------------
-- [2/5] 数据读取
-- - get_data() 返回气压(hPa)与温度(℃)get_pressure()/get_temperature() 单次读取
--------------------------------------------------------------
local function demo_read_data()
log.info("spl06_app", "===== [2/5] 数据读取 =====")
log.info("spl06_app", "连续读取 5 次气压/温度(单位 hPa / ℃)")
-- 连续读取 5 次气压/温度
for i = 1, 5 do
local data = exs_spl06.get_data()
if data then
log.info("spl06_app", string.format("第%d次: 气压=%.2fhPa 温度=%.1f℃",
i, data.pressure, data.temperature))
else
log.error("spl06_app", string.format("第%d次读取失败", i))
end
sys.wait(1000) -- 每隔 1 秒读取一次,1000ms
end
-- 演示单次读取接口
local pressure = exs_spl06.get_pressure()
local temperature = exs_spl06.get_temperature()
log.info("spl06_app", string.format("单次气压=%.2fhPa 单次温度=%.1f℃",
pressure or 0, temperature or 0))
end
--------------------------------------------------------------
-- [3/5] 海拔高度计算
-- - get_altitude() 按国际气压测高公式计算海拔(纯计算接口,不耗时)
-- - 默认海平面气压 1013.25hPa,也可传当地海平面气压提高精度
--------------------------------------------------------------
local function demo_altitude()
log.info("spl06_app", "===== [3/5] 海拔高度计算 =====")
local pressure = exs_spl06.get_pressure()
if not pressure then
log.error("spl06_app", "气压读取失败,跳过海拔计算")
return
end
local h_default = exs_spl06.get_altitude(pressure) -- 默认 1013.25hPa
local h_custom = exs_spl06.get_altitude(pressure, 1005) -- 自定义 1005hPa
log.info("spl06_app", string.format("气压=%.2fhPa,默认海平面气压海拔=%.1f米,自定义1005hPa海拔=%.1f米",
pressure, h_default, h_custom))
end
--------------------------------------------------------------
-- [4/5] 过采样率切换
-- - set_osr() 在线切换气压过采样率,切换后扩展库自动完成缩放因子动态自校准
-- - OSR 越高数据越稳定,但单次测量耗时越长;校准失败自动回退默认 16x/8x
--------------------------------------------------------------
local function demo_switch_osr()
log.info("spl06_app", "===== [4/5] 过采样率切换 =====")
log.info("spl06_app", "依次切换气压过采样率 1x/16x/64x(温度保持 8x),观察数据精度")
-- 依次切换 3 档气压过采样率(1x/16x/64x
local osr_list = {0, 4, 6}
local osr_names = {"1x", "16x", "64x"}
for i = 1, #osr_list do
local ok = exs_spl06.set_osr(osr_list[i], 3)
if not ok then
log.error("spl06_app", string.format("气压OSR切换为%d(%s)失败,已回退默认16x/8x", osr_list[i], osr_names[i]))
end
log.info("spl06_app", string.format("气压OSR=%d(%s),开始采集", osr_list[i], osr_names[i]))
local sum = 0
local cnt = 0
for j = 1, 5 do
local data = exs_spl06.get_data()
if data then
sum = sum + data.pressure
cnt = cnt + 1
log.info("spl06_app", string.format("OSR=%d 第%d次: 气压=%.2fhPa 温度=%.1f℃",
osr_list[i], j, data.pressure, data.temperature))
else
log.error("spl06_app", "读取失败")
end
sys.wait(1000) -- 每隔 1 秒读取一次,1000ms
end
if cnt > 0 then
log.info("spl06_app", string.format("OSR=%d 平均气压=%.2fhPa", osr_list[i], sum / cnt))
end
end
-- 恢复默认过采样(16x/8x
exs_spl06.set_osr(4, 3)
end
--------------------------------------------------------------
-- [5/5] 测量模式切换与休眠唤醒
-- - set_mode() 切换待机/单次气压/连续气压+温度测量模式
-- - sleep() 进入低功耗待机(电流<1µA),wakeup() 唤醒恢复连续测量
-- - 最后 close() 释放资源
--------------------------------------------------------------
local function demo_mode_and_sleep()
log.info("spl06_app", "===== [5/5] 模式切换与休眠唤醒 =====")
-- 待机模式(电流 < 1µA)
exs_spl06.set_mode("standby")
log.info("spl06_app", "已切换待机模式(standby")
-- 单次气压测量模式(测完自动回待机)
exs_spl06.set_mode("pressure")
log.info("spl06_app", "已切换单次气压测量模式(pressure)")
-- 连续气压+温度测量模式(后台模式)
exs_spl06.set_mode("both")
log.info("spl06_app", "已切换连续气压+温度测量模式(both)")
sys.wait(500) -- 等待后台连续测量稳定,500ms
local data = exs_spl06.get_data()
if data then
log.info("spl06_app", string.format("连续模式数据: 气压=%.2fhPa 温度=%.1f℃", data.pressure, data.temperature))
end
-- 休眠唤醒(睡眠 3 秒后唤醒)
exs_spl06.sleep()
log.info("spl06_app", "已进入睡眠(待机),3 秒后唤醒")
sys.wait(3000) -- 睡眠 3 秒,3000ms
exs_spl06.wakeup()
log.info("spl06_app", "已唤醒,恢复连续测量")
sys.wait(500) -- 等待连续测量恢复,500ms
data = exs_spl06.get_data()
if data then
log.info("spl06_app", string.format("唤醒后数据: 气压=%.2fhPa 温度=%.1f℃", data.pressure, data.temperature))
end
-- 关闭传感器(进入待机并释放 I2C 资源)
exs_spl06.close()
log.info("spl06_app", "已关闭传感器")
end
--------------------------------------------------------------
-- 主任务:按 [1/5]~[5/5] 顺序执行全部演示
--------------------------------------------------------------
local function spl06_demo_task_func()
log.info("spl06_app", "HELLO")
sys.wait(1000) -- 等待系统稳定,1000ms
-- HELLO 开始 → [1/5] → [2/5] → [3/5] → [4/5] → [5/5] → End
local ok = demo_init()
if not ok then
log.error("spl06_app", "初始化失败,演示终止")
return
end
sys.wait(500) -- 步骤间隔,500ms
demo_read_data()
sys.wait(500) -- 步骤间隔,500ms
demo_altitude()
sys.wait(500) -- 步骤间隔,500ms
demo_switch_osr()
sys.wait(500) -- 步骤间隔,500ms
demo_mode_and_sleep()
log.info("spl06_app", "===== [演示完毕] =====")
log.info("spl06_app", "End")
end
sys.taskInit(spl06_demo_task_func)
@@ -0,0 +1,73 @@
--[[
@module main
@summary TM1637 数码管驱动芯片测试 Demo 入口
@version 1.0
@date 2026.07.13
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_tm1637 扩展库的完整功能,顺序为:
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]
5、综合演示([5/6]
6、日期/时钟/温度([6/6]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "TM1637_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 tm1637_demo.lua 演示模块
require "tm1637_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,240 @@
# TM1637 数码管驱动芯片演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **tm1637_demo.lua** - TM1637 功能演示模块,包含数码管显示、按键检测、亮度调节、综合展示等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_tm1637** - TM1637 扩展库,提供数码管段码显示、按键检测、亮度调节等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_tm1637 扩展库的 6 项功能:HELLO → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
### 2.1 功能演示项说明
1. **[1/6] 字符串显示** - 数码管逐项显示数字、带小数点数字、字母组合、指定位置显示
2. **[2/6] 按键轮询** - 通过 `get_key()` API 轮询检测按键,10 秒超时
3. **[3/6] 按键回调** - 通过 `set_key_callback()` API 设置按键回调函数,12 秒演示窗口
4. **[4/6] 亮度调节** - 展示 0~7 级亮度调节效果
5. **[5/6] 综合演示** - 倒计时、HELLO 闪烁、全 8 显示、亮度渐变
6. **[6/6] 日期/时钟/温度** - 日期展示(YY-MM-DD 格式)、时钟模拟走时(HH-MM-SS)、温度展示(摄氏度°C 和华氏度°F)
## 三、显示效果
![](https://docs.openluat.com/cdn/image/Air780EHM_tm1637.png)
## 四、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- TM1637 数码管模块(6 位数码管 + 16 个按键) × 1
demo所演示的TM1637数码管[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=1007027539110&mi_id=0000QVgQh2GhxEs_zMNZK6bhYAbQv0eR95YG6IfO11pxPlY&ns=1&priceTId=213e00d417839212452374999e1068&skuId=6005284956850&spm=a21n57.1.hoverItem.5&utparam=%7B%22aplus_abtest%22%3A%22d1bf89b9b16b6e795e9cbd76e360f1b3%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 TM1637 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>TM1637 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>CLK<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>DIO<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 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 脚本文件
1. **main.lua** - 主程序入口
2. **tm1637_demo.lua** - TM1637 功能演示模块
3. **exs_tm1637** - TM1637 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 TM1637 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 tm1637_demo.lua 演示模块
require "tm1637_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 字符串显示演示
1. 设备启动后显示 "HELLO",随后自动进入 [1/6] 字符串显示
2. 观察数码管依次显示:`123456``88.88``AbCd` → 从第 3 位显示 `1234`
3. 检查字符显示是否正确,带小数点功能是否正常
#### 6.4.2 按键检测演示
1. 自动进入 [2/6] 按键轮询演示,数码管显示 "KEY---"
2. 在 10 秒内按下 TM1637 模块上的任意按键
3. 观察数码管显示按键编码(如 "K01"、"K02" 等)
4. 若 10 秒内无按键按下,自动跳过
#### 6.4.3 按键回调演示
1. 自动进入 [3/6] 按键回调演示,数码管显示 "Cb-Key"
2. 在 12 秒内按下任意按键,观察数码管显示对应按键编码
3. 观察日志输出,确认回调函数 `on_key` 被正常触发
4. 12 秒后自动结束回调演示
#### 6.4.4 亮度调节演示
1. 自动进入 [4/6] 亮度调节演示,数码管显示 "888888"
2. 观察数码管亮度从 0 级到 7 级逐级变化
3. 最终恢复到 4 级亮度
#### 6.4.5 综合演示
1. 自动进入 [5/6] 综合演示
2. 观察倒计时效果:5 → 4 → 3 → 2 → 1 → 0
3. 观察 "-HELLO" 闪烁效果,重复 4 次
4. 观察全 8 显示 + 亮度渐变
5. 数码管全灭
#### 6.4.6 日期/时钟/温度演示
1. 自动进入 [6/6] 日期/时钟/温度演示
2. 日期展示:6 位数码管显示 "YYMMDD"(如 "260713"),停留 3 秒
3. 时钟展示:模拟走时 "083000" → "083001" → ... → "083009"
4. 温度展示:
- 摄氏度:25°C → 32°C 逐度变化,亮度随温度升高而增加
- 华氏度:77°F → 90°F 逐度变化
5. 演示结束后显示 "End" 并停留 3 秒
### 6.5 预期效果
- **字符串显示**:数字、字母、小数点均能正确显示,支持指定起始位置
- **按键检测**:轮询模式和回调模式均能正确检测按键,编码显示正常
- **亮度调节**:0~7 级亮度逐级变化,效果明显
- **日期/时钟/温度**:格式显示正确,°C 和 °F 符号正常显示
- **日志如下:**
```lua
[2026-07-21 10:33:01.174][000000000.180] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:33:01.177][000000000.180] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:33:01.179][000000000.182] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:33:01.180][000000000.185] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:33:01.182][000000000.185] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:33:01.184][000000000.185] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:33:01.186][000000000.203] I/user.main TM1637_Demo 001.999.000
[2026-07-21 10:33:01.188][000000000.217] D/user.exs_tm1637 version -> 202607161200
[2026-07-21 10:33:01.190][000000000.226] I/user.exs_tm1637 , clk=31 dio=30 =5
[2026-07-21 10:33:01.193][000000000.227] I/user.tm1637_demo TM1637 : 202607161200
[2026-07-21 10:33:01.396][000000001.233] I/user.tm1637_demo ===== [1/6] =====
[2026-07-21 10:33:01.397][000000001.234] I/user.tm1637_demo 123456
[2026-07-21 10:33:02.359][000000002.745] I/user.tm1637_demo 88.88
[2026-07-21 10:33:03.870][000000004.258] I/user.tm1637_demo AbCd
[2026-07-21 10:33:05.377][000000005.770] I/user.tm1637_demo 3 1234
[2026-07-21 10:33:05.497][000000005.829] I/mobile sim0 sms ready
[2026-07-21 10:33:05.506][000000005.830] D/mobile cid1, state0
[2026-07-21 10:33:05.512][000000005.831] D/mobile bearer act 0, result 0
[2026-07-21 10:33:05.516][000000005.831] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:33:05.521][000000005.886] D/mobile TIME_SYNC 0 tm 1784601188
[2026-07-21 10:33:07.395][000000007.790] I/user.tm1637_demo ===== [2/6] =====
[2026-07-21 10:33:07.399][000000007.791] I/user.tm1637_demo 10
[2026-07-21 10:33:08.328][000000008.724] I/user.tm1637_demo , : 1
[2026-07-21 10:33:09.461][000000009.851] I/user.tm1637_demo , : 2
[2026-07-21 10:33:11.605][000000011.999] I/user.tm1637_demo , : 3
[2026-07-21 10:33:12.428][000000012.822] I/user.tm1637_demo , : 4
[2026-07-21 10:33:13.452][000000013.846] I/user.tm1637_demo , : 5
[2026-07-21 10:33:14.382][000000014.771] I/user.tm1637_demo , : 6
[2026-07-21 10:33:15.203][000000015.592] I/user.tm1637_demo , : 7
[2026-07-21 10:33:16.431][000000016.822] I/user.tm1637_demo , : 8
[2026-07-21 10:33:17.350][000000017.744] I/user.tm1637_demo , : 9
[2026-07-21 10:33:18.280][000000018.668] I/user.tm1637_demo , : 10
[2026-07-21 10:33:19.103][000000019.488] I/user.tm1637_demo , : 11
[2026-07-21 10:33:20.018][000000020.412] I/user.tm1637_demo , : 12
[2026-07-21 10:33:20.843][000000021.233] I/user.tm1637_demo , : 13
[2026-07-21 10:33:21.668][000000022.055] I/user.tm1637_demo , : 14
[2026-07-21 10:33:22.490][000000022.875] I/user.tm1637_demo , : 15
[2026-07-21 10:33:23.811][000000024.206] I/user.tm1637_demo , : 16
[2026-07-21 10:33:30.758][000000031.145] I/user.tm1637_demo
[2026-07-21 10:33:30.761][000000031.145] I/user.tm1637_demo ===== [4/6] =====
[2026-07-21 10:33:30.766][000000031.146] I/user.tm1637_demo 12
[2026-07-21 10:33:32.123][000000032.512] I/user.tm1637_demo , : 16
[2026-07-21 10:33:42.771][000000043.160] I/user.tm1637_demo
[2026-07-21 10:33:42.774][000000043.160] I/user.tm1637_demo ===== [3/6] =====
[2026-07-21 10:33:43.282][000000043.673] I/user.tm1637_demo : 0
[2026-07-21 10:33:44.090][000000044.475] I/user.tm1637_demo : 1
[2026-07-21 10:33:44.880][000000045.277] I/user.tm1637_demo : 2
[2026-07-21 10:33:45.688][000000046.078] I/user.tm1637_demo : 3
[2026-07-21 10:33:46.494][000000046.880] I/user.tm1637_demo : 4
[2026-07-21 10:33:47.285][000000047.681] I/user.tm1637_demo : 5
[2026-07-21 10:33:48.092][000000048.483] I/user.tm1637_demo : 6
[2026-07-21 10:33:48.895][000000049.285] I/user.tm1637_demo : 7
[2026-07-21 10:33:50.199][000000050.594] I/user.tm1637_demo ===== [5/6] =====
[2026-07-21 10:33:59.171][000000059.565] I/user.tm1637_demo ---- 综合演示结束 ----
[2026-07-21 10:33:59.175][000000059.566] I/user.tm1637_demo ===== [6/6] // =====
[2026-07-21 10:33:59.179][000000059.566] I/user.tm1637_demo : 260713
[2026-07-21 10:34:02.692][000000063.084] I/user.tm1637_demo : 083000 ~ 083009
[2026-07-21 10:34:22.982][000000083.365] I/user.tm1637_demo ===== [] =====
```
### 6.6 故障排除
1. **数码管无显示**
- 检查 TM1637 接线是否正确(CLK、DIO、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_tm1637 扩展库已正常加载
2. **按键无响应**
- 检查按键接线和 TM1637 模块是否正常
- 确认扩展库初始化成功
- 查看日志确认 `setup()` 返回值为 true
3. **亮度无法调节**
- 确认调用 `set_brightness()` 时参数为 0~7
4. **显示内容错乱**
- 检查杜邦线是否过长导致通信不稳定
### 6.7 扩展功能建议
exs_tm1637 更多接口的使用可以查看 [exs_tm1637 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_tm1637/)
@@ -0,0 +1,232 @@
--[[
@module tm1637_demo
@summary TM1637 驱动芯片演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.13
@author 江访
@usage
本文件包含 TM1637 的逐项功能演示和综合展示。
通过 exs_tm1637 扩展库的 API 逐一演示数码管显示、按键检测等功能。
]]
-- 加载 exs_tm1637 扩展库
local exs_tm1637 = require "exs_tm1637"
-- 演示字符串显示
local function demo_string()
log.info("tm1637_demo", "===== [1/6] 字符串显示 =====")
log.info("tm1637_demo", "显示 123456")
exs_tm1637.clear()
exs_tm1637.set_display("123456")
sys.wait(1500)
log.info("tm1637_demo", "显示 88.88")
exs_tm1637.clear()
exs_tm1637.set_display("88.88")
sys.wait(1500)
log.info("tm1637_demo", "显示 AbCd")
exs_tm1637.clear()
exs_tm1637.set_display("AbCd")
sys.wait(1500)
log.info("tm1637_demo", "从第 3 位开始显示 1234")
exs_tm1637.clear()
exs_tm1637.set_display("1234", 3)
sys.wait(1500)
exs_tm1637.clear()
sys.wait(500)
end
-- 演示按键轮询
local function demo_keys()
log.info("tm1637_demo", "===== [2/6] 按键轮询 =====")
log.info("tm1637_demo", "按下任意按键测试,10 秒无操作后自动结束")
exs_tm1637.clear()
exs_tm1637.set_display("KEY---")
local timeout = 10000
local step = 100
local elapsed = 0
while elapsed < timeout do
local key = exs_tm1637.get_key()
if key then
log.info("tm1637_demo", "检测到按键, 编码:", key)
local key_str = string.format("K%02d", key)
exs_tm1637.set_display(key_str)
sys.wait(800)
exs_tm1637.clear()
exs_tm1637.set_display("KEY---")
else
sys.wait(step)
elapsed = elapsed + step
end
end
log.info("tm1637_demo", "按键检测结束")
end
-- 演示亮度调节
local function demo_brightness()
log.info("tm1637_demo", "===== [3/6] 亮度调节 =====")
exs_tm1637.clear()
exs_tm1637.set_display("888888")
sys.wait(500)
for brightness = 0, 7 do
log.info("tm1637_demo", "设置亮度:", brightness)
exs_tm1637.set_brightness(brightness)
sys.wait(800)
end
exs_tm1637.set_brightness(4)
exs_tm1637.clear()
sys.wait(500)
end
-- 按键回调函数
local function on_key(key_code)
log.info("tm1637_demo", "按键回调触发, 编码:", key_code)
local display = string.format("K%02d", key_code)
exs_tm1637.set_display(display)
end
-- 演示按键回调
local function demo_key_callback()
log.info("tm1637_demo", "===== [4/6] 按键回调 =====")
log.info("tm1637_demo", "接下来 12 秒内按按键查看回调效果")
exs_tm1637.clear()
exs_tm1637.set_display("Cb-Key")
exs_tm1637.set_key_callback(on_key)
sys.wait(12000)
exs_tm1637.set_key_callback(nil)
log.info("tm1637_demo", "按键回调演示结束")
end
-- 综合演示
local function demo_final_show()
log.info("tm1637_demo", "===== [5/6] 综合演示 =====")
for i = 5, 0, -1 do
exs_tm1637.clear()
exs_tm1637.set_display(string.format("%d", i))
sys.wait(500)
end
for i = 0, 3 do
exs_tm1637.clear()
exs_tm1637.set_display("-HELLO")
sys.wait(500)
exs_tm1637.clear()
sys.wait(300)
end
exs_tm1637.clear()
exs_tm1637.set_display("888888")
for i = 0, 7 do
exs_tm1637.set_brightness(i)
sys.wait(200)
end
sys.wait(1000)
exs_tm1637.clear()
log.info("tm1637_demo", "---- 综合演示结束 ----")
end
-- 展示日期:6 位数码管,格式 "260713"
local function show_date()
log.info("tm1637_demo", "日期展示: 260713")
exs_tm1637.set_display("260713")
sys.wait(3000)
exs_tm1637.clear()
end
-- 展示时间:模拟走时 "083000" ~ "083009"
local function show_clock()
exs_tm1637.clear()
sys.wait(200)
log.info("tm1637_demo", "时钟展示: 083000 ~ 083009")
for sec = 0, 9 do
exs_tm1637.set_display(string.format("0830%02d", sec))
sys.wait(500)
end
end
-- 展示温度:摄氏度用 "°C",华氏度用 "°F"
local function show_temperature()
exs_tm1637.clear()
sys.wait(200)
local DEGREE = "°"
for temp = 25, 32 do
local cel = string.format("%02d", temp) .. DEGREE .. "C"
exs_tm1637.set_display(cel)
local bright = math.floor((temp - 25) / 7 * 7 + 0.5)
if bright < 0 then bright = 0 end
if bright > 7 then bright = 7 end
exs_tm1637.set_brightness(bright)
sys.wait(800)
end
for temp = 77, 90, 2 do
local fah = string.format("%02d", temp) .. DEGREE .. "F"
exs_tm1637.set_display(fah)
sys.wait(600)
end
exs_tm1637.set_brightness(5)
sys.wait(1000)
exs_tm1637.clear()
end
local function run_main()
log.info("tm1637_demo", "===== [6/6] 日期/时钟/温度 =====")
show_date()
sys.wait(300)
show_clock()
sys.wait(300)
show_temperature()
end
-- 演示主函数(运行在协程中)
local function tm1637_demo_task_func()
-- 初始化
local result = exs_tm1637.setup({
clk = 31,
dio = 30,
bright = 5,
})
if not result then
log.error("tm1637_demo", "TM1637 初始化失败,请检查接线")
return
end
log.info("tm1637_demo", "TM1637 初始化成功,版本:", exs_tm1637.version())
-- HELLO 开始 → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
exs_tm1637.set_display("HELLO")
sys.wait(1000)
demo_string()
demo_keys()
demo_key_callback()
demo_brightness()
demo_final_show()
run_main()
exs_tm1637.set_display("End")
sys.wait(3000)
log.info("tm1637_demo", "===== [演示完毕] =====")
end
sys.taskInit(tm1637_demo_task_func)
@@ -0,0 +1,74 @@
--[[
@module main
@summary TM1638 数码管驱动芯片测试 Demo 入口
@version 1.0
@date 2026.07.07
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_tm1638 扩展库的完整功能,顺序为:
HELLO→[1/7]→[2/7]→[3/7]→[4/7]→[5/7]→[6/7]→[7/7]→End
1、数码管字符串显示([1/7]
2、LED 指示灯控制([2/7]
3、按键轮询检测([3/7]
4、亮度调节([4/7]
5、按键回调([5/7]
6、综合演示([6/7]
7、日期/时钟/温度([7/7]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "TM1638_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 tm1638_demo.lua 演示模块
require "tm1638_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,249 @@
# TM1638 数码管驱动芯片演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **tm1638_demo.lua** - TM1638 功能演示模块,包含数码管显示、LED 控制、按键检测、亮度调节、综合展示等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_tm1638** - TM1638 扩展库,提供数码管段码显示、LED 指示灯控制、按键检测、亮度调节等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_tm1638 扩展库的 7 项功能:HELLO → [1/7] → [2/7] → [3/7] → [4/7] → [5/7] → [6/7] → [7/7] → End
### 2.1 功能演示项说明
1. **[1/7] 字符串显示** - 数码管逐项显示数字、带小数点数字、字母组合、指定位置显示
2. **[2/7] LED 控制** - 逐个点亮/熄灭 LED 指示灯、LED 跑马灯效果
3. **[3/7] 按键轮询** - 通过 `get_key()` API 轮询检测按键,10 秒超时
4. **[4/7] 亮度调节** - 展示 0~7 级亮度调节效果
5. **[5/7] 按键回调** - 通过 `set_key_callback()` API 设置按键回调函数,15 秒演示窗口
6. **[6/7] 综合演示** - 倒计时、HELLO 闪烁、全亮全灭、亮度渐变
7. **[7/7] 日期/时钟/温度** - 日期展示(YY-MM-DD 格式)、时钟模拟走时(HH-MM-SS)、温度展示(摄氏度°C 和华氏度°F)
## 三、显示效果
![](https://docs.openluat.com/cdn/image//Air780EPM_tm1638.png)
## 四、演示硬件环境
### 4.1 硬件清单
- Air780EPM 核心板 × 1
- TM1638 数码管模块(8 位数码管 + 8 个 LED 指示灯 + 8 个按键) × 1
demo所演示的TM1638数码管[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=862890736053&mi_id=0000hQbdi599Ci7xSkwlh3JRFNce012vqjQLR8ZnTZt0bG8&ns=1&spm=a21n57.1.hoverItem.7&utparam=%7B%22aplus_abtest%22%3A%22224c84d139b925f8f3d8268f9e91b200%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 5
- TYPE-C 数据线 × 1
### 4.2 接线配置
#### 4.2.1 TM1638 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>TM1638 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>CLK<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>DIO<br/></td></tr>
<tr>
<td>33/GPIO32<br/></td><td>STB<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:TM1638 模块通常还带有 VCC 和 GND 引脚,直接连接核心板的 VDD_EXT(3.3V)和 GND 即可。接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780egh/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 主程序入口
2. **tm1638_demo.lua** - TM1638 功能演示模块
3. **exs_tm1638** - TM1638 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 TM1638 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 tm1638_demo.lua 演示模块
require "tm1638_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 字符串显示演示
1. 设备启动后显示 "HELLO",随后自动进入 [1/7] 字符串显示
2. 观察数码管依次显示:`87654321``88.88``AbCd` → 从第 3 位显示 `1234`
3. 检查字符显示是否正确,带小数点功能是否正常
#### 6.4.2 LED 控制演示
1. 自动进入 [2/7] LED 控制演示
2. 观察 LED 指示灯从 1 到 8 逐个点亮,再逐个熄灭
3. 观察 LED 跑马灯效果,共循环 3 轮
4. 数码管同步显示当前位置数字
#### 6.4.3 按键检测演示
1. 自动进入 [3/7] 按键轮询演示,数码管显示 "KEY----"
2. 在 10 秒内按下 TM1638 模块上的任意按键
3. 观察数码管显示按键编码(如 "K01"、"K02" 等)
4. 若 10 秒内无按键按下,自动跳过
#### 6.4.4 亮度调节演示
1. 自动进入 [4/7] 亮度调节演示,数码管显示 "88888888"
2. 观察数码管亮度从 0 级到 7 级逐级变化
3. 最终恢复到 4 级亮度
#### 6.4.5 按键回调演示
1. 自动进入 [5/7] 按键回调演示,数码管显示 "Cb-Key"
2. 在 15 秒内按下任意按键,观察数码管显示对应按键编码
3. 观察日志输出,确认回调函数 `on_key` 被正常触发
4. 15 秒后自动结束回调演示
#### 6.4.6 综合演示
1. 自动进入 [6/7] 综合演示
2. 观察倒计时效果:5 → 4 → 3 → 2 → 1 → 0
3. 观察 "-HELLO-" 闪烁效果,重复 4 次
4. 观察全 8 显示 + 全部 LED 点亮 + 亮度渐变
5. 数字和 LED 指示灯全灭
#### 6.4.7 日期/时钟/温度演示
1. 自动进入 [7/7] 日期/时钟/温度演示
2. 日期展示:显示格式 "YY-MM-DD"(如 "26-07-08"),停留 3 秒
3. 时钟展示:模拟走时 "08-30-00" → "08-30-01" → ... → "08-30-09"
4. 温度展示:
- 摄氏度:25°C → 32°C 逐度变化,亮度随温度升高而增加
- 华氏度:77°F → 90°F 逐度变化
5. 演示结束后显示 "End" 并停留 3 秒
### 6.5 预期效果
- **字符串显示**:数字、字母、小数点均能正确显示,支持指定起始位置
- **LED 控制**:8 颗 LED 指示灯均可独立控制,跑马灯效果流畅
- **按键检测**:轮询模式和回调模式均能正确检测按键,编码显示正常
- **亮度调节**:0~7 级亮度逐级变化,效果明显
- **日期/时钟/温度**:格式显示正确,°C 和 °F 符号正常显示
- **日志如下:**
```lua
[2026-07-21 10:36:28.010][000000000.184] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:36:28.014][000000000.184] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:36:28.017][000000000.186] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:36:28.020][000000000.188] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:36:28.044][000000000.188] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:36:28.046][000000000.189] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:36:28.048][000000000.207] I/user.main TM1638_Demo 001.999.000
[2026-07-21 10:36:28.051][000000000.223] D/user.exs_tm1638 version -> 202607131200
[2026-07-21 10:36:28.055][000000000.239] I/user.exs_tm1638 , clk=31 dio=30 stb=32 =5
[2026-07-21 10:36:28.058][000000000.239] I/user.tm1638_demo TM1638 : 202607131200
[2026-07-21 10:36:28.689][000000001.251] I/user.tm1638_demo ===== [1/7] =====
[2026-07-21 10:36:28.692][000000001.252] I/user.tm1638_demo 87654321
[2026-07-21 10:36:29.798][000000002.300] I/mobile sim0 sms ready
[2026-07-21 10:36:29.800][000000002.300] D/mobile cid1, state0
[2026-07-21 10:36:29.801][000000002.301] D/mobile bearer act 0, result 0
[2026-07-21 10:36:29.810][000000002.301] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:36:29.818][000000002.356] D/mobile TIME_SYNC 0 tm 1784601393
[2026-07-21 10:36:30.210][000000002.776] I/user.tm1638_demo 88.88
[2026-07-21 10:36:31.730][000000004.300] I/user.tm1638_demo AbCd
[2026-07-21 10:36:33.254][000000005.824] I/user.tm1638_demo 3 1234
[2026-07-21 10:36:35.285][000000007.859] I/user.tm1638_demo ===== [2/7] LED =====
[2026-07-21 10:36:38.805][000000011.371] I/user.tm1638_demo
[2026-07-21 10:36:40.422][000000012.987] I/user.tm1638_demo LED
[2026-07-21 10:36:44.912][000000017.474] I/user.tm1638_demo ===== [3/7] =====
[2026-07-21 10:36:45.426][000000017.998] I/user.tm1638_demo : 0
[2026-07-21 10:36:46.229][000000018.799] I/user.tm1638_demo : 1
[2026-07-21 10:36:47.036][000000019.600] I/user.tm1638_demo : 2
[2026-07-21 10:36:47.826][000000020.401] I/user.tm1638_demo : 3
[2026-07-21 10:36:48.631][000000021.202] I/user.tm1638_demo : 4
[2026-07-21 10:36:49.437][000000022.003] I/user.tm1638_demo : 5
[2026-07-21 10:36:50.242][000000022.804] I/user.tm1638_demo : 6
[2026-07-21 10:36:51.033][000000023.605] I/user.tm1638_demo : 7
[2026-07-21 10:36:52.356][000000024.919] I/user.tm1638_demo ===== [4/7] =====
[2026-07-21 10:36:52.366][000000024.919] I/user.tm1638_demo 10 TM1638 ...
[2026-07-21 10:36:53.831][000000026.399] I/user.tm1638_demo , : 17
[2026-07-21 10:36:54.842][000000027.412] I/user.tm1638_demo [5/7] 15
[2026-07-21 10:36:55.774][000000028.342] I/user.tm1638_demo , : 19
[2026-07-21 10:36:56.721][000000029.292] I/user.tm1638_demo , : 21
[2026-07-21 10:36:57.468][000000030.042] I/user.tm1638_demo , : 23
[2026-07-21 10:36:58.227][000000030.792] I/user.tm1638_demo , : 18
[2026-07-21 10:36:58.929][000000031.492] I/user.tm1638_demo , : 20
[2026-07-21 10:36:59.581][000000032.142] I/user.tm1638_demo , : 22
[2026-07-21 10:37:00.218][000000032.792] I/user.tm1638_demo , : 24
[2026-07-21 10:37:09.864][000000042.439] I/user.tm1638_demo
[2026-07-21 10:37:09.876][000000042.439] I/user.tm1638_demo ===== [6/7] =====
[2026-07-21 10:37:19.103][000000051.675] I/user.tm1638_demo ---- 综合演示结束 ----
[2026-07-21 10:37:19.116][000000051.675] I/user.tm1638_demo ===== [7/7] // =====
[2026-07-21 10:37:19.128][000000051.675] I/user.tm1638_demo : 26-07-08
[2026-07-21 10:37:22.644][000000055.209] I/user.tm1638_demo : 08-30-00 ~ 08-30-09
[2026-07-21 10:37:43.095][000000075.654] I/user.tm1638_demo ===== [] =====
```
### 6.6 故障排除
1. **数码管无显示**
- 检查 TM1638 接线是否正确(CLK、DIO、STB、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_tm1638 扩展库已正常加载
2. **LED 指示灯不亮**
- 检查 TM1638 模块供电是否正常
- 确认模块是否支持 LED 功能(部分精简模块可能没有 LED)
3. **按键无响应**
- 检查按键接线和 TM1638 模块是否正常
- 确认扩展库初始化成功
- 查看日志确认 `init()` 返回值为 true
4. **亮度无法调节**
- 确认调用 `set_brightness()` 时参数为 0~7
5. **显示内容错乱**
- 检查杜邦线是否过长导致通信不稳定
### 6.7 扩展功能建议
exs_tm1638 更多接口的使用可以查看 [exs_tm1638 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_tm1638/)
@@ -0,0 +1,280 @@
--[[
@module tm1638_demo
@summary TM1638 驱动芯片演示模块,包含所有功能的演示用例
@version 2.0
@date 2026.07.13
@author 江访
@usage
本文件包含 TM1638 的逐项功能演示和综合展示。
通过 exs_tm1638 扩展库的 API 逐一演示数码管显示、LED 控制、按键检测等功能。
]]
-- 加载 exs_tm1638 扩展库
local exs_tm1638 = require "exs_tm1638"
-- 演示字符串显示
local function demo_string()
log.info("tm1638_demo", "===== [1/7] 字符串显示 =====")
log.info("tm1638_demo", "显示 87654321")
exs_tm1638.clear()
exs_tm1638.set_display("87654321")
sys.wait(1500)
log.info("tm1638_demo", "显示 88.88")
exs_tm1638.clear()
exs_tm1638.set_display("88.88")
sys.wait(1500)
log.info("tm1638_demo", "显示 AbCd")
exs_tm1638.clear()
exs_tm1638.set_display("AbCd")
sys.wait(1500)
log.info("tm1638_demo", "从第 3 位开始显示 1234")
exs_tm1638.clear()
exs_tm1638.set_display("1234", 3)
sys.wait(1500)
exs_tm1638.clear()
sys.wait(500)
end
-- 演示 LED 指示灯
local function demo_leds()
log.info("tm1638_demo", "===== [2/7] LED =====")
for i = 1, 8 do
exs_tm1638.set_display(string.format("%d", i))
exs_tm1638.set_led(true, i)
sys.wait(300)
end
sys.wait(1000)
log.info("tm1638_demo", "逐个熄灭")
for i = 1, 8 do
exs_tm1638.set_led(false, i)
sys.wait(200)
end
log.info("tm1638_demo", "LED 跑马灯")
exs_tm1638.clear()
for round = 1, 3 do
for i = 1, 8 do
if i > 1 then
exs_tm1638.set_led(false, i - 1)
else
exs_tm1638.set_led(false, 8)
end
exs_tm1638.set_led(true, i)
exs_tm1638.set_display(string.format("%d", i))
sys.wait(150)
end
end
exs_tm1638.clear()
sys.wait(500)
end
-- 演示亮度调节
local function demo_brightness()
log.info("tm1638_demo", "===== [3/7] 亮度调节 =====")
exs_tm1638.clear()
exs_tm1638.set_display("88888888")
sys.wait(500)
for brightness = 0, 7 do
log.info("tm1638_demo", "设置亮度:", brightness)
exs_tm1638.set_brightness(brightness)
sys.wait(800)
end
exs_tm1638.set_brightness(4)
exs_tm1638.clear()
sys.wait(500)
end
-- 演示按键轮询
local function demo_keys()
log.info("tm1638_demo", "===== [4/7] 按键轮询 =====")
log.info("tm1638_demo", "请在 10 秒内按下连接到 TM1638 的按键...")
exs_tm1638.clear()
exs_tm1638.set_display("KEY----")
local timeout = 10000
local step = 100
local elapsed = 0
while elapsed < timeout do
local key = exs_tm1638.get_key()
if key then
exs_tm1638.clear()
log.info("tm1638_demo", "检测到按键, 编码:", key)
local key_str = string.format("K%02d", key)
exs_tm1638.set_display(key_str)
sys.wait(1000)
return
end
sys.wait(step)
elapsed = elapsed + step
end
log.info("tm1638_demo", "按键检测超时,跳过")
end
-- 按键回调函数
local function on_key(key_code)
log.info("tm1638_demo", "按键回调触发, 编码:", key_code)
local display = string.format("K%02d", key_code)
exs_tm1638.set_display(display)
end
-- 演示按键回调
local function demo_key_callback()
log.info("tm1638_demo", "[5/7] 按键回调(15 秒内按按键演示)")
exs_tm1638.clear()
exs_tm1638.set_display("Cb-Key")
exs_tm1638.set_key_callback(on_key)
sys.wait(15000)
exs_tm1638.set_key_callback(nil)
log.info("tm1638_demo", "按键回调演示结束")
end
-- 综合演示
local function demo_final_show()
log.info("tm1638_demo", "===== [6/7] 综合演示 =====")
for i = 5, 0, -1 do
exs_tm1638.clear()
exs_tm1638.set_display(string.format("%d", i))
for j = 1, 8 do
exs_tm1638.set_led(false, j)
end
sys.wait(500)
end
for i = 0, 3 do
exs_tm1638.clear()
exs_tm1638.set_display("-HELLO-")
sys.wait(500)
exs_tm1638.clear()
sys.wait(300)
end
exs_tm1638.clear()
exs_tm1638.set_display("88888888")
for i = 1, 8 do
exs_tm1638.set_led(true, i)
end
for i = 0, 7 do
exs_tm1638.set_brightness(i)
sys.wait(200)
end
sys.wait(1000)
exs_tm1638.clear()
for i = 1, 8 do
exs_tm1638.set_led(false, i)
end
log.info("tm1638_demo", "---- 综合演示结束 ----")
end
-- 展示日期:格式 "26-07-08"
local function show_date()
log.info("tm1638_demo", "日期展示: 26-07-08")
exs_tm1638.set_display("26-07-08")
sys.wait(3000)
exs_tm1638.clear()
end
-- 展示时间:模拟走时 "08-30-00" ~ "08-30-09"
local function show_clock()
exs_tm1638.clear()
sys.wait(200)
log.info("tm1638_demo", "时钟展示: 08-30-00 ~ 08-30-09")
for sec = 0, 9 do
exs_tm1638.set_display(string.format("08-30-%02d", sec))
sys.wait(500)
end
end
-- 展示温度:摄氏度用 "°C",华氏度用 "°F"
local function show_temperature()
exs_tm1638.clear()
sys.wait(200)
local DEGREE = "°"
for temp = 25, 32 do
local cel = string.format("%02d", temp) .. DEGREE .. "C"
exs_tm1638.set_display(cel)
local bright = math.floor((temp - 25) / 7 * 7 + 0.5)
if bright < 0 then bright = 0 end
if bright > 7 then bright = 7 end
exs_tm1638.set_brightness(bright)
sys.wait(800)
end
for temp = 77, 90, 2 do
local fah = string.format("%02d", temp) .. DEGREE .. "F"
exs_tm1638.set_display(fah)
sys.wait(600)
end
exs_tm1638.set_brightness(5)
sys.wait(1000)
exs_tm1638.clear()
end
local function run_main()
log.info("tm1638_demo", "===== [7/7] 日期/时钟/温度 =====")
show_date()
sys.wait(300)
show_clock()
sys.wait(300)
show_temperature()
end
-- 演示主函数(运行在协程中)
local function tm1638_demo_task_func()
-- 初始化
local result = exs_tm1638.setup({
clk = 31,
dio = 30,
stb = 32,
bright = 5,
write_mode = "auto",
})
if not result then
log.error("tm1638_demo", "TM1638 初始化失败,请检查接线")
return
end
log.info("tm1638_demo", "TM1638 初始化成功,版本:", exs_tm1638.version())
-- HELLO 开始 → [1/7] → [2/7] → [3/7] → [4/7] → [5/7] → [6/7] → [7/7] → End
exs_tm1638.set_display("HELLO")
sys.wait(1000)
demo_string()
demo_leds()
demo_brightness()
demo_keys()
demo_key_callback()
demo_final_show()
run_main()
exs_tm1638.set_display("End")
sys.wait(3000)
log.info("tm1638_demo", "===== [演示完毕] =====")
end
sys.taskInit(tm1638_demo_task_func)
@@ -0,0 +1,71 @@
--[[
@module main
@summary TM1640 数码管驱动芯片测试 Demo 入口
@version 1.0
@date 2026.07.13
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_tm1640 扩展库的完整功能,顺序为:
HELLO→[1/4]→[2/4]→[3/4]→[4/4]→End
1、数码管字符串显示([1/4]
2、亮度调节([2/4]
3、综合演示([3/4]
4、日期/时钟/温度([4/4]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "TM1640_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 tm1640_demo.lua 演示模块
require "tm1640_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,195 @@
# TM1640 数码管驱动芯片演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **tm1640_demo.lua** - TM1640 功能演示模块,包含数码管显示、亮度调节、综合展示等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_tm1640** - TM1640 扩展库,提供数码管段码显示、亮度调节等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_tm1640 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/4] 字符串显示** - 数码管逐项显示数字、带小数点数字、字母组合、指定位置显示(利用 16 位显示能力)
2. **[2/4] 亮度调节** - 展示 0~7 级亮度调节效果
3. **[3/4] 综合演示** - 倒计时、HELLO 闪烁、全亮全灭、亮度渐变
4. **[4/4] 日期/时钟/温度** - 日期展示(YY-MM-DD HH:MM 格式)、时钟模拟走时(HH-MM-SS)、温度展示(摄氏度°C 和华氏度°F)
## 三、显示效果
![](https://docs.openluat.com/cdn/image//Air780EHM_tm1640.png)
## 四、演示硬件环境
### 4.1 硬件清单
- Air780EPM 核心板 × 1
- TM1640 数码管模块(16 位数码管) × 1
demo所演示的TM1640数码管[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=993241354836&mi_id=0000snMzr-QYn9Az44HHVAD7gjY_XJWQal2Z8EbIpIZRa-k&ns=1&priceTId=213e00d417839211007372882e1068&skuId=5970246265328&spm=a21n57.1.hoverItem.4&utparam=%7B%22aplus_abtest%22%3A%22dc10f02e922922befeb0841a2cbe561e%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 4.2 接线配置
#### 4.2.1 TM1640 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>TM1640 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>CLK<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>DIN<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 主程序入口
2. **tm1640_demo.lua** - TM1640 功能演示模块
3. **exs_tm1640** - TM1640 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 TM1640 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 tm1640_demo.lua 演示模块
require "tm1640_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 字符串显示演示
1. 设备启动后显示 "HELLO",随后自动进入 [1/4] 字符串显示
2. 观察数码管依次显示:`0123456789ABCDEF``88.88` → 从第 3 位显示 `AbCd` → 从第 8 位显示 `1234` → 从第 12 位显示 `5678`
3. 检查字符显示是否正确,带小数点功能是否正常
#### 6.4.2 亮度调节演示
1. 自动进入 [2/4] 亮度调节演示,数码管显示 16 个 8
2. 观察数码管亮度从 0 级到 7 级逐级变化
3. 最终恢复到 4 级亮度
#### 6.4.3 综合演示
1. 自动进入 [3/4] 综合演示
2. 观察倒计时效果:5 → 4 → 3 → 2 → 1 → 0
3. 观察 "HELLO" 闪烁效果,重复 4 次
4. 观察 16 位全 8 显示 + 亮度渐变
5. 数码管全灭
#### 6.4.4 日期/时钟/温度演示
1. 自动进入 [4/4] 日期/时钟/温度演示
2. 日期时钟展示:显示格式 "YY-MM-DD HH:MM"(如 "26-07-13 08:30"),停留 4 秒
3. 时钟展示:模拟走时 "08-30-00" → "08-30-01" → ... → "08-30-09"
4. 温度展示:
- 摄氏度:25°C → 32°C 逐度变化,亮度随温度升高而增加
- 华氏度:77°F → 90°F 逐度变化
5. 演示结束后显示 "End" 并停留 3 秒
### 6.5 预期效果
- **字符串显示**:数字、字母、小数点均能正确显示,支持指定起始位置,16 位完整显示
- **亮度调节**:0~7 级亮度逐级变化,效果明显
- **日期/时钟/温度**:格式显示正确,°C 和 °F 符号正常显示
- **日志如下:**
```lua
[2026-07-21 10:40:01.164][000000000.185] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:40:01.175][000000000.185] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:40:01.185][000000000.187] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:40:01.193][000000000.190] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:40:01.201][000000000.190] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:40:01.210][000000000.190] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:40:01.221][000000000.209] I/user.main TM1640_Demo 001.999.000
[2026-07-21 10:40:01.229][000000000.223] D/user.exs_tm1640 version -> 202607131200
[2026-07-21 10:40:01.237][000000000.250] I/user.exs_tm1640 , clk=31 din=30 =5
[2026-07-21 10:40:01.245][000000000.250] I/user.tm1640_demo TM1640 : 202607131200
[2026-07-21 10:40:01.745][000000001.273] I/user.tm1640_demo ===== [1/4] =====
[2026-07-21 10:40:01.752][000000001.274] I/user.tm1640_demo 0123456789ABCDEF
[2026-07-21 10:40:02.854][000000002.318] I/mobile sim0 sms ready
[2026-07-21 10:40:02.859][000000002.319] D/mobile cid1, state0
[2026-07-21 10:40:02.865][000000002.320] D/mobile bearer act 0, result 0
[2026-07-21 10:40:02.889][000000002.320] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:40:02.905][000000002.374] D/mobile TIME_SYNC 0 tm 1784601606
[2026-07-21 10:40:03.797][000000003.323] I/user.tm1640_demo 88.88
[2026-07-21 10:40:05.337][000000004.870] I/user.tm1640_demo 3 AbCd
[2026-07-21 10:40:06.878][000000006.416] I/user.tm1640_demo 1234 8
[2026-07-21 10:40:08.430][000000007.962] I/user.tm1640_demo 12 5678
[2026-07-21 10:40:10.496][000000010.032] I/user.tm1640_demo ===== [2/4] =====
[2026-07-21 10:40:11.041][000000010.579] I/user.tm1640_demo : 0
[2026-07-21 10:40:11.851][000000011.380] I/user.tm1640_demo : 1
[2026-07-21 10:40:12.644][000000012.181] I/user.tm1640_demo : 2
[2026-07-21 10:40:13.454][000000012.982] I/user.tm1640_demo : 3
[2026-07-21 10:40:14.252][000000013.783] I/user.tm1640_demo : 4
[2026-07-21 10:40:15.060][000000014.584] I/user.tm1640_demo : 5
[2026-07-21 10:40:15.854][000000015.385] I/user.tm1640_demo : 6
[2026-07-21 10:40:16.646][000000016.186] I/user.tm1640_demo : 7
[2026-07-21 10:40:17.983][000000017.510] I/user.tm1640_demo ===== [3/4] =====
[2026-07-21 10:40:27.394][000000026.923] I/user.tm1640_demo ---- 综合演示结束 ----
[2026-07-21 10:40:27.410][000000026.924] I/user.tm1640_demo ===== [4/4] // =====
[2026-07-21 10:40:27.421][000000026.924] I/user.tm1640_demo : 26-07-13 08:30
[2026-07-21 10:40:31.964][000000031.492] I/user.tm1640_demo : 08-30-00 ~ 08-30-09
[2026-07-21 10:40:52.715][000000052.244] I/user.tm1640_demo ===== [] =====
```
### 6.6 故障排除
1. **数码管无显示**
- 检查 TM1640 接线是否正确(CLK、DIN、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_tm1640 扩展库已正常加载
2. **亮度无法调节**
- 确认调用 `set_brightness()` 时参数为 0~7
3. **显示内容错乱**
- 检查杜邦线是否过长导致通信不稳定
### 6.7 扩展功能建议
exs_tm1640 更多接口的使用可以查看 [exs_tm1640 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_tm1640/)
@@ -0,0 +1,186 @@
--[[
@module tm1640_demo
@summary TM1640 驱动芯片演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.13
@author 江访
@usage
本文件包含 TM1640 的逐项功能演示和综合展示。
通过 exs_tm1640 扩展库的 API 逐一演示数码管显示、亮度调节等功能。
]]
-- 加载 exs_tm1640 扩展库
local exs_tm1640 = require "exs_tm1640"
-- 演示字符串显示(利用 16 位数码管)
local function demo_string()
log.info("tm1640_demo", "===== [1/4] 字符串显示 =====")
log.info("tm1640_demo", "显示 0123456789ABCDEF")
exs_tm1640.clear()
exs_tm1640.set_display("0123456789ABCDEF")
sys.wait(2000)
log.info("tm1640_demo", "显示 88.88")
exs_tm1640.clear()
exs_tm1640.set_display("88.88")
sys.wait(1500)
log.info("tm1640_demo", "从第 3 位开始显示 AbCd")
exs_tm1640.clear()
exs_tm1640.set_display("AbCd", 3)
sys.wait(1500)
log.info("tm1640_demo", "偏移显示 1234 在第 8 位")
exs_tm1640.clear()
exs_tm1640.set_display("1234", 8)
sys.wait(1500)
log.info("tm1640_demo", "从第 12 位显示 5678")
exs_tm1640.clear()
exs_tm1640.set_display("5678", 12)
sys.wait(1500)
exs_tm1640.clear()
sys.wait(500)
end
-- 演示亮度调节
local function demo_brightness()
log.info("tm1640_demo", "===== [2/4] 亮度调节 =====")
exs_tm1640.clear()
exs_tm1640.set_display("8888888888888888")
sys.wait(500)
for brightness = 0, 7 do
log.info("tm1640_demo", "设置亮度:", brightness)
exs_tm1640.set_brightness(brightness)
sys.wait(800)
end
exs_tm1640.set_brightness(4)
exs_tm1640.clear()
sys.wait(500)
end
-- 综合演示
local function demo_final_show()
log.info("tm1640_demo", "===== [3/4] 综合演示 =====")
-- 倒计时
for i = 5, 0, -1 do
exs_tm1640.clear()
exs_tm1640.set_display(string.format("%d", i))
sys.wait(500)
end
-- HELLO 闪烁
for i = 0, 3 do
exs_tm1640.clear()
exs_tm1640.set_display("HELLO")
sys.wait(500)
exs_tm1640.clear()
sys.wait(300)
end
-- 全 8 显示加亮度渐变
exs_tm1640.clear()
exs_tm1640.set_display("8888888888888888")
for i = 0, 7 do
exs_tm1640.set_brightness(i)
sys.wait(200)
end
sys.wait(1000)
exs_tm1640.clear()
log.info("tm1640_demo", "---- 综合演示结束 ----")
end
-- 展示日期:16 位显示 "26-07-13 08:30"
local function show_datetime()
log.info("tm1640_demo", "日期时钟展示: 26-07-13 08:30")
exs_tm1640.set_display("26-07-13 08:30")
sys.wait(4000)
exs_tm1640.clear()
end
-- 展示时间:模拟走时 "08-30-00" ~ "08-30-09"
local function show_clock()
exs_tm1640.clear()
sys.wait(200)
log.info("tm1640_demo", "时钟展示: 08-30-00 ~ 08-30-09")
for sec = 0, 9 do
exs_tm1640.set_display(string.format("08-30-%02d", sec))
sys.wait(500)
end
end
-- 展示温度:摄氏度用 "°C",华氏度用 "°F"
local function show_temperature()
exs_tm1640.clear()
sys.wait(200)
local DEGREE = "°"
for temp = 25, 32 do
local cel = string.format("%02d", temp) .. DEGREE .. "C"
exs_tm1640.set_display(cel)
local bright = math.floor((temp - 25) / 7 * 7 + 0.5)
if bright < 0 then bright = 0 end
if bright > 7 then bright = 7 end
exs_tm1640.set_brightness(bright)
sys.wait(800)
end
for temp = 77, 90, 2 do
local fah = string.format("%02d", temp) .. DEGREE .. "F"
exs_tm1640.set_display(fah)
sys.wait(600)
end
exs_tm1640.set_brightness(5)
sys.wait(1000)
exs_tm1640.clear()
end
local function run_main()
log.info("tm1640_demo", "===== [4/4] 日期/时钟/温度 =====")
show_datetime()
sys.wait(300)
show_clock()
sys.wait(300)
show_temperature()
end
-- 演示主函数(运行在协程中)
local function tm1640_demo_task_func()
-- 初始化(2-wireCLK + DIN,无 STB
local result = exs_tm1640.setup({
clk = 31,
din = 30,
bright = 5,
})
if not result then
log.error("tm1640_demo", "TM1640 初始化失败,请检查接线")
return
end
log.info("tm1640_demo", "TM1640 初始化成功,版本:", exs_tm1640.version())
-- HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
exs_tm1640.set_display("HELLO")
sys.wait(1000)
demo_string()
demo_brightness()
demo_final_show()
run_main()
exs_tm1640.set_display("End")
sys.wait(3000)
log.info("tm1640_demo", "===== [演示完毕] =====")
end
sys.taskInit(tm1640_demo_task_func)
@@ -0,0 +1,71 @@
--[[
@module main
@summary TM1650 数码管驱动芯片测试 Demo 入口
@version 1.0
@date 2026.07.13
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_tm1650 扩展库的完整功能,顺序为:
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]
5、7段/8段模式切换([5/6]
6、综合演示-日期/温度([6/6])
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "TM1650_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 tm1650_demo.lua 演示模块
require "tm1650_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,256 @@
# TM1650 数码管驱动芯片演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **tm1650_demo.lua** - TM1650 功能演示模块,包含数码管显示、按键检测、亮度调节、模式切换、综合展示等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_tm1650** - TM1650 扩展库,提供数码管段码显示、按键检测、亮度调节、模式切换等 API
## 二、演示流程介绍
本 demo 按顺序演示 exs_tm1650 扩展库的 6 项功能:HELLO → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
### 2.1 功能演示项说明
1. **[1/6] 字符串显示** - 数码管逐项显示数字、带小数点数字、字母组合、指定位置显示
2. **[2/6] 按键轮询** - 通过 `get_key()` API 轮询检测按键,10 秒超时
3. **[3/6] 亮度调节** - 展示 0~7 级亮度调节效果
4. **[4/6] 按键回调** - 通过 `set_key_callback()` API 设置按键回调函数,15 秒演示窗口
5. **[5/6] 模式切换** - 8 段与 7 段模式切换演示
6. **[6/6] 综合演示** - 倒计时、HELLO 闪烁、全 8 显示、亮度渐变、日期展示、温度展示
## 三、显示效果
![](https://docs.openluat.com/cdn/image/Air780EHM_tm1650.png)
## 四、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- TM1650 数码管模块(4 位数码管 + 28 个按键) × 1
demo所演示的TM1650数码管[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=894744356431&mi_id=0000-4_DHCxKAB3Xj0nlwc0YnvYkgo5-xS43MtZ8NtPojx4&ns=1&priceTId=213e00d417839211691737686e1068&skuId=5910411206496&spm=a21n57.1.hoverItem.3&utparam=%7B%22aplus_abtest%22%3A%22e04de8195726cbf718476351001570dd%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 TM1650 模块接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>TM1650 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>CLK<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>DIO<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。
## 五、演示软件环境
### 5.1 开发工具
- [Luatools下载调试工具](https://docs.openluat.com/air780epm/luatos/common/download/) - 固件烧录和代码调试
### 5.2 内核固件
- [点击下载Air780EPM系列最新版本内核固件](https://docs.openluat.com/air780epm/luatos/firmware/780epm_version/)demo所使用的是 LuatOS-SoC_V2046_Air780EPM 1号固件
### 5.3 脚本文件
1. **main.lua** - 主程序入口
2. **tm1650_demo.lua** - TM1650 功能演示模块
3. **exs_tm1650** - TM1650 扩展库
## 六、演示核心步骤
### 6.1 硬件准备
1. 按照接线表将 TM1650 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 6.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 tm1650_demo.lua 演示模块
require "tm1650_demo"
```
### 6.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 6.4 功能测试
#### 6.4.1 字符串显示演示
1. 设备启动后显示 "HELLO",随后自动进入 [1/6] 字符串显示
2. 观察数码管依次显示:`1234``88.8``AbCd` → 从第 3 位显示 `1234`
3. 检查字符显示是否正确,带小数点功能是否正常
#### 6.4.2 按键检测演示
1. 自动进入 [2/6] 按键轮询演示,数码管显示 "KEY-"
2. 在 10 秒内按下 TM1650 模块上的任意按键
3. 观察数码管显示按键编码(如 "K01"、"K02" 等)
4. 若 10 秒内无按键按下,自动跳过
#### 6.4.3 亮度调节演示
1. 自动进入 [3/6] 亮度调节演示,数码管显示 "8888"
2. 观察数码管亮度从 0 级到 7 级逐级变化
3. 最终恢复到 4 级亮度
#### 6.4.4 按键回调演示
1. 自动进入 [4/6] 按键回调演示,数码管显示 "Cb-Key"
2. 在 15 秒内按下任意按键,观察数码管显示对应按键编码
3. 观察日志输出,确认回调函数 `on_key` 被正常触发
4. 15 秒后自动结束回调演示
#### 6.4.5 模式切换演示
1. 自动进入 [5/6] 模式切换演示
2. 观察数码管从 8 段模式切换到 7 段模式
3. 再切回 8 段模式,观察显示效果变化
4. 确认小数点(DP)在 7 段模式下不显示
#### 6.4.6 综合演示
1. 自动进入 [6/6] 综合演示
2. 观察倒计时效果:5 → 4 → 3 → 2 → 1 → 0
3. 观察 "HELO" 闪烁效果,重复 4 次
4. 观察全 8 显示 + 亮度渐变
5. 日期展示:显示格式 "YY-MM-DD"(如 "26-07-13"),停留 3 秒
6. 温度展示:
- 摄氏度:25°C → 32°C 逐度变化,亮度随温度升高而增加
- 华氏度:77°F → 90°F 逐度变化
7. 演示结束后显示 "End" 并停留 3 秒
### 6.5 预期效果
- **字符串显示**:数字、字母、小数点均能正确显示,支持指定起始位置
- **按键检测**:轮询模式和回调模式均能正确检测按键,编码显示正常
- **亮度调节**:0~7 级亮度逐级变化,效果明显
- **模式切换**:8 段和 7 段模式切换正常,小数点显示状态正确
- **日期/温度**:格式显示正确,°C 和 °F 符号正常显示
- **日志如下:**
```lua
[2026-07-21 10:42:14.862][000000000.178] I/main LuatOS@Air780EPM base 26.04 bsp V2046 32bit
[2026-07-21 10:42:14.863][000000000.178] I/main ROM Build: Jul 7 2026 20:39:17
[2026-07-21 10:42:14.865][000000000.180] W/pins /luadb/pins_air780epm.json not exist!!
[2026-07-21 10:42:14.867][000000000.182] D/main loadlibs luavm 1048568 14784 14784
[2026-07-21 10:42:14.870][000000000.182] D/main loadlibs sys 2366664 82756 89356
[2026-07-21 10:42:14.872][000000000.182] D/main loadlibs psram 2366664 82756 89356
[2026-07-21 10:42:14.874][000000000.200] I/user.main TM1650_Demo 001.000.001
[2026-07-21 10:42:14.876][000000000.213] D/user.exs_tm1650 version -> 202607161200
[2026-07-21 10:42:14.878][000000000.224] I/user.exs_tm1650 , clk=31 dio=30 =5
[2026-07-21 10:42:14.880][000000000.224] I/user.tm1650_demo TM1650 : 202607161200
[2026-07-21 10:42:15.073][000000001.230] I/user.tm1650_demo ===== [1/6] =====
[2026-07-21 10:42:15.075][000000001.231] I/user.tm1650_demo 1234
[2026-07-21 10:42:15.958][000000002.611] I/mobile sim0 sms ready
[2026-07-21 10:42:15.960][000000002.611] D/mobile cid1, state0
[2026-07-21 10:42:15.973][000000002.612] D/mobile bearer act 0, result 0
[2026-07-21 10:42:15.977][000000002.613] D/mobile NETIF_LINK_ON -> IP_READY
[2026-07-21 10:42:15.982][000000002.654] D/mobile TIME_SYNC 0 tm 1784601739
[2026-07-21 10:42:16.024][000000002.744] I/user.tm1650_demo 88.8
[2026-07-21 10:42:17.529][000000004.256] I/user.tm1650_demo AbCd
[2026-07-21 10:42:19.038][000000005.768] I/user.tm1650_demo 3 12
[2026-07-21 10:42:20.562][000000007.280] I/user.tm1650_demo HELO
[2026-07-21 10:42:22.567][000000009.297] I/user.tm1650_demo ===== [2/6] =====
[2026-07-21 10:42:22.571][000000009.298] I/user.tm1650_demo 10 TM1650 ...
[2026-07-21 10:42:24.405][000000011.130] I/user.tm1650_demo , : 1
[2026-07-21 10:42:24.515][000000011.238] I/user.tm1650_demo , : 1
[2026-07-21 10:42:25.431][000000012.155] I/user.tm1650_demo , : 2
[2026-07-21 10:42:25.540][000000012.262] I/user.tm1650_demo , : 2
[2026-07-21 10:42:26.053][000000012.774] I/user.tm1650_demo , : 3
[2026-07-21 10:42:26.161][000000012.881] I/user.tm1650_demo , : 3
[2026-07-21 10:42:26.674][000000013.394] I/user.tm1650_demo , : 4
[2026-07-21 10:42:27.389][000000014.107] I/user.tm1650_demo , : 5
[2026-07-21 10:42:27.903][000000014.620] I/user.tm1650_demo , : 6
[2026-07-21 10:42:28.308][000000015.030] I/user.tm1650_demo , : 7
[2026-07-21 10:42:28.711][000000015.440] I/user.tm1650_demo , : 8
[2026-07-21 10:42:29.333][000000016.054] I/user.tm1650_demo , : 9
[2026-07-21 10:42:29.738][000000016.465] I/user.tm1650_demo , : 10
[2026-07-21 10:42:30.250][000000016.977] I/user.tm1650_demo , : 11
[2026-07-21 10:42:30.670][000000017.387] I/user.tm1650_demo , : 12
[2026-07-21 10:42:31.278][000000018.000] I/user.tm1650_demo , : 13
[2026-07-21 10:42:31.682][000000018.413] I/user.tm1650_demo , : 14
[2026-07-21 10:42:32.103][000000018.823] I/user.tm1650_demo , : 15
[2026-07-21 10:42:32.507][000000019.233] I/user.tm1650_demo , : 16
[2026-07-21 10:42:32.817][000000019.541] I/user.tm1650_demo
[2026-07-21 10:42:32.822][000000019.541] I/user.tm1650_demo ===== [3/6] =====
[2026-07-21 10:42:32.827][000000019.542] I/user.tm1650_demo 15
[2026-07-21 10:42:33.035][000000019.755] I/user.tm1650_demo , : 17
[2026-07-21 10:42:33.581][000000020.306] I/user.tm1650_demo , : 18
[2026-07-21 10:42:34.125][000000020.855] I/user.tm1650_demo , : 19
[2026-07-21 10:42:34.529][000000021.255] I/user.tm1650_demo , : 20
[2026-07-21 10:42:35.024][000000021.755] I/user.tm1650_demo , : 21
[2026-07-21 10:42:35.428][000000022.155] I/user.tm1650_demo , : 22
[2026-07-21 10:42:35.833][000000022.556] I/user.tm1650_demo , : 23
[2026-07-21 10:42:36.175][000000022.905] I/user.tm1650_demo , : 24
[2026-07-21 10:42:36.577][000000023.305] I/user.tm1650_demo , : 25
[2026-07-21 10:42:36.981][000000023.705] I/user.tm1650_demo , : 26
[2026-07-21 10:42:37.323][000000024.055] I/user.tm1650_demo , : 27
[2026-07-21 10:42:37.730][000000024.455] I/user.tm1650_demo , : 28
[2026-07-21 10:42:47.837][000000034.556] I/user.tm1650_demo
[2026-07-21 10:42:47.841][000000034.556] I/user.tm1650_demo ===== [4/6] =====
[2026-07-21 10:42:48.350][000000035.068] I/user.tm1650_demo : 0
[2026-07-21 10:42:49.141][000000035.870] I/user.tm1650_demo : 1
[2026-07-21 10:42:49.950][000000036.672] I/user.tm1650_demo : 2
[2026-07-21 10:42:50.742][000000037.474] I/user.tm1650_demo : 3
[2026-07-21 10:42:51.550][000000038.276] I/user.tm1650_demo : 4
[2026-07-21 10:42:52.360][000000039.078] I/user.tm1650_demo : 5
[2026-07-21 10:42:53.154][000000039.880] I/user.tm1650_demo : 6
[2026-07-21 10:42:53.963][000000040.682] I/user.tm1650_demo : 7
[2026-07-21 10:42:55.268][000000041.991] I/user.tm1650_demo ===== [5/6] 7/8 =====
[2026-07-21 10:43:01.812][000000048.536] I/user.tm1650_demo ===== [6/6] =====
[2026-07-21 10:43:10.772][000000057.497] I/user.tm1650_demo : 0713
[2026-07-21 10:43:26.692][000000073.417] I/user.tm1650_demo ---- 综合演示结束 ----
[2026-07-21 10:43:29.693][000000076.423] I/user.tm1650_demo ===== [] =====
```
### 6.6 故障排除
1. **数码管无显示**
- 检查 TM1650 接线是否正确(CLK、DIO、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_tm1650 扩展库已正常加载
2. **按键无响应**
- 检查按键接线和 TM1650 模块是否正常
- 确认扩展库初始化成功
- 查看日志确认 `setup()` 返回值为 true
3. **亮度无法调节**
- 确认调用 `set_brightness()` 时参数为 0~7
4. **显示内容错乱**
- 检查杜邦线是否过长导致通信不稳定
### 6.7 扩展功能建议
exs_tm1650 更多接口的使用可以查看 [exs_tm1650 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_tm1650/)
@@ -0,0 +1,243 @@
--[[
@module tm1650_demo
@summary TM1650 驱动芯片演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.13
@author 江访
@usage
本文件包含 TM1650 的逐项功能演示和综合展示。
通过 exs_tm1650 扩展库的 API 逐一演示数码管显示、按键检测、亮度调节、
7段/8段模式切换等功能。
]]
-- 加载 exs_tm1650 扩展库
local exs_tm1650 = require "exs_tm1650"
-- 演示字符串显示
local function demo_string()
log.info("tm1650_demo", "===== [1/6] 字符串显示 =====")
log.info("tm1650_demo", "显示 1234")
exs_tm1650.clear()
exs_tm1650.set_display("1234")
sys.wait(1500)
log.info("tm1650_demo", "显示 88.8")
exs_tm1650.clear()
exs_tm1650.set_display("88.8")
sys.wait(1500)
log.info("tm1650_demo", "显示 AbCd")
exs_tm1650.clear()
exs_tm1650.set_display("AbCd")
sys.wait(1500)
log.info("tm1650_demo", "从第 3 位开始显示 12")
exs_tm1650.clear()
exs_tm1650.set_display("12", 3)
sys.wait(1500)
log.info("tm1650_demo", "显示 HELO")
exs_tm1650.clear()
exs_tm1650.set_display("HELO")
sys.wait(1500)
exs_tm1650.clear()
sys.wait(500)
end
-- 演示按键轮询
local function demo_keys()
log.info("tm1650_demo", "===== [2/6] 按键轮询 =====")
log.info("tm1650_demo", "请在 10 秒内按下连接到 TM1650 的按键...")
exs_tm1650.clear()
exs_tm1650.set_display("KEYS")
local timeout = 10000
local step = 100
local elapsed = 0
while elapsed < timeout do
local key = exs_tm1650.get_key()
if key then
log.info("tm1650_demo", "检测到按键, 编码:", key)
local key_str = string.format("%02d", key)
exs_tm1650.set_display(key_str)
sys.wait(100)
elapsed = elapsed + 100
else
sys.wait(step)
elapsed = elapsed + step
end
end
log.info("tm1650_demo", "按键检测超时,跳过")
end
-- 按键回调函数
local function on_key(key_code)
log.info("tm1650_demo", "按键回调触发, 编码:", key_code)
local display = string.format("%02d", key_code)
exs_tm1650.set_display(display)
end
-- 演示按键回调
local function demo_key_callback()
log.info("tm1650_demo", "===== [3/6] 按键回调 =====")
log.info("tm1650_demo", "接下来 15 秒内按按键查看回调效果")
exs_tm1650.clear()
exs_tm1650.set_display("CALL")
exs_tm1650.set_key_callback(on_key)
sys.wait(15000)
exs_tm1650.set_key_callback(nil)
log.info("tm1650_demo", "按键回调演示结束")
end
-- 演示亮度调节
local function demo_brightness()
log.info("tm1650_demo", "===== [4/6] 亮度调节 =====")
exs_tm1650.clear()
exs_tm1650.set_display("8888")
sys.wait(500)
for bright = 0, 7 do
log.info("tm1650_demo", "设置亮度:", bright)
exs_tm1650.set_brightness(bright)
sys.wait(800)
end
-- 恢复默认亮度
exs_tm1650.set_brightness(4)
exs_tm1650.clear()
sys.wait(500)
end
-- 演示 7段/8段 模式切换
local function demo_segment_mode()
log.info("tm1650_demo", "===== [5/6] 7段/8段模式切换 =====")
-- 8 段模式(含 DP 段),显示数字
exs_tm1650.clear()
exs_tm1650.set_mode(0)
exs_tm1650.set_display("8SEG")
sys.wait(2000)
-- 显示带小数点的内容(8 段模式特性)
exs_tm1650.clear()
exs_tm1650.set_display("88.8")
sys.wait(2000)
-- 切换到 7 段模式
exs_tm1650.clear()
exs_tm1650.set_mode(1)
exs_tm1650.set_display("7SEG")
sys.wait(2000)
exs_tm1650.clear()
sys.wait(500)
-- 切回 8 段模式
exs_tm1650.set_mode(0)
end
-- 综合演示
local function demo_final()
log.info("tm1650_demo", "===== [6/6] 综合演示 =====")
-- 倒计时
for i = 5, 0, -1 do
exs_tm1650.clear()
exs_tm1650.set_display(string.format("%04d", i))
sys.wait(500)
end
-- HELO 闪烁
for i = 0, 3 do
exs_tm1650.clear()
exs_tm1650.set_display("HELO")
sys.wait(500)
exs_tm1650.clear()
sys.wait(300)
end
-- 全 8 显示 + 亮度渐变
exs_tm1650.clear()
exs_tm1650.set_display("8888")
for i = 0, 7 do
exs_tm1650.set_brightness(i)
sys.wait(200)
end
sys.wait(1000)
exs_tm1650.set_brightness(4)
exs_tm1650.clear()
-- 日期展示(4 位数码管,显示月日)
log.info("tm1650_demo", "日期展示: 0713")
exs_tm1650.set_display("0713")
sys.wait(3000)
-- 温度展示(摄氏度)
exs_tm1650.clear()
sys.wait(200)
local DEGREE = "\xB0"
for temp = 25, 32 do
local cel = string.format("%02d", temp) .. DEGREE .. "C"
exs_tm1650.set_display(cel)
local bright = math.floor((temp - 25) / 7 * 7 + 0.5)
if bright < 0 then bright = 0 end
if bright > 7 then bright = 7 end
exs_tm1650.set_brightness(bright)
sys.wait(800)
end
-- 温度展示(华氏度)
for temp = 77, 90, 2 do
local fah = string.format("%02d", temp) .. DEGREE .. "F"
exs_tm1650.set_display(fah)
sys.wait(600)
end
exs_tm1650.set_brightness(5)
sys.wait(2000)
exs_tm1650.clear()
log.info("tm1650_demo", "---- 综合演示结束 ----")
end
-- 演示主函数(运行在协程中)
local function tm1650_demo_task_func()
-- 初始化
local result = exs_tm1650.setup({
clk = 31,
dio = 30,
bright = 5,
})
if not result then
log.error("tm1650_demo", "TM1650 初始化失败,请检查接线")
return
end
log.info("tm1650_demo", "TM1650 初始化成功,版本:", exs_tm1650.version())
-- HELLO 开始 → [1/6] → [2/6] → [3/6] → [4/6] → [5/6] → [6/6] → End
exs_tm1650.set_display("HELO")
sys.wait(1000)
demo_string()
demo_keys()
demo_key_callback()
demo_brightness()
demo_segment_mode()
demo_final()
exs_tm1650.set_display("End-")
sys.wait(3000)
log.info("tm1650_demo", "===== [演示完毕] =====")
end
sys.taskInit(tm1650_demo_task_func)
@@ -0,0 +1,72 @@
--[[
@module main
@summary VL53L1X 飞行时间测距传感器 Demo 入口
@version 1.0
@date 2026.07.20
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_vl53l1x 扩展库的完整功能,顺序为:
HELLO→[1/5]→[2/5]→[3/5]→[4/5]→[5/5]→End
1、标准测距演示([1/5]
2、测距模式切换([2/5]
3、中断读取演示([3/5]
4、休眠与唤醒([4/5]
5、持续测距演示([5/5]
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "VL53L1X_Demo" -- 项目命名
VERSION = "001.999.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)
-- 加载 vl53l1x_demo.lua 演示模块
require "vl53l1x_demo"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,224 @@
# VL53L1X 激光红外测距传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **vl53l1x_demo.lua** - VL53L1X 功能演示模块,包含标准测距、模式切换、休眠唤醒、持续测距等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_vl53l1x** - VL53L1X 扩展库,提供初始化、测距数据读取、模式切换、睡眠/唤醒、关闭等 API
### 1.3 诊断工具模块(按需使用)
1. **vl53l1x_scan.lua** - 基础扫描:I2C 地址检测、软件复位、固件状态、Model ID 读取
2. **vl53l1x_deep_scan.lua** - 深度扫描:扫描全寄存器,判断芯片响应是否正常
3. **vl53l1x_xshut_test.lua** - XSHUT 硬件复位测试
> 注:诊断工具模块需要在 main.lua 中通过 `require` 切换加载,不参与演示流程。
## 二、演示流程介绍
本 demo 按顺序演示 exs_vl53l1x 扩展库的 4 项功能:HELLO → [1/4] → [2/4] → [3/4] → [4/4] → End
### 2.1 功能演示项说明
1. **[1/5] 标准测距** - 初始化传感器,连续读取 5 次距离数据(默认 standard 模式)
2. **[2/4] 测距模式切换** - 演示 short(抗强光)和 long(远距离)两种模式切换
3. **[3/4] 休眠与唤醒** - 演示 sleep() 进入软件待机、wakeup() 唤醒恢复测距
4. **[4/4] 持续测距** - 演示 sleep() 进入软件待机、wakeup() 唤醒恢复测距
** - 每隔 1 秒读取一次距离数据,共 5 次
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- VL53L1X 激光红外测距传感器模块(如 GY-VL53L1X 等) × 1
demo所演示的VL53L1X 激光红外测距传感器模块[购买链接](https://item.taobao.com/item.htm?app=chrome&bxsign=scd0Y1aCFssu5WNBf4zgfL9PHTC771hSBaYQhJANVSUUTh2awzexyQyuQSg4mN6Pbloj55y5X5F66iW6hytMgKrQauVMZK_2eg_EVTNPrAckp1M2aoTVhEBdqhZFkPdhK0r&cpp=1&h5_spm=a-tb-item.b-tb-item&id=597981154375&share_crt_v=1&shareurl=true&short_name=h.8XEIWwnTbGSsdlI&sp_tk=MGdwOGd0NWtvME0%3D&spm=a2159r.13376460.0.0&tbSocialPopKey=shareItem&tk=0gp8gt5ko0M&un=33e3e4902f14a2acade3a8129ff9a52d&un_site=0&ut_sk=1.aHcpu1x%2F2XQDACU%2ByKbrGPzO_21646297_1784598506860.TaoPassword-WeiXin.1&wxsign=tbwRNA5Uhd58zJzNm2egOHG_-dy1Cgfx7h_MWqBNsxfXGOARu2O3Fc6t-RivRx0dfSe2M0HcrjjRwaVMelu4sxeZQ-Sg6ckAT9CCpBMe0GKHQ-1vgswN6QAK7KQCMvOfph_&x-ssr=true)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 I2C 模式接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>VL53L1X 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>SDA<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC3.3V<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。VL53L1X 模块上的 SCL/SDA 通常已集成上拉电阻,无需额外配置。XSHUT 引脚不接时传感器会自动启动。
![](https://docs.openluat.com/cdn/image/Air780EHM_exs_vl53l1x.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 脚本文件
1. **main.lua** - 程序入口
2. **vl53l1x_demo.lua** - VL53L1X 功能演示模块
3. **exs_vl53l1x** - VL53L1X 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 VL53L1X 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 vl53l1x_demo.lua 演示模块
require "vl53l1x_demo"
```
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 标准测距演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/5] 标准测距
2. 观察日志输出,确认初始化成功(应看到 `MID=0xEA MT=0xCC`
3. 观察距离数据(单位 mm)的连续读取,共 5 次
#### 5.4.2 测距模式切换演示
1. 自动进入 [2/4] 测距模式切换
2. short 模式:抗环境光干扰最强,适合窗口附近等强光环境
3. long 模式:最远可测约 3.6m,适合室内暗光远距离场景
#### 5.4.3 休眠与唤醒演示
1. 自动进入 [3/4] 休眠与唤醒演示
2. 读一次当前距离作为参考
3. sleep() 进入软件待机(约 6μA),3 秒后 wakeup() 唤醒恢复测距
4. 观察唤醒后测距是否正常恢复
#### 5.4.4 持续测距演示
1. 自动进入 [4/4] 持续测距演示
2. 每隔 1 秒读取一次距离数据,共 5 次
3. 观察数据稳定性与状态码
### 5.5 预期效果
- **标准测距**:初始化成功,距离数据正常输出
- **模式切换**short/long 模式均能正常读取距离
- **休眠唤醒**sleep 后停止测距,wakeup 后测距恢复正常
- **持续测距**:每秒一帧数据,状态码 0(测距成功)
- **日志如下:**
```lua
[2026-07-21 20:17:13.093][000000000.246] I/user.main VL53L1X_Demo 001.999.000
[2026-07-21 20:17:13.099][000000000.263] I/user.vl53l1x_demo HELLO
[2026-07-21 20:17:13.228][000000001.264] I/user.vl53l1x_demo ===== [1/5] =====
[2026-07-21 20:17:13.232][000000001.275] I/user.exs_vl53l1x 0x00E5=0x03
[2026-07-21 20:17:13.238][000000001.385] I/user.exs_vl53l1x (mode=standard)
[2026-07-21 20:17:13.243][000000001.388] I/user.exs_vl53l1x MID=0xEA MT=0xCC
[2026-07-21 20:17:13.377][000000001.591] I/user.exs_vl53l1x Standard Ranging
[2026-07-21 20:17:13.382][000000001.591] I/user.vl53l1x_demo VL53L1X : 202607201200
[2026-07-21 20:17:13.400][000000001.845] I/user.vl53l1x_demo 1: =201mm = stream=1
[2026-07-21 20:17:13.820][000000002.354] I/user.vl53l1x_demo 2: =200mm = stream=2
[2026-07-21 20:17:14.325][000000002.863] I/user.vl53l1x_demo 3: =200mm = stream=3
[2026-07-21 20:17:14.827][000000003.372] I/user.vl53l1x_demo 4: =196mm = stream=4
[2026-07-21 20:17:15.339][000000003.881] I/user.vl53l1x_demo 5: =197mm = stream=5
[2026-07-21 20:17:15.837][000000004.385] I/user.exs_vl53l1x
[2026-07-21 20:17:15.845][000000004.385] I/user.vl53l1x_demo ---- [1/5] 完成 ----
[2026-07-21 20:17:16.343][000000004.886] I/user.vl53l1x_demo ===== [2/5] =====
[2026-07-21 20:17:16.348][000000004.886] I/user.vl53l1x_demo short
[2026-07-21 20:17:16.358][000000004.898] I/user.exs_vl53l1x 0x00E5=0x03
[2026-07-21 20:17:16.466][000000005.008] I/user.exs_vl53l1x (mode=short)
[2026-07-21 20:17:16.471][000000005.011] I/user.exs_vl53l1x MID=0xEA MT=0xCC
[2026-07-21 20:17:16.668][000000005.215] I/user.exs_vl53l1x Standard Ranging
[2026-07-21 20:17:16.916][000000005.461] I/user.vl53l1x_demo short : =24mm =
[2026-07-21 20:17:16.923][000000005.465] I/user.exs_vl53l1x
[2026-07-21 20:17:16.928][000000005.465] I/user.vl53l1x_demo long
[2026-07-21 20:17:16.934][000000005.477] I/user.exs_vl53l1x 0x00E5=0x03
[2026-07-21 20:17:17.040][000000005.587] I/user.exs_vl53l1x (mode=long)
[2026-07-21 20:17:17.056][000000005.590] I/user.exs_vl53l1x MID=0xEA MT=0xCC
[2026-07-21 20:17:17.258][000000005.794] I/user.exs_vl53l1x Standard Ranging
[2026-07-21 20:17:17.524][000000006.058] I/user.vl53l1x_demo long : =199mm =
[2026-07-21 20:17:17.530][000000006.062] I/user.exs_vl53l1x
[2026-07-21 20:17:17.534][000000006.062] I/user.vl53l1x_demo ---- [2/5] 完成 ----
[2026-07-21 20:17:18.021][000000006.563] I/user.vl53l1x_demo ===== [4/5] =====
[2026-07-21 20:17:18.036][000000006.574] I/user.exs_vl53l1x 0x00E5=0x03
[2026-07-21 20:17:18.144][000000006.684] I/user.exs_vl53l1x (mode=standard)
[2026-07-21 20:17:18.151][000000006.688] I/user.exs_vl53l1x MID=0xEA MT=0xCC
[2026-07-21 20:17:18.345][000000006.891] I/user.exs_vl53l1x Standard Ranging
[2026-07-21 20:17:18.609][000000007.144] I/user.vl53l1x_demo : =201mm =
[2026-07-21 20:17:18.614][000000007.144] I/user.vl53l1x_demo
[2026-07-21 20:17:18.620][000000007.149] I/user.exs_vl53l1x
[2026-07-21 20:17:21.610][000000010.149] I/user.vl53l1x_demo
[2026-07-21 20:17:21.626][000000010.162] I/user.exs_vl53l1x
[2026-07-21 20:17:21.875][000000010.415] I/user.vl53l1x_demo : =199mm =
[2026-07-21 20:17:21.882][000000010.419] I/user.exs_vl53l1x
[2026-07-21 20:17:21.887][000000010.419] I/user.vl53l1x_demo ---- [4/5] 完成 ----
[2026-07-21 20:17:22.385][000000010.920] I/user.vl53l1x_demo ===== [5/5] =====
[2026-07-21 20:17:22.391][000000010.931] I/user.exs_vl53l1x 0x00E5=0x03
[2026-07-21 20:17:22.508][000000011.041] I/user.exs_vl53l1x (mode=standard)
[2026-07-21 20:17:22.514][000000011.044] I/user.exs_vl53l1x MID=0xEA MT=0xCC
[2026-07-21 20:17:22.711][000000011.248] I/user.exs_vl53l1x Standard Ranging
[2026-07-21 20:17:22.721][000000011.248] I/user.vl53l1x_demo 1 5
[2026-07-21 20:17:23.764][000000012.302] I/user.vl53l1x_demo [1]: =201mm =
[2026-07-21 20:17:24.771][000000013.311] I/user.vl53l1x_demo [2]: =199mm =
[2026-07-21 20:17:25.779][000000014.320] I/user.vl53l1x_demo [3]: =197mm =
[2026-07-21 20:17:26.788][000000015.329] I/user.vl53l1x_demo [4]: =198mm =
[2026-07-21 20:17:27.803][000000016.338] I/user.vl53l1x_demo [5]: =197mm =
[2026-07-21 20:17:27.808][000000016.342] I/user.exs_vl53l1x
[2026-07-21 20:17:27.813][000000016.342] I/user.vl53l1x_demo ---- [5/5] 完成 ----
[2026-07-21 20:17:28.299][000000016.843] I/user.vl53l1x_demo ===== [] =====
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 VL53L1X 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_vl53l1x 扩展库已正常加载
2. **读取数据始终为 0 或异常**
- 检查传感器供电是否正常
- 确认 I2C 地址是否正确(默认 0x29)
- 确认接线无松动
3. **测距状态码异常**
- 状态码 1(Sigma 失效):测量波动太大,目标反射率过低
- 状态码 2(信号失效):信号太弱,目标距离过远或反射率低
- 状态码 9(串扰信号):有保护玻璃串扰,可运行串扰校准
- 状态码 14(范围无效):通常对应第一帧无效数据,会自动跳过
### 5.7 扩展功能建议
exs_vl53l1x 更多接口的使用可以查看 [exs_vl53l1x 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_vl53l1x/)
@@ -0,0 +1,151 @@
--[[
@module vl53l1x_demo
@summary VL53L1X 飞行时间测距传感器演示模块,包含所有功能的演示用例
@version 1.0
@date 2026.07.20
@author 江访
@usage
本文件包含 VL53L1X 的逐项功能演示。
通过 exs_vl53l1x 扩展库的 API 逐一演示标准测距、模式切换、中断读取、休眠唤醒、持续测距等功能。
]]
-- 加载 exs_vl53l1x 扩展库
local exs_vl53l1x = require "exs_vl53l1x"
-- [1/5] 标准测距演示
local function demo_standard_ranging()
log.info("vl53l1x_demo", "===== [1/5] 标准测距 =====")
-- 软件 I2C 模式:SCL=GPIO31, SDA=GPIO30Air780EPM 接线)
local result = exs_vl53l1x.setup({scl = 31, sda = 30})
if not result then
log.error("vl53l1x_demo", "初始化失败,请检查接线")
return false
end
log.info("vl53l1x_demo", "VL53L1X 初始化成功,版本:", exs_vl53l1x.version())
sys.wait(200)
for i = 1, 5 do
local data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("第%d次: 距离=%dmm 状态=%s stream=%d",
i, data.distance, data.status_str, data.stream_count or 0))
else
log.error("vl53l1x_demo", "读取数据失败")
end
sys.wait(500)
end
exs_vl53l1x.close()
log.info("vl53l1x_demo", "---- [1/5] 完成 ----")
return true
end
-- [2/5] 测距模式切换
local function demo_range_mode()
log.info("vl53l1x_demo", "===== [2/5] 测距模式切换 =====")
-- short 模式(抗强光,约 1.36m
log.info("vl53l1x_demo", "切换 short 模式(抗强光)")
local ok = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "short"})
if not ok then return end
sys.wait(200)
local data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("short 模式: 距离=%dmm 状态=%s", data.distance, data.status_str))
end
exs_vl53l1x.close()
-- long 模式(远距离,约 3.6m)
log.info("vl53l1x_demo", "切换 long 模式(远距离)")
ok = exs_vl53l1x.setup({scl = 31, sda = 30, range_mode = "long"})
if not ok then return end
sys.wait(200)
data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("long 模式: 距离=%dmm 状态=%s", data.distance, data.status_str))
end
exs_vl53l1x.close()
log.info("vl53l1x_demo", "---- [2/5] 完成 ----")
end
-- [3/5] 休眠与唤醒
local function demo_sleep_wakeup()
log.info("vl53l1x_demo", "===== [4/5] 休眠与唤醒演示 =====")
local result = exs_vl53l1x.setup({scl = 31, sda = 30})
if not result then return end
sys.wait(200)
-- 读一次数据作为参考
local data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("休眠前: 距离=%dmm 状态=%s", data.distance, data.status_str))
end
-- 进入软件待机(约 6μA
log.info("vl53l1x_demo", "进入软件待机模式")
exs_vl53l1x.sleep()
sys.wait(3000)
-- 唤醒(自动恢复测距)
log.info("vl53l1x_demo", "从软件待机模式唤醒")
exs_vl53l1x.wakeup()
sys.wait(200)
data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("唤醒后: 距离=%dmm 状态=%s", data.distance, data.status_str))
end
exs_vl53l1x.close()
log.info("vl53l1x_demo", "---- [4/5] 完成 ----")
end
-- [5/5] 持续测距演示
local function demo_continuous_ranging()
log.info("vl53l1x_demo", "===== [5/5] 持续测距演示 =====")
local result = exs_vl53l1x.setup({scl = 31, sda = 30})
if not result then return end
log.info("vl53l1x_demo", "每隔 1 秒读取一次距离数据,共 5 次")
for i = 1, 5 do
sys.wait(1000)
local data = exs_vl53l1x.get_data()
if data then
log.info("vl53l1x_demo", string.format("持续测距[%d]: 距离=%dmm 状态=%s",
i, data.distance, data.status_str))
else
log.warn("vl53l1x_demo", string.format("持续测距[%d] 读取失败", i))
end
end
exs_vl53l1x.close()
log.info("vl53l1x_demo", "---- [5/5] 完成 ----")
end
-- 演示主函数(运行在协程中)
local function vl53l1x_demo_task_func()
log.info("vl53l1x_demo", "HELLO")
sys.wait(1000)
-- HELLO 开始 → [1/5] → [2/5] → [3/5] → [4/5] → End
local ok = demo_standard_ranging()
if not ok then return end
sys.wait(500)
demo_range_mode()
sys.wait(500)
demo_sleep_wakeup()
sys.wait(500)
demo_continuous_ranging()
sys.wait(500)
log.info("vl53l1x_demo", "===== [演示完毕] =====")
end
sys.taskInit(vl53l1x_demo_task_func)
@@ -0,0 +1,62 @@
--[[
@module main
@summary VL6180X 飞行时间(ToF)测距传感器 Demo 入口
@version 1.0
@date 2026.08.08
@author 江访
]]
--[[
=== 演示内容 ===
本 demo 演示 exs_vl6180x 扩展库的完整功能,顺序为:
HELLO→[1/2]→[2/2]→End
1、标准测距演示([1/2]
2、持续测距演示([2/2]
更多说明参考本目录下的 readme.md 文件
]]
--[[
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
-- main.lua - 程序入口文件
PROJECT = "VL6180X_Demo" -- 项目命名
VERSION = "001.999.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进行使用
-- 加载 vl6180x_app.lua 演示模块
require "vl6180x_app"
-- 用户代码已结束
-- 结尾总是这一句
sys.run()
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
@@ -0,0 +1,219 @@
# VL6180X 飞行时间(ToF)测距传感器演示
## 一、功能模块介绍
### 1.1 核心主程序模块
1. **main.lua** - 主程序入口,负责项目初始化、版本定义和任务调度
2. **vl6180x_app.lua** - VL6180X 功能演示模块,包含标准测距、持续测距等所有功能的演示用例
### 1.2 扩展库模块
1. **exs_vl6180x** - VL6180X 扩展库,提供初始化、测距数据读取、睡眠/唤醒/关闭等 API
### 1.3 I2C 模式说明
**推荐使用软件 I2C 模式**VL6180X 使用标准 I2C 接口通信,软件 I2C 模式无需指定 I2C 控制器,任意 GPIO 均可模拟 I2C 时序,灵活性更高。本 demo 默认使用软件 I2C 模式(SCL=GPIO31, SDA=GPIO30)演示,也可通过 `vl6180x_app.lua` 中的 `MODE` 变量切换为硬件 I2C 模式。
## 二、演示流程介绍
本 demo 按顺序演示 exs_vl6180x 扩展库的 2 项功能:HELLO → [1/2] → [2/2] → End
### 2.1 功能演示项说明
1. **[1/2] 标准测距** - 初始化传感器,连续读取 5 次距离数据
2. **[2/2] 持续测距** - 每隔 1 秒读取一次距离数据,共 5 次
## 三、演示硬件环境
### 3.1 硬件清单
- Air780EPM 核心板 × 1
- VL6180X 飞行时间测距传感器模块(如 GY-VL6180X 等) × 1
demo所演示的 VL6180X 飞行时间测距传感器模块[购买链接](https://item.taobao.com/item.htm?abbucket=12&id=583699439059&mi_id=0000ftVCg3WGnMAvz4efKE6nBbeZruB15ngNbwYKbhrQsRw&ns=1&priceTId=214781b017847853977586144e1012&skuId=6173431898563&spm=a21n57.1.hoverItem.6&utparam=%7B%22aplus_abtest%22%3A%2211ba2879882e2dc210ad662f97fd02e0%22%7D&xxc=taobaoSearch)
- 母对母杜邦线 × 4
- TYPE-C 数据线 × 1
### 3.2 接线配置
#### 3.2.1 I2C 模式接线
<table>
<tr>
<td>Air780EPM 核心板<br/></td><td>VL6180X 模块<br/></td></tr>
<tr>
<td>32/GPIO31<br/></td><td>SCL<br/></td></tr>
<tr>
<td>31/GPIO30<br/></td><td>SDA<br/></td></tr>
<tr>
<td>VDD_EXT<br/></td><td>VCC3.3V<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
> 说明:接线时注意杜邦线不宜过长,以免通信不稳定。VL6180X 模块上的 SCL/SDA 通常已集成上拉电阻,无需额外配置。
![](https://docs.openluat.com/cdn/image/Air780EHM_VL6180X.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 脚本文件
1. **main.lua** - 程序入口
2. **vl6180x_app.lua** - VL6180X 功能演示模块
3. **exs_vl6180x** - VL6180X 扩展库
## 五、演示核心步骤
### 5.1 硬件准备
1. 按照接线表将 VL6180X 模块连接到核心板
2. 确保电源连接正确,通过 TYPE-C USB 口供电
3. 检查所有接线无误,避免短路
### 5.2 软件配置
`main.lua` 中加载对应的演示模块:
```lua
-- 加载 vl6180x_app.lua 演示模块
require "vl6180x_app"
```
如需切换通信模式,修改 `vl6180x_app.lua` 中的 `MODE` 变量(1=软件 I2C2=硬件 I2C)。
### 5.3 软件烧录
1. 使用 Luatools 选择最新内核固件
2. 下载本项目所有脚本文件
3. 将固件和脚本一起烧录到设备
4. 烧录成功后设备自动重启后开始运行
### 5.4 功能测试
#### 5.4.1 标准测距演示
1. 设备启动后打印 "HELLO",随后自动进入 [1/2] 标准测距
2. 观察日志输出,确认初始化成功(应看到 `芯片识别成功 Model ID=0xB4`
3. 观察距离数据(单位 mm)的连续读取,共 5 次
#### 5.4.2 持续测距演示
1. 自动进入 [2/2] 持续测距演示
2. 每隔 1 秒读取一次距离数据,共 5 次
3. 观察数据稳定性与状态码
### 5.5 预期效果
- **标准测距**:初始化成功,距离数据正常输出(0~255mm 范围)
- **持续测距**:每秒一帧数据,状态码 0(测距成功)
- **日志如下:**
```lua
[2026-08-20 21:30:06.039][000000000.246] I/user.main BME680_Demo 001.999.000
[2026-08-20 21:30:06.044][000000000.263] D/user.exs_bme680 version -> 202608080000
[2026-08-20 21:30:06.048][000000000.265] I/user.bme680_demo HELLO
[2026-08-20 21:30:06.172][000000001.265] I/user.bme680_demo ===== [1/5] =====
[2026-08-20 21:30:06.176][000000001.271] I/user.exs_bme680 I2C总线恢复完成
[2026-08-20 21:30:06.181][000000001.334] I/user.exs_bme680 BME680 @ I2C 0x77, CHIP_ID=0x61
[2026-08-20 21:30:06.194][000000001.347] I/user.exs_bme680 raw_T1=25923 raw_P1=36006 raw_T2=26603 raw_P2=-10347
[2026-08-20 21:30:06.215][000000001.347] I/user.exs_bme680 : T1=25923 T2=26603 T3=3 P1=36006 P2=-10347 H1=667 H2=1031 GH1=-34 GH2=-6023 GH3=18
[2026-08-20 21:30:06.233][000000001.355] I/user.exs_bme680 BME680 , os_temp=4 os_pres=3 os_hum=2 filter=3 heater_temp=320°C heater_dur=150ms
[2026-08-20 21:30:06.241][000000001.355] I/user.exs_bme680 : res_heat_range=1 res_heat_val=40 range_sw_err=1 heatr_res=0x76
[2026-08-20 21:30:06.250][000000001.355] I/user.exs_bme680 : MOX 20~30
[2026-08-20 21:30:06.258][000000001.355] I/user.exs_bme680 gas_resistance 0
[2026-08-20 21:30:06.270][000000001.355] I/user.bme680_demo BME680 : 202608080000
[2026-08-20 21:30:06.390][000000002.048] I/user.bme680_demo =24.12°C =1008.40hPa 湿=66.6%RH =2.4
[2026-08-20 21:30:09.534][000000005.241] I/user.bme680_demo 1: =24.12°C =1008.4hPa 湿=66.4%RH =3.4
[2026-08-20 21:30:11.735][000000007.434] I/user.bme680_demo 2: =24.14°C =1008.4hPa 湿=66.3%RH =4.6
[2026-08-20 21:30:13.922][000000009.627] I/user.bme680_demo 3: =24.16°C =1008.4hPa 湿=66.1%RH =5.9
[2026-08-20 21:30:13.929][000000009.628] I/user.bme680_demo ---- [1/5] 完成 ----
[2026-08-20 21:30:14.421][000000010.128] I/user.bme680_demo ===== [2/5] IIR =====
[2026-08-20 21:30:14.427][000000010.128] I/user.bme680_demo filter=0
[2026-08-20 21:30:14.437][000000010.136] I/user.exs_bme680 IIR 0 (0)
[2026-08-20 21:30:14.716][000000010.428] I/user.bme680_demo filter=0: =24.58°C =1008.42hPa 湿=66.1%RH
[2026-08-20 21:30:15.723][000000011.429] I/user.bme680_demo filter=1
[2026-08-20 21:30:15.729][000000011.436] I/user.exs_bme680 IIR 1 (1)
[2026-08-20 21:30:16.019][000000011.729] I/user.bme680_demo filter=1: =24.45°C =1008.44hPa 湿=65.8%RH
[2026-08-20 21:30:17.026][000000012.730] I/user.bme680_demo filter=2
[2026-08-20 21:30:17.033][000000012.737] I/user.exs_bme680 IIR 2 (3)
[2026-08-20 21:30:17.322][000000013.030] I/user.bme680_demo filter=2: =24.45°C =1008.40hPa 湿=65.5%RH
[2026-08-20 21:30:18.330][000000014.031] I/user.bme680_demo filter=3
[2026-08-20 21:30:18.336][000000014.038] I/user.exs_bme680 IIR 3 (7)
[2026-08-20 21:30:18.628][000000014.331] I/user.bme680_demo filter=3: =24.43°C =1008.40hPa 湿=65.3%RH
[2026-08-20 21:30:19.620][000000015.332] I/user.bme680_demo filter=4
[2026-08-20 21:30:19.634][000000015.339] I/user.exs_bme680 IIR 4 (15)
[2026-08-20 21:30:19.930][000000015.632] I/user.bme680_demo filter=4: =24.42°C =1008.36hPa 湿=65.0%RH
[2026-08-20 21:30:20.921][000000016.633] I/user.bme680_demo filter=5
[2026-08-20 21:30:20.937][000000016.640] I/user.exs_bme680 IIR 5 (31)
[2026-08-20 21:30:21.231][000000016.933] I/user.bme680_demo filter=5: =24.43°C =1008.40hPa 湿=64.9%RH
[2026-08-20 21:30:22.234][000000017.934] I/user.bme680_demo filter=6
[2026-08-20 21:30:22.240][000000017.941] I/user.exs_bme680 IIR 6 (63)
[2026-08-20 21:30:22.527][000000018.234] I/user.bme680_demo filter=6: =24.44°C =1008.42hPa 湿=64.7%RH
[2026-08-20 21:30:23.534][000000019.235] I/user.bme680_demo filter=7
[2026-08-20 21:30:23.540][000000019.242] I/user.exs_bme680 IIR 7 (127)
[2026-08-20 21:30:23.829][000000019.535] I/user.bme680_demo filter=7: =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:24.831][000000020.536] I/user.bme680_demo ---- [2/5] 完成 ----
[2026-08-20 21:30:25.326][000000021.036] I/user.bme680_demo ===== [3/5] =====
[2026-08-20 21:30:25.331][000000021.036] I/user.bme680_demo 1X过采样
[2026-08-20 21:30:25.342][000000021.043] I/user.exs_bme680 1
[2026-08-20 21:30:25.349][000000021.051] I/user.exs_bme680 1
[2026-08-20 21:30:25.357][000000021.058] I/user.exs_bme680 湿 1
[2026-08-20 21:30:25.621][000000021.326] I/user.bme680_demo : =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:26.621][000000022.327] I/user.bme680_demo 16X过采样
[2026-08-20 21:30:26.626][000000022.334] I/user.exs_bme680 5
[2026-08-20 21:30:26.636][000000022.342] I/user.exs_bme680 5
[2026-08-20 21:30:26.642][000000022.349] I/user.exs_bme680 湿 5
[2026-08-20 21:30:26.993][000000022.705] I/user.bme680_demo : =24.46°C =1008.40hPa 湿=64.7%RH
[2026-08-20 21:30:26.998][000000022.706] I/user.bme680_demo ---- [3/5] 完成 ----
[2026-08-20 21:30:27.504][000000023.206] I/user.bme680_demo ===== [4/5] =====
[2026-08-20 21:30:27.512][000000023.206] I/user.bme680_demo 250°C/100ms
[2026-08-20 21:30:27.518][000000023.214] I/user.exs_bme680 250°C, 100ms
[2026-08-20 21:30:27.919][000000023.620] I/user.bme680_demo =32.2 =24.46°C 湿=64.7%RH
[2026-08-20 21:30:28.909][000000024.621] I/user.bme680_demo 320°C/150ms
[2026-08-20 21:30:28.925][000000024.628] I/user.exs_bme680 320°C, 150ms
[2026-08-20 21:30:29.374][000000025.085] I/user.bme680_demo =16.6 =24.46°C 湿=64.6%RH
[2026-08-20 21:30:30.382][000000026.086] I/user.bme680_demo 350°C/200ms
[2026-08-20 21:30:30.389][000000026.093] I/user.exs_bme680 350°C, 200ms
[2026-08-20 21:30:30.894][000000026.600] I/user.bme680_demo =14.4 =24.46°C 湿=64.6%RH
[2026-08-20 21:30:31.888][000000027.601] I/user.bme680_demo ---- [4/5] 完成 ----
[2026-08-20 21:30:32.387][000000028.101] I/user.bme680_demo ===== [5/5] =====
[2026-08-20 21:30:32.700][000000028.409] I/user.bme680_demo =24.46°C =1008.4hPa 湿=64.6%RH =16.8 =40.5
[2026-08-20 21:30:32.706][000000028.409] I/user.exs_bme680
[2026-08-20 21:30:32.712][000000028.410] I/user.bme680_demo ---- [5/5] 完成 ----
[2026-08-20 21:30:33.210][000000028.910] I/user.bme680_demo ===== [] =====
[2026-08-20 21:30:33.215][000000028.910] I/user.bme680_demo End
```
### 5.6 故障排除
1. **传感器初始化失败**
- 检查 VL6180X 接线是否正确(SCL、SDA、VCC、GND
- 确认 GPIO 引脚配置与接线一致
- 检查电源电压是否稳定(3.3V
- 确认 exs_vl6180x 扩展库已正常加载
2. **读取数据始终为 0 或异常**
- 检查传感器供电是否正常
- 确认 I2C 地址是否正确(默认 0x29)
- 确认接线无松动
3. **测距状态码异常**
- 状态码 1/5(系统错误):芯片异常,可尝试重新上电
- 状态码 7(未检测到目标):目标超出 255mm 量程或反射率过低
- 状态码 11(环境光过强):环境光太强影响测量,可遮挡环境光
### 5.7 扩展功能建议
exs_vl6180x 更多接口的使用可以查看 [exs_vl6180x 扩展库说明](https://docs.openluat.com/osapi/ext/sensor/exs_vl6180x/)
@@ -0,0 +1,127 @@
--[[
@module vl6180x_app
@summary VL6180X 飞行时间测距传感器演示模块,包含测距演示用例
@version 1.0
@date 2026.08.20
@author 江访
@usage
本文件包含 VL6180X 的逐项功能演示。
通过 exs_vl6180x 扩展库的 API 逐一演示标准测距、持续测距等功能。
通过 MODE 变量选择通信模式(改 MODE 的值即可,无需注释/取消注释代码):
MODE = 1 软件 I2C(默认推荐,仅需 scl/sda 引脚,任意 GPIO 均可,接线最灵活)
MODE = 2 硬件 I2C(指定 i2c_id,占用 CPU 少)
]]
-- 加载 exs_vl6180x 扩展库
local exs_vl6180x = require "exs_vl6180x"
-- ==================== 通信模式选择 ====================
-- 改 MODE 的值选择通信模式:
-- 1 = 软件 I2C(默认) 2 = 硬件 I2C
-- 各模式所需引脚/参数见下方 init_config,接线说明见 readme.md
local MODE = 1
local init_config
if MODE == 1 then
-- 软件 I2CSCL/SDA 用任意 GPIOAir780EPM 接线示例用 GPIO31/GPIO30
init_config = {scl = 31, sda = 30}
elseif MODE == 2 then
-- 硬件 I2Ci2c_id 为总线号(Air780EPM 用 1),不传 scl/sda 时无总线恢复能力
init_config = {i2c_id = 1}
else
log.error("vl6180x_app", "MODE 取值错误,应为 1/2")
return
end
--------------------------------------------------------------
-- [1/2] 标准测距演示
-- - setup() 自动完成芯片 ID 校验、I2C 总线恢复、测距参数配置
-- - get_range() 返回距离(mm)与测量状态 status_str
--------------------------------------------------------------
local function demo_standard_ranging()
log.info("vl6180x_app", "===== [1/2] 标准测距 =====")
local result = exs_vl6180x.setup(init_config)
if not result then
log.error("vl6180x_app", "初始化失败")
log.error("vl6180x_app", "检查接线: VCC=3.3V GND SCL SDAI2C 地址是否正确(如 0x29)")
return false -- 初始化失败必须返回 false
end
log.info("vl6180x_app", "VL6180X 初始化成功,版本:", exs_vl6180x.version())
sys.wait(200) -- 等待系统稳定,200ms
for i = 1, 5 do
local data = exs_vl6180x.get_range()
if data then
log.info("vl6180x_app", string.format("第%d次: 距离=%dmm 状态=%s",
i, data.range_mm, data.status_str))
else
log.error("vl6180x_app", "读取测距数据失败")
end
sys.wait(1000) -- 每隔 1 秒读取一次
end
exs_vl6180x.close()
log.info("vl6180x_app", "---- [1/2] 完成 ----")
return true
end
--------------------------------------------------------------
-- [2/2] 持续测距演示
-- - 每隔 1 秒读取一次距离数据,观察数据稳定性与状态码
--------------------------------------------------------------
local function demo_continuous_ranging()
log.info("vl6180x_app", "===== [2/2] 持续测距演示 =====")
local result = exs_vl6180x.setup(init_config)
if not result then
log.error("vl6180x_app", "初始化失败")
log.error("vl6180x_app", "检查接线: VCC=3.3V GND SCL SDAI2C 地址是否正确(如 0x29)")
return false -- 初始化失败必须返回 false
end
log.info("vl6180x_app", "每隔 1 秒读取一次距离数据,共 5 次")
for i = 1, 5 do
sys.wait(1000) -- 每隔 1 秒读取一次
local data = exs_vl6180x.get_range()
if data then
log.info("vl6180x_app", string.format("持续测距[%d]: 距离=%dmm 状态=%s",
i, data.range_mm, data.status_str))
else
log.warn("vl6180x_app", string.format("持续测距[%d] 读取失败", i))
end
end
exs_vl6180x.close()
log.info("vl6180x_app", "---- [2/2] 完成 ----")
return true
end
--------------------------------------------------------------
-- 主任务:按 [1/2]~[2/2] 顺序执行全部演示
--------------------------------------------------------------
local function vl6180x_demo_task_func()
log.info("vl6180x_app", "HELLO")
sys.wait(1000) -- 等待系统稳定,1000ms
-- HELLO 开始 → [1/2] → [2/2] → End
local ok = demo_standard_ranging()
if not ok then
log.error("vl6180x_app", "初始化失败,演示终止")
return
end
sys.wait(500) -- 步骤间隔,500ms
ok = demo_continuous_ranging()
if not ok then
log.error("vl6180x_app", "初始化失败,演示终止")
return
end
sys.wait(500) -- 步骤间隔,500ms
log.info("vl6180x_app", "===== [演示完毕] =====")
log.info("vl6180x_app", "End")
end
sys.taskInit(vl6180x_demo_task_func)