跳转至

定时器(timer)

一、简介

定时器(timer)是一项基础且关键的服务。它允许开发者在特定的时间点或周期性地执行代码段,为物联网设备的运行提供了精确的时间控制。定时器在多种应用场景中都发挥着重要作用,如定时发送数据、周期性检查传感器状态等。

二、演示功能概述

本教程教你如何用 Air724 开发板,演示定时器功能,并且通过日志观察实验结果。

三、准备硬件环境

3.1 开发板准备

使用 EVB_Air724 开发板,如下图所示:

淘宝购买链接:Air724UG-NFM 开发板淘宝购买链接

此开发板的详细使用说明参考:Air724UG 产品手册 中的《EVB_Air724UG_AXX 开发板使用说明》,写这篇文章时最新版本的使用说明为:《EVB_Air724UG_A14 开发板使用说明》;开发板使用过程中遇到任何问题,可以直接参考这份使用说明文档。

api:https://doc.openluat.com/wiki/21?wiki_page_id=2068

3.2 数据通信线

USB 数据线一根(micro USB)。

3.3 PC 电脑

WIN7 以及以上版本的 WINDOWS 系统。

3.4 SIM 卡

中国大陆环境下,可以上网的 SIM 卡。一般来说,使用移动,电信,联通的物联网卡或者手机卡都行。

3.5 组装硬件环境

USB 数据线插入 USB 口,另一端与电脑相连,拨码开关全部拨到 ON,串口切换开关选择 UART1,USB 供电的 4V 对应开关拨至 ON 档,SIM 卡放到 SIM 卡槽中锁紧,如下图所示。

四、准备软件环境

4.1 下载调试工具

使用说明参考:Luatools 下载和详细使用

4.2 源码及固件

1.底层 core 下载

下载底层固件,并解压

链接:https://docs.openluat.com/air724ug/luatos/firmware/

如下图所示,红框的是我们要使用到的。

2.本教程使用的 demo:

右键点我,另存为,下载完整压缩文件包

4.3 下载固件和脚本到开发板中

打开 Luatools,开发板上电开机,如开机成功 Luatools 会打印如下信息。

点击项目管理测试选项。

进入管理界面,如下图所示。

  • 点击选择文件,选择底层固件,我的文件放在 D:\luatOS\Air724 路径中。

  • 点击增加脚本或资源文件,选择 之前下载的程序源码,如下图所示。

  • 点击下载底层和脚本,下载完成如下图所示。

五、代码示例介绍

5.1 API 说明

5.1.1 sys.timerStop(val, …)

关闭 sys.timerStart 和 sys.timerLoopStart 创建的定时器

有两种方式可以唯一标识一个定时器:

1、定时器 ID

2、定时器回调函数和可变参数

  • 参数
名称
传入值类型
释义
val
param

有两种形式:1、为 number 类型时,表示定时器 ID2、为 function 类型时,表示定时器回调函数

param
可变参数,当 val 为定时器回调函数时,此可变参数才有意义,表示定时器回调函数的可变回调参数
  • 返回值

nil

  • 例子
_-- 通过定时器ID关闭一个定时器:_local timerId = sys.timerStart(publicTimerCbFnc,8000,"second")
sys.timerStop(timerId)
_-- 通过定时器回调函数和可变参数关闭一个定时器:_
sys.timerStart(publicTimerCbFnc,8000,"first")
sys.timerStop(publicTimerCbFnc,"first")

5.1.2 sys.timerStopAll(fnc)

关闭 sys.timerStart 和 sys.timerLoopStart 创建的某个回调函数的所有定时器

  • 参数
名称
传入值类型
释义
fnc
function
定时器回调函数
  • 返回值

nil

  • 例子
_-- 关闭回调函数为publicTimerCbFnc的所有定时器_local function publicTimerCbFnc(tag)log.info("publicTimerCbFnc",tag)
end
sys.timerStart(publicTimerCbFnc,8000,"first")
sys.timerStart(publicTimerCbFnc,8000,"second")
sys.timerStart(publicTimerCbFnc,8000,"third")
sys.timerStopAll(publicTimerCbFnc)

5.1.3 sys.timerStart(fnc, ms, …)

创建并且启动一个单次定时器

有两种方式可以唯一标识一个定时器:

1、定时器 ID

2、定时器回调函数和可变参数

  • 参数
名称
传入值类型
释义
fnc
param
定时器回调函数,必须存在,不允许为 nil 当定时器超时时间到达时,回调函数的调用形式为 fnc(…),其中…为回调参数
ms
number
定时器超时时间,单位毫秒,最小 1,最大 0x7FFFFFFF 实际上支持的最小超时时间是 5 毫秒,小于 5 毫秒的时间都会被转化为 5 毫秒

param
可变参数,回调函数 fnc 的回调参数
  • 返回值

number timerId,创建成功返回定时器 ID;创建失败返回 nil

  • 例子
_-- 创建一个5秒的单次定时器,回调函数打印"timerCb",没有可变参数:_
sys.timerStart(function() log.info("timerCb") end, 5000)
_-- 创建一个5秒的单次定时器,回调函数打印"timerCb"和"test",可变参数为"test":_
sys.timerStart(function(tag) log.info("timerCb",tag) end, 5000, "test")

5.1.4 sys.timerLoopStart(fnc, ms, …)

创建并且启动一个循环定时器

有两种方式可以唯一标识一个定时器:

1、定时器 ID

2、定时器回调函数和可变参数

  • 参数
名称
传入值类型
释义
fnc
param
定时器回调函数,必须存在,不允许为 nil 当定时器超时时间到达时,回调函数的调用形式为 fnc(…),其中…为回调参数
ms
number
定时器超时时间,单位毫秒,最小 1,最大 0x7FFFFFFF 实际上支持的最小超时时间是 5 毫秒,小于 5 毫秒的时间都会被转化为 5 毫秒

param
可变参数,回调函数 fnc 的回调参数
  • 返回值

number timerId,创建成功返回定时器 ID;创建失败返回 nil

  • 例子
_-- 创建一个5秒的循环定时器,回调函数打印"timerCb",没有可变参数:_
sys.timerLoopStart(function() log.info("timerCb") end, 5000)
_-- 创建一个5秒的循环定时器,回调函数打印"timerCb"和"test",可变参数为"test":_
sys.timerLoopStart(function(tag) log.info("timerCb",tag) end, 5000, "test")

5.1.5 sys.timerIsActive(val, …)

判断“通过 timerStart 或者 timerLoopStart 创建的定时器”是否处于激活状态

  • 参数
名称
传入值类型
释义
val
param
定时器标识,有两种表示形式 1、number 类型,通过 timerStart 或者 timerLoopStart 创建定时器时返回的定时器 ID,此情况下,不需要传入回调参数…就能唯一标识一个定时器 2、function 类型,通过 timerStart 或者 timerLoopStart 创建定时器时的回调函数,此情况下,如果存在回调参数,需要传入回调参数…才能唯一标识一个定时器

param
回调参数,和“通过 timerStart 或者 timerLoopStart 创建定时器”的回调参数保持一致
  • 返回值

status,定时器激活状态;根据 val 的表示形式,有不同的返回值:

1、val 为 number 类型时:如果处于激活状态,则返回 function 类型的定时器回调函数;否则返回 nil

2、val 为 function 类型时:如果处于激活状态,则返回 bool 类型的 true;否则返回 nil

  • 例子
_-- 定时器ID形式标识定时器的使用参考:_local timerId1 = sys.timerStart(function() end,5000)
sys.taskInit(function()
sys.wait(3000)
log.info("after 3 senonds, timerId1 isActive?",sys.timerIsActive(timerId1))
sys.wait(3000)
log.info("after 6 senonds, timerId1 isActive?",sys.timerIsActive(timerId1))
end)
_-- 回调函数和回调参数标识定时器的使用参考:_local function timerCbFnc2(tag)log.info("timerCbFnc2",tag)
end
sys.timerStart(timerCbFnc2,5000,"test")
sys.taskInit(function()
sys.wait(3000)
log.info("after 3 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
sys.wait(3000)
log.info("after 6 senonds, timerCbFnc2 test isActive?",sys.timerIsActive(timerCbFnc2,"test"))
end)

5.1.6 sys.waitUntil(id, ms)

task 任务条件等待函数(支持事件消息和定时器消息)

只能直接或者间接的被 task 任务主函数调用,调用本接口的 task 会挂起

  • 参数
名称
传入值类型
释义
id
string
消息 ID,建议使用 string 类型
ms
number
可选参数,默认为 nil 延时时间,单位毫秒,最小 1,最大 0x7FFFFFFF 实际上支持的最小超时时间是 5 毫秒,小于 5 毫秒的时间都会被转化为 5 毫秒
  • 返回值

result,data,分为如下三种情况:

1、如果存在超时时间参数:

(1)、在超时时间到达之前,如果收到了等待的消息 ID,则 result 为 true,data 为消息 ID 携带的参数(可能是多个参数)

(2)、在超时时间到达之前,如果没收到等待的消息 ID,则 result 为 false,data 为 nil

2、如果不存在超时时间参数:如果收到了等待的消息 ID,则 result 为 true,data 为消息 ID 携带的参数(可能是多个参数)

(1)、如果收到了等待的消息 ID,则 result 为 true,data 为消息 ID 携带的参数(可能是多个参数)

(2)、如果没收到等待的消息 ID,则 task 一直挂起

3、还存在一种特殊情况,本 task 挂起时,可能被 task 的外部应用逻辑给主动激活(如果不是故意为之,可能是写 bug 了)

  • 例子
task延时120秒或者收到"SIM_IND"消息:
sys.taskInit(function()local result, data = sys.waitUntil("SIM_IND",120000)
end)

5.1.7 sys.waitUntilExt(id, ms)

Task 任务的条件等待函数扩展(包括事件消息和定时器消息等条件),只能用于任务函数中。

  • 参数
名称
传入值类型
释义
id
param
消息 ID
ms
number
等待超时时间,单位 ms,最大等待 126322567 毫秒
  • 返回值

message 接收到消息返回 message,超时返回 false

data 接收到消息返回消息参数

  • 例子
result, data = sys.waitUntilExt("SIM_IND", 120000)

5.2 timer.lua 代码

系统启动后创建了一个 sys.wait(3000),延时 3s 后再进行当前协程。避免开机后程序跑太快,我们看不清日志输出。开始后,创建了一个单次定时器且只有收到消息id才会执行 sys.waitUntil(“MSG_ID”),这个定时器又会将当前协程停下来,它需要等待一个指令才肯放行("MSG_ID"这个消息 ID)。还记得上面循环定时器里那个 sys.publish(“MSG_ID”)嘛,每隔 5s 就会发送 sys.waitUntil(“MSG_ID”)需要的消息 ID。

module(...,package.seeall)

--循环定时器打印函数
local function log1()
    log.info("log","每5s打印一次,循环定时器的作用")
    sys.publish("MSG_ID")
end
--循环定时器,5s执行一次log1函数
sys.timerLoopStart(log1,5000)

--第二个task的调用函数,给第一个task判断定时器是否激活用的。
local a
local function timer_2()
    a=sys.timerStart(function ()
        log.info("log", "我的用途就是给下面的sys.timerIsActive判断死活用的")
    end,20000)
end

--第一个task的执行函数
local function timer_1()
    --sys.wait(ms),单次定时器且只能用在task中,或者被task的主函数直接或者间接调用。
    sys.wait(4000)
    log.info("log", "开始!")

    --sys.waitUntil(),单次定时器且只有收到消息id才会执行。有超时参数时:如果时间到达没有等到消息ID也会会执行。没有超时参数时:死等
    sys.waitUntil("MSG_ID")
    log.info("log", "接收到\"MSG_ID\"消息")

    --判断timer_2里定时器的激活状态,如果激活则执行。
    if sys.timerIsActive(a) then
        sys.timerStart(function ()
            log.info("log", "循环定时器活着,我是有timerStart定时器执行的")
        end,1000)
    end
    --等待10s后开始执行rtos.sleep(6000),让lua虚拟机挂起6s。
    sys.wait(10000)
    log.info("log", "先睡6s")
    --rtos.sleep(6000)

end
sys.taskInit(timer_2)
sys.taskInit(timer_1)

5.3 main.lua 代码

本代码为主程序脚本,系统启动后首先会对 4G 网络进行配置,等待网络连接成功,然后加载测试模块。

六、开机调试

6.1 开发板开机

连接好硬件并下载固件后,启动 Luatools 软件,系统运行信息将显示在界面中。红框中为开发板连接到 PC 机后正常打印的信息,如下图所示。

6.2 功能调试

定时器启动后创建了一个单次定时器 sys.waitUntil(“MSG_ID”),这个定时器又会将当前协程停下来,它需要等待一个指令才肯放行("MSG_ID"这个消息 ID)。

七、常见问题

7.1 使用定时器 sys.wait()或者 sys.waitUntil(),程序出错。

sys.wait()和 sys.waitUntil()只能用于 task 中。

7.2 定时器精度问题

LuatOS 开发最小仅支持 5 毫秒的定时器,超时时间最小 1ms(实际支持的最小时间是 5ms,小于 5ms 的时间都被转化为 5ms) 超时间最大 0x7FFFFFFF(24.85 天)。 另外毫秒级的定时器的误差较大,原因可参考 LuatOS 运行框架

给读者的话

本篇文章由杨超开发;

本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;

请登录合宙技术交流论坛,点击文档找错赢奖金-Air724UG-LuatOS-软件指南-常用功能实现-定时器(timer)

用截图标注+文字描述的方式跟帖回复,记录清楚您发现的问题;

我们会迅速核实并且修改文档;

同时也会为您累计找错积分,您还可能赢取月度找错奖金!