一、lua版本不一样
LuatOS-Air使用的是lua5.1版本,本身不支持位移运算符。
LuatOS使用的是lua5.3版本,取消了module(..., package.seeall)这种形式的跨文件调用。
二、api不同
首先说明,core和脚本有所不同,用户可以理解为,core是安卓/ios系统,脚本为一个又一个的app,只有core+脚本,才能支撑起完整的一个二次开发项目。
LuatOS-Air的api:
在https://doc.openluat.com/wiki/21?wiki_page_id=2068这里,又分为了5.1原生接口,提供的额外接口两种。
在额外的接口其中,又分为了底层接口和二次封装接口,底层接口叫做core api,二次封装接口叫做script lib api,下面会简称为lib层api。
core api实现过程不可见,封装在了core里,受限于和RDA的协议,这部分实现过程不开源,而lib层的api,实现过程可见,用户可以自行修改。
lib层api一般是将底层提供的接口进行合并与封装,更加的简单与易用,也有部分lib层api是直接给core发送AT指令然后处理AT指令的返回值,并且以函数返回值的形式返回给调用该api的位置。
LuatOS的api
在https://docs.openluat.com/osapi/这里,和LuatOS-Air一样,分为了5.3原生接口和合宙提供的额外接口两种。
在额外的接口其中,又分为了核心库接口和扩展库接口,核心库接口叫做core api,扩展库接口叫做script lib api,下面会简称为lib层api。
core api实现过程不可见,封装在了core里,这部分实现过程不开源,而lib层的api,实现过程可见,用户可以自行修改。
LuatOS 核心库是在底层实现的功能库,调用核心库无需代码使用 require 操作;
LuatOS 扩展库是用 Lua 脚本实现的功能库,必须用 requre 调用才能够使用扩展库。
三、跨文件调用方式不同
LuatOS-Air跨文件调用方式
LuatOS-Air在每一个非main.lua的文件头部,第一行可执行代码永远是module(..., package.seeall),主要作用是将该文件中所有的全局变量/全局函数,加入到一张名为 _G的table中方便其他.lua文件调用,在这里不做过多讲解,能有转移需求的客户,基本都会LuatOS-Air的跨文件调用方法。
luatos跨文件调用方式
luatos跨文件调用方式有两种,一种和LuatOS-Air类似,不过是在文件第一行,新建一个和文件名相同的table,文件结尾处return这个table,接下来举个例子
首先封装一个函数
---函数功能:
-- 生成从1-max的table
-- @输入值:table的最大值
-- @返回: table结果
-- @例子: local list = getNumberList(10)
function getNumberList(max)
local t = {}
for i=1,max do
table.insert(t,i)
end
return t
end
我们新建一个文件叫tools.lua,把这个函数放进去,现在,整个文件如下面这样:
tools.lua---函数功能:
-- 生成从1-max的table
-- @输入值:table的最大值
-- @返回: table结果
-- @例子: local list = getNumberList(10)
local function getNumberList(max)
local t = {}
for i=1,max do
table.insert(t,i)
end
return t
end
--手动返回一个table,包含了上面的函数
return {
getNumberList = getNumberList,
}
现在,我们封装的这个函数就能在其他文件(例如main.lua)里被调用了,具体代码如下:
--引用tools.lua文件,并加载
local tool = require("tools")
local list = tool.getNumberList(12)
当调用了require接口后,Lua虚拟机会自动加载你调用的文件,执行文件的内容,然后返回你文件里return的结果。
为了更好地理解这段话,我们可以看下面两个文件,其中main.lua是被运行的那个入口文件,
test.lua--以便一会儿返回使用的table
local temp = {}
--把全局变量a更改了
a = 1
--local变量无法被外部调用
--但是可以在文件内被调用
local b = 2
--文件在被require的时候,会被执行
--把全局变量c更改了
c = a + b
--使函数在table里
function temp.addB()
--文件内部可以调用变量b
b = b + 1
return b
end
--返回table
return temp
main.lua-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "adcdemo"
VERSION = "1.0.0"
log.info("main", PROJECT, VERSION)
-- 添加硬狗防止程序卡死
if wdt then
wdt.init(9000) -- 初始化watchdog设置为9s
sys.timerLoopStart(wdt.feed, 3000) -- 3s喂一次狗
end
local test = require("test")
--引用test.lua,这里require的作用如果不是很清楚可以类比成C语言中的#include,当然熟悉lua和C语言的用户可以很明确的知道require和#include的区别,但是方便新用户理解,可以暂时当成#include来看待
print(a)--输出1
print(b)--输出nil,因为b是local变量
print(c)--输出3
print(test.addB())--输出3
print(test.addB())--输出4
print(test.addB())--输出5
同时,每个文件最多只会被require一次,如果有多个require,只有第一次会执行
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
此处为第一种调用方法,简单来说,被调用文件头部,将module(..., package.seeall)换成文件名={},文件末尾处加return {本文件中写的函数名=本文件中写的函数名},有多个函数的时候,可以添加多个元素名= 元素名进table里。
第二种调用方法依旧是在文件开头写上文件名={},不同的是,需要被调用的函数名,可以写成文件名.函数名的形式,最后的return不需要return一个很长的table了,只需要return 文件名,例如:
需要在main.lua 中调用test.lua的test函数,那么除了固定格式以外的main.lua可以写成
test=require("test")
test.test()
而test.lua中完整的样子为
test = {}
function test.test()
log.info(“我被调用了”)
end
return test
四、实例
以uart的demo为例,笔者将带着用户,将LuatOS-Air uart的demo,移植到luatos上(仅讲解uart1的移植过程,其他串口通用),除去无关本次移植过程的部分,LuatOS-Air的uart1完整demo如下,是一个自发自收的测试demo,luatos完整的demo也会放在最后,方便用户对比。
开始移植
1、main.lua的改造
PROJECT和VERSION这两个参数不变,下载时候需要这两个参数
require "log"这句可以删除,底层已经写好了log库,并提供了和LuatOS-Air lib层api几乎一致的core api,查看对应的 luatos log库api https://docs.openluat.com/osapi/core/log/
后得知,几种日志模式的常量有所不同,所以LOG_LEVEL = log.LOGLEVEL_TRACE这句,可以改成LOG_LEVEL = log.LOG_INFO,再添加一句log.setLevel(LOG_LEVEL )
因为主逻辑都在testUart1文件中,不需要在main.lua中调用,所以保持 require "testUart1" 原样即可,为了用户更直观的看出跨文件调用的不同,所以我在testUart1中又写了一个名为function_name的函数,然后在main.lua中进行循环调用。sys.init函数不需要,直接删去即可
完成上述步骤以后,main.lua就被我们改造成了下面这样
2、testUart1.lua的改造
接下来进入testUart1.lua中
module(...,package.seeall)改为 testUart1 ={},pm和utils两个库,utils不需要,直接删除,pm库底层提供了,无需require,也删除。
接下来会先将proc、read、write、writeOk和我刚刚写的function_name这几个函数会加载到内存中,但是还没有执行,接下来执行的是pm.wake("testUart"),查看luatos的pm接口,可以看到luatos没有wake接口,但是有不休眠模式,所以先设置下不休眠,也就是将pm.wake("testUart")换成pm.request(pm.NONE)
然后执行的是uart.on两个注册函数,当时串口有接收事件产生时候,会去执行read函数,当串口有发送事件产生时,会执行writeOK函数,对比luatos的注册串口收发事件,可以看出,这两个芯片收发事件函数一致,无需更改。
最后执行的是串口设置指令,LuatOS-Air和luatos有很大不同
这两个接口,LuatOS-Air的和luatos最大区别就是,LuatOS-Air将485半自动收发控制分开了,单独写了一个uart.set_rs485_oe
而luatos将其写在了一起,用户在使用该接口时,一定要注意不同接口之间参数的位置。
当有串口接收事件产生时,模块会进入read函数,在read函数里,打印了data原始数据和转成hex以后的数据后,便进入了proc函数中,并且将串口来的数据传入给proc函数,进行处理。
值得注意的是,read函数里有将串口来的数据通过uart.read函数赋值给data变量这个操作,但是luatos截至当前文章完成时,uart.read函数的第二个参数,只能填number,意为每次接收的字节数,也就是需要将代码中的uart.read(UART_ID,"*l")换成uart.read(UART_ID,1024)后面这个1024,为uart.setup的第7个参数,串口缓冲区你设置的大小,未设置默认为1024字节,如果需要用户自行设置,则最小512,最大4096
而当有串口发送事件产生时,模块会进入writeOk函数,该函数比较简单,就打印了下发送成功字样。
最后一行因为有跨文件调用,所以需要return 文件名,也就是加一句return testUart1
最后整个testUart1.lua就被我们改造成了这样
至此 整个改造过程结束