模块化
在继续推进我们这个迷你气象站之前,我们需要了解一下lua的模块与包。具体的概念这里不赘述。
引入模块和包的一个主要原因是,后面我们要涉及的功能模块比较多。按照模块化编程的思想是,让不同的功能代码分散在不同的文件中,以减少代码的耦合性。
先来看一个简单的例子
module = { }
module.value = 1
function module.func()
print(module.value)
end
return module
首先,定义一个module的table。然后往这个table写入一堆东西。最后返回这个module。实现一个模块就是这么简单。
而引用这个模块也是相当的简单,在另外一个文件里面使用require "module"
即可。引号里是module的名字。一般来说把模块名与文件名保持一致即可。
第一个模块
既然是第一个lua模块,那就要拿最最最简单的功能模块来练手了。我想最简单的非gpio莫属了。
不知道你忘记没有,nodemcu上面有2个IO外设。没错,一个flash按键和一个蓝色的led。我们先把这两个外设封装起来成为我们第一个模块,命名为myIO。模块包含了以下几个功能:
- IO初始化
- 写IO
- 读IO
让我们一点一点来实现,
--1
myIO = { }
local key, led = 3, 0
local keymode, ledmode = gpio.INPUT, gpio.OUTPUT
local keyCnt = 0
这里我们先定义一个myIO的table,和一些局部变量。局部变量只可以被模块内部访问。另外,local key, led = 3, 0
是lua里面一个有趣的语法,和下面的写法是等效的。
local key = 3
local led = 0
接下来是初始化IO
--3
function myIO.gpioInit()
gpio.mode(key, keymode)
gpio.mode(led, ledmode)
return true
end
这是模块的一个函数,初始化key和led,是可以被模块外的其他函数调用的。下面这个函数是用来控制led状态的,我想不需要我多说了。
--4
function myIO.setLED(s)
if s == true then
gpio.write(led, gpio.LOW)
else
gpio.write(led, gpio.HIGH)
end
end
弄完led的,接着来写一个按键的。
--2
local function keyScan()
local v = gpio.read(key)
if v == 0 then
keyCnt = keyCnt + 1
if keyCnt > 50 then
v = 2
keyCnt = 0
end
else
if keyCnt > 5 then
v = 1
keyCnt = 0
else
v = 0
keyCnt = 0
end
end
return v
end
你可能发现了,这个函数和前面不一样。恩,没错。这个多了个local
,少了个myIO
。这其实是个局部函数,或者说模块内部函数。
这个用来做按键扫描,可以判断按键是长按还是短按(短按在松手后判断)。为啥这个函数要做出local
的呢?后面在说明原因。
接着来看最后一个函数
--5
function myIO.setKey(short, long)
local s = 0
if myIO.ktimer == nil then
myIO.ktimer = tmr.create()
end
tmr.stop(myIO.ktimer)
--20ms
tmr.register(myIO.ktimer, 20, tmr.ALARM_AUTO, function()
local k = keyScan()
--调整扫描频率
if s == 1 then
tmr.interval(myIO.ktimer, 20)
s = 0
end
if k == 1 then
short()
elseif k == 2 then
long()
tmr.interval(myIO.ktimer, 500)
s = 1
end
end)
tmr.start(myIO.ktimer)
end
函数有点长。不过,你应该看出来了。这里面其实就是创建了一个定时器,并注册了回调事件来处理键盘。
不知道你有没有注意到这里,
if myIO.ktimer == nil then
myIO.ktimer = tmr.create()
end
是的,这个定义了一个模块变量。以为着你可以在其他模块里面使用myIO.ktimer
来操作键盘扫描这个定时器。
也许,你还留意到了short, long
。这两个参数在函数内部被当做函数使用了。
这种做的好处是,我们在模块内部对按键的行为做好封装,外部代码不用知道键盘的具体逻辑,这需要把长按和短按的两个函数传进来即可,类似于回调。这样更符合模块化编程。
这也是为什么keyScan()
是一个局部函数。因为外面的世界不需要知道里面的世界是什么样子的!
最后,别忘记了
--6
return myIO
测试模块
模块写好了,能不能用还要测试才知道。建议先按--1 ~ --6
拷贝代码到文件,并命名为myIO.lua。然后使用我们平时用的那个软件的upload功能把文件上传到nodemcu上。
重新写个测试文件test.lua。代码如下。
local myIO = require "myIO"
myIO.gpioInit()
function a()
print("short")
end
function b()
print("long")
end
myIO.setKey(a, b)
使用local myIO = require "myIO"
来引入包,并给这个包重新起个名字,方便后面使用。
初始化IO,并定义2个函数,在设置按键事件回调。
最后,按一下nodemcu上面的flash来看效果吧。长按和短按是不一样的!
关于模块化,暂且先到这。后面我们都会讲用的的功能封装成一个一个模块!
点完赞再走啊!