Files
mobile_comm/luatos/air780epm/module/Air780EPM/demo/ioqueue/dht11_capture.lua
T
wangfq 173dd6874f init: Air780EPM 官方 LuatOS 项目代码基线
- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
2026-08-31 08:53:49 +08:00

170 lines
6.5 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--[[
@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)