LuaTask教程之运行原理
这一章讲解一下程序的运行原理,可能有的开发者一直在main.lua里面找main函数,一直没找到,然后就懵逼了。Luat采用的Lua语言,Lua 是一种轻量小巧的脚本语言。
先来看一下main.lua
--必须在这个位置定义PROJECT和VERSION变量
--PROJECT:ascii string类型,可以随便定义,只要不使用,就行
--VERSION:ascii string类型,如果使用Luat物联云平台固件升级的功能,必须按照"X.X.X"定义,X表示1位数字;否则可随便定义
PROJECT = "DEMO_TASK"
VERSION = "2.0.0"
-- 日志级别
require "log"
LOG_LEVEL = log.LOGLEVEL_TRACE
require "sys"
require "utils"
-- 加载GSM
require "net"
--8秒后查询第一次csq
net.startQueryAll(8 * 1000, 600 * 1000)
-- 控制台
require "console"
console.setup(1, 115200)
-- 系统工具
require "misc"
-- 看门狗
require "wdt"
wdt.setup(pio.P0_31, pio.P0_29)
-- 系統指示灯
require "led"
led.setup(pio.P0_28)
-- 测试任务
require "test"
-- 启动系统框架
sys.init(0, 0)
sys.run()
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。也就是说在main.lua里面加载了多个lua文件(简单粗暴的理解为require的文件内容复制到了main.lua),脚本语言顺序执行,所以执行顺序为:
-
加载log.lua,sys.lua,utils.lua文件
-
加载net.lua文件,执行net.startQueryAll函数设置第一次查询GSM信号时间(这个函数在net.lua里实现的,所以在使用前必须require"net")
-
加载console.lua文件,执行console.setup函数设置控制台波特率
-
加载misc.lua,wdt.lua,执行wdt.setup设置看门狗管脚
-
加载led.lua,执行led.setup设置led灯管脚
-
最后加载test.lua,也就是我们自己写的测试程序,执行test.lua里面的内容,输出信息
-
执行sys.init函数,sys.init是什么呢?引用wiki上对sys.init的描述
系统初始化- sys.init(mode, lprfnc)
- mode:整数型。充电开机是否启动 GSM 协议栈,1 不启动,否则启动 。mode=1.
- lprfnc:用户应用脚本中定义的“低电关机处理函数”,如果有函数名,则低电时,本文件中的run接口不会执行任何动作,否则,会延时1分钟自动关机
-
执行sys.run函数,同样引用wiki上的描述
主架构程序。一般情况下,这个程序是必须要有,而且必须要写在 main.lua 里。没有过多的描述,看一下这个函数的函数体
------------------------------------------ Luat 主调度框架 ------------------------------------------ --- run()从底层获取core消息并及时处理相关消息,查询定时器并调度各注册成功的任务线程运行和挂起 -- @return 无 -- @usage sys.run() function run() while true do -- 分发内部消息 dispatch() -- 阻塞读取外部消息 local msg, param = rtos.receive(rtos.INF_TIMEOUT) -- 判断是否为定时器消息,并且消息是否注册 if msg == rtos.MSG_TIMER and timerPool[param] then if param `msg,msgpara = rtos.receive(timeout)` > > 如果 msg 为 table 类型,msg 根据不同的消息 msg.id 会有不同的数据: > > 如果 msg 为 number 类型,msg 根据不同的消息 msg 会有不同的数据 > > 1.rtos.MSG_TIMER 定时器超时消息 msg.timer_id 或者 msgpara 为超时的定时器 id > > 2.rtos.MSG_UART_RXDATA 串口 ATC 数据提醒 msg.uart_id 或者msgpara为收到的数据的串口id或者atc,收到该消息后可以通过uart.read 接口读取数据 > > 3.rtos.MSG_KEYPAD 键盘消息,必须初始化按键(#rtos.init_module#)后才会有键盘消息 > > msg.pressed 按键按下/弹起 msg.key_matrix_row 按键所在行值 msg.key_matrix_col 按键所在列值 4.rtos.WAIT_MSG_TIMEOUT 等待消息超时 > > 5.rtos.MSG_INT 中断消息 msg.int_id 中断 id msg.int_resnum 中断 pin 脚编号 > > 6.rtos.MSG_PMD 电源管理消息 msg.present 电池在位状态 msg.level 百分比 0-100 msg.voltage 电池电压 msg.charger 充电器在位状态 msg.state 充电状态:0-不在充电 1-充电中 2-充电停止
也就是说,通过不断的接收消息来判断发生了什么事件,应该做什么事情。
最后在分析一下test.lua。重构版采取的协程的方式。什么是协程?举个例子,有一盘菜,你和你女友两人吃。按照老的方法,顺序是这样的,女友先吃,吃饱了,然后自己吃。这样就不科学了,说好的男女平等了(emmmm....)。现在换一种方式:女友吃两分钟,自己吃两分钟,女友吃两分钟,自己吃两分钟...loop.....。这样是不是科学很多了。第一种方式就是我们常用的方式顺序执行,在单片机里面经常用while(1)来死循环控制流程。这样有个缺陷是,太死板,不能根据调整程序的执行时间,就比如,女友在吃菜的时候,自己已经饿得不行了,但是还是得等女友吃完(没人性啊!)。第二种方式就相对方便很多,可以控制程序运行的时间。比如:男生饭量大,需要吃三分钟,女生饭量小只吃一分钟,这样就可以合理分配时间。
回到test.lua里面的内容
module(..., package.seeall) --必须,目的是让其他文件能包含该文件
sys.taskInit(function() --任务1
while true do
print("Hello World")
sys.wait(1000)
end
end)
sys.taskInit(function() --任务2
while true do
print("Luat is esay")
sys.wait(2000)
end
end)
还记得上一章输出的内容吗?输出两次Hello World,输出一次Luat is esay。明明是while true的死循环,怎么会切换到其他任务?可以看到每个程序后都有sys.wait,程序执行sys.wait将自己挂起(说白一点,就是不在执行自己了),然后执行其他任务。第一个任务将自己挂起1000ms,也就是说每隔1000ms执行一次。第二个任务将自己挂起2000ms,也就是说将自己挂起2000ms。
希望这一章能让你明白Luat的运行原理