- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码 - 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等) - 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
170 lines
6.5 KiB
Lua
170 lines
6.5 KiB
Lua
--[[
|
||
@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: 延时10ms(10000微秒)
|
||
-- 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)
|