18video性欧美19sex,欧美高清videosddfsexhd,性少妇videosexfreexxx片中国,激情五月激情综合五月看花,亚洲人成网77777色在线播放

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

嵌入式開發(fā)新選擇:LuatOS腳本框架入門教程

青山老竹農(nóng) ? 來源:jf_82863998 ? 作者:jf_82863998 ? 2025-09-26 17:34 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群


LuatOS正成為嵌入式開發(fā)的新趨勢!本教程帶你從基礎(chǔ)入手,全面了解其基于Lua的腳本開發(fā)模式與輕量級(jí)運(yùn)行框架。

一、LuatOS 編程起步

1.1 底層固件怎么啟動(dòng) LuatOS 腳本

1.1.1 腳本入口執(zhí)行文件

簡單來說,底層固件首先就是要找到 main.lua 這個(gè)文件,然后啟動(dòng)它。

所有的其他功能,都需要在 main.lua 發(fā)起。

1.1.2 LuatOS 啟動(dòng)腳本的詳細(xì)流程

進(jìn)一步詳細(xì)的說,LuatOS 的底層固件啟動(dòng)腳本的流程如下:

1,系統(tǒng)上電或者復(fù)位后,底層固件(core)首先啟動(dòng),進(jìn)行硬件初始化、內(nèi)存分配、文件系統(tǒng)掛載等系統(tǒng)底層的基礎(chǔ)操作。

2,加載 Lua 虛擬機(jī):底層固件加載 Lua 虛擬機(jī),為執(zhí)行 Lua 腳本提供運(yùn)行環(huán)境;

3,自動(dòng)查找并加載存儲(chǔ)在設(shè)備上的主腳本 main.lua;

4,按順序執(zhí)行 main.lua 腳本中的代碼,通常包括任務(wù)創(chuàng)建(如sys.taskInit)、功能初始化等,從這一步,已經(jīng)正式開始運(yùn)行用戶邏輯。

5,進(jìn)入任務(wù)調(diào)度:腳本最后通常調(diào)用sys.run(),進(jìn)入事件循環(huán)和多任務(wù)調(diào)度。

1.1.3 怎么把固件和腳本燒錄到硬件:

1,使用LuatTools ,將底層固件和用戶 Lua 腳本燒錄到模組或者引擎硬件;

2,上電后,底層固件自動(dòng)完成上述啟動(dòng)和腳本加載流程,無需手動(dòng)干預(yù)。

1.2 main.lua 需要包含哪些部分?

1.2.1 項(xiàng)目信息聲明

在 main.lua 的文件開頭,需要聲明項(xiàng)目名和版本號(hào),便于管理和調(diào)試。后續(xù)的遠(yuǎn)程升級(jí),也需要用到項(xiàng)目名和版本號(hào)。

例如:

wKgZO2jWUOGAIwzMAABxJMLKJJ8624.png


1.2.2 核心庫,擴(kuò)展庫以及如何加載

在 main.lua 需要加載 LuatOS 的基礎(chǔ)庫和擴(kuò)展庫(如 zbuff,onewire,gnss 等)用來實(shí)現(xiàn)具體的業(yè)務(wù)邏輯。

核心庫和擴(kuò)展庫的內(nèi)容,在后續(xù)的章節(jié)里面介紹。

核心庫在底層固件加載Lua虛擬機(jī)的時(shí)候,在底層固件已經(jīng)自動(dòng)加載,不需要在用戶腳本中再去加載,例如sys,rtos等;

擴(kuò)展庫是Lua腳本文件寫的庫,需要在用戶腳本中,使用require語句加載,例如libnet,httpplus等,加載方式如下:

wKgZO2jWUXiAV6SBAABY1MvYK9s803.png

1.2.3 至少啟動(dòng)一個(gè)任務(wù)

在 main.lua 里面,至少需要啟動(dòng)一個(gè)任務(wù),否則這個(gè) main 就無所事事,是一個(gè)沒什么實(shí)際用處的主腳本了。

啟動(dòng)一個(gè)任務(wù)的方法,分為 2 個(gè)步驟:

1,創(chuàng)建一個(gè)函數(shù),把要做的事情,放在這個(gè)函數(shù)里面使用。這個(gè)函數(shù)必須是無限循環(huán)的,防止很快結(jié)束生命,不妨把這個(gè)函數(shù)命名為 task1(),

2,調(diào)用 sys.taskInit(task1),啟動(dòng)這個(gè)函數(shù),于是這個(gè)任務(wù),就放在待運(yùn)行的任務(wù)列表里面了。

1.2.4 初步理解 sys.run()

sys.run() 是一個(gè)無限循環(huán)的函數(shù)。

main.lua 的最后一行, 只能是 sys.run(),代表 sys.run() 接管了 LuatOS 的所有的執(zhí)行調(diào)度工作。

sys.run() 是 LuatOS 的運(yùn)行中樞。

在本文的 3.3 節(jié)和 7.3 節(jié),還會(huì)繼續(xù)介紹 sys.run()這個(gè)函數(shù)。

1.3 LuatOS 腳本編程的核心要點(diǎn)

1.3.1 LuatOS 實(shí)現(xiàn)的典型功能

LuatOS 腳本是利用了 Lua 的語法,以及基于 LuatOS 的核心庫和擴(kuò)展庫提供的 API,進(jìn)行簡便的編程,實(shí)現(xiàn)如下功能:

1,實(shí)現(xiàn)和云端服務(wù)器通信;

2,采集外設(shè)的數(shù)據(jù),控制外設(shè)設(shè)備;

3,實(shí)現(xiàn)人機(jī)交互,包括圖形交互和語音交互;

1.3.2 LuatOS 的學(xué)習(xí)要點(diǎn)

要想寫好 LuatOS 的軟件,實(shí)現(xiàn)上述三個(gè)功能,除了逐漸掌握 Lua 的基本語法之外,還需要熟悉 LuatOS 的核心庫和擴(kuò)展庫,這樣才能開發(fā)出優(yōu)質(zhì)的基于 LuatOS 的物聯(lián)網(wǎng)設(shè)備軟件。

學(xué)習(xí)的方法有如下幾個(gè):

1, 運(yùn)行各個(gè)功能模塊的 demo 代碼;

2, 閱讀 docs.openluat.com 的教程文檔;

3, 遇到不懂問 AI;

1.3.3 一個(gè)典型的 LuatOS 實(shí)現(xiàn)

一個(gè)典型的 LuatOS 實(shí)現(xiàn),包含 main.lua 入口文件和若干個(gè)功能模塊文件。

這里用 Air780EPM 模組的蜂鳴器的代碼為例, 有兩個(gè)腳本文件以及一個(gè)管腳描述 json 文件:

1, main.lua 文件, 作用是啟動(dòng)一個(gè)任務(wù),讓蜂鳴器響一秒鐘,再停頓一秒鐘,如此往復(fù);

2, airbuzzer.lua 封裝了驅(qū)動(dòng)蜂鳴器的功能實(shí)現(xiàn);

3, pins_Air780EPM.json 描述了本例使用到的管腳的功能,780EPM 的 26 管腳,用作 PWM4。

main.lua 內(nèi)容如下:

wKgZO2jWUn-AWJRaAAKAcy3IaWY302.png

airbuzzer.lua 內(nèi)容如下:

wKgZPGjWUr2AQOdjAAIMsxtcII8010.png

pins_air780EPM.json 內(nèi)容如下:

wKgZPGjWUviAAvadAADh8uf_JqA979.png

把上述幾個(gè)文件,連同 airr780EPM 最新的固件版本,用 Luatools 建立一個(gè)工程,燒錄到 780EPM 開發(fā)板,就可以聽到蜂鳴器的播放聲音了。

二、幾個(gè)要熟悉的常識(shí)

2.1 匿名函數(shù)

在 Lua 代碼里面,經(jīng)??吹?jīng)]有名字的函數(shù)。

這種函數(shù)定義之后, 要么馬上運(yùn)行,要么作為另一個(gè)函數(shù)的返回值賦給其他變量,所以并不需要一個(gè)函數(shù)名字。

這種函數(shù),稱為匿名函數(shù)。

匿名函數(shù)可以某些時(shí)候簡化代碼,初學(xué)者寫代碼可以先不考慮匿名函數(shù)。

但是由于匿名函數(shù)在你能閱讀到的 Lua 代碼里面出現(xiàn)的頻次實(shí)在是太高了,所以你也不得不重視和習(xí)慣匿名函數(shù)。

2.2 閉包

閉包的實(shí)現(xiàn)通常是通過在外部函數(shù)內(nèi)部定義一個(gè)函數(shù),并將這個(gè)內(nèi)部函數(shù)作為外部函數(shù)的返回值。

這樣一來,內(nèi)部函數(shù)就可以訪問外部函數(shù)作用域中的變量,即使外部函數(shù)已經(jīng)執(zhí)行完畢,這些變量依然可以被內(nèi)部函數(shù)訪問,從而形成閉包。

常見的閉包實(shí)現(xiàn)模式如下:

wKgZPGjWU8yAJPlrAAEZPBSMJ6A664.png

這樣的好處是,可以定義一個(gè)函數(shù),能夠在一定范圍內(nèi),訪問外部的變量,實(shí)現(xiàn)可控的持續(xù)行為。

很多初學(xué)者會(huì)被這段代碼迷惑,會(huì)被繞暈。

這里做一下解釋:

(1)z 不是函數(shù)里面聲明的變量,z 是函數(shù)的參數(shù);

所以 在代碼里面, 因?yàn)?f=outer(10), 所以, f(5)就意味著是調(diào)用了 兩次函數(shù),傳入了兩個(gè)函數(shù)的參數(shù): outer(10)(5)。

第一次調(diào)用,out(10) ,意味著 在 outer 函數(shù)里面, y = x 這句, x 換成 10, 就是 y = 10;

outer(10)(5)意味著 5 是內(nèi)部匿名函數(shù)的參數(shù),就是替代 z 的;

匿名函數(shù)返回 y+z, 這里 y 是 10,z 是 5, 返回的就是 10+5=15.

這里比較繞的,就是給了兩次參數(shù),一個(gè)是 10 對(duì)應(yīng) x, 一個(gè)是 5 對(duì)應(yīng) z。

匿名參數(shù)和閉包,對(duì)初學(xué)者有點(diǎn)繞,很多讀者不明白為什么 z 為什么是 outer 的第二個(gè)參數(shù),

這里需要特別搞清楚的是, outer 這個(gè)函數(shù)的返回值是個(gè)函數(shù), 而且這個(gè)函數(shù)是有參數(shù)的。

那么,這個(gè)帶參數(shù)的函數(shù)賦值給 f 之后, f 就是個(gè)函數(shù)了, 于是給 f 一個(gè)參數(shù) 5, 這個(gè) 5 自然就是返回的函數(shù)的參數(shù)了,也就是 z 了。

雖然并不是所有的閉包都是上面這種代碼的實(shí)現(xiàn)形式,但是初學(xué)者可以先記住這樣的閉包形式。

如果不習(xí)慣閉包,初學(xué)者可以先避免在代碼里面體現(xiàn)閉包的代碼形式。

2.3 回調(diào)函數(shù)

2.3.1 回調(diào)函數(shù)是什么


回調(diào)函數(shù)是在 LuatOS 編程過程中經(jīng)常用到的一個(gè)技術(shù)。

理解 LuatOS 的回調(diào)函數(shù),可以從“事件驅(qū)動(dòng)”和“函數(shù)作為參數(shù)”兩個(gè)角度來把握:

回調(diào)函數(shù)(Callback)是在特定事件發(fā)生時(shí),由系統(tǒng)或框架自動(dòng)調(diào)用你事先定義好的函數(shù)。你只需要把自己的函數(shù)注冊(cè)給系統(tǒng),等事件觸發(fā)時(shí),系統(tǒng)就會(huì)幫你調(diào)用它。

本質(zhì)上,回調(diào)函數(shù)就是一個(gè)普通函數(shù),但它被作為參數(shù)傳遞或注冊(cè)到其他地方,由系統(tǒng)或其他代碼在合適的時(shí)機(jī)自動(dòng)執(zhí)行。

回調(diào)函數(shù)的作用是實(shí)現(xiàn)事件響應(yīng),異步處理。

消息到來,定時(shí)器到點(diǎn),網(wǎng)絡(luò)收發(fā)等功能都經(jīng)常會(huì)用到回調(diào)函數(shù)的處理。

總之,LuatOS 的回調(diào)函數(shù),就是你注冊(cè)給系統(tǒng)的,在特定事件發(fā)生時(shí)自動(dòng)被調(diào)用的函數(shù)。

回調(diào)函數(shù)讓事件響應(yīng)、異步處理、任務(wù)解耦變得簡單靈活,是 LuatOS 事件驅(qū)動(dòng)編程的核心機(jī)制之一。

2.3.2 回調(diào)函數(shù)做消息訂閱與發(fā)布

LuatOS 支持通過sys.subscribe訂閱消息并注冊(cè)回調(diào)函數(shù),消息發(fā)布時(shí)自動(dòng)調(diào)用回調(diào):

wKgZPGjWVH-AXceZAAEn-P9VxJQ823.png

當(dāng)sys.publish("TEST", 123)被調(diào)用時(shí),"TEST"消息以及攜帶的參數(shù)123會(huì)被插入到用戶消息列表中,LuatOS 內(nèi)部的sys.run()調(diào)度中樞會(huì)遍歷訂閱者列表,找到所有訂閱了 "TEST" 的回調(diào)函數(shù),并自動(dòng)把參數(shù) 123 傳給這些回調(diào)函數(shù)。

通過這樣的處理,事件觸發(fā)和處理邏輯就被解耦,方便擴(kuò)展和維護(hù)。

2.3.3 回調(diào)函數(shù)做定時(shí)器和異步操作

定時(shí)器到點(diǎn)后自動(dòng)調(diào)用注冊(cè)的回調(diào)函數(shù):

wKgZPGjWVLKAQFO-AACcCSIndd0859.png

2.3.4 任務(wù)和協(xié)程場景的回調(diào)函數(shù)使用

在多任務(wù),也就是 LuatOS 的協(xié)程場景下,回調(diào)函數(shù)也常用于任務(wù)喚醒、事件響應(yīng)等。

解耦調(diào)用者與被調(diào)用者:調(diào)用者只需知道“有回調(diào)”,不用關(guān)心回調(diào)具體做什么,提升靈活性。

你只需更換回調(diào)函數(shù),就能實(shí)現(xiàn)不同的處理邏輯,無需修改底層框架代碼。

任務(wù)和協(xié)程的詳細(xì)信息,在下一章講解。

三、LuatOS 的多任務(wù)并行實(shí)現(xiàn)詳解

3.1 LuatOS 的多任務(wù)是怎么實(shí)現(xiàn)的

3.1.1 通過協(xié)程實(shí)現(xiàn)多任務(wù)的效果

LuatOS 使用一種協(xié)程(coroutine)的機(jī)制,實(shí)現(xiàn)多任務(wù)。

協(xié)程并不是真的多任務(wù),也不是多線程,而是通過同一時(shí)間只可能有一個(gè)協(xié)程執(zhí)行,來等價(jià)實(shí)現(xiàn)多任務(wù)的效果。

和 RTOS 的搶占式多任務(wù)方式不同,協(xié)程不能搶占其他任務(wù)的時(shí)間片,只能由一個(gè)獨(dú)立的調(diào)度器來判斷是哪個(gè)協(xié)程占用 CPU 時(shí)間來運(yùn)行。

一個(gè) LuatOS 可以創(chuàng)建多個(gè)任務(wù),每一個(gè)任務(wù)都是協(xié)程,為了簡化描述,后續(xù)我們經(jīng)常會(huì)用”任務(wù)“這個(gè)詞來指代協(xié)程。

LuatOS 創(chuàng)建的任務(wù)無法設(shè)定優(yōu)先級(jí), 所以 LuatOS 的每個(gè)任務(wù)的優(yōu)先級(jí)都是相同的。

每一個(gè) LuatOS 的任務(wù)在做運(yùn)算的時(shí)候,是 100% 占用了 CPU 時(shí)間片的。

執(zhí)行完運(yùn)算之后,要主動(dòng)調(diào)用 yield() 函數(shù),讓自己掛起,其他任務(wù)才能獲得時(shí)間片運(yùn)行。

如果某個(gè)任務(wù), 持續(xù)進(jìn)行運(yùn)算,不做 yield() 調(diào)用,其他任務(wù)是無法獲取 CPU 時(shí)間片的。

協(xié)程掛起后,自己是無法恢復(fù)的,只能其他的任務(wù)調(diào)用 resume 系統(tǒng)函數(shù)來恢復(fù)。

我們?cè)趯懘a的時(shí)候,不需要調(diào)用 yield() 把自己掛起,只需要調(diào)用 sys.wait() 做時(shí)延,由調(diào)度器統(tǒng)一在 sys.wait()里面把任務(wù)掛起。

在 LuatOS 里面,所有掛起的協(xié)程,都由一個(gè)獨(dú)立的調(diào)度器通過調(diào)用 resume 來恢復(fù)。

這個(gè)獨(dú)立的調(diào)度器, 在 LuatOS 里面是 sys.run() 函數(shù)。

3.1.2 LuatOS 的任務(wù)函數(shù)怎么掛起和恢復(fù)

LuatOS 的每一個(gè)通過 sys.taskInit() 發(fā)起的任務(wù)函數(shù),都不會(huì)直接調(diào)用 yield 把自己掛起,因?yàn)橹苯诱{(diào)用 yield 掛起的話,并不知道什么時(shí)候恢復(fù)這個(gè)任務(wù)。

LuatOS 的做法是,每個(gè)任務(wù)在執(zhí)行完自己的事情之后,都必須是調(diào)用一個(gè)等待函數(shù), 這樣的等待函數(shù)有如下幾個(gè):

1,sys.wait(timeout)

這個(gè)函數(shù),會(huì)在掛起任務(wù)的同時(shí),啟動(dòng)一個(gè)定時(shí)器,定時(shí)器的觸發(fā)時(shí)間就是 timeout,并且把任務(wù) id 跟這個(gè)定時(shí)器綁定。

到定時(shí)器觸發(fā)之后,sys.run 會(huì)根據(jù)該定時(shí)器綁定的任務(wù) id,重新恢復(fù)該任務(wù)的運(yùn)行。

2,sys.waitUntil(topic, timeout)

在掛起任務(wù)的同時(shí),訂閱一個(gè)名為 topic 的消息。待到有其他的任務(wù)發(fā)布這個(gè)消息后,sys.run 恢復(fù)這個(gè)任務(wù)。

如果沒有等到其他任務(wù)發(fā)布這個(gè)topic 消息,超時(shí)timeout 了,sys.run()也會(huì)恢復(fù)任務(wù)的運(yùn)行。

總結(jié)來說,LuatOS 的任務(wù)在掛起自己之前,會(huì)在系統(tǒng)的表里面,放一個(gè)讓自己恢復(fù)運(yùn)行的條件,這個(gè)條件或者是一個(gè)超時(shí)時(shí)間,或者是其他任務(wù)發(fā)布一個(gè)消息。sys.run() 函數(shù)會(huì)去判斷這些恢復(fù)運(yùn)行的條件是否滿足,一旦滿足條件,就會(huì)恢復(fù)對(duì)應(yīng)的任務(wù)。

3.2 怎么實(shí)現(xiàn)單個(gè)任務(wù)

在 LuatOS 里面,一個(gè)任務(wù),可以理解為一個(gè)無限循環(huán)的函數(shù),啟動(dòng)一個(gè)任務(wù),有如下步驟:

1,定義這個(gè)無限循環(huán)的函數(shù) task1;

2,調(diào)用 sys.taskInit(task1), 在 taskInit 函數(shù)里面,先為 task1 函數(shù)創(chuàng)建一個(gè)協(xié)程,同時(shí)把這個(gè)協(xié)程注冊(cè)到系統(tǒng)的協(xié)程列表,這樣 sys.run() 就會(huì)去運(yùn)行這個(gè)協(xié)程。

這樣就新增了一個(gè)持續(xù)運(yùn)行,永不退出的協(xié)程了。

一個(gè)在 LuatOS 系統(tǒng)里面合法的任務(wù), 必須運(yùn)行很少量的時(shí)間,執(zhí)行完自己的操作之后,馬上就把自己掛起。 掛起的方式就是 調(diào)用 sys.wait 或者 sys.waitUtil 函數(shù)。

一個(gè)正常的 LuatOS 任務(wù),執(zhí)行計(jì)算的時(shí)間是很短暫的,絕大部分的時(shí)間,都是在掛起狀態(tài)。

在掛起狀態(tài), 是不消耗 CPU 資源的。

所以, LuatOS 的協(xié)程機(jī)制,具備了實(shí)現(xiàn)低功耗系統(tǒng)的前提。

3.3 進(jìn)一步理解 sys.run()

LuatOS 的sys.run()函數(shù)是系統(tǒng)任務(wù)調(diào)度器的啟動(dòng)入口,其主要工作流程如下:

3.3.1 進(jìn)入任務(wù)調(diào)度主循環(huán)

當(dāng)執(zhí)行到sys.run()時(shí),LuatOS 會(huì)啟動(dòng)任務(wù)調(diào)度器,正式進(jìn)入事件驅(qū)動(dòng)和多任務(wù)調(diào)度階段。

此后,所有通過sys.taskInit注冊(cè)的任務(wù)都會(huì)被納入系統(tǒng)統(tǒng)一調(diào)度。

3.3.2 循環(huán)處理底層消息與事件

sys.run()會(huì)不斷從底層(如硬件中斷、驅(qū)動(dòng)、系統(tǒng)內(nèi)核,定時(shí)器等)獲取消息或事件,并將這些消息分發(fā)到相應(yīng)的任務(wù)或回調(diào)函數(shù)進(jìn)行處理。

這包括定時(shí)器到期、外設(shè)事件、網(wǎng)絡(luò)數(shù)據(jù)到達(dá)、用戶自定義消息等。

3.3.3 定時(shí)器與任務(wù)切換

sys.run 會(huì)周期性檢查所有注冊(cè)的定時(shí)器,并在定時(shí)器到期時(shí)喚醒相應(yīng)的任務(wù)協(xié)程。

同時(shí),系統(tǒng)會(huì)根據(jù)任務(wù)的掛起或喚醒狀態(tài),合理切換協(xié)程,實(shí)現(xiàn)多任務(wù)并發(fā)。

3.3.4 任務(wù)間消息通信與同步

sys.run()支持任務(wù)間通過消息發(fā)布/訂閱、等待/喚醒等機(jī)制進(jìn)行通信與同步。

例如,任務(wù)可以通過sys.publish發(fā)布消息,其他任務(wù)通過sys.waitUntil或sys.subscribe等方式等待或響應(yīng)這些消息。

3.3.5 持續(xù)運(yùn)行,直至系統(tǒng)重啟或退出

sys.run()會(huì)持續(xù)運(yùn)行,不會(huì)主動(dòng)退出。

sys.run() 系統(tǒng)的主循環(huán),確保所有任務(wù)和事件都能被及時(shí)處理。

只有在系統(tǒng)重啟、腳本異常終止或手動(dòng)退出時(shí),sys.run() 這個(gè)調(diào)度循環(huán)才會(huì)結(jié)束。

3.3.6 簡要流程圖

(1)啟動(dòng)任務(wù)調(diào)度器;

(2)進(jìn)入主循環(huán)

(3)輪詢底層消息、定時(shí)器

(4)喚醒/調(diào)度任務(wù)協(xié)程

(5)分發(fā)和處理事件、消息

(6)返回主循環(huán),直到系統(tǒng)重啟或退出

3.4 怎么實(shí)現(xiàn)多個(gè)任務(wù)

3.4.1 協(xié)程大多數(shù)時(shí)間應(yīng)該是掛起狀態(tài)

由于協(xié)程的運(yùn)行原理是,同一時(shí)間只有一個(gè)協(xié)程在運(yùn)行,其他協(xié)程在掛起狀態(tài)。

所以如果有多個(gè)協(xié)程存在的話,多個(gè)協(xié)程的運(yùn)行,只可能有兩種情況:

第一種情況, 所有的協(xié)程都在掛起狀態(tài),這時(shí)候系統(tǒng)有可能進(jìn)入低功耗;

第二種情況, 有一個(gè)協(xié)程在運(yùn)行,其他協(xié)程在掛起。這時(shí)候系統(tǒng)是喚醒狀態(tài),不可能是低功耗狀態(tài)。

3.4.2 LuatOS 多任務(wù)的核心是掛起和恢復(fù)的調(diào)度

一個(gè)協(xié)程運(yùn)行的時(shí)間越長,掛起的就越慢,其他的協(xié)程就無法得到時(shí)間片運(yùn)行。

只有所有的協(xié)程都盡量減少時(shí)間占用, 都盡快掛起自己,這樣的多任務(wù)的調(diào)度的效率才能更高。

因此, LuatOS 多任務(wù)的編程核心,是使得每個(gè)任務(wù)函數(shù)的執(zhí)行時(shí)間盡可能的短,盡可能快速的掛起自己,整個(gè)系統(tǒng)的多任務(wù)并發(fā)處理的效率才會(huì)更高。

如果某個(gè)協(xié)程的運(yùn)算時(shí)間很長,導(dǎo)致自己無法很快掛起,就會(huì)拖累整個(gè)系統(tǒng),使得整個(gè)系統(tǒng)的實(shí)時(shí)響應(yīng)的性能降低。

3.4.3 怎么防止某個(gè)協(xié)程長時(shí)間不掛起

為了防止某個(gè)協(xié)程長時(shí)間做運(yùn)算,不把自己掛起,LuatOS 設(shè)計(jì)了 watchdog 機(jī)制,起一個(gè)定時(shí)器,幾秒鐘喂狗一次。

如果超時(shí)沒有喂狗,系統(tǒng)就會(huì)被重啟。

把下面這段代碼放到 main.lua,即可實(shí)現(xiàn)喂狗的功能:

wKgZO2jWVcmAa_SdAAESSQ3Vh5s064.png

3.5 多個(gè)任務(wù)之間怎么分配時(shí)間片

LuatOS 系統(tǒng)里面,是沒有給某個(gè)任務(wù)分配時(shí)間片這樣的動(dòng)作的。

LuatOS 的任務(wù),必須盡快把自己掛起,釋放出 CPU,才能夠讓整個(gè)系統(tǒng)實(shí)時(shí)運(yùn)行。

當(dāng)所有任務(wù)都把自己掛起后,系統(tǒng)就就可能會(huì)低功耗休眠狀態(tài)。

只要有任何一個(gè)任務(wù)沒有掛起,系統(tǒng)都不可能進(jìn)入低功耗休眠狀態(tài)。

通過 sys.run()函數(shù), 對(duì)多個(gè)任務(wù)按照業(yè)務(wù)需要進(jìn)行恢復(fù)運(yùn)行的調(diào)度,保證整個(gè)系統(tǒng)的順暢運(yùn)行。

sys.run()調(diào)度的依據(jù),一個(gè)是定時(shí)器機(jī)制,一個(gè)是消息機(jī)制。

四、LuatOS 的定時(shí)器機(jī)制

LuatOS 的定時(shí)器機(jī)制是實(shí)現(xiàn)多任務(wù)系統(tǒng)的核心組件之一。

支持單次觸發(fā)和周期循環(huán),適用于物聯(lián)網(wǎng)設(shè)備中的定時(shí)任務(wù)、數(shù)據(jù)采集、狀態(tài)監(jiān)測等場景。

4.1 定時(shí)器類型與適用場景

類型
特點(diǎn)
適用場景
單次定時(shí)器
延遲指定時(shí)間后觸發(fā)一次
初始化延時(shí)、事件超時(shí)處理
循環(huán)定時(shí)器
周期性觸發(fā),可指定次數(shù)
心跳包發(fā)送、傳感器輪詢

4.2 核心 API 與用法

4.2.1 單次定時(shí)器

功能: 延遲 timeout 毫秒后執(zhí)行函數(shù), 可傳多個(gè)參數(shù)local timerId = sys.timerStart(callback, timeout, arg1, arg2, ...) 參數(shù)說明: callback: 定時(shí)器觸發(fā)時(shí)執(zhí)行的函數(shù) timeout: 延遲時(shí)間(毫秒) argN: 傳遞給回調(diào)函數(shù)的參數(shù) 代碼示例:

wKgZO2jWVwOARKTCAADhw2Ctp_4116.pngwKgZPGjWVxyAIP0RAABBwYdnhcU843.png

4.2.2 循環(huán)定時(shí)器

功能: 每隔 timeout 毫秒重復(fù)執(zhí)行函數(shù)localtimerId = sys.timerLoopStart(callback, timeout, arg1, arg2, ...)

代碼示例:

wKgZPGjWV0aAVZ-IAAFMek5u5pg503.png

運(yùn)行結(jié)果為:

wKgZPGjWV4eAfnDVAADWuw8Xi3A944.png

4.2.3 定時(shí)器停止

LuatOS 有兩個(gè) API 用于停止正在生效的定時(shí)器:

1, 停止制定 timerid 的單個(gè)定時(shí)器

sys.timerStop(timerId)

2,停止制定回調(diào)函數(shù)的所有定時(shí)器。

sys.timerStopAll(callback)

4.3 典型代碼示例

4.3.1 組合使用單次與循環(huán)定時(shí)器

wKgZPGjWV7-AFHXuAAH8jz32Ur4983.png


4.3.2 動(dòng)態(tài)管理定時(shí)器

wKgZO2jWV-uAMIiPAAFtHLuYLI0354.png

4.3.3 5 秒后重連網(wǎng)絡(luò)

wKgZO2jWWC2ARvk7AADrz3Ro-tM440.png

4.4 定時(shí)器的數(shù)量限制

LuatOS 最多支持 64 個(gè)定時(shí)器。

由于任務(wù)里面的 sys.wait()、帶timeout參數(shù)的sys.waitUntil()、帶timeout參數(shù)的sys.waitMsg()調(diào)用也會(huì)引發(fā)調(diào)度器啟動(dòng)一個(gè)定時(shí)器管理該任務(wù)的運(yùn)行恢復(fù),所以用戶實(shí)際能夠啟用的定時(shí)器,會(huì)比 64 個(gè)更少。

所以,在開發(fā)過程中, 需要注意這一點(diǎn),不要無節(jié)制的使用定時(shí)器。

4.5 為什么 LuatOS 的定時(shí)器不太準(zhǔn)

LuatOS 的定時(shí)器往往“不太準(zhǔn)”,主要原因在于其定時(shí)器機(jī)制依賴于消息總線(Message Bus)和系統(tǒng)調(diào)度,而不是直接精準(zhǔn)地控制硬件定時(shí)。具體來說有如下幾點(diǎn)原因:

4.5.1 定時(shí)器基于消息機(jī)制

LuatOS 的定時(shí)器設(shè)計(jì)是基于 RTOS 的 timer API。

當(dāng)定時(shí)器超時(shí)時(shí),系統(tǒng)只是在消息總線中插入一條定時(shí)器消息,由主循環(huán) sys.run()消費(fèi)和處理,這會(huì)帶來兩種可能的時(shí)延:

1,當(dāng)調(diào)度器在處理消息時(shí),可能會(huì)因?yàn)槠渌蝿?wù)、消息隊(duì)列長度、系統(tǒng)負(fù)載等原因出現(xiàn)延遲。

2,定時(shí)器回調(diào)的實(shí)際執(zhí)行時(shí)機(jī),取決于消息被調(diào)度和消費(fèi)的時(shí)刻,而不是定時(shí)器超時(shí)的精確時(shí)刻。

4.5.2 系統(tǒng)調(diào)度與任務(wù)競爭

LuatOS 采用事件驅(qū)動(dòng)和多任務(wù)協(xié)作,主循環(huán)需要處理各種消息(包括定時(shí)器、外設(shè)、網(wǎng)絡(luò)等);

如果系統(tǒng)中有大量任務(wù)或消息,定時(shí)器消息可能會(huì)被延后處理,導(dǎo)致定時(shí)精度下降。

4.5.3 軟件定時(shí)器的局限

(1)軟件定時(shí)器本質(zhì)上依賴于系統(tǒng) tick(通常為 1ms),但 tick 的處理、消息入隊(duì)、Lua 虛擬機(jī)調(diào)度等環(huán)節(jié)都會(huì)引入微小延遲。

(2)在高負(fù)載或消息堆積時(shí),這種延遲會(huì)被放大,表現(xiàn)為“定時(shí)器不準(zhǔn)”。

4.5.4 Lua 腳本無法實(shí)現(xiàn)高精度定時(shí)器

LuatOS 定時(shí)器不太準(zhǔn)的根本原因是:定時(shí)器只是觸發(fā)消息,實(shí)際執(zhí)行依賴消息總線和主循環(huán)調(diào)度,受系統(tǒng)負(fù)載、任務(wù)數(shù)量、消息堆積、網(wǎng)絡(luò)中斷優(yōu)先級(jí)最高等多因素影響。

LuatOS 定時(shí)器不能實(shí)現(xiàn)高精度定時(shí)器(例如微秒級(jí)別,幾十毫秒級(jí)別,甚至幾百毫秒級(jí)別也會(huì)有誤差)。

如果要實(shí)現(xiàn)高精度定時(shí)器,只能外掛單片機(jī)實(shí)現(xiàn),或者后續(xù)有可能推出集成單片機(jī),專門用來實(shí)現(xiàn)高精度定時(shí)器和其他對(duì)實(shí)時(shí)性和精度要求比較高的需求。

4.6 sys.lua 里面的 timerPool 變量

如果你有興趣查看 sys.lua 的話,會(huì)發(fā)現(xiàn) timerPool 這個(gè) table 類型的變量,在 0-0x1FFFFF 范圍內(nèi)存儲(chǔ) 恢復(fù)運(yùn)行協(xié)程的定時(shí)器消息 ID, 在 0x200000-0x7FFFF 范圍內(nèi)存儲(chǔ)有回調(diào)函數(shù)的定時(shí)器消息 ID。

所以,凡是某個(gè)協(xié)程調(diào)用 sys.wait()延時(shí)函數(shù),都會(huì)在注冊(cè)一個(gè)定時(shí)器,定時(shí)器超時(shí)后,就會(huì)由調(diào)度器重新恢復(fù)這個(gè)協(xié)程的運(yùn)行;

當(dāng)使用 timerStart 函數(shù)注冊(cè)的定時(shí)器超時(shí)后, 調(diào)度器會(huì)調(diào)用定時(shí)器回調(diào)函數(shù)。

這兩種情況的超時(shí)處理,都是在 timerPool 這個(gè)變量實(shí)現(xiàn)的。

4.7 LuatOS 定時(shí)器總結(jié)

LuatOS 的定時(shí)器機(jī)制通過sys庫提供了消息驅(qū)動(dòng)架構(gòu),合理運(yùn)用定時(shí)器可顯著提升物聯(lián)網(wǎng)設(shè)備的自動(dòng)化程度和能效比。

在使用定時(shí)器機(jī)制的時(shí)候,需要注意如下幾點(diǎn):

4.7.1 避免阻塞回調(diào)

1, 定時(shí)器回調(diào)函數(shù)中禁止使用sys.wait操作

因?yàn)槎〞r(shí)器回調(diào)函數(shù)是由調(diào)度器直接調(diào)用的,如果在定時(shí)器回調(diào)函數(shù)里面使用 sys.wait 操作,會(huì)使得調(diào)度器阻塞,從而使得整個(gè)系統(tǒng)停止運(yùn)行。

2, 定時(shí)器回調(diào)函數(shù)禁止進(jìn)行長時(shí)間阻塞操作

這樣會(huì)極大的降低系統(tǒng)效率,使得系統(tǒng)的反應(yīng)變慢。

4.7.2 注意資源釋放

任務(wù)退出時(shí),如果在任務(wù)運(yùn)行過程中創(chuàng)建的定時(shí)器不再需要,需調(diào)用sys.timerStop()或者sys.timerStopAll()清理關(guān)聯(lián)定時(shí)器,防止內(nèi)存泄漏,或者引起定時(shí)器資源耗盡。

如果不想主動(dòng)寫代碼清理關(guān)聯(lián)定時(shí)器,只能等待定時(shí)器時(shí)間到了之后,自動(dòng)清除,這時(shí)就會(huì)多占用了一個(gè)沒有任何實(shí)際功能的定時(shí)器,如果定時(shí)器資源非常緊張的情況下,創(chuàng)建新的定時(shí)器有可能會(huì)失敗。

4.7.3 不要期待有高精確度的延時(shí)和定時(shí)

由于消息機(jī)制和虛擬機(jī)的運(yùn)行限制,導(dǎo)致延時(shí)函數(shù)和定時(shí)器的精度都不會(huì)很高,在實(shí)現(xiàn)業(yè)務(wù)邏輯的時(shí)候,一定要注意這一點(diǎn)。


五、 LuatOS 的消息機(jī)制

LuatOS 的消息機(jī)制是其多任務(wù)協(xié)作的核心,通過sys庫實(shí)現(xiàn)事件驅(qū)動(dòng)編程。以下從消息發(fā)送、消息接收、消息訂閱三個(gè)維度詳細(xì)解析:

5.1 發(fā)送消息

5.1.1 廣播式消息(一對(duì)多)

API:sys.publish(topic, arg1, arg2, ...)

功能:向所有訂閱者廣播消息,無目標(biāo)標(biāo)識(shí)。

代碼示例:

wKgZO2jWWSSAYyl7AADHbohWgA0000.png

5.1.2 定向消息(點(diǎn)對(duì)點(diǎn))

API:sys.sendMsg(taskName, target, arg2, arg3, arg4)

功能:向指定任務(wù)發(fā)送消息,支持目標(biāo)標(biāo)識(shí)和參數(shù)。

代碼示例:

wKgZO2jWWVuAawNVAADJAGxZKmo453.png

5.2 消息接收

5.2.1 等待消息

在協(xié)程內(nèi)部等待:sys.waitUntil(topic, timeout)

特別提醒: 該 API 只能在協(xié)程內(nèi)執(zhí)行

代碼示例:

wKgZPGjWWYqAAmOPAAEo-23-at8768.png

5.2.2 定向接收

API:sys.waitMsg(taskName, target, timeout)

特點(diǎn):按任務(wù)名和目標(biāo)標(biāo)識(shí)精準(zhǔn)接收,支持超時(shí),該代碼只能在協(xié)程內(nèi)執(zhí)行。

注意,該 API 的第一個(gè)參數(shù) taskName, 是指等待消息的任務(wù)名稱,也就是自己的任務(wù)名稱,不是發(fā)送消息的任務(wù)名稱。

調(diào)用該 API 的任務(wù),和接收任務(wù),不一定是同一個(gè)任務(wù)。

當(dāng)接收消息的任務(wù)在掛起的時(shí)候,可以由其他任務(wù)或者調(diào)度器通過 WaitMsg API 喚醒掛起的任務(wù)。

代碼示例:

wKgZPGjWWcCADlyMAAEuAc6Uu0Q065.png

5.3 消息訂閱

5.3.1 全局訂閱

API:sys.subscribe(topic, func)

特點(diǎn):如果訂閱了同一主題有多個(gè)回調(diào)函數(shù),這些回調(diào)函數(shù)都會(huì)被觸發(fā)。

代碼示例:

wKgZO2jWWfGAVDYpAACtu1ef4is485.png

5.3.2 任務(wù)私有訂閱

實(shí)現(xiàn)方式:通過sys.taskInitEx創(chuàng)建任務(wù)時(shí)注冊(cè)回調(diào)。

當(dāng)有其他的任務(wù)發(fā)送消息給目標(biāo)任務(wù)的時(shí)候, 但是目標(biāo)任務(wù)并沒有通過 WaitMsg 函數(shù)設(shè)定消息處理,這時(shí)候該消息的處理就交給回調(diào)函數(shù)處理。

代碼示例:

wKgZPGjWWiGAeqbIAAJL_UPBeFM687.png

5.4 LuatOS 消息機(jī)制的典型應(yīng)用場景

5.4.1 網(wǎng)絡(luò)模塊與主任務(wù)通信

wKgZO2jWWk-AVlj0AAJy1EAXiWY350.png


5.4.2 全局事件通知(sys)

wKgZPGjWWneANOX8AAIZITPbR1I878.png

5.5 消息機(jī)制設(shè)計(jì)要點(diǎn)

5.5.1 消息機(jī)制的不同設(shè)計(jì)

(1)處理全局事件,sys.publish發(fā)布的消息,已經(jīng)訂閱這個(gè)消息的所有sys.subscribe對(duì)應(yīng)的處理函數(shù)都能收到。 (2)處理模塊間通信(如網(wǎng)絡(luò)請(qǐng)求-響應(yīng)),sys.sendMsg發(fā)布的消息,會(huì)攜帶一個(gè)task name參數(shù),只有sys.waitMsg時(shí)也攜帶同樣的task name參數(shù),才能收到消息。

5.5.2 避免消息風(fēng)暴:

高頻消息(如傳感器數(shù)據(jù))建議合并發(fā)送或降低頻率。

5.5.3 消息機(jī)制的核心目的之一是軟件解耦

通過合理運(yùn)用sys庫的消息機(jī)制,可構(gòu)建高效、解耦的物聯(lián)網(wǎng)應(yīng)用架構(gòu)。

六、多任務(wù)之間的信息交換

6.1 用全局變量做信息交換

如果信息量很小,比如就一個(gè)字符串或者標(biāo)志位,任務(wù)之間可以通過共享全局變量來通信,一個(gè)任務(wù)去對(duì)這個(gè)全局變量賦值,其他任務(wù)讀取這個(gè)全局變量,任務(wù)之間就達(dá)到了通信的目的了;

6.2 用消息做信息交換

但是如果想要交換多個(gè)數(shù)據(jù),每個(gè)數(shù)據(jù)都用全局變量的話,就有點(diǎn)過于累贅了。

這時(shí)候,可以通過發(fā)送消息來通信。

任務(wù)之間怎么發(fā)送消息,接收消息,參考第五章的內(nèi)容。

七、再次理解調(diào)度器 sys 庫

LuatOS 的 sys 庫是系統(tǒng)調(diào)度和多任務(wù)管理的核心庫,提供了豐富的 API 用于任務(wù)創(chuàng)建、延時(shí)、消息通信、定時(shí)器管理等。

7.1.1 任務(wù)與協(xié)程管理

API: sys.taskInit(func, arg1, arg2, ...)功能: 創(chuàng)建一個(gè)新的任務(wù)(協(xié)程),并傳遞參數(shù)給任務(wù)函數(shù)。

7.1.2 延時(shí)與等待

(1) sys.wait(timeout)功能: 任務(wù)延時(shí)掛起指定毫秒數(shù),只能在任務(wù)函數(shù)中調(diào)用。

(2)sys.waitUntil(topic, timeout)功能: 任務(wù)掛起,直到收到指定 topic 的消息或超時(shí),只能在任務(wù)函數(shù)中調(diào)用。

7.1.3 定時(shí)器相關(guān)

(1) sys.timerStart(func, timeout, arg1, ...)創(chuàng)建單次定時(shí)器,到時(shí)后執(zhí)行回調(diào)函數(shù)。

(2)sys.timerLoopStart(func, timeout, arg1, ...)創(chuàng)建循環(huán)定時(shí)器,周期性執(zhí)行回調(diào)函數(shù)。

(3)sys.timerStop(timerId)停止指定 ID 的定時(shí)器。

(4)sys.timerStopAll(func)停止所有與指定回調(diào)函數(shù)相關(guān)的定時(shí)器。

(5)sys.timerIsActive(timerId)判斷定時(shí)器是否處于激活狀態(tài)。

7.1.4 消息通信

(1)sys.publish(topic, arg1, ...)發(fā)布(廣播)一個(gè)消息,喚醒等待該 topic 的任務(wù)或觸發(fā)訂閱回調(diào)。

(2)sys.subscribe(topic, callback)訂閱指定 topic 的消息,消息到來時(shí)自動(dòng)執(zhí)行回調(diào)。

(3)sys.unsubscribe(topic, callback)取消訂閱。

7.1.5 主循環(huán)控制

sys.run()功能: 是 LuatOS 的調(diào)度器,是系統(tǒng)主循環(huán),調(diào)度所有注冊(cè)的任務(wù)和定時(shí)器。

7.1.6 典型用法示例

wKgZO2jWWwaAKFFTAANU3gIj2mM923.png

7.1.7 任務(wù)與協(xié)程管理

(1)sys.taskInitEx(func, taskName, cbFun, ...)

功能:創(chuàng)建一個(gè)具名任務(wù)線程,并注冊(cè)任務(wù)函數(shù)和非目標(biāo)消息回調(diào)。

(2)sys.taskDel(taskName)

功能:刪除由taskInitEx創(chuàng)建的任務(wù)線程,釋放資源。

7.1.8 消息通信機(jī)制

(1)sys.waitMsg(taskName, target, timeout)

功能:等待接收一個(gè)目標(biāo)消息(可指定超時(shí)),任務(wù)會(huì)掛起直到收到目標(biāo)消息或超時(shí)。

(2)sys.sendMsg(taskName, target, arg2, arg3, arg4)

功能:向目標(biāo)任務(wù)發(fā)送一個(gè)消息,可攜帶最多 4 個(gè)參數(shù)。

(3)sys.cleanMsg(taskName)

功能:清除指定任務(wù)的消息隊(duì)列,防止消息堆積。

7.1.9 sys.run() 怎么實(shí)現(xiàn)多個(gè)任務(wù)的協(xié)同工作

sys.run()函數(shù)的實(shí)現(xiàn)過程是這樣的:

1, 查看消息隊(duì)列里面是否有未處理的消息, 如果有,就根據(jù)消息的處理類型,調(diào)用回調(diào)函數(shù)或者是喚醒對(duì)應(yīng)的任務(wù)進(jìn)行消息處理;

2, 等待底層 RTOS 操作系統(tǒng)的定時(shí)器消息;等待的過程,就是低功耗的過程;

3, 定時(shí)器消息等到之后, 調(diào)用定時(shí)器回調(diào)函數(shù)或者喚醒對(duì)應(yīng)的任務(wù)。

4, 循環(huán) 1-3 步。

通過以上過程,我們可以看到,這個(gè) LuatOS 系統(tǒng), 大多數(shù)時(shí)間都是在等待底層 RTOS 操作系統(tǒng)的定時(shí)器消息,在等待期間,系統(tǒng)是可以處于低功耗休眠狀態(tài)的。

當(dāng)任務(wù)的時(shí)延很短, 或者定時(shí)器非常頻繁,或者是消息太多,是會(huì)影響到系統(tǒng)的低功耗性能的。

八、怎么封裝一個(gè) LuatOS 的軟件功能模塊

在 LuatOS 中封裝功能模塊為單獨(dú) Lua 文件的標(biāo)準(zhǔn)做法:

1、新建一個(gè) Lua 文件,定義一個(gè) table,比如名字為 myflib,所有對(duì)外接口作為其字段。

2、用 local 修飾內(nèi)部變量和函數(shù),實(shí)現(xiàn)信息隱藏。

3、定義 myflib 的成員變量,成員函數(shù),用作對(duì)外的接口。

4、文件末尾用returnmyflib ,導(dǎo)出模塊 table。

5、外部的文件,用require("模塊名")加載和復(fù)用模塊。

這樣可以讓你的功能模塊獨(dú)立、可維護(hù)、易擴(kuò)展,是 Lua 及 LuatOS 推薦的開發(fā)范式。

代碼示例:

wKgZPGjWW4SAYcqyAAb3zMmZzHE881.png

九、LuatOS 的核心庫和擴(kuò)展庫

LuatOS 在 Lua 5.3 版本的基礎(chǔ)上, 封裝了 74 個(gè)核心庫,17 個(gè)擴(kuò)展庫,提供了極其強(qiáng)大的通信和硬件的開發(fā)功能。

9.1 LuatOS 核心庫

LuatOS 核心庫,提供了 LuatOS 系統(tǒng)的核心功能。 不同的硬件型號(hào),支持不同的核心庫的子集。

LuatOS 的核心庫, 是不需要用戶 require,可以直接調(diào)用的。

780EPM 對(duì)這些核心庫的支持情況參見如下鏈接:

http://docs.openluat.com/air780epm/common/lutos_coreapilist/

9.2 LuatOS 擴(kuò)展庫

除了用戶可以直接使用的核心庫之外, LuatOS 還提供了豐富的擴(kuò)展庫。

使用擴(kuò)展庫,需要用戶在代碼里面做 require 動(dòng)作,Luatools 看到 require 關(guān)鍵字后,會(huì)把用到的擴(kuò)展庫合并入燒錄包,一起燒錄到硬件里面。

如果用戶不做 require 的動(dòng)作, luatools 就不會(huì)合并這個(gè)擴(kuò)展庫的代碼。

所有的擴(kuò)展庫,都是用 Lua 代碼實(shí)現(xiàn)的。

當(dāng)前 LuatOS 已經(jīng)支持的擴(kuò)展庫參見如下鏈接:

http://docs.openluat.com/air780epm/common/lutos_coreapilist/

十、LuatOS 實(shí)際工程代碼解讀

780EPM 1.3 開發(fā)板的出廠固件代碼, 是一個(gè)實(shí)際的 LuatOS 開發(fā)的簡單案例。

代碼的位置在:

780EPM 開發(fā)板 V1.3 出廠固件源碼

這個(gè)固件分為幾個(gè)部分:

1, 780EPM core 固件: 目前最新的固件是 2005 版本,后續(xù)更新的固件版本也可以繼續(xù)使用;

最新的 780EPM 固件在這里下載:

http://docs.openluat.com/air780epm/luatos/firmware/version/

2,管腳復(fù)用描述文件

wKgZO2jWXC6AXcg7AABBziyXB5U106.png

3, 資源圖片

實(shí)現(xiàn)開機(jī)固件所需的圖片。(詳見780EPM 開發(fā)板 V1.3 出廠固件源碼的pic目錄下的圖片 )

4, 實(shí)現(xiàn)腳本。

下面重點(diǎn)講解一下腳本實(shí)現(xiàn)的邏輯。

10.1 編碼要求

為了降低用戶理解成本,這份開機(jī)固件的代碼有如下要求:

(1)不允許用云編譯擴(kuò)大腳本區(qū),不允許用云編譯擴(kuò)大文件系統(tǒng),保持腳本 + 資源總體尺寸不能大于 256K 字節(jié);

(2)main.lua 作為邏輯主線,其他的功能代碼封裝成子模塊,提供成員函數(shù),也可以提供成員變量, 被 main.lua 調(diào)用;

(3)不允許使用匿名函數(shù);

10.2 已實(shí)現(xiàn)功能

如下代碼,已經(jīng)實(shí)現(xiàn)了如下功能:

(1)主界面九宮格的按鍵切換,

(2)長按進(jìn)入具體功能界面;再長按回到主界面;

(3)圖片顯示功能,

(4)攝像頭預(yù)覽,

(5)俄羅斯方塊,

(6)天氣數(shù)據(jù)獲取,并顯示不同的天氣圖標(biāo);

使用的是 780EPM 默認(rèn) 2005 固件,不需要擴(kuò)大文件系統(tǒng)和代碼區(qū)。

其中,airlcd.lua, camera780epm_simple.lua, russia.lua,statusbar.lua, 分別用 table 的方式,封裝了 LCD 的參數(shù)初始化,camera 的初始化,預(yù)覽,退出,俄羅斯方塊的初始化,更新數(shù)據(jù),響應(yīng)按鍵等事件。

在 main.lua 調(diào)用這些封裝好的 table 的函數(shù)即可,不需要過度關(guān)心子模塊的實(shí)現(xiàn)細(xì)節(jié)。

10.3 待實(shí)現(xiàn)功能

(1)以太網(wǎng) LAN

(2)以太網(wǎng) WAN

(3)硬件自檢

(4)modbus TCP

(5)modbus RTU

(6)CAN 總線

10.4 main.lua 解讀

10.4.1 系統(tǒng)初始化

整個(gè)系統(tǒng),做了兩個(gè)全局初始化:

1, 看門狗的初始化,wdt.Init()防止系統(tǒng)被某個(gè)任務(wù)異常占用 CPU 讓系統(tǒng)鎖死;

2, LCD 的初始化: airlcd.lcd_init("AirLCD_0001"),其中 AirLCD_0001 是合宙 LCD 配件的型號(hào)。

10.4.2 業(yè)務(wù)主循環(huán)

UITask() 函數(shù), 是 main.lua 啟動(dòng)之后的主循環(huán)。

在 UITask 函數(shù)里面,先做按鍵的初始化之后,就無限循環(huán)的不斷調(diào)用三個(gè)函數(shù):keypressed, update, draw。

其中,keypressed 是查看按鍵是否有待處理的事件;

update 是更新業(yè)務(wù)數(shù)據(jù);

draw 是更新 UI 畫面。

10.4.3 按鍵事件的處理

由于 780EPM 開發(fā)板只有三個(gè)按鍵: 開機(jī)鍵,boot 鍵,reset 鍵。

reset 鍵無法被捕獲事件,只能復(fù)位硬件,所以固件只能處理 開機(jī)鍵和 boot 鍵的事件。

boot 按鍵是一個(gè)特殊的 GPIO, 編號(hào)為 GPIO0。

開機(jī)鍵也是個(gè)特殊的 GPIO, 編號(hào)為 GPIO.PWR_KEY。

在 KeyInit()這個(gè)函數(shù), 分別配置了 gpio.PWR_KEY 和 GPIO0 為雙邊沿中斷,中斷處理函數(shù)分別為 PowerInterrupt 和 BootInterrupt。

根據(jù)開發(fā)板的原理圖,開機(jī)鍵初始電平是上拉為高電平,boot 鍵初始電平為下拉低電平。

在 PowerInterrupt() 和 BootInterrupt()這兩個(gè)函數(shù)的處理邏輯是類似的,都是計(jì)算按下和抬起的時(shí)間間隔,從而判斷是短按還是長按,然后給 key 這個(gè)全局變量賦值。

key 是字符串類型,是一個(gè)比較關(guān)鍵的變量,根據(jù) key 的值不同, main.lua 進(jìn)入不同的功能。

這個(gè)邏輯是在 keypressed 來實(shí)現(xiàn)。

在 keypressed() 函數(shù)里面,檢查 key 變量的值,然后做不同的處理。

在主界面, 處理 "main" 和 "enter"這兩個(gè)值,分別是切換按鈕加亮顯示,以及進(jìn)入具體功能按鈕;

在具體功能界面, enter 按鍵時(shí)間會(huì)返回主界面;

在俄羅斯方塊界面, 有 5 種按鍵:

(1)right: 短按 boot, 往右移動(dòng)方塊;

(2) left:短按開機(jī),往左移動(dòng)方塊;

(3)up:長按 boot,旋轉(zhuǎn)方塊;

(4)fast:長按開機(jī),快速下落;

(5)quit:超長按開機(jī),退出游戲回到主界面。

10.4.4 UI 界面的循環(huán)刷新

在 draw 函數(shù)里面刷新界面。

當(dāng)需要把繪圖權(quán)限交給其他的功能模塊的時(shí)候, 根據(jù)情況做不同的處理:

(1)俄羅斯方塊的刷新函數(shù)就是自己實(shí)現(xiàn) drawrus 函數(shù), draw 函數(shù)調(diào)用 drawrus 函數(shù)刷新屏幕;

(2)攝像頭預(yù)覽功能接管了屏幕后,draw 函數(shù)判斷當(dāng)前是攝像頭預(yù)覽功能,就直接退出,如果判斷不是攝像頭,再繼續(xù)處理刷新任務(wù);

(3)在刷新之前,調(diào)用 update 函數(shù)更新用于刷新的關(guān)鍵數(shù)據(jù)。

10.4.5 管理當(dāng)前功能狀態(tài)機(jī)

有兩個(gè)關(guān)鍵變量:

cur_sel: 整數(shù),范圍是 1-9, 當(dāng)前選擇的九宮格是哪個(gè);

cur_fun: 字符串,10 種值:

記錄當(dāng)前已經(jīng)進(jìn)入的界面是主界面,還是 9 個(gè)之一。

“main”: 主界面;

另外 9 個(gè)界面用一個(gè)數(shù)組記錄,并根據(jù) cur_sel 賦值給到 cur_fun。

local funlist = {

"picshow", "camshow","russia",

"LAN", "WAN","selftest",

"modbusTCP","modbusRTU","CAN"

}

cur_sel 和 cur_fun,結(jié)合 key 的值,組成了整個(gè)的邏輯切換,可以決定該進(jìn)入什么軟件功能,該顯示什么界面。

理解了 cur_sel, cur_fun, key 這三個(gè)變量的運(yùn)用,就可以看明白整個(gè)軟件的邏輯。

10.5 總結(jié)

這份 780EPM 的開發(fā)板的出廠固件的代碼,展示了一個(gè)完整的 LuatOS 工程的基本實(shí)現(xiàn)的方法。

腳本文件一共只有 10 個(gè),全部加一起只有 30k 字節(jié),1000 行代碼,實(shí)現(xiàn)了 9 宮格界面,電量,信號(hào)強(qiáng)度,天氣的狀態(tài)欄顯示,包括俄羅斯方塊在內(nèi)的多種功能的演示。

這份代碼后續(xù)還會(huì)繼續(xù)更新,并且都不會(huì)采用非常高難度的編碼技巧,只需要用最簡單的編程邏輯就可以實(shí)現(xiàn)相對(duì)復(fù)雜的業(yè)務(wù)邏輯。

今天的內(nèi)容就分享到這里了~


審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5177

    文章

    20003

    瀏覽量

    325583
  • LuatOS
    +關(guān)注

    關(guān)注

    0

    文章

    119

    瀏覽量

    2509
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    LuatOS腳本開發(fā)入門嵌入式運(yùn)行框架全解析!

    想搞懂LuatOS如何運(yùn)行Lua腳本?本文深入剖析其嵌入式運(yùn)行框架,涵蓋虛擬機(jī)加載、任務(wù)協(xié)程、系統(tǒng)初始化等關(guān)鍵環(huán)節(jié),適合初學(xué)者。 一、LuatOS
    的頭像 發(fā)表于 09-26 17:45 ?140次閱讀
    <b class='flag-5'>LuatOS</b><b class='flag-5'>腳本</b><b class='flag-5'>開發(fā)</b><b class='flag-5'>入門</b>:<b class='flag-5'>嵌入式</b>運(yùn)行<b class='flag-5'>框架</b>全解析!

    新一代嵌入式開發(fā)平臺(tái) AMD嵌入式軟件和工具2025.1版現(xiàn)已推出

    AMD 2025.1 版嵌入式軟件和工具是面向新一代嵌入式系統(tǒng)開發(fā)而打造的綜合平臺(tái),全面加速概念構(gòu)想到部署落地。 2025.1 版嵌入式軟件和工具的新 增功能 AMD
    的頭像 發(fā)表于 08-20 09:15 ?3413次閱讀

    嵌入式開發(fā)就業(yè)還有前景嗎?

    嵌入式的發(fā)展前景如何呢?隨著科技的飛速發(fā)展,嵌入式開發(fā)在各個(gè)領(lǐng)域的應(yīng)用越來越廣泛,因此嵌入式開發(fā)的就業(yè)前景也備受關(guān)注。以下將從智能家居、醫(yī)療設(shè)備、工業(yè)控制、汽車電子、航空航天等方面,探討嵌入式
    發(fā)表于 07-16 10:47

    嵌入式開發(fā),如何選擇適合的系統(tǒng)?

    嵌入式ARM開發(fā)中,面對(duì)多種操作系統(tǒng)的選擇,如何做出最適合項(xiàng)目的決策?本文將為您梳理常見系統(tǒng)的特性,幫助您快速了解它們的優(yōu)缺點(diǎn),以便更好地選擇適合的系統(tǒng)。引言在
    的頭像 發(fā)表于 05-20 11:32 ?753次閱讀
    <b class='flag-5'>嵌入式開發(fā)</b>,如何<b class='flag-5'>選擇</b>適合的系統(tǒng)?

    嵌入式開發(fā)入門指南:從零開始學(xué)習(xí)嵌入式

    隨著物聯(lián)網(wǎng)、智能硬件的發(fā)展,嵌入式開發(fā)成為熱門技能之一。以下將為初學(xué)者提供一份詳細(xì)的嵌入式開發(fā)入門指南,涵蓋學(xué)習(xí)路徑、必備工具、推薦資源等內(nèi)容。 1. 嵌入式系統(tǒng)的定義與應(yīng)用
    發(fā)表于 05-15 09:29

    嵌入式開發(fā):高門檻的系統(tǒng)性工程與 996 的行業(yè)困局

    嵌入式開發(fā)的門檻,往往被培訓(xùn)機(jī)構(gòu)和表象所掩蓋。許多人誤以為 “用 C 語言寫個(gè)跑在 ARM 上的程序” 就是嵌入式,實(shí)則連皮毛都未觸及。真正的嵌入式開發(fā)是硬件與軟件深度融合的系統(tǒng)性工程,需跨越三重壁壘
    的頭像 發(fā)表于 04-09 11:06 ?544次閱讀
    <b class='flag-5'>嵌入式開發(fā)</b>:高門檻的系統(tǒng)性工程與 996 的行業(yè)困局

    2025年嵌入式開發(fā)趨勢:新技術(shù)如何影響你的工作?

    嵌入式開發(fā)的世界正在快速變化,新技術(shù)的崛起正悄然改變硬件設(shè)計(jì)、軟件架構(gòu)以及開發(fā)方式。無論你是剛入門的新手,還是經(jīng)驗(yàn)豐富的工程師,理解這些趨勢不僅能幫助你應(yīng)對(duì)技術(shù)變革,還能在職業(yè)發(fā)展中占據(jù)主動(dòng)
    的頭像 發(fā)表于 03-14 17:36 ?2334次閱讀
    2025年<b class='flag-5'>嵌入式開發(fā)</b>趨勢:新技術(shù)如何影響你的工作?

    BlackBerry QNX推出通用嵌入式開發(fā)平臺(tái)

    BlackBerry有限公司(紐交所代碼:BB;多倫多證券交易所代碼:BB)旗下部門QNX今日宣布推出QNX 通用嵌入式開發(fā)平臺(tái)(General Embedded Development
    的頭像 發(fā)表于 03-11 16:04 ?834次閱讀

    AI來襲!嵌入式開發(fā)者該如何應(yīng)對(duì)轉(zhuǎn)型?

    AI正在改變世界!從自動(dòng)駕駛到智能家居,從工業(yè)自動(dòng)化到語音助手,各行各業(yè)都在朝著智能化方向邁進(jìn)。對(duì)于嵌入式開發(fā)者來說,AI的到來既是一次前所未有的挑戰(zhàn),也是一個(gè)充滿機(jī)遇的時(shí)代。過去,我們專注于硬件
    的頭像 發(fā)表于 03-04 14:41 ?1248次閱讀
    AI來襲!<b class='flag-5'>嵌入式開發(fā)</b>者該如何應(yīng)對(duì)轉(zhuǎn)型?

    代碼+案例+生態(tài):武漢芯源半導(dǎo)體CW32嵌入式開發(fā)實(shí)戰(zhàn)正式出版

    尊敬的各位電子工程師、嵌入式開發(fā)愛好者們:大家好!今天,我們懷著無比激動(dòng)與自豪的心情,向大家宣布一個(gè)重大喜訊——武漢芯源半導(dǎo)體的單片機(jī)CW32正式出書啦!《基于ARMCortex-M0+的CW32
    的頭像 發(fā)表于 03-03 15:15 ?922次閱讀
    代碼+案例+生態(tài):武漢芯源半導(dǎo)體CW32<b class='flag-5'>嵌入式開發(fā)</b>實(shí)戰(zhàn)正式出版

    如何成為嵌入式開發(fā)工程師?

    如何成為嵌入式開發(fā)工程師? 成為嵌入式開發(fā)工程師通常需要掌握一系列技能和知識(shí),并且在實(shí)踐中不斷積累經(jīng)驗(yàn)。以下是一些基本步驟和建議: 1. 基礎(chǔ)教育:- 獲取電子工程、計(jì)算機(jī)科學(xué)或相關(guān)領(lǐng)域的學(xué)位
    發(fā)表于 02-19 10:39

    盤點(diǎn)工程師常用的嵌入式開發(fā)工具

    嵌入式開發(fā)涉及硬件與軟件的緊密結(jié)合,開發(fā)工具的選擇對(duì)效率和質(zhì)量至關(guān)重要。以下是嵌入式開發(fā)中常用的工具分類及代表性工具,涵蓋開發(fā)全流程: 1.
    的頭像 發(fā)表于 02-07 10:06 ?3269次閱讀

    哪些專業(yè)適合學(xué)習(xí)嵌入式開發(fā)?

    哪些專業(yè)適合學(xué)習(xí)嵌入式開發(fā)? 在傳統(tǒng)行業(yè)不斷被取代的當(dāng)下,對(duì)于大學(xué)生、從業(yè)者而言,選擇一個(gè)熱門主流的職業(yè)方向,是未來發(fā)展的關(guān)鍵因素之一。 嵌入式作為當(dāng)下高薪崗位之一,轉(zhuǎn)行、轉(zhuǎn)專業(yè)的人群已經(jīng)占據(jù)求職
    發(fā)表于 01-03 13:44

    嵌入式開發(fā)必備-RK3562演示Linux常用系統(tǒng)查詢命令(上)觸覺智能出品

    實(shí)用干貨!嵌入式開發(fā)學(xué)習(xí)必備-RK3562演示Linux常用系統(tǒng)查詢命令(上)關(guān)注觸覺智能持續(xù)為大家?guī)砀?b class='flag-5'>嵌入式開發(fā)內(nèi)容
    的頭像 發(fā)表于 12-31 10:51 ?117次閱讀
    <b class='flag-5'>嵌入式開發(fā)</b>必備-RK3562演示Linux常用系統(tǒng)查詢命令(上)觸覺智能出品

    如何使用 RISC-V 進(jìn)行嵌入式開發(fā)

    、準(zhǔn)備開發(fā)環(huán)境 選擇開發(fā)板 :RISC-V架構(gòu)的嵌入式開發(fā)板有多種選擇,如GD32VF103R-START、飛凌
    的頭像 發(fā)表于 12-11 17:32 ?2525次閱讀