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,169 @@
--[[
@module test_ioqueue
@summary IO队列功能测试
@version 1.0
@date 2025.10.18
@author 孟伟
@usage
本功能模块演示的内容为:
DHT11 温湿度传感器数据读取
本文件没有对外接口,直接在main.lua中require "dht11_capture"就可以加载运行;
]]
-- 定义硬件定时器ID和捕获引脚号,这里使用硬件定时器0,捕获引脚25
local hw_timer_id, capture_pin = 0, 25
-- 测试单总线DHT11
function dht11_capture()
local _, tick_us = mcu.tick64()
--确保为GPIO功能
gpio.setup(capture_pin, nil, nil)
local buff1 = zbuff.create(100)
local buff2 = zbuff.create(100)
local cnt1, cnt2, i, lastTick, bit1Tick, nowTick, j, bit
bit1Tick = 100 * tick_us
-- 第一步:确保硬件定时器空闲
ioqueue.stop(hw_timer_id)
-- 第二步:初始化io队列
ioqueue.init(hw_timer_id, 100, 1)
-- 第三步:初始状态设置
ioqueue.setgpio(hw_timer_id, capture_pin, true, gpio.PULLUP)
-- 参数详解:
-- 10000: 延时10ms10000微秒)
-- 0: 时间微调值
-- false: 单次延时(非连续模式)
-- 作用:初始空闲状态时长为10ms,给传感器足够的准备时间
ioqueue.setdelay(hw_timer_id, 10000, 0, false)
-- 第四步:配置DHT11传感器的启动信号,主机主动拉低总线开始通信
ioqueue.setgpio(hw_timer_id, capture_pin, false, 0, 0)
-- 18000: 延时18ms,这是DHT11协议要求的启动信号最小时间
ioqueue.setdelay(hw_timer_id, 18000, 0, false)
-- 第五步:配置捕获参数,为接收传感器数据做准备,此命令仅配置参数,实际捕获需配合后续的capture()命令执行
-- 参数详解:
-- gpio.PULLUP: 设置引脚为上拉输入模式(释放总线控制权)
-- gpio.FALLING: 只捕获下降沿,捕获到后会记录io编号,电平高低,以及时间
-- 100000 * tick_us: 单个capture()命令的最大等待时间100ms,超时后继续执行后续命令,每个capture()都是独立的100ms等待窗口
ioqueue.set_cap(hw_timer_id, capture_pin, gpio.PULLUP, gpio.FALLING, 100000 * tick_us)
--[[关于一个捕获周期含义:
set_cap不等于开始捕获,只是配置参数捕获参数
其中set_cap配置的最大等待时间是防止因传感器故障导致程序永久卡住
开始:当执行 ioqueue.capture() 命令时开始一个捕获周期
结束:满足以下任一条件时结束:
检测到下降沿 → 立即记录时间戳并结束本次捕获周期,然后执行下一条命令
达到100ms超时 → 直接结束,不记录数据,然后执行下一条命令
调用cap_done() → 强制结束命令队列
以上面代码为例:
在100ms内,一次下降沿也没有检测到,则直接结束,执行队列中的下一条命令
在100ms内,检测到一次下降沿,立即记录时间戳并结束,然后执行队列中的下一条命令;
]]
-- 第六步:预分配捕获缓冲区 ,对io操作队列增加42次捕获IO状态命令
for i = 1, 42, 1 do
ioqueue.capture(hw_timer_id)
end
-- 停止捕获,不再监听该引脚的边沿变化
ioqueue.cap_done(hw_timer_id, capture_pin)
-- 恢复数据线为上拉输入状态,释放总线
ioqueue.setgpio(hw_timer_id, capture_pin, true, gpio.PULLUP)
-- 第七步:执行整个命令序列
-- 开始按顺序执行前面设置的所有命令
ioqueue.start(hw_timer_id)
-- 等待执行完成,系统会在完成时发布这个事件
-- 这是异步操作,不会阻塞其他任务
sys.waitUntil("IO_QUEUE_DONE_" .. hw_timer_id)
-- 停止硬件定时器
ioqueue.stop(hw_timer_id)
-- 第八步:读取捕获的数据
cnt1, cnt2 = ioqueue.get(hw_timer_id, buff1, buff2)
-- 参数详解:
-- buff1: 存储输入数据的缓冲区
-- buff2: 存储捕获数据的缓冲区
-- cnt1: 读取io数据的数量,此返回值对应ioqueue.input()接口所配置的对读取gpio命令数量,此代码中是nil
-- cnt2: 捕获数据的数量(应该是42)
if cnt2 ~= 42 then
log.info('test fail')
goto TEST_OUT
end
-- 如果捕获数据不是42个,说明通信失败
-- 第九步:解析数据
-- 从捕获缓冲区读取第二个下降沿的时间戳
-- 数据结构:每个捕获点占6字节
-- 字节0: GPIO ID编号
-- 字节1: 电平状态(0=下降沿,1=上升沿)
-- 字节2-5: 32位时间戳(4字节)
-- 所以第二个捕获点在偏移量6处,时间戳在6+2处
lastTick = buff2:query(6 + 2, 4, false)
j = 0
bit = 8
buff1[0] = 0
for i = 2, 41, 1 do -- 遍历40个数据位(跳过第1个下降沿的DHT11响应信号)
-- 验证数据完整性
if buff2[i * 6 + 0] ~= capture_pin or buff2[i * 6 + 1] ~= 0 then
log.error("capture", i, buff2[i * 6 + 0], buff2[i * 6 + 1])
end
-- 计算时间间隔
-- 当前位的时间戳
nowTick = buff2:query(i * 6 + 2, 4, false)
-- 左移1位,为新的数据位腾出空间
buff1[j] = buff1[j] << 1
-- DHT11数据编码原理:
-- 每个数据位都以50us低电平开始
-- 然后高电平持续时间不同:
-- 26-28us → 数据0
-- 70us → 数据1
-- bit1Tick 是100us阈值,总时间 > 100us → 判断为位1,≤ 100us → 判断为位0
if (nowTick - lastTick) > bit1Tick then
buff1[j] = buff1[j] + 1 -- 设置最低位为1
end
bit = bit - 1
if bit == 0 then -- 完成1字节(8位)
j = j + 1 -- 移动到下一字节
bit = 8 -- 重置位计数器
end
lastTick = nowTick -- 更新参考时间戳
end
-- 第十步:数据校验
buff1[5] = buff1[0] + buff1[1] + buff1[2] + buff1[3]
-- DHT11协议:第5字节是前4字节的校验和
if buff1[4] ~= buff1[5] then
log.info('check fail', buff1[4], buff1[5])
else
log.info("湿度", buff1[0] .. '.' .. buff1[1], "温度", buff1[2] .. '.' .. buff1[3])
-- buff1[0]: 湿度整数部分
-- buff1[1]: 湿度小数部分
-- buff1[2]: 温度整数部分
-- buff1[3]: 温度小数部分
-- buff1[4]: 校验和
end
::TEST_OUT::
-- 释放硬件定时器资源,可以重新分配使用
ioqueue.release(hw_timer_id)
end
sys.taskInit(dht11_capture)
@@ -0,0 +1,61 @@
--[[
@module test_ioqueue
@summary IO队列功能测试
@version 1.0
@date 2025.10.18
@author 孟伟
@usage
本功能模块演示的内容为:
高精度固定间隔脉冲输出功能
输出脉冲信息:
输出固定间隔对称方波
- 低电平持续时间:20微秒(固定)
- 高电平持续时间:20微秒(固定)
- 脉冲周期:40微秒(完整周期)
- 占空比50%
- 脉冲数量:41个完整周期(通过循环40次生成)
- 使用ioqueue.setdelay的连续模式,所有延时间隔自动保持20us
本文件没有对外接口,直接在main.lua中require "fix_pulse_output"就可以加载运行;
]]
-- 选好硬件定时器和输出引脚,这里使用硬件定时器0,输出引脚2
local hw_timer_id, out_pin = 0, 2
function fix_pulse_output_fun()
local _, tick_us = mcu.tick64()
--确保为GPIO功能
gpio.setup(out_pin, nil, nil)
-- 第一步:初始化
-- 100个命令,循环10次(生成100个脉冲)
ioqueue.init(hw_timer_id, 100, 10)
-- 设置GPIO为输出模式,初始输出高电平
ioqueue.setgpio(hw_timer_id, out_pin, false, 0, 1)
-- 第二步:配置连续延时模式
ioqueue.setdelay(hw_timer_id, 20, tick_us - 3, true)
-- 第三步:生成脉冲序列
-- 每个循环生成1个完整周期:低电平20us + 高电平20us = 40us周期
for i = 0, 40, 1 do
ioqueue.output(hw_timer_id, out_pin, 0) -- 输出低电平
ioqueue.delay(hw_timer_id) -- 延时20us
ioqueue.output(hw_timer_id, out_pin, 1) -- 输出高电平
ioqueue.delay(hw_timer_id) -- 延时20us
end
-- 第四步:执行
ioqueue.start(hw_timer_id)
-- 等待执行完成,系统会在完成时发布这个事件
sys.waitUntil("IO_QUEUE_DONE_" .. hw_timer_id)
-- 停止硬件定时器
ioqueue.stop(hw_timer_id)
-- 释放硬件定时器资源,可以重新分配使用
ioqueue.release(hw_timer_id)
end
sys.taskInit(fix_pulse_output_fun)
@@ -0,0 +1,68 @@
--[[
@module main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date 2025.10.17
@author 孟伟
@usage
本demo演示的功能为:
IO队列功能测试,包括:
DHT11温湿度传感器数据读取
高精度固定间隔脉冲输出
高精度可变间隔脉冲输出
]]
--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为999
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "ioqueue_demo"
VERSION = "001.999.000"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 如果内核固件支持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)
-- 加载dht11功能模块
require "dht11_capture"
-- 加载高精度可变间隔脉冲输出功能模块
require "var_pulse_output"
-- 加载高精度固定间隔脉冲输出功能模块
require "fix_pulse_output"
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
@@ -0,0 +1,109 @@
## 演示模块概述
1、main.lua:主程序入口;
2、fix_pulse_output.lua:高精度固定间隔脉冲输出功能模块;
3、var_pulse_output.lua:高精度可变间隔脉冲输出功能模块;
4、dht11_capture.luaDHT11温湿度传感器数据读取功能模块;
## 演示功能概述
使用Air780EPM核心板测试ioqueue功能。
IO队列功能测试,包括:
DHT11温湿度传感器数据读取
高精度固定间隔脉冲输出
输出脉冲信息:
输出固定间隔对称方波
- 低电平持续时间:20微秒(固定)
- 高电平持续时间:20微秒(固定)
- 脉冲周期:40微秒(完整周期)
- 占空比50%
- 脉冲数量:41个完整周期(通过循环40次生成)
- 使用ioqueue.setdelay的连续模式,所有延时间隔自动保持20us
高精度可变间隔脉冲输出
输出脉冲信息:
输出可变间隔非对称脉冲
- 10次完整序列
- 输出波形:低电平20us → 高电平30us → 低电平40us → 高电平50us→ 低电平60us → 高电平70us
- 使用ioqueue.setdelay 单次模式,每个延时独立配置
## 演示硬件环境
![](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
![](https://docs.openLuat.com/cdn/image/780epm_ioqueue2.PNG)
1、Air780EPM核心板一块
2、TYPE-C USB数据线一根
- Air780EPM V1.3 版本开发板通过 TYPE-C USB 口供电;(外部供电/USB 供电 拨动开关 拨到 USB 供电一端)
- TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
3、DHT11温湿度传感器一个
接线说明:780epm的GPIO25引脚连接dht11的DATA引脚,VDD_EXT引脚连接dht11的VCC引脚,GND引脚连接dht11的GND引脚。
dht11传感器硬件连接如下:
<table>
<tr>
<td>DHT11<br/></td><td>Air780EPM<br/></td></tr>
<tr>
<td>VCC<br/></td><td>VDD_EXT<br/></td></tr>
<tr>
<td>DATA<br/></td><td>GPIO25<br/></td></tr>
<tr>
<td>GND<br/></td><td>GND<br/></td></tr>
</table>
## 演示软件环境
1、Luatools下载调试工具
2、[Air780EPM V2016版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
## 演示核心步骤
1、搭建好硬件环境
2、Luatools烧录内核固件和demo脚本代码
3、烧录成功后,自动开机运行
4、可以接逻辑分析仪看对应io的波形,下面具体分析下:
1)固定间隔脉冲输出:
![](https://docs.openluat.com/osapi/core/image/ASpubdhLGoBFBJxMaMmcXPbOnPh.png)
2)可变间隔脉冲输出:
![](https://docs.openluat.com/osapi/core/image/FvGjbOkJcozClex7xlzcEs9On6b.png)
3)DHT11温湿度传感器数据读取:
起始信号:
![](https://docs.openluat.com/osapi/core/image/WYT0b7wuLowfJzxxtEXco9l8nxF.png)
dht11响应信号:
![](https://docs.openluat.com/osapi/core/image/ZxnLb73XNo8I3nxaAt4cq5p8nre.png)
dht11数据信号及分析:
![](https://docs.openluat.com/osapi/core/image/BSNUb4lL5oM7tDxAEZHc9hLbnLe.png)
结束信号:
![](https://docs.openluat.com/osapi/core/image/LDCfblEFdot9Ywx7AFoc9jkEnBb.png)
@@ -0,0 +1,78 @@
--[[
@module test_ioqueue
@summary IO队列功能测试
@version 1.0
@date 2025.10.18
@author 孟伟
@usage
本功能模块演示的内容为:
高精度可变间隔脉冲输出
输出脉冲信息:
输出可变间隔非对称脉冲
- 10次完整序列
- 输出波形:低电平20us → 高电平30us → 低电平40us → 高电平50us→ 低电平60us → 高电平70us
- 使用ioqueue.setdelay 单次模式,每个延时独立配置
本文件没有对外接口,直接在main.lua中require "fix_pulse_output"就可以加载运行;
]]
--[[
@module test_ioqueue
@summary IO队列功能测试
@version 1.0
@date 2025.10.18
@author 孟伟
@usage
本功能模块演示的内容为:
高精度可变间隔脉冲输出功能
本文件没有对外接口,直接在main.lua中require "var_pulse_output"就可以加载运行;
]]
-- 选好硬件定时器和输出引脚,这里使用硬件定时器0,输出引脚2
local hw_timer_id, out_pin = 0, 2
function var_pulse_output_fun()
local _, tick_us = mcu.tick64()
--确保为GPIO功能
gpio.setup(out_pin, nil, nil)
log.info('output 2 start')
--测试高精度可变间隔定时输出
ioqueue.init(hw_timer_id, 100, 10)
--设置成输出口,电平1
ioqueue.setgpio(hw_timer_id, out_pin, false, 0, 1)
--单次延迟20us,如果不准,对time_tick微调
ioqueue.setdelay(hw_timer_id, 20, tick_us - 3)
--低电平
ioqueue.output(hw_timer_id, out_pin, 0)
--单次延迟30us
ioqueue.setdelay(hw_timer_id, 30, tick_us - 3)
--高电平
ioqueue.output(hw_timer_id, out_pin, 1)
--单次延迟40us
ioqueue.setdelay(hw_timer_id, 40, tick_us - 3)
--低电平
ioqueue.output(hw_timer_id, out_pin, 0)
--单次延迟50us
ioqueue.setdelay(hw_timer_id, 50, tick_us - 3)
--高电平
ioqueue.output(hw_timer_id, out_pin, 1)
--单次延迟60us
ioqueue.setdelay(hw_timer_id, 60, tick_us - 3)
--低电平
ioqueue.output(hw_timer_id, out_pin, 0)
--单次延迟70us
ioqueue.setdelay(hw_timer_id, 70, tick_us - 3)
--高电平
ioqueue.output(hw_timer_id, out_pin, 1)
ioqueue.start(hw_timer_id)
sys.waitUntil("IO_QUEUE_DONE_" .. hw_timer_id)
log.info('output 2 done')
ioqueue.stop(hw_timer_id)
ioqueue.release(hw_timer_id)
sys.wait(500)
end
sys.taskInit(var_pulse_output_fun)