chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

LuatOS框架的使用(上)

合宙LuatOS ? 來源:合宙LuatOS ? 作者:合宙LuatOS ? 2026-01-27 19:38 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

在資源受限的物聯(lián)網(wǎng)終端設備中,如何實現(xiàn)快速開發(fā)與穩(wěn)定運行是關鍵挑戰(zhàn)。LuatOS框架通過將Lua語言與底層硬件抽象層深度融合,提供了一套簡潔高效的開發(fā)范式。本文將圍繞LuatOS框架的使用展開,從環(huán)境搭建、模塊調用到任務調度,全面解析其開發(fā)流程與最佳實踐。

本篇文章主要講LuatOS 框架;LuatOS 框架是整個 LuatOS 開發(fā)中最基礎也是最核心的內容,無論使用 LuatOS 開發(fā)什么功能,都會用到它;

LuatOS 框架主要包含以下幾部分:

1、LuatOS 軟件的構成

2、LuatOS 開發(fā)環(huán)境的說明

3、LuatOS 用戶腳本程序的運行機制

4、LuatOS 的 sys 核心庫中任務、消息、定時器和調度器功能的使用

一、初步認識 LuatOS 開發(fā)

現(xiàn)在我們開始正式進入 LuatOS 開發(fā)的世界。

1.1 Lua 語言介紹

用戶基于 LuatOS 進行軟件二次開發(fā),使用的編程語言為 Lua 語言,Lua 語言相對來說還是比較簡單的,大家只要有其他編程語言的開發(fā)經(jīng)驗,就不用在 Lua 語言學習上花費太多時間,花個一兩天大概看一遍就夠了;后續(xù)可以邊看 LuatOS demo,邊學習 Lua 語言,這樣學習效率更高。

1.2 LuatOS 軟件的構成

1.2.1 LuatOS 軟件架構

wKgZO2l4Q-GAVeINAAHY2lJJFtM876.png

LuatOS 軟件的總體架構參考上圖,從二次開發(fā)的角度來看,主要理解幾個概念:內核固件,標準庫,核心庫,擴展庫,demo,project,用戶開發(fā)的 LuatOS 項目應用軟件;

接下來我結合架構圖逐一分析下這幾個概念(為了演示方便,在這里我打開兩個瀏覽器窗口,左邊是框架圖,右邊是文字介紹)。

1.2.2 LuatOS 內核固件

1、內核固件(又叫固件,底層固件,或者 core),我們會針對每種硬件編譯好并且對外發(fā)布內核固件

2、內核固件文件以 soc 做為后綴,例如:

(1) LuatOS-SoC_V2008_Air8000_1.soc 表示Air8000 硬件的V2008版本的1 號LuatOS 固件文件;

(2) LuatOS-SoC_V2008_Air780EHM.soc 表示Air780EHM 硬件的V2008版本的 LuatOS 固件文件;

(3) LuatOS-SoC_V1004_Air8101.soc 表示Air8101 硬件的V1004版本的 LuatOS 固件文件;

3、內核固件包含主芯片系統(tǒng)平臺層(這里有一個嵌入式操作系統(tǒng),目前比較流行的是 FreeRTOS,接下來本文用到的嵌入式操作系統(tǒng)都會以 FreeRTOS 為例來說明),LuatOS 適配層,Lua 虛擬機,Lua 標準庫,LuatOS 核心庫幾部分,除了主芯片系統(tǒng)平臺層的源碼沒有開放外,其余四部分的源碼全部開放;

4、內核固件中和用戶軟件二次開發(fā)息息相關的兩部分是 Lua 標準庫和 LuatOS 核心庫,接下來我們看下這兩部分內容;

1.2.3 Lua 標準庫

Lua 標準庫是 Lua 語言內置的一組核心功能模塊,它們?yōu)?Lua 提供了基礎編程能力。標準庫的設計遵循 Lua 的“小而精”哲學,僅包含最必要的功能。

Lua 標準庫已經(jīng)編譯到了 LuatOS 內核固件中,用戶無法修改,可以直接使用。

主要包含以下幾種庫

基礎庫(Basic Library):支持 collectgarbage(垃圾回收)、_G(全局變量表)、ipairs(迭代數(shù)組元素)、pairs(迭代鍵值對)、tostring(轉換為字符串)、tonumber(轉換為數(shù)字)、type(獲取類型名)等功能函數(shù);

協(xié)程庫(Coroutine Library):支持 coroutine.create(創(chuàng)建協(xié)程)、coroutine.yield(掛起協(xié)程)、coroutine.resume(恢復協(xié)程)、coroutine.status(獲取協(xié)程狀態(tài))、coroutine.running(獲取正在運行的協(xié)程)等功能函數(shù);

字符串處理庫(String Library):支持 string.len(獲取字符串長度)、string.match(字符串模式匹配)、string.byte(獲取字符的 ascii 碼)、string.char(獲取 ascii 碼對應的字符)等功能函數(shù);

表處理庫(Table Library):支持 table.insert(插入元素)、table.remove(移除元素)、table.concat(將表中的元素連接為一個字符串)、table.unpack(解包表中的元素為多個返回值)等功能函數(shù);

此外還有數(shù)學庫,輸入輸出庫,操作系統(tǒng)庫,調試庫等功能模塊,在這里我就不逐一列舉了;

LuatOS 中使用的 Lua 是 5.3 版本

1.2.4 LuatOS 核心庫

LuatOS 核心庫是 專為嵌入式設備設計的 Lua 運行時擴展庫,針對物聯(lián)網(wǎng)(IoT)和資源受限環(huán)境(如 MCU)進行了深度優(yōu)化和功能擴展。

LuatOS 核心庫已經(jīng)編譯到了 LuatOS 內核固件中,用戶無法修改,可以直接使用。

目前支持 74 個核心庫,核心庫中的所有功能模塊,我們在后續(xù)文章中都會逐一講解;

在核心庫中有一個 sys 庫,在本章中會重點介紹。

1.2.5 LuatOS 擴展庫

LuatOS 擴展庫是 在核心庫基礎上針對特定場景或硬件功能提供的附加功能模塊,用于增強 LuatOS 在物聯(lián)網(wǎng)(IoT)和嵌入式系統(tǒng)中的能力。

LuatOS 擴展庫是用 Lua 語言實現(xiàn)的功能模塊

用戶開發(fā)項目軟件時,需要主動加載擴展庫文件,才能使用。

目前支持 19 個擴展庫

擴展庫中的部分功能模塊,我們在后續(xù)文章中都會進行講解。

1.2.6 LuatOS demo

LuatOS demo 是在 Lua 標準庫、LuatOS 核心庫、LuatOS 擴展庫的基礎上,我們針對獨立的應用場景,開發(fā)的示例代碼合集。

展示了 Lua 標準庫、LuatOS 核心庫、LuatOS 擴展庫的實際應用方法,可以幫助開發(fā)者快速驗證硬件功能、理解各種庫的 API 使用場景,開發(fā)者可以參考這些 demo 快速開發(fā)自己的項目。

1.2.7 LuaOS project

LuatOS project 是基于功能相對完整的硬件(例如整機開發(fā)板,硬件上集成了模組或者工業(yè)引擎,lcd,tp,攝像頭,485 接口,矢量字庫芯片,tf 卡,以太網(wǎng)等),開發(fā)的完整項目軟件。開發(fā)者可以參考這種硬件和項目軟件,更加快速地開發(fā)自己的項目。

1.2.8 用戶開發(fā)的 LuaOS 項目應用軟件

用戶開發(fā)的 LuatOS 項目應用軟件,是指開發(fā)者基于自己的實際項目需求(可以是演示 demo 需求,也可以是真實的項目需求),使用 Lua 腳本語言,調用 Lua 標準庫、LuatOS 核心庫、LuatOS 擴展庫編寫的項目應用腳本代碼。

用戶開發(fā)的 LuatOS 項目應用軟件,我們開發(fā)的 LuatOS demo, LuatOS project,這三種軟件都屬于 LuatOS 項目應用軟件,區(qū)別在于 demo 和 project 是我們開發(fā)的,另外一種是我們的用戶開發(fā)的。

1.3 LuatOS 項目應用軟件(hello_luatos)開發(fā)調試過程

了解了 LuatOS 軟件構成之后,我們接下來以一個 hello_luatos 項目為例,先總體看下用戶如何開發(fā)調試自己的項目應用軟件。

再來回顧一下 LuatOS 軟件架構圖:

用戶開發(fā)的項目應用軟件,位于架構圖中的最上層(也就是黃色背景的這一層);

開發(fā)用戶項目應用軟件時,需要調用 Lua 標準庫、LuatOS 核心庫、LuatOS 擴展庫來實現(xiàn)。

hello_luatos 項目應用軟件的需求為:每隔一秒鐘通過日志輸出一次 Hello, LuatOS。

1.3.1 根據(jù)項目需求編寫項目應用軟件代碼

開發(fā)項目軟件使用的編程語言為 Lua 腳本語言,編寫的腳本文件后綴為.lua;

代碼編寫工具推薦使用 Visual Studio Code;

hello_luatos 的項目軟件代碼我已經(jīng)編寫好了,源碼已經(jīng)提交到:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/luatos_framework/hello_luatos

打開這個路徑,可以看到,一共包含如下三個文件:

wKgZPGl4WkuAXLyRAAAQBJww2vw844.png

在這里我們先簡單的看下這三個文件的核心內容,詳細內容在后續(xù)章節(jié)會細細講解。

1、main.lua:這個文件的作用大家可以看文件頭注釋,是一個入口文件,類似于 C 語言的 main 函數(shù),LuatOS 項目軟件代碼是從 main.lua 開始執(zhí)行的,所以 main.lua 文件必須存在,并且文件名也必須是 main.lua

wKgZO2l4WiuAO034AAAqNPba9Rs898.png

在 main.lua 中還有一行代碼,如下圖所示,加載 hello_luatos 應用功能模塊,也就是說執(zhí)行到這行代碼時,就會加載并且運行 hello_luatos.lua 這個文件;和 main.lua 不同的是,hello_luatos.lua 的文件名可以根據(jù)自己的應用功能模塊含義自定義(只要定義的文件名和 Lua 標準庫、LuatOS 核心庫、LuatOS 擴展庫不要重復就行),文件名修改后,在 main.lua 中 require 新的文件名即可;例如文件名修改為 hello_air8000.lua,在 main.lua 中 require "hello_air8000"即可。

wKgZPGl4WomAW-pyAAAIJORqHNo599.png

2、hello_luatos.lua:還是先看下圖中的這個文件頭注釋,重點看下選中的幾行文字描述:

wKgZO2l4WsWANbB3AAA74f1NahA476.png

為什么單獨創(chuàng)建一個 hello_luatos.lua 去實現(xiàn)每隔一秒打印一次Hello, LuatOS的業(yè)務功能呢? 這個是為了應用功能設計模塊化,不同功能模塊之間解耦,邏輯清晰,代碼閱讀成本低;這個是一個很好的設計思想,希望大家以后開發(fā)項目軟件都要遵循。

3、readme.md:這個文件是對當前 demo 項目軟件的使用說明,從功能概述、硬件環(huán)境、軟件環(huán)境,操作步驟幾方面說明了當前這個 demo 項目如何使用。

wKgZO2l4WyaAT1kUAADWSlY9OG4295.png

1.3.2 LuatOS 軟件的運行載體

開發(fā)好 hello_luatos 的項目應用軟件代碼后,這個項目完整的 LuatOS 軟件也就完成了;

完整的 LuatOS 軟件 = LuatOS 內核固件 + LuatOS 擴展庫 + hello_luatos 的項目應用軟件;

完整的 LuatOS 軟件需要一個運行載體,才能看到軟件的運行效果;

目前提供了兩類運行載體:

1、硬件核心板或者開發(fā)板:例如 Air8000 的核心板和開發(fā)板,Air780EHM/EHV/EGH/EPM 的核心板和開發(fā)板,Air8101 的核心板和開發(fā)板; 本篇中我們將使用 Air8000 的核心板來運行 LuatOS 軟件進行演示;

2、PC 模擬:直接可以在電腦上使用 LuatOS 模擬器來運行,使用模擬器運行有一些限制,模擬器支持的功能并不完整,例如和外設有關的功能,lcd,字庫等可能并不支持,但是一些純軟件的功能,例如 LuatOS 運行框架,網(wǎng)絡應用等還是可以模擬運行的。所以本講的 LuatOS 運行框架,我們也會使用 PC 模擬器來進行演示,這樣可以節(jié)省硬件載體燒錄軟件所花費的時間。

1.3.3 燒錄 完整的 hello_luatos 項目 LuatOS 軟件 到 Air8000 核心板 運行

我們先來看下,完整的 hello_luatos 項目的 LuatOS 軟件第一種運行載體(以 Air8000 核心板為例)上的運行效果。

要將完整的 LuatOS 軟件燒錄到 Air8000 核心板中,并且觀察軟件的運行效果,需要用到 Luatools 下載調試工具;

在這里我就不詳細說明 Luatools 的用法了,只重點說明一下燒錄 hello_luatos 這個完整的項目軟件過程中涉及到的一些關鍵操作:

(1)首先我們要準備好一塊 Air8000 核心板、一根帶有數(shù)據(jù)傳輸功能的 type-c 接口的 usb 數(shù)據(jù)線、一個 WINDOWS10 以及以上版本操作系統(tǒng)的電腦;

(2)Air8000 核心板正面的供電/充電撥動開關 撥到供電一端,背面的USB ON/USB OFF撥動開關 撥到 USB ON 一端;

(3)type-c 接口的 usb 數(shù)據(jù)線連接 Air8000 核心板和電腦;

(4)打開 Luatools,勾選 4G 模塊 USB 打印,點擊項目管理測試,創(chuàng)建一個項目,選擇好 LuatOS 內核固件,hello_luatos 的應用軟件腳本代碼,勾選 USB BOOT 下載,然后點擊 下載底層和腳本 按鈕;

wKgZPGl4XEaAIUYQAAB7U_VsqMU825.pngwKgZO2l4XGOAMhvVAABqwfWp8Oo465.png


此時如果 Air8000 核心板已經(jīng)處于開機運行狀態(tài),則等一段時間,會自動開始燒錄軟件; 如果處于開機運行狀態(tài)下,沒有自動燒錄軟件,則用手按住核心板正面的下載按鈕不放開,然后再按一下核心板的復位按鈕就可以開始燒錄軟件; 如果沒有處于開機運行狀態(tài),則用手按住核心板正面的下載按鈕不放開,然后再長按開機按鈕 2 秒,就可以開始燒錄軟件; 出現(xiàn)如下圖所示的下載完成狀態(tài)就表示燒錄成功:

wKgZPGl4XI-AR61LAAAhZXNmixI697.png

下載成功后,Air8000 核心板自動開機運行,在 Luatools 主界面的日志窗口就可以看到運行日志,我們可以看到大約每隔 1 秒鐘,日志輸出一次 Hello, LuatOS

wKgZO2l4XLaAN-N1AAA8WXHJ1qg574.png

下面我來實際演示這個過程。

1.3.4 使用 PC 模擬器 運行 完整的 hello_luatos 項目 LuatOS 軟件

打開 Luatools,點擊 賬戶-> 打開資源下載 的菜單

wKgZO2l4XOmAc8o1AAAeicw2Uw0745.png

會彈出 Luatools 資源管理窗口

wKgZO2l4XQiACEHrAAB7QQ8sW6E116.png

勾選公共資源->LuatOS 的 PC 模擬器->VXXXX 版本下的默認資源,然后點擊右上角的開始下載(非刷機)按鈕;

下載成功后,點擊右上角的打開本地資源目錄按鈕,resource 目錄下的LuatOS_PC就是 LuatOS 的 PC 模擬器,找到最新版本的壓縮包,解壓縮之后,打開解壓縮后的目錄,有如下文件:

wKgZO2l4XVSAFbM3AAANOOFA66w059.png

其中 luatos-pc.exe 就是模擬器的可執(zhí)行文件,luatos.bat 就是運行模擬器的批處理文件。

在這個目錄下,創(chuàng)建一個 cmd 命令行窗口的快捷方式,如下圖所示:

wKgZPGl4XXaAErqRAAAQ4rtJ9VE681.png

雙擊 cmd 命令行窗口,然后輸入下面一行命令,運行 luatos 批處理文件,同時輸入要運行的 luatos 項目配置文件

luatos --llt=H:Luatoolsprojectluatos_framework_hello_luatos_Air8000.ini

然后按回車鍵,就可以運行 hello_luatos 項目軟件;

這行命令的前半部分固定為luatos --llt=,表示要加載 Luatools 創(chuàng)建的項目配置文件,根據(jù)配置文件找到所有的應用腳本文件來運行;

這行命令的后半部分為使用 Luatools 創(chuàng)建的 hello_luatos 項目軟件的配置文件的絕對路徑,根據(jù)你自己的實際情況進行填寫;配置文件都存儲在 Luatools 目錄下的 project 目錄;

模擬器運行后的效果如下圖所示:

wKgZO2l4Xa-ALFcRAACv2LmPwXY979.png

我們可以看到:每隔 1 秒鐘,日志輸出一次 Hello, LuatOS

下面我來實際演示下這個過程。

1.4 LuatOS 項目應用軟件(hello_luatos)的運行邏輯詳解

在使用 Air8000 核心板和模擬器實際運行 hello_luatos 項目軟件之后,大家對這個項目實現(xiàn)的功能應該是比較清楚了;

接下來,我用 Visual Studio Code,打開 hello_luatos 的項目應用腳本 main.lua 和 hello_luatos.lua,逐行分析應用邏輯的運行過程。

在分析腳本代碼之前,先來看看簡化后的下面這張圖,這張圖說明了 LuatOS 項目應用軟件的總體運行過程

wKgZO2l4Xe6AAa-RAAH46s7p-l8055.png

從這張圖可以看出,在 LuatOS 內核固件中有一個 FreeRTOS,F(xiàn)reeRTOS 運行起來之后,創(chuàng)建了很多任務,有軟件定時器任務,TCP/IP 協(xié)議棧任務,文件系統(tǒng)任務,Lua 虛擬機任務等。

其中 Lua 虛擬機任務和 LuatOS 項目的應用軟件關系最為密切;

Lua 虛擬機任務運行起來之后,經(jīng)過必要的初始化動作,就會去尋找 main.lua 腳本文件,找到之后,從 main.lua 的第一行代碼開始解析執(zhí)行,main.lua 會執(zhí)行必要的初始化動作并且加載運行其他的 Lua 腳本應用功能模塊,main.lua 的最后一行代碼為 sys.run(),sys.run()是一個 while true 的循環(huán)函數(shù),實際上也是 Lua 虛擬機任務的處理函數(shù);

在這個 while 循環(huán)里面,不斷的分發(fā)處理各種消息,調度 LuatOS 項目應用軟件的正常運行。

在理解了 LuatOS 項目應用軟件的基本運行邏輯之后,接下來,我們一起來看下 hello_luatos 的項目應用腳本 main.lua 和 hello_luatos.lua 的運行過程。

hello_luatos 的應用邏輯比較簡單,并且代碼中的注釋也比較詳細,我們就邊看代碼邊講解。

二、全面認識 LuatOS 運行框架如何使用

我們在上一小節(jié)中分析了 hello_luatos 的應用腳本代碼,雖然 hello_luatos 這個項目很小,但是我們已經(jīng)接觸到了:

1、LuatOS 中的兩個核心概念:任務和定時器;

2、LuatOS 的調度器:sys.run()函數(shù)

3、LuatOS 的一個核心庫 sys

從本章開始,我們一起系統(tǒng)性地學習 LuatOS 運行框架如何使用,主要包含以下幾項內容:

1、LuatOS 的三個核心概念:任務(task),消息(message),定時器(timer);

2、LuatOS 的一個調度器:sys.run()函數(shù)

3、LuatOS 的一個核心庫:sys 核心庫

4、基于一個相對完整的 LuatOS 項目,分析本節(jié)課學習到的核心概念,調度器和核心庫知識,系統(tǒng)理解本節(jié)課的知識在實際項目中的應用方法;

2.1 LuatOS 的任務(task)

2.1.1 基本概念

2.1.1.1 FreeRTOS task 和 LuatOS task

先來看一下這張圖,和上一張LuatOS項目應用軟件的總體運行過程圖相比,新增了一段說明文字以及幾個 LuatOS task 示意圖:

在 LuatOS 項目應用軟件腳本運行過程中,只要代碼可以被執(zhí)行到,就可以調用 sys.taskInit 和 sys.taskInitEx 兩個核心庫的 API,創(chuàng)建 LuatOS 的 task。

wKgZO2l4XsmADuBYAAJitibt_5I213.png

從這張圖中我們可以看到,有兩種任務:

第一種是 LuatOS 內核固件中的任務,也就是 FreeRTOS 創(chuàng)建的任務;這種任務我們把它命名為 FreeRTOS task;

第二種是 LuatOS 項目應用腳本中的任務,也就是用戶腳本代碼中調用 sys.taskInit 和 sys.taskInitEx 兩個 API 創(chuàng)建的任務,這種任務我們把它命名為 LuatOS task;嚴格意義上說,LuatOS task 并不是真正的 task,而是利用了 Lua 中的協(xié)程(coroutine)概念,來等價實現(xiàn)的一種 task 效果,因為 task 的受眾面比協(xié)程的受眾面要廣的很多,所以我們在 LuatOS 中,將協(xié)程(coroutine)包裝成了 task 的概念,這樣更容易理解和使用;關于協(xié)程(coroutine)的知識不屬于本課程講解的范疇,大家有興趣的可以借助網(wǎng)絡資源自行學習。

在這里我使用一個形象的比喻,爭取可以讓大家更加直觀的理解這兩種任務的聯(lián)系和區(qū)別:

1、有一個森林,這個森林就是 FreeRTOS;

2、森林里生長了很多棵大樹,每一棵大樹都是 FreeRTOS 創(chuàng)建的一個任務,就是 FreeRTOS task;

3、這些大樹分成了兩種:

一種是長出了樹干,樹干上還有很多樹枝,這一種大樹只有一棵,就是 Lua 虛擬機任務

一種是只長了光禿禿的樹干,樹干上沒有樹枝,這種大樹有很多,除 Lua 虛擬機之外,其余所有任務都是這種大樹

4、Lua 虛擬機任務這棵大樹的樹干上長出的所有樹枝,就是一個個的 LuatOS task

根據(jù)以上描述畫一張簡圖如下:

wKgZPGl4X2aAdoXQAAKass2YwnM676.png


第一次接觸 LuatOS 開發(fā)的用戶,對 LuatOS task 的功能特性可能會有誤區(qū),所以在這里我們先對比下 FreeRTOS task 和 LuatOS task 的一些重要的功能特性區(qū)別;

LuatOS 內核固件中的任務(FreeRTOS task)和 LuatOS 項目應用腳本中的任務(LuatOS task)的重要區(qū)別如下:

wKgZPGl4X9yAEzgpAAEQqt6tflY955.png

通過上面這個表格中的文字描述,可能理解的不是很直觀,接下來舉兩個例子,來實際說明一下 LuatOS task 的特性。

2.1.1.2 LuatOS 的多任務調度機制

第一個例子用來說明 LuatOS task 的協(xié)作式的任務調度機制;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZO2l4YCmAc1klAATRPpDLcNs136.png

我們使用 PC 模擬器來實際運行一下這個例子:

我已經(jīng)在 Luatools 工具上創(chuàng)建了一個 luatos_framework_luatos_task_Air8000 項目,并且已經(jīng)把應用腳本添加到這個項目下,為了節(jié)省時間,使用模擬器來運行一下這個例子來看看實際的效果:

打開 cmd 命令行窗口,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZPGl4a2KATo3lAASae401H7o631.png

下面我們結合代碼來分析一下關鍵步驟的運行日志;

通過以上代碼和日志的分析,可以知道,在這個程序中有 task1 和 task2 兩個 LuatOS task;

當 task1 內部的代碼運行到 sys.wait(500)時,task1 會掛起自己,task1 掛起之后,下一個恢復運行的 task 就是 task2,為什么呢?因為 task2 掛起時長是 300ms,task1 的掛起時長是 500ms,task1 要等 500ms 之后才能恢復運行,task2 最長只需要等待 300ms 就可以恢復運行,所以接下來肯定是 task2 先恢復運行;

當 task2 內部的代碼運行到 sys.wait(300)時,task2 會掛起自己,task2 掛起之后,下一個恢復運行的 task 有可能是 task1,也有可能是 task2,為什么呢?因為 task2 掛起時長是 300ms,task1 的掛起時長是 500ms;

如果 task1 之前已經(jīng)被掛起的時長超過了 200ms,接下來不到 300ms,task1 就應該重新恢復運行,這種情況下,因為 task2 在 300ms 之后就能恢復運行,所以 task1 就應該先恢復運行;

如果 task1 之前已經(jīng)被掛起的時長小于 200ms,接下來超過 300ms,task1 才能重新恢復運行,這種情況下,因為 task2 在 300ms 之后就能恢復運行,所以 task2 就應該先恢復運行;

從這個例子,我們可以看出,task1 和 task2 都會通過 sys.wait(timeout)函數(shù)將自己掛起來實現(xiàn)不同 task 之間的任務調度;

如果我們簡單地方修改一下代碼,如下圖所示,注釋掉黃色背景的一行代碼-- sys.wait(500) :

wKgZO2l4ao-AZukdAATMIzfiD-0725.png

會發(fā)生什么事情呢?在模擬器上實際運行一下看看:

wKgZO2l4axmAblCZAAlWG33KogU154.png

可以看到,task1 一直在運行,task2 永遠得不到運行;

因為 task 沒有優(yōu)先級的概念,task1 首先運行,并且 task1 沒有掛起自己,所以 task1 就會一直運行,task2 就沒機會運行;

2.1.1.3 LuatOS 的多任務訪問共享資源方式

第二個例子用來說明 LuatOS task 的多任務如何使用共享資源;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZPGl4bAuAKqp_AAXMYhqqQfg003.png

我們使用 PC 模擬器來實際運行一下這個例子:

我已經(jīng)在 Luatools 工具上創(chuàng)建了一個 luatos_framework_luatos_task_Air8000 項目,并且已經(jīng)把應用腳本添加到這個項目下,為了節(jié)省時間,使用模擬器來運行一下這個例子來看看實際的效果:

打開 cmd 命令行窗口,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZPGl4bsmAeqn1AAbWo-miTSg057.png

可以看到:

1、task1 中 for 循環(huán)前后,全局共享變量的值增加了 100;

2、task2 中 for 循環(huán)前后,全局共享變量的值增加了 100;

非常有規(guī)律,說明:

1、task1 在 for 循環(huán)執(zhí)行過程中,沒有被其他 task(這個 demo 中就是 task2)打斷;

2、task2 在 for 循環(huán)執(zhí)行過程中,沒有被其他 task(這個 demo 中就是 task1)打斷;

3、如果被打斷了,循環(huán)前后的值就不會增加 100,要比 100 要大;

如果我們簡單地修改一下代碼,如下圖所示,打開黃色背景的兩行代碼 sys.wait(5) :

wKgZO2l4byCAXLmbAANV25ZHCZU343.png

會發(fā)生什么事情呢?在模擬器上實際運行一下看看

wKgZO2l4b2KAQpreAALI2QQFGsE004.png

可以看到:

無論是 task1 還是 task2,都不是增加了 100

1、task1 中 for 循環(huán)前后,全局共享變量的值增加了 200;

2、task2 中 for 循環(huán)前后,全局共享變量的值增加了 199;

說明:

1、task1 在 for 循環(huán)執(zhí)行過程中,task2 插入執(zhí)行了;

2、task2 在 for 循環(huán)執(zhí)行過程中,task1 插入執(zhí)行了;

看到這里,就出現(xiàn)了共享資源沖突的問題,但這個沖突完全是我們自己寫 LuatOS 腳本代碼故意讓他沖突的;

task1 和 task2 在各自的 for 循環(huán)執(zhí)行了 sys.wait(5)語句,執(zhí)行這個語句,就是把自己掛起,讓其他 task 運行;

只要我們在編碼時,在一個 task 內部操作共享資源過程中,不要主動掛起這個 task,就不會存在共享資源競爭和沖突的問題;

講到這里,大家對 LuatOS task 的概念應該有了一個基本的認識,接下來我們詳細看下 LuatOS task 如何使用。

注意事項:

若無特別指定是 FreeRTOS task 還是 LuatOS task,后續(xù)本文中的任務(或者 task)都是指 LuatOS task;

2.1.2 作用

我們首先想一個問題,為什么要有 task 這個概念,task 有什么用呢?

2.1.2.1 編程設計更簡單

帶著這個問題,我先來回顧一下 LuatOS 的最初的一次版本演變過程;

在 LuatOS 誕生的前一兩年,應該是 2012 年到 2013 年,具體的時間記不清楚了;

當時在 LuatOS 中是沒有 task 這個概念的,沒有 task 的話,寫代碼以及邏輯跳轉太繁瑣了;

例如有個簡單的指示燈閃爍功能需求:一個指示燈亮 500 毫秒,然后滅 500 毫秒,一直這樣循環(huán);

如果沒有 task,核心代碼片段如下:

wKgZO2l4b-2AEVdBAAF6D5pjUys298.png


在這段代碼中,需要通過定時器不斷地進行異步處理,根據(jù)異步處理邏輯代碼運行會不斷地發(fā)生跳轉,設計或者閱讀代碼時,用戶的思路也要不斷地跳來跳去,使用起來比較繁瑣;

后來 LuatOS 支持了 task,這個功能需求,使用 task 來實現(xiàn),核心代碼片段如下:

wKgZO2l4cCuAJAfeAAFQ2_j551A561.png


在一個 task 的處理函數(shù)中線性的直來直去的控制指示燈閃爍,比在非 task 中繞來繞去的控制指示燈閃爍,思路更清晰,邏輯也更簡單。

通過剛才的這個例子,我們基本可以明白,LuatOS 最初設計 task 時,很重要的一個原因就是為了讓用戶編程設計更簡單。

2.1.2.2 其他作用

到后來,隨著 LuatOS task 的應用越來越廣泛,LuatOS task 在以下幾方面所起的作用也越來越明顯:

1、實現(xiàn)多任務協(xié)作式的并發(fā)執(zhí)行;

2、更簡捷地支持了模塊化解耦設計;

3、更簡捷的處理異步事件,可以很方便地將異步處理邏輯封裝成同步處理邏輯;

這些作用,在接下來的內容中都會講解到,我們繼續(xù)往下看;

2.1.3 創(chuàng)建

2.1.3.1 創(chuàng)建 API

怎么創(chuàng)建一個 task,在 sys 核心庫中提供了兩個 api:

sys.taskInit(task_func, ...)

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

這兩個 api 分別創(chuàng)建兩種 task,首先我們給這兩種 task 起個名字,一種叫基礎 task,一種叫高級 task;

sys.taskInit(task_func, ...)創(chuàng)建的 task 是基礎 task;

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)創(chuàng)建的 task 是高級 task;

從設計原理的角度來看,基礎 task 和高級 task 的區(qū)別是:

(1) 所有的基礎 task 共享一個全局消息隊列;

(2) 每個高級 task 都有自己獨立的消息隊列,同時又能使用全局消息隊列;

從用戶使用的角度來看,基礎 task 和高級 task 的區(qū)別是:

(1) 基礎 task 如果阻塞功能使用不當,可能會丟失自己應該處理的消息;

(2) 高級 task 如果阻塞功能使用不當,不會丟失自己應該處理的消息;

雖然從設計原理來看,高級 task 比基礎 task 使用起來不容易犯錯;

但是由于基礎 task 使用起來簡潔,基礎 task 還是需要掌握,一旦掌握之后,也不容易犯錯;

接下來我們先看下這兩個 api 的說明,結合說明再運行一些實際的例子,來理解如何創(chuàng)建 task;

sys.taskInit(task_func, ...)

功能:創(chuàng)建并且啟動運行一個基礎 task;這里表述的是基礎 task,既然有基礎 task,肯定還會有另外一種 task,在這里我們先不展開說明,后續(xù)的小節(jié)內容會講到。

注意事項:

1、可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);關于這一點,我們在這里先不展開講,等到下一小節(jié)再展開;

2、在 LuatOS 中,對創(chuàng)建的 task 數(shù)量沒有特別限制,只要 ram 夠用,可以一直創(chuàng)建;不同的硬件,用戶可用 RAM 資源也不同,例如:

(1) Air8000 系列的用戶可用 RAM 為 4MB

(2) Air780 系列中 Air780EPM 用戶可用 RAM 為 1MB,Air780EHM/EHV/EGH 用戶可用 RAM 為 4MB

(3) Air8101 系列的用戶可用 RAM 為 2MB

(4) PC 模擬器用戶可用 RAM 為 2MB

下面這個例子用來說明如何查看用戶可用 ram 信息;

我們首先分析下這段代碼的業(yè)務邏輯

wKgZPGl4cZeAPrVVAATDJqVyxbw569.png

我們在模擬器上實際運行一下看看,輸入命令luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZPGl4cfyAaDxQAAMW3Z5KfI4057.png

通過上圖的這份日志,可以看出,Lua虛擬機中的用戶可用ram信息:

- 第一個數(shù)值為分配給Lua虛擬機的總ram為2097144字節(jié),將近2MB字節(jié);這個值在運行過程中一直保持不變;

- 第二個數(shù)值為LuatOS應用程序運行過程中實時使用的ram,根據(jù)業(yè)務邏輯會發(fā)生動態(tài)變化,我們演示的這個demo業(yè)務邏輯比較簡單,所有變化幅度比較小,一直在35KB左右波動;如果業(yè)務邏輯復雜,波動幅度就會比較大;

- 第三個數(shù)值為LuatOS應用程序運行過程中歷史最高使用的ram,我們演示的這個demo中,為37040字節(jié),即歷史最高水位; 平時開發(fā)過程中,大家可以加上這段代碼,實時觀察下第二個數(shù)值和第三個數(shù)值,如果這兩個數(shù)值頻繁的接近第一個數(shù)值,那就說明你的程序占用的內存就比較多了,此時就需要重點分析解決問題,否則很容易就會造成內存不足而導致重啟;(一般來說,不會出現(xiàn)這樣的問題;關于如何分析解決內存使用接近上限的問題,后續(xù)會有文章專門來講,今天在這里就不說了)

知道了怎么查看用戶可用ram信息后,我們再來看一個問題,每創(chuàng)建一個基礎task需要占用多少ram資源? 下面這個例子用來說明如何分析一個基礎task占用的ram資源; 這個例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZPGl4cvWAL0RKAAWmZyTokIY562.png

在模擬器上運行下面一段代碼實際看下日志

wKgZO2l4czCAHSQUAAEKULVgH4E457.png

從日志可以看出: led task創(chuàng)建前,Lua ram使用35296字節(jié),led task創(chuàng)建后,Lua ram使用36112字節(jié),led這個task創(chuàng)建并且運行需要816字節(jié)的ram; 因為led task的邏輯很簡單,所以占用的ram較小,如果創(chuàng)建并且運行一個邏輯復雜的task,占用的ram就會變大; 因為task的ram消耗主要包括兩部分: - 一部分是創(chuàng)建開銷,這個相對較小,每個task的創(chuàng)建開銷基本一樣; - 一部分是運行開銷,這個和task內部創(chuàng)建的所有局部變量、表、字符串等都有關系,不同task的消耗不一樣,差別就比較大;

剛才這個例子演示的task很簡單,創(chuàng)建并且運行消耗了816字節(jié)的ram; 大家可能會有疑問,這個demo只有這個task是我自己寫的,僅消耗了816字節(jié)的ram,為什么日志中打印的總消耗ram是36112字節(jié)呢?剩余的35KB左右的ram被誰用去了? Lua虛擬機初始化運行時,會創(chuàng)建Lua狀態(tài)機,加載標準庫/核心庫中的函數(shù)名和常量,加載運行main.lua以及main.lua中require的其余l(xiāng)ua文件,這些都需要消耗ram,可以說,這些都是LuatOS應用程序運行的基本開銷,不同的LuatOS應用項目,這個基本開銷也不一樣,主要取決于初始化過程中用戶main.lua以及其他應用腳本的復雜度;

了解了用戶可用ram以及每個基礎task大概占用的ram之后,我們再回到原始的問題:在LuatOS中,對創(chuàng)建的task數(shù)量沒有特別限制,只要ram夠用,可以一直創(chuàng)建。 帶著這個問題,我們再回到剛才的日志截圖,以剛才的簡單demo為例,初始化之后,創(chuàng)建用戶的第一個task之前,剩余的可用ram是2097144 - 35296 = 2061848字節(jié),假設創(chuàng)建并且運行每個task需要消耗816字節(jié),則應該可以創(chuàng)建 2053120/816 = 2526個task。

下面這個例子用來說明可以創(chuàng)建多少個task; 這個例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZO2l4c42ABbFuAAKv15QnnfU066.png

在模擬器上實際運行一下看看,輸入命令luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZPGl4c9KAV5r1AAIK39BE2fQ967.png

可以看到,創(chuàng)建了2366個task之后,就提示memory不足,報錯了; 和計算的理論值2526個有一定誤差,這個在可以接受的范圍內,一方面是因為存在內存碎片(上圖的日志還有2097144-2042032=55112字節(jié)的ram無法使用,如果這部分ram可以利用,還能再多創(chuàng)建55112/816=67個task),另一方面可能還會存在一些我們沒考慮到的其他一些地方需要消耗比較少的ram; 至此,大家應該理解了在LuatOS中,對創(chuàng)建的task數(shù)量沒有特別限制,只要ram夠用,可以一直創(chuàng)建所表達的意思。

參數(shù)

task_func

wKgZPGl4dDiAZuVKAAJha8vrujo338.png


下面這個例子用來說明_task_func 參數(shù)的一種典型的錯誤使用方式_;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZPGl4dGeAAMF0AAJb-aIT7HA192.png


在模擬器上實際運行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZO2l4dKuAOD1ZAABPcCs3yu4822.png

黃色背景提示出現(xiàn)的錯誤是指:創(chuàng)建 task 的時候,傳入的第一個參數(shù)期望是 function 類型,但是實際卻傳入了 nil 類型;

為什么會出現(xiàn)這個錯誤呢,和 Lua 語言的解析執(zhí)行順序有關;

Lua 解析器是按照從上到下的順序解析執(zhí)行代碼的,首先執(zhí)行第 1 行的sys.taskInit(led_task_func),執(zhí)行到這里發(fā)現(xiàn)led_task_func變量不存在,所以就報錯;因為此時還沒有執(zhí)行過第 3 行;犯的錯誤是:先使用后定義。

怎么解決這個問題呢?有兩種方式:

一種方式是還是在同一個 lua 文件中,參考以下代碼,調整一下 led_task_func 函數(shù)定義和使用的順序,先定義,再使用就沒問題

wKgZO2l4dOiAK1ufAADGmUK4J20013.png

另一種方式是以模塊化的方式創(chuàng)建多個 lua 文件,函數(shù)定義和聲明放到一個單獨的 lua 庫文件中,函數(shù)使用放到另一個 lua 應用文件中,在應用文件中,加載庫文件,就可以使用庫文件的函數(shù);這種方式我們在這里就不介紹了,后續(xù)有完整的項目 demo,大家可以參考;

這兩種解決方式,本質上,采用的都是:先定義后使用的思路。

...

參數(shù)含義:task的處理函數(shù)攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個,1個,2個,...,多個;

參數(shù)示例:

不傳入,或者bool類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;

下面這個例子用來說明_可變參數(shù)...的使用方式_;

核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZO2l4eciAcsuaAANyASFg17o347.png


在模擬器上實際運行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下

wKgZPGl4efiAeV0RAADaEH-tQlo465.png

從日志可以看出,sys.taskInit(led_task_func, "arg1", 3, nil, true, led_task_func) 的可變參數(shù),"arg1", 3, nil, true, led_task_func 按照順序傳遞給了任務處理函數(shù) led_task_func;

返回值

local task_object = sys.taskInit(task_func, ...)

有一個返回值 task_object

task_object

含義說明:創(chuàng)建的task對象;如果為thread類型,表示創(chuàng)建成功;如果為nil類型,表示創(chuàng)建失敗;

數(shù)據(jù)類型:thread或者nil;

取值范圍:無特別限制;

注意事項:

雖然這個函數(shù)有返回值,但是這個返回值在整個LuatOS系統(tǒng)中,沒有應用場景;

所以不用深入了解這個返回值的用途;

sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

功能

創(chuàng)建并且啟動運行一個高級 task;剛才我們看過的 api,sys.taskInit 是創(chuàng)建一個基礎 task;在這里,sys.taskInitEx 是創(chuàng)建一個高級 task,看到這里,我們接觸到了兩種 task:基礎 task 和高級 task,等我們把這個 api 看完,再總結下基礎 task 和高級 task 的區(qū)別;

sys.taskInitEx這個 api 和sys.taskInit這個 api 的區(qū)別主要體現(xiàn)在task_name, non_targeted_msg_cbfunc這兩個參數(shù)上,其余內容完全一樣;

所以我們接下來我們重點看一下這兩個參數(shù),其余內容就簡單的過一遍;

注意事項

1、可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);關于這一點,我們在這里先不展開講,等到下一小節(jié)再展開;

2、在 LuatOS 中,對創(chuàng)建的 task 數(shù)量沒有特別限制,只要 ram 夠用,可以一直創(chuàng)建;這一點在介紹 sys.taskInit 時已經(jīng)講過,內容完全一樣,這里就不重復介紹了;

參數(shù)

task_func

wKgZPGl4esaAAYFMAANUqNCMKOA650.png

task_name

參數(shù)含義:task的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:

在一個的LuatOS項目中,創(chuàng)建的所有高級task的task_name不能重復;

目前在核心庫中沒有檢查是否重復,需要用戶自行保證;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;

task_name 這個參數(shù),字面意思來看,表示 task 的名稱;實際上,在 sys 核心庫內,會根據(jù)這個 task_name 創(chuàng)建一個資源表,資源表中有一個消息隊列,這個消息隊列就可以看做是這個高級 task 專有的消息隊列;所以說,當一個高級 task 有了自己的消息隊列,誰都可以發(fā)送定向消息到這個 task 的消息隊列中,提高了消息處理的效率;

我們再看一下創(chuàng)建基礎 task 的 api,sys.taskInit(task_func, ...),沒有 task_name,在 sys 核心庫內,不會為基礎 task 創(chuàng)建獨立的消息隊列。雖然如此,但是會創(chuàng)建一個全局的消息隊列,所有的基礎 task 和高級 task 都會共享使用這一個全局消息隊列。

現(xiàn)在大家對基礎 task,高級 task,獨立消息隊列,全局消息隊列先有一個基本的認識,后續(xù)我們還會通過示例以及文字描述來做進一步總結。

non_targeted_msg_cbfunc

wKgZPGl4fIOABbRiAAY9k-d19es344.png

這個參數(shù)的意思是:當一個高級 task 在讀取自己的獨立消息隊列中的某一種消息時,發(fā)現(xiàn)讀出來的消息不是自己期望的消息,則直接把這個消息丟給_non_targeted_msg_cbfunc_處理。

下面這個例子用來說明如何使用;

這個例子的核心代碼片段如下,我們首先分析下這段代碼的業(yè)務邏輯

wKgZPGl4fMCASSb8AA4VEdyqwlM560.png

我們在模擬器上實際運行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行日志如下:

wKgZPGl4fPiAWjUpAAFvnbuk938166.png


紅色背景的日志為非目標消息回調函數(shù)的運行邏輯;

綠色背景的日志為目標消息的運行邏輯;

從日志可以看出,在高級 task 內部使用 sys.waitMsg 等待目標消息(消息名為"MQTT_EVENT")時,如果收到了非目標消息,都給非目標消息回調函數(shù) mqtt_client_main_cbfunc 去處理了。

參數(shù)含義:task的處理函數(shù)攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:...是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個,1個,2個,...,多個;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;

返回值

local task_object = sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)

有一個返回值 task_object

task_object

含義說明:表示創(chuàng)建的task對象;如果為thread類型,表示創(chuàng)建成功;如果為nil類型,表示創(chuàng)建失敗;

數(shù)據(jù)類型:thread或者nil;

取值范圍:無特別限制;

注意事項:雖然這個函數(shù)有返回值,但是這個返回值在整個LuatOS系統(tǒng)中,沒有應用場景,所以不用深入了解這個返回值的用途;

基礎 task 和高級 task 的區(qū)別

現(xiàn)在我們回顧一下剛才講的兩個 api,一個是創(chuàng)建基礎 task,一個是創(chuàng)建高級 task;

在這里我們對基礎 task 和高級 task 先做一個簡單的總結:

task分為基礎task和高級task兩種;

從設計原理的角度來看,基礎task和高級task的區(qū)別是:

(1) 所有的基礎task共享一個全局消息隊列;

(2) 每個高級task都有自己獨立的消息隊列,同時又能使用全局消息隊列;

從用戶使用的角度來看,基礎task和高級task的區(qū)別是:

(1) 基礎task如果阻塞功能使用不當,可能會丟失自己應該處理的消息;

(2) 高級task如果阻塞功能使用不當,不會丟失自己應該處理的消息;

雖然從設計原理來看,高級task比基礎task使用起來不容易犯錯;

但是由于基礎task使用起來簡潔,基礎task還是需要掌握,一旦掌握之后,也不容易犯錯;

sys核心庫提供的task管理功能有以下幾種:

(1) 基礎task的創(chuàng)建和啟動運行:sys.taskInit(task_func, ...)

(2) 高級task的創(chuàng)建和啟動運行:sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)


在這里,大家對這兩種 task 的區(qū)別有一個基本的認識就行,在后續(xù)消息章節(jié),我們還會進一步深入講解;

2.1.3.2 什么時間創(chuàng)建

理解了怎么創(chuàng)建 task 之后,我們來看下一個問題;

在什么時間點創(chuàng)建 task,在代碼的什么位置創(chuàng)建 task?

task 的創(chuàng)建時間點非常靈活,例如:

1、每個應用功能模塊初始化時,可以創(chuàng)建 task;

2、檢測到某個事件發(fā)生時,例如 4G 模組檢測到插入了一張 sim 卡時,可以創(chuàng)建 task;

3、在一個 task 的處理函數(shù)中運行時,可以創(chuàng)建另外一個 task;

4、......等等各種場景

總結下來只有一句話:只要寫的一段代碼能被執(zhí)行到,在這段代碼中就可以使用 api 創(chuàng)建 task。

那是不是意味著,在項目應用軟件代碼的任何位置都能創(chuàng)建 task,也并不是,只有一個例外;

在 main.lua 中 sys.run()之后不能寫代碼創(chuàng)建 task;

為什么有這個限制,我們在 1.4 章節(jié),基于 hello_luatos 講解 LuatOS 應用軟件的運行邏輯時已經(jīng)講到,我們再根據(jù)下面這張圖以及 hello_luatos 代碼來簡單的回顧一下:

wKgZO2l4f06ADxONAAJeFHBoR54665.png

2.1.3.3 應用腳本代碼運行位置

剛才在學習 sys.taskInit 和 sys.taskInitEx 這兩個 api 時,有以下兩段話:

只要寫的一段代碼能被執(zhí)行到,在這段代碼中就可以使用這兩個 api 創(chuàng)建 task;

回調函數(shù)是在 task 之外的業(yè)務邏輯中被執(zhí)行的; 在回調函數(shù)內部無法使用 sys.wait(timeout)、sys.waitUntil(msg, timeout)、sys.waitMsg(task_name, msg, timeout)等必須用在 task 中的函數(shù);

從這兩段話中,引出一個問題:在 LuatOS 應用腳本開發(fā)過程中,我們所編寫的應用腳本代碼,存在兩種業(yè)務運行的邏輯環(huán)境:

1、一種是在 task 的任務處理函數(shù)內部的業(yè)務邏輯環(huán)境中運行,我們簡稱為:在 task 內部運行;

2、一種是在 task 的任務處理函數(shù)外部的業(yè)務邏輯環(huán)境中運行,我們簡稱為:在 task 外部運行;

怎么理解這兩種業(yè)務邏輯運行環(huán)境?我們看下面這張圖

看右邊生長出分支的這棵大樹,這棵大樹就是 FreeRTOS 創(chuàng)建的 Lua 虛擬機 task,是一個 FreeRTOS task;

在這個 Lua 虛擬機 FreeRTOS task 上,這棵大樹再分為兩部分:

1、樹干部分:樹干部分運行的業(yè)務邏輯環(huán)境就是 LuatOS task 外部運行環(huán)境;

2、樹枝部分:每個樹枝都是一個獨立的 LuatOS task,樹枝部分運行的業(yè)務邏輯環(huán)境就是 LuatOS task 內部運行環(huán)境;

在這里,大家只要知道有 task 內部運行和 task 外部運行兩種環(huán)境即可,后續(xù)我們講解其他功能時,會用到這兩種概念,并且也會結合示例來說明二者的差別;

wKgZO2l4f8-AAF4MAALlCDYTNzg628.png

2.1.4 狀態(tài)

task 被創(chuàng)建之后,就會按照代碼中設計的業(yè)務邏輯來來運行,task 的狀態(tài)有三種:

運行狀態(tài),阻塞狀態(tài),死亡狀態(tài);

我們先來看下一個 task 的狀態(tài)機

wKgZO2l4gAGAHEeFAAFEO_T2PvE958.png

1、調用 sys.taskInit 或者 sys.taskInitEx 兩個 api 后,創(chuàng)建的 task 就會進入運行狀態(tài);

2、當發(fā)生以下兩種事件中的任意一種,運行狀態(tài)切換為死亡狀態(tài),一旦進入死亡狀態(tài),這個 task 也就結束了

(1) task 的任務處理函數(shù),按照正常的業(yè)務邏輯已經(jīng)運行結束,正常退出函數(shù);也就是狀態(tài)機中的 normal exit

(2) task 的任務處理函數(shù),在運行過程中,出現(xiàn)了異常,例如對非 number 類型的變量進行數(shù)據(jù)運算,把一個 number 類型當做 table 來用等等等等,一旦出現(xiàn)類似的異常, 任務處理函數(shù)就會異常退出;也就是狀態(tài)機中的 abnormal exit

3、當發(fā)生以下三種事件中的任意一種,運行狀態(tài)切換為阻塞狀態(tài),一旦進入阻塞狀態(tài),就是 task 任務處理函數(shù)中的代碼把自己給掛起了

(1) 任務處理函數(shù)中執(zhí)行到 sys.wait(timeout),表示在這里阻塞等待 timeout 毫秒的時間;

(2) 任務處理函數(shù)中執(zhí)行到 sys.waitUntil(msg, timeout),表示在這里阻塞等待全局的 msg 消息,或者阻塞等待 timeout 毫秒的時間;

(3) 任務處理函數(shù)中執(zhí)行到 sys.waitMsg(task_name, msg, timeout),表示在這里阻塞等待定向發(fā)給 task_name 的 msg 消息,或者阻塞等待 timeout 毫秒的時間;

4、當發(fā)生以下三種事件中的任意一種,阻塞狀態(tài)切換為運行狀態(tài)

(1) 任務處理函數(shù)中執(zhí)行到 sys.wait(timeout),sys.waitUntil(msg, timeout),sys.waitMsg(task_name, msg, timeout)創(chuàng)建的定時器超時時間到達,此時會產(chǎn)生一個定時器到達事件

(2) 任意代碼處執(zhí)行 sys.publish(msg, ...),發(fā)布了全局 msg 消息,在等待這個全局 msg 消息的所有處于阻塞狀態(tài)的 task 都能依次接收到此消息,并且從阻塞狀態(tài)切換為運行狀態(tài)繼續(xù)運行

(3) 任意代碼處執(zhí)行 sys.sendMsg(task_name, msg, arg2, arg3, arg4),向名稱為 task_name 的高級 task 發(fā)送了定向 msg 消息,在等待這個定向 msg 消息的并且處于阻塞狀態(tài)的 task 可以接收到此消息,并且從阻塞狀態(tài)切換為運行狀態(tài)繼續(xù)運行

通過以上的 task 狀態(tài)機介紹,我們可以看出,運行狀態(tài)和阻塞狀態(tài)之間的切換最為復雜;

這兩種狀態(tài)之間的切換,用到定時器和消息兩種事件,接下來我們來重點看下消息和定時器兩個概念。

2.2 LuatOS的消息(message)

2.2.1 基本概念

LuatOS 的消息機制是LuatOS task協(xié)作和事件驅動編程的核心部分,消息機制包括消息的訂閱、發(fā)布/發(fā)送、接收處理;

消息需要存儲到消息隊列中,消息隊列中的消息遵循先進先出(FIFO)原則;

根據(jù)消息隊列的創(chuàng)建位置,把LuatOS 的消息隊列分為兩種: 內核消息隊列和應用消息隊列;

內核消息隊列是內核固件中創(chuàng)建的,應用消息隊列是擴展庫腳本和項目應用腳本中創(chuàng)建的;

wKgZO2l4gPuAUHRwAAJY7QiAPZ4457.png

根據(jù)消息存儲使用的消息隊列類型以及消息發(fā)送方的不同,對消息做以下劃分:

wKgZO2l4gTuALjtJAAUMA0xy3IU558.png

2.2.2 內核消息

看下面這張圖,黃色背景的就是內核消息隊列;

FreeRTOS創(chuàng)建的每一個task都有一個消息隊列,這種消息隊列就叫內核消息隊列;

內核消息隊列是FreeRTOS直接管理的,存儲每個FreeRTOS task的內核消息的隊列;

內核消息的發(fā)送,由內核固件自行處理,不開放給LuatOS用戶使用;

wKgZO2l4gXiAOU0WAALpcxok11M910.pngwKgZPGl4gaCAXdvyAAH6RaZfFB4517.png

從內核消息的接收處理方式來劃分,內核消息可以分為定時器消息和非定時器消息兩大類

2.2.2.1 非定時器消息

在LuatOS的應用腳本程序中,針對某一種消息或者某一類消息,注冊回調函數(shù),當內核固件中的驅動程序或者FreeRTOS的task給FreeRTOS的Lua虛擬機task發(fā)送消息后,在sys.run()調度器中會讀取內核消息隊列中的消息,讀到消息后,直接執(zhí)行注冊的回調函數(shù);在這里我舉兩個例子,同時結合上面的兩張圖說明一下這個過程:

第一個例子是串口接收數(shù)據(jù)處理,核心代碼如下:

wKgZPGl4geKAF5jCAAII-mw9N6A896.png

第二個例子是mqtt客戶端異步消息處理,核心代碼如下:

wKgZPGl4gg6AA019AAN7UyfvDJw237.png

除了上面列舉的這兩種非定時器消息的uart,mqtt內核消息處理之外,LuatOS應用腳本開發(fā)中,還會用到很多種類似的內核消息處理邏輯,例如socket異步消息處理,http異步消息處理等等,這些內核消息的處理邏輯都是完全一樣,只要注冊一個回調處理數(shù)據(jù)即可,后續(xù)我們講解具體的功能模塊時,會詳細講解;

2.2.2.2 定時器消息

第二類內核消息是定時器消息,和第一類的內核消息相比,定時器消息不僅僅可以通過注冊回調函數(shù)來處理,還能控制task的阻塞和運行,所以定時器消息功能更加強大,使用起來更加靈活,定時器內容在后續(xù)章節(jié)會重點講解,在這里就暫時跳過了;

2.2.3 應用消息

應用消息隊列由Lua虛擬機內部管理,應用消息隊列中的消息訂閱,發(fā)布/發(fā)送,和接收處理,LuatOS用戶可以直接控制;

應用消息隊列中的消息,和LuatOS應用腳本程序的關系最為密切,本章節(jié),我們將重點學習應用消息隊列中的消息如何使用;

應用消息隊列又可以進一步劃分為全局消息隊列和定向消息隊列:

全局消息隊列中存儲的全局消息有系統(tǒng)全局消息用戶全局消息;

定向消息隊列中存儲的定向消息有系統(tǒng)定向消息用戶定向消息;

2.2.3.1 系統(tǒng)全局消息

系統(tǒng)全局消息是內核固件的C代碼中調用sys.publish接口發(fā)布的消息,例如"IP_READY";

LuatOS用戶在腳本程序中可以使用sys.subscribe和sys.waitUntil訂閱和接收處理;

這部分消息牽涉到的功能模塊比較多,,有個簡單的認識就行,這些系統(tǒng)全局消息,在后續(xù)的LuatOS課程中,每講到一個主題,都會講解對應的系統(tǒng)全局消息;

在本章節(jié)就不細講了。

2.2.3.2 系統(tǒng)定向消息

系統(tǒng)定向消息是內核固件的C代碼中調用sys.sendMsg接口發(fā)布的消息,例如socket.EVENT;

LuatOS用戶在腳本程序中可以使用sys.waitMsg接收處理;

但是由于這部分內容和內核固件的耦合比較大,一般來說都是LuatOS的擴展庫去使用,目前只有在socket功能有關的擴展庫中使用過系統(tǒng)定向消息,用戶編寫的Lua應用腳本程序中很少用到,所以在這里也不細講了;

在后續(xù)的LuatOS課程中,每講到一個主題,如果有對應的系統(tǒng)定向消息,我們再逐一講解;

接下來我們重點看下用戶全局消息和用戶定向消息:

從消息接收處理方的角度來劃分,消息可以分為全局消息和定向消息;

全局消息是指消息可以被任意訂閱方接收處理;

定向消息是指消息只能被指定的task接收處理;

sys核心庫提供的全局消息管理功能有以下幾種:

(1) 全局消息發(fā)布:sys.publish(msg, ...)

(2) 全局消息訂閱:sys.subscribe(msg, msg_cbfunc)

(3) 全局消息取消訂閱:sys.unsubscribe(msg, msg_cbfunc)

(4) 全局消息阻塞等待(只能在task中使用):sys.waitUntil(msg, timeout)

sys核心庫提供的定向消息管理功能有以下幾種:

(1) 定向消息發(fā)布:sys.sendMsg(task_name, msg, arg2, arg3, arg4)

(2) 定向消息阻塞等待(只能在task中使用):sys.waitMsg(task_name, msg, timeout)

(3) 定向消息清除:sys.cleanMsg(task_name)


2.2.3.3 用戶全局消息

用戶全局消息處理的完整周期

用戶全局消息處理的完整周期包括以下幾部分:

wKgZO2l4g5-ATsOqAAJ-7JWDRTM032.pngwKgZPGl4g7OAKFbcAAH7Mo2bLQU890.png

1、消息訂閱:有兩個api可以訂閱全局消息

sys.subscribe(msg, cbfunc):為全局消息msg訂閱一個回調函數(shù)cbfunc;相當于在全局消息訂閱表中增加一項[msg] = {cbfunc = true}

sys.waitUntil(msg, timeout):為全局消息msg訂閱一個task,這個api只能在task任務處理函數(shù)的業(yè)務邏輯中使用;相當于在全局消息訂閱表中增加一項[msg] = {task = true}

2、消息發(fā)布:有一個api可以發(fā)布全局消息

sys.publish(msg, ...):發(fā)布一條全局消息;相當于在全局消息隊列中的隊尾位置增加一項{msg, ...}

3、消息調度處理:有一個api對全局消息進行調度處理

sys.run():讀取全局消息并且分發(fā)給訂閱者去處理;

從全局消息隊列中取出第一條消息{msg1, ...}

根據(jù)消息名msg1,到全局消息訂閱表中找msg1的訂閱者

如果訂閱者是回調函數(shù)bfunc1,則執(zhí)行cbfunc1,執(zhí)行完之后,并不會自動從全局消息訂閱表中刪除msg1對應的cbfunc1 = true標記,只有腳本程序中主動調用sys.unsubscribe(msg1, cbfunc1),才會刪除這個標記;

如果訂閱者是一個task1,task1處于阻塞狀態(tài),則讓task1退出阻塞狀態(tài),繼續(xù)運行;然后自動從全局消息訂閱表中刪除msg1對應的task1 = true標記

在全局消息表中處理完msg1的所有訂閱者;

從全局消息隊列中刪除{msg1, ...}這條消息

4、刪除訂閱者:訂閱者有兩種,刪除的方式各不相同

回調函數(shù)類型的訂閱者,在消息調度處理過程中,不會自動刪除;只有腳本程序中主動調用sys.unsubscribe(msg, cbfunc),才會刪除

task類型的訂閱者,在消息調度處理過程中,處理完之后,會自動刪除;

5、刪除消息:sys.run()消息調度處理過程中,自動刪除;

有一個重要注意事項:

全局消息必須先訂閱,然后再發(fā)布,這樣才能保證發(fā)布的消息可以被訂閱者處理;怎么理解呢?我們再看下上面這張圖,在sys.sun()調度時,首先從全局消息隊列中取出隊首的一條消息,然后從全局消息訂閱表中找到這條消息對應的所有訂閱者進行處理,處理完之后,就刪除了這條消息,所以此時如果訂閱者還沒有準備好,則無法處理消息;

了解了用戶全局消息的完整生命周期后,接下來我們:

1、先看下在整個生命周期中用到的幾個sys核心庫的api

2、寫一個完整的demo示例來實際運行演示一下如何使用;

sys.publish(msg, ...)

功能

發(fā)布一個全局消息;

注意事項

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.publish(msg, ...)是全局消息的生產(chǎn)者,全局消息有生產(chǎn)就會有消費,不然消息就沒有存在的意義了;

有兩個接口可以注冊全局消息的消費者:

1、一個是sys.subscribe(msg, msg_cbfunc)中注冊的msg_cbfunc消息回調函數(shù);

2、一個是sys.waitUntil(msg, timeout)所在的task;

所以全局消息的生產(chǎn)者和消費者的使用組合,有以下兩種:

1、sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊消息回調函數(shù);

這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調函數(shù)處理;

2、sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

參數(shù)

msg

參數(shù)含義:消息的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;

...

參數(shù)含義:消息攜帶的可變參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:...是Lua語言的一種語法,表示可變參數(shù),參數(shù)數(shù)量可以是0個,1個,2個,...,多個;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;

返回值

nil

示例

wKgZO2l4hMiAPfgoAABrk40xdbw489.png

sys.subscribe(msg, msg_cbfunc)

功能

訂閱一個全局消息的回調函數(shù);

注意事項

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.publish(msg, ...) 和 sys.subscribe(msg, msg_cbfunc)配合使用時:

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊消息回調函數(shù);

這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調函數(shù)處理;

同一個全局消息msg,可以多次調用sys.subscribe(msg, msg_cbfunc)訂閱多個不同的回調函數(shù);

參數(shù)

msg

參數(shù)含義:全局消息的名稱,和sys.publish(msg, ...)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


msg_cbfunc

wKgZO2l4hb-AFspFAAVJJsZGhwY248.png

返回值

nil

示例

wKgZO2l4hquAZPMYAAHND7TnlkU437.png

sys.unsubscribe(msg, msg_cbfunc)

功能

取消訂閱一個全局消息的回調函數(shù);

注意事項

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

參數(shù)

msg

參數(shù)含義:全局消息的名稱,和sys.subscribe(msg, msg_cbfunc)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;

msg_cbfunc

wKgZO2l4h1yADlm0AATpNo_PMoQ837.png

返回值

nil

示例

wKgZPGl4h5CAeIG2AAJub8Wlxk8864.png

sys.waitUntil(msg, timeout)

功能

在task中阻塞等待一個全局消息;

注意事項

只能在基礎task和高級task處理函數(shù)的業(yè)務邏輯中使用此函數(shù);

sys.publish(msg, ...) 和 sys.waitUntil(msg, timeout)配合使用時:

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

同一個全局消息msg,可以被多個正在sys.waitUntil(msg, timeout)代碼處阻塞等待的task處理;

參數(shù)

msg


參數(shù)含義:全局消息的名稱,和sys.publish(msg, ...)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


timeout

參數(shù)含義:阻塞等待全局消息msg的超時時長,單位毫秒;

數(shù)據(jù)類型:number或者nil;

取值范圍:

大于等于1,小于等于0x7FFFFFFF,之間的所有正整數(shù);最大時長0x7FFFFFFF毫秒 ≈ 596小時 ≈ 24.85天;

如果為nil,表示一直阻塞等待全局消息,不會超時;

是否必選:可選傳入此參數(shù);

注意事項:

此處的超時機制基于軟件定時器實現(xiàn),受系統(tǒng)負載、任務數(shù)量、消息堆積、網(wǎng)絡中斷優(yōu)先級最高等多因素影響,無法實現(xiàn)高精度;

尤其是幾個毫秒級別,幾十毫秒級別,幾百毫秒級別的超時時長,誤差都比較大,秒級別以上的超時時長誤差較??;

例如:設置的1毫秒,可能要等幾十毫秒;設置的幾十毫秒,可能要等上百毫秒;設置的幾百毫秒,可能要等幾百幾十毫秒;

可以簡單的認為會延遲幾十毫秒左右,以此來評估超時時長精度是否可以滿足業(yè)務需求;

調用本接口時,如果傳入了timeout參數(shù),則sys核心庫內部會自動創(chuàng)建并且運行一個軟件定時器,超時時長到達后,會自動刪除這個定時器;

LuatOS應用程序中可用的軟件定時器總數(shù)量為64個;

注意控制自已應用程序中的同時運行的軟件定時器總數(shù)量不要超過64個,否則創(chuàng)建新的定時器會返回失??;

參數(shù)示例:50、1000、60000等;


返回值

local result, arg1, arg2, arg3, argN = sys.waitUntil(msg, timeout)

有數(shù)量不固定的返回值:

第一個返回值為result

剩余的返回值arg1, arg2, arg3, argN,表示可變數(shù)量的返回值,只有當?shù)谝粋€返回值result為true時,這些可變數(shù)量的返回值才有意義,和sys.publish(msg, ...)中...表示的可變參數(shù)一一對應

result

含義說明:阻塞等待的結果;true表示收到了msg消息,false表示超時沒有收到msg消息;

數(shù)據(jù)類型:boolean;

取值范圍:true或者false;

注意事項:暫無;


arg1, arg2, arg3, argN

含義說明:

當result為true時,arg1, arg2, arg3, argN表示sys.publish(msg, ...)中...可變參數(shù),從前到后一一對應;

當result為false時,arg1, arg2, arg3, argN全部都為nil,沒有任何意義;

數(shù)據(jù)類型:

當result為true時,arg1, arg2, arg3, argN的數(shù)據(jù)類型和sys.publish(msg, ...)中...可變參數(shù)的數(shù)據(jù)類型,從前到后一一對應;

當result為false時,arg1, arg2, arg3, argN全部都為nil類型;

取值范圍:無特別限制;

注意事項:暫無;


正確示例

wKgZPGl4iHiAUZNrAAK5Yh0Kg0A008.png

錯誤示例

wKgZO2l4iOeAcazJAAU1eaQ4BHQ479.png


用戶全局消息處理的代碼示例

在了解了全局消息的幾個api之后,我們再看下圖回顧一下全局消息處理的完整周期

wKgZPGl4iSqAPBeEAAJVyg56vnM642.png

下面這個例子用來說明用戶全局消息的完整處理過程;

這個例子的完整代碼鏈接:global_msg_receiver1.luaglobal_msg_receiver2.luaglobal_msg_sender.lua

因為是三個文件,并且代碼比較多,所以在這里我就不粘貼代碼到這里了,直接打開vscode來分析代碼的業(yè)務邏輯;

我們在模擬器上實際運行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行15秒鐘的日志如下,為了方便分析,我把日志做了處理,每個訂閱者訂閱同一類型的消息日志匯總到了一起:

init_subscribe_cbfunc訂閱者

init_subscribe_cbfunc訂閱的task內部發(fā)布的消息,運行正常,符合預期,每秒接收到一次task內部發(fā)布的消息;

因為10秒鐘之后,取消了訂閱,所以只收到前10條消息

wKgZPGl4icmAG1aKAAE8geQZICQ141.png

init_subscribe_cbfunc訂閱的timer發(fā)布的消息,運行正常,符合預期,每秒接收到一次task內部發(fā)布的消息;

因為10秒鐘之后,取消了訂閱,所以只收到前10條消息

wKgZPGl4ie-AEa4aAAEo3E25Wg4744.png

delay_subscribe_cbfunc訂閱者

delay_subscribe_cbfunc訂閱的task內部發(fā)布的消息,我們可以看到是從第6條消息開始接收處理,前面的5條丟失了;這是因為sys.timerStart(sys.subscribe, 5000, "SEND_DATA_REQ", delay_subscribe_cbfunc)這行代碼,是開機之后延遲5秒,然后才訂閱了"SEND_DATA_REQ"回調函數(shù)的delay_subscribe_cbfunc,所以前5秒的消息都無法接收處理;這段運行日志就可以驗證本文前面描述的這段話:

在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊消息回調函數(shù);這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調函數(shù)處理;再簡單點來說,訂閱要在發(fā)布前,就像郵局定報紙一樣,你只有了訂閱服務,在郵局發(fā)報紙的時候才不會把你漏掉;

wKgZO2l4ipGAXazVAACxUGa3Lvo444.png

同理,delay_subscribe_cbfunc訂閱的timer內部發(fā)布的消息,表現(xiàn)也是一樣

wKgZO2l4iraAWXbUAACveS1UH0c482.png

task(success_wait_until_base_task_func)訂閱者

task任務處理函數(shù)success_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的task內部發(fā)布的消息,運行正常,符合預期,每秒接收到一次task內部發(fā)布的消息,一共收到15條消息;

wKgZO2l4ivWAQ928AAIzlFNL-6I915.png

task任務處理函數(shù)success_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的timer發(fā)布的消息,運行正常,符合預期,每秒接收到一次task內部發(fā)布的消息,一共收到15條消息;

wKgZPGl4ix-AVRHpAAI--cQerSQ309.png

task(lost_wait_until_base_task_func)訂閱者

task任務處理函數(shù)lost_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的task內部發(fā)布的消息,運行異常,一共收到0條消息;15條消息全部丟失

為什么task內部發(fā)布的15條消息,task(lost_wait_until_base_task_func)全部丟失,我們添加一些日志來分析一下,如下兩部分代碼塊的黃色背景為新增的日志打印代碼

wKgZO2l4i0-AbMQGAAUrXu7Kb7E008.png

再來看一下運行日志

wKgZPGl4i62AQ-JCAAu8fkhRtlg722.png

在這段日志中:

lost_wait_until_base_task_func before wait 3000,表示接下來lost_wait_until_base_task_func的task阻塞等待3秒鐘,此狀態(tài)下,其他地方發(fā)布的"SEND_DATA_REQ"無法處理,會丟失;global_sender_msg_task_func這個task所發(fā)布的消息"SEND_DATA_REQ",都是在lost_wait_until_base_task_func before wait 3000之后發(fā)布的,所以消息全部丟失;

lost_wait_until_base_task_func before wait SEND_DATA_REQ,表示接下來lost_wait_until_base_task_func的task阻塞等待消息"SEND_DATA_REQ",此狀態(tài)下,其他地方發(fā)布的"SEND_DATA_REQ"可以被處理;在lost_wait_until_base_task_func before wait SEND_DATA_REQ之后,都是global_sender_msg_timer_cbfun這個定時器回調函數(shù)在發(fā)布"SEND_DATA_REQ",所以這個時間點的"SEND_DATA_REQ"都可以被處理;

task任務處理函數(shù)lost_wait_until_base_task_func中通過sys.waitUntil("SEND_DATA_REQ")訂閱的定時器發(fā)布的消息,運行異常,一共收到4條消息;11條消息全部丟失

wKgZPGl4i-yAD74nAACcGd_Q74A230.png


這段運行日志就可以驗證本文前面描述的這段話:

在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);

這樣才能保證發(fā)布的msg消息可以被task處理;

我們再看下這個訂閱者task的任務處理函數(shù)代碼,在如下代碼的第4行和第5行,都會使task阻塞,其中第4行會阻塞3秒,這3秒內,無論是task發(fā)布的消息還是timer發(fā)布的消息,都無法接收處理,直接丟失;3秒后,在第5行阻塞等待"SEND_DATA_REQ"消息,等到一條消息后,重新又阻塞3秒,在本demo中的表現(xiàn)就是每隔3秒接收到一次task或者timer發(fā)布的消息;

wKgZPGl4jBuAT2jFAAFyVw-rBgM284.png

如何避免這種問題呢?

在接收消息的task任務處理函數(shù)業(yè)務邏輯中,最好只有sys.waitUntil,并且也只有一處sys.waitUntil阻塞當前task;上面這個代碼塊,只要把第4行去掉就沒有問題了;

2.2.3.4 用戶定向消息

用戶定向消息處理的完整周期

簡化后用戶定向消息處理的完整周期包括以下幾部分:

wKgZO2l4jFaADhZPAAP1TiESXFw913.pngwKgZPGl4jG-AetcJAAH1SJqu7yE038.png

消息發(fā)送:有一個api可以發(fā)送定向消息

sys.sendMsg(task_name, msg, arg2, arg3, arg4):發(fā)送一條定向消息給task_name;相當于在定向消息隊列中的隊尾位置增加一項{msg, arg2, arg3, arg4}

消息接收:有一個api可以接收定向消息

sys.waitMsg(task_name, msg, timeout):讀取task_name的一條定向消息,如果定向消息隊列中有指定的msg消息,則讀出處理;如果有其他非指定的消息,給非目標消息回調函數(shù)處理;如果沒有消息,則阻塞等待

消息調度處理:有一個api對定向消息進行調度處理

如果發(fā)送定向消息后,接收者高級task處于阻塞狀態(tài),則sys.run()調度器會控制處于阻塞狀態(tài)的task,退出阻塞狀態(tài),讀取定向消息進行處理;

刪除接收者:在消息調度處理過程中,sys.waitMsg(task_name, msg, timeout)讀出指定的msg消息,處理完之后,會自動刪除接收者,只有等到下次運行到sys.waitMsg(task_name, msg, timeout)時才會創(chuàng)建新的接收者;

刪除消息:sys.run()消息調度處理過程中,sys.waitMsg(task_name, msg, timeout)讀出指定的msg消息,處理完消息之后會自動刪除;

上一小節(jié)我們討論了用戶全局消息,還記得用戶全局消息處理有兩個注意事項:

1、在sys.publish(msg, ...)之前,必須使用sys.subscribe(msg, msg_cbfunc)注冊消息回調函數(shù);這樣才能保證發(fā)布的msg消息可以被msg_cbfunc消息回調函數(shù)處理;

2、在sys.publish(msg, ...)之前,必須保證task正在sys.waitUntil(msg, timeout)代碼處,處于阻塞等待狀態(tài);這樣才能保證發(fā)布的msg消息可以被task處理;

現(xiàn)在我們討論的用戶定向消息已經(jīng)不存在這個問題,消息發(fā)布和消息處理沒有嚴格的時序要求,可以先發(fā)送定向消息,消息會存儲到定向消息隊列中,只有當調用sys.waitMsg(task_name, msg, timeout)接口主動去讀取定向消息時,才會從定向消息隊列中讀出消息進行處理,所以不存在消息丟失的問題;但是如果使用不當,可能會出現(xiàn)消息處理延遲的問題;

了解了用戶定向消息的完整生命周期后,接下來我們:

1、先看下在整個生命周期中用到的幾個sys核心庫的api

2、寫一個完整的demo示例來實際運行演示一下如何使用;

sys.sendMsg(task_name, msg, arg2, arg3, arg4)

功能

向名稱為task_name的task發(fā)布一個定向消息;

注意事項

可以在能夠執(zhí)行到的任意代碼位置使用此函數(shù);

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生產(chǎn)者,定向消息有生產(chǎn)就會有消費,不然消息就沒有存在的意義了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消費者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保證名稱為task_name的task已經(jīng)被創(chuàng)建,否則定向消息也會丟失;

參數(shù)

task_name

參數(shù)含義:task的名稱,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;


msg

參數(shù)含義:消息的名稱;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


arg2

參數(shù)含義:msg消息攜帶的第一個參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;


arg3

參數(shù)含義:msg消息攜帶的第二個參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;


arg4

參數(shù)含義:msg消息攜帶的第三個參數(shù);

數(shù)據(jù)類型:任意數(shù)據(jù)類型;

取值范圍:無特別限制;

是否必選:可選傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:

不傳入,或者boolean類型的true,或者number類型的2,或者string類型的"led",或者table類型的{name="LuatOS", password="123456"}等等等等;

總之,任何數(shù)據(jù)類型的任何自定義內容都行;


返回值

local result = sys.sendMsg(task_name, msg, arg2, arg3, arg4)

result

含義說明:定向消息發(fā)布結果,成功返回true,失敗返回false;

數(shù)據(jù)類型:boolean;

取值范圍:true或者false;

注意事項:暫無;


示例

wKgZO2l4j0uAL2euAACOd9SUZ00875.png

sys.waitMsg(task_name, msg, timeout)

功能

在task中阻塞等待名稱為task_name的task的定向消息;

注意事項

只能在高級task處理函數(shù)的業(yè)務邏輯中使用此函數(shù);

sys.sendMsg(task_name, msg, arg2, arg3, arg4)是定向消息的生產(chǎn)者,定向消息有生產(chǎn)就會有消費,不然消息就沒有存在的意義了;

sys.waitMsg(task_name, msg, timeout)所在的task是定向消息的消費者;

sys.sendMsg(task_name, msg, arg2, arg3, arg4) 和 sys.waitMsg(task_name, msg, timeout)配合使用;

在sys.sendMsg(task_name, msg, arg2, arg3, arg4)之前,需要保證名稱為task_name的task已經(jīng)被創(chuàng)建,否則定向消息也會丟失;

參數(shù)

task_name

參數(shù)含義:task的名稱,和sys.taskInitEx(task_func, task_name, non_targeted_msg_cbfunc, ...)中的task_name保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"LED_TASK"、"GPIO_TASK"等任意自定義的字符串;


msg

參數(shù)含義:消息的名稱,和sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的msg保持一致;

數(shù)據(jù)類型:推薦string類型(雖然number類型也行,但是不好理解,不要使用);

取值范圍:任意string類型的字符串都行,無特別限制;

是否必選:必須傳入此參數(shù);

注意事項:暫無;

參數(shù)示例:"SEND_DATA_REQ"等任意自定義的字符串;


timeout

參數(shù)含義:阻塞等待定向消息msg的超時時長,單位毫秒,nil表示一直阻塞等待;

數(shù)據(jù)類型:number或者nil;

取值范圍:

大于等于1,小于等于0x7FFFFFFF,之間的所有正整數(shù);最大時長0x7FFFFFFF毫秒 ≈ 596小時 ≈ 24.85天;

如果為nil,表示一直阻塞等待全局消息,不會超時;

是否必選:可選傳入此參數(shù);

注意事項:

此處的超時機制基于軟件定時器實現(xiàn),受系統(tǒng)負載、任務數(shù)量、消息堆積、網(wǎng)絡中斷優(yōu)先級最高等多因素影響,無法實現(xiàn)高精度;

尤其是幾個毫秒級別,幾十毫秒級別,幾百毫秒級別的超時時長,誤差都比較大,秒級別以上的超時時長誤差較??;

例如:設置的1毫秒,可能要等幾十毫秒;設置的幾十毫秒,可能要等上百毫秒;設置的幾百毫秒,可能要等幾百幾十毫秒;

可以簡單的認為會延遲幾十毫秒左右,以此來評估超時時長精度是否可以滿足業(yè)務需求;

調用本接口時,如果傳入了timeout參數(shù),則sys核心庫內部會自動創(chuàng)建并且運行一個軟件定時器,超時時長到達后,會自動刪除這個定時器;

LuatOS應用程序中可用的軟件定時器總數(shù)量為64個;

注意控制自已應用程序中的同時運行的軟件定時器總數(shù)量不要超過64個,否則創(chuàng)建新的定時器會返回失??;

參數(shù)示例:50、1000、60000等;


返回值

local message = sys.waitMsg(task_name, msg, timeout)

有一個返回值為message

message

含義說明:

阻塞等待的結果;table類型表示接收到msg消息,false表示超時沒有收到msg消息;

當接收到msg消息時,message[1],message[2],message[3],message[4]和sys.sendMsg(task_name, msg, arg2, arg3, arg4)中的 msg, arg2, arg3, arg4 一一對應;

當超時沒有收到msg消息時,message為false;

數(shù)據(jù)類型:table或者boolean;

取值范圍:無特別限制;

注意事項:暫無;


示例

wKgZPGl4kCKAZRHbAAPz6rlPN8w945.png

用戶定向消息處理的代碼示例

在了解了定向消息的兩個api之后,我們再看下圖回顧一下定向消息處理的完整周期

wKgZO2l4kIaAMrk4AAJPv_zc334783.png

下面這個例子用來說明用戶定向消息的完整處理過程;

這個例子的完整代碼鏈接:tgted_msg_receiver.luatargeted_msg_sender.lua

核心代碼片段如下,我們首先分析下這兩段代碼的業(yè)務邏輯

wKgZPGl4kUGANjYqAAtFCMrTnJ8067.png

我們在模擬器上實際運行一下看看,輸入命令

luatos --llt=H:Luatoolsprojectluatos_framework_luatos_task_Air8000.ini

運行15秒鐘的日志如下,為了方便分析,我把日志做了處理,每個消息接收者的日志匯總到了一起:

normal_wait_msg_task接收者

nromal_wait_msg_task的任務處理函數(shù)中,調用sysplus.waitMsg("nromal_wait_msg_task", "SEND_DATA_REQ")接收消息,在15秒的時間內,一共收到15條消息,運行正常,符合預期,每秒接收到一次消息;

wKgZPGl4kXyAGyfhAALC9rV6F48731.png

delay_wait_msg_task接收者

delay_wait_msg_task的任務處理函數(shù)中,調用sysplus.waitMsg("nromal_wait_msg_task", "SEND_DATA_REQ")接收消息,在15秒的時間內,一共收到5條消息,和代碼設計相符,每延時3秒接收到一次消息;

但是我們再仔細觀察下收到消息,消息的計數(shù)參數(shù)依次為1,2,3,4,5,說明雖然延時收到消息,但是消息并沒有出現(xiàn)丟失;

這一點兒和上一小節(jié)講的全局消息的處理完全不同,全局消息在這種情況下會丟失消息;

wKgZO2l4kaKACU2rAADx08tBc3Y335.png

2.2.3.5 用戶定向消息 vs 用戶全局消息,如何選擇?

在了解了用戶定向消息和用戶全局消息之后,大家可能會有一個疑問:我平時開發(fā)程序時,怎么判斷應該使用定向消息還是全局消息呢?一般來說,建議大家按照以下幾步來做選擇:

1、首先確認你的程序中有沒有使用高級task,因為有些應用功能模塊是必須要使用高級task的,例如創(chuàng)建一個socket時,要使用高級task;如果使用了高級task,那就只能使用用戶定向消息了;如果沒有使用高級task,繼續(xù)向下判斷;

如果你對用戶全局消息的處理過程以及其中容易出現(xiàn)的消息丟失問題,非常清楚,知道如何規(guī)避這種消息丟失問題,則可以使用全局消息,因為全局消息使用起來簡單;

如果你對用戶全局消息的處理過程以及其中容易出現(xiàn)的問題都不是很清楚,這種情況下,建議使用定向消息和高級task,因為這種方式下,只要在發(fā)送定向消息前,高級task已經(jīng)創(chuàng)建,則消息不會丟失;

由于篇幅過長,更多精彩內容,請看下篇~


審核編輯 黃宇

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

    關注

    2943

    文章

    47715

    瀏覽量

    412599
  • LuatOS
    +關注

    關注

    0

    文章

    146

    瀏覽量

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

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    基于LuatOS的MQTT物聯(lián)網(wǎng)通信全解

    在構建物聯(lián)網(wǎng)終端設備時,通信協(xié)議的選擇直接決定系統(tǒng)的穩(wěn)定性與擴展性。LuatOS通過內置MQTT客戶端支持,使開發(fā)者能以極少代碼實現(xiàn)設備云。本文將從協(xié)議原理到代碼實現(xiàn),全面解析基于LuatOS
    的頭像 發(fā)表于 01-29 19:42 ?23次閱讀
    基于<b class='flag-5'>LuatOS</b>的MQTT物聯(lián)網(wǎng)通信全解

    輕松掌握——LuatOS socket基礎知識和應用開發(fā)

    socket課程主要包含以下幾個部分: 1、TCP/IP總體介紹; 2、LuatOS的?4G/WiFi/以太網(wǎng)?三種網(wǎng)絡環(huán)
    的頭像 發(fā)表于 01-28 20:07 ?38次閱讀
    輕松掌握——<b class='flag-5'>LuatOS</b>  socket基礎知識和應用開發(fā)

    解鎖:LuatOS框架的使用(下篇)

    接上一篇 2.3 LuatOS 的定時器(timer) 對于 LuatOS 應用程序來說,定時器本質也算是一種特殊的消息,因為定時器太常用了,所以把他單獨拎出來,單獨的一個章節(jié)進行講解
    的頭像 發(fā)表于 01-28 13:18 ?38次閱讀
    解鎖:<b class='flag-5'>LuatOS</b><b class='flag-5'>框架</b>的使用(下篇)

    LuatOS-Air腳本移植到LuatOS版本注意事項

    一、lua版本不一樣 LuatOS-Air使用的是lua5.1版本,本身不支持位移運算符。 LuatOS使用的是lua5.3版本,取消了module(..., package.seeall)這種形式
    的頭像 發(fā)表于 01-17 14:48 ?1085次閱讀
    <b class='flag-5'>LuatOS</b>-Air腳本移植到<b class='flag-5'>LuatOS</b>版本注意事項

    LuatOS-Air轉LuatOS常見故障排查手冊

    LuatOS-Air腳本在LuatOS環(huán)境中運行失敗,問題往往集中在幾個關鍵模塊:任務調度、外設驅動、網(wǎng)絡配置和固件版本匹配。本文以故障排查的邏輯為主線,列出常見報錯現(xiàn)象、可能原因及快速修復
    的頭像 發(fā)表于 01-13 19:20 ?57次閱讀
    <b class='flag-5'>LuatOS</b>-Air轉<b class='flag-5'>LuatOS</b>常見故障排查手冊

    警惕兼容性陷阱:LuatOS-Air腳本在LuatOS中的運行異常分析

    即使語法正確的LuatOS-Air腳本,在LuatOS環(huán)境中也可能出現(xiàn)“靜默失敗”——程序無報錯但功能未執(zhí)行。這類問題多與系統(tǒng)事件循環(huán)、模塊加載時機或硬件抽象層調用方式有關。本文通過多個真實
    的頭像 發(fā)表于 01-13 19:20 ?85次閱讀
    警惕兼容性陷阱:<b class='flag-5'>LuatOS</b>-Air腳本在<b class='flag-5'>LuatOS</b>中的運行異常分析

    掌握LuatOS系統(tǒng)消息:新手也能看懂的列表詳解

    視角出發(fā),用通俗語言解析其工作原理與配置方法。此處列舉了LuatOS框架中自帶的系統(tǒng)消息列表。 ? 一、sys ? 文檔鏈接:https://docs.openluat.com/osapi/core
    的頭像 發(fā)表于 01-13 18:12 ?53次閱讀
    掌握<b class='flag-5'>LuatOS</b>系統(tǒng)消息:新手也能看懂的列表詳解

    教程來啦!LuatOS中的消息通信機制詳解及其應用場景

    在資源受限的嵌入式環(huán)境中,LuatOS采用消息機制實現(xiàn)模塊間解耦與高效通信。通過預定義消息名稱(如“new_msg”),開發(fā)者可輕松構建響應式程序結構。接下來我們將深入剖析其實現(xiàn)原理與典型使用方法
    的頭像 發(fā)表于 09-26 18:59 ?353次閱讀
    教程來啦!<b class='flag-5'>LuatOS</b>中的消息通信機制詳解及其應用場景

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

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

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

    LuatOS正成為嵌入式開發(fā)的新趨勢!本教程帶你從基礎入手,全面了解其基于Lua的腳本開發(fā)模式與輕量級運行框架。 一、LuatOS 編程起步 1.1 底層固件怎么啟動 LuatOS
    的頭像 發(fā)表于 09-26 17:34 ?496次閱讀
    嵌入式開發(fā)新選擇:<b class='flag-5'>LuatOS</b>腳本<b class='flag-5'>框架</b>入門教程

    Task任務:LuatOS實現(xiàn)“任務級并發(fā)”的核心引擎

    Task任務通過其強大的并發(fā)處理能力,使LuatOS能夠在單線程環(huán)境中模擬多線程執(zhí)行,通過協(xié)程的掛起與恢復機制,實現(xiàn)任務級的并行操作,顯著提升系統(tǒng)效能。 sys核心庫是LuatOS運行框架庫,也是
    的頭像 發(fā)表于 08-28 13:49 ?452次閱讀
    Task任務:<b class='flag-5'>LuatOS</b>實現(xiàn)“任務級并發(fā)”的核心引擎

    揭秘LuatOS Task:多任務管理的“智能中樞”

    Task任務作為LuatOS的核心組成部分,通過智能化的任務管理機制,實現(xiàn)任務的創(chuàng)建、調度與協(xié)同運行,讓復雜應用得以高效并行處理,滿足實時場景下的嚴苛需求。 sys核心庫是LuatOS運行框架
    的頭像 發(fā)表于 08-28 13:48 ?561次閱讀
    揭秘<b class='flag-5'>LuatOS</b> Task:多任務管理的“智能中樞”

    解碼LuatOS:短信功能的底層運作機制

    短信功能在LuatOS中的運行并非表面所見那么簡單。本文將深入系統(tǒng)底層,解碼其通信協(xié)議、數(shù)據(jù)處理與系統(tǒng)交互,呈現(xiàn)完整的運作圖譜。 我們這期主要拆解airsms.lua文件,講清楚,短信功能
    的頭像 發(fā)表于 06-27 18:05 ?564次閱讀
    解碼<b class='flag-5'>LuatOS</b>:短信功能的底層運作機制

    零基礎學習LuatOS編程:快速上手開發(fā)實戰(zhàn)教程!

    無論你是剛接觸物聯(lián)網(wǎng)編程的新手,還是希望拓展技能的技術愛好者,本教程將為零基礎的讀者提供一條清晰的LuatOS學習路徑。從安裝開發(fā)工具到編寫第一個程序,我們將通過實例講解核心概念,助你快速實現(xiàn)從理論
    的頭像 發(fā)表于 06-13 17:27 ?559次閱讀
    零基礎學習<b class='flag-5'>LuatOS</b>編程:快速上手開發(fā)實戰(zhàn)教程!

    LuatOS編程基礎教程:手把手帶你入門物聯(lián)網(wǎng)開發(fā)!

    對于渴望進入物聯(lián)網(wǎng)開發(fā)領域的初學者來說,LuatOS是一個理想的起點。本教程將通過由淺入深的教學方式,從搭建開發(fā)環(huán)境、理解核心API到編寫簡單應用,逐步引導你掌握LuatOS編程的核心技能,開啟智能
    的頭像 發(fā)表于 06-11 13:03 ?718次閱讀
    <b class='flag-5'>LuatOS</b>編程基礎教程:手把手帶你入門物聯(lián)網(wǎng)開發(fā)!