打破單片機傳統的開發模式-膠水語言(JavaScript)

沃愛單片機 發佈 2024-03-11T02:47:16.311723+00:00

JerryScriptPikaScript資源占用RAM <= 64KB, Flash <= 200KBRAM <= 4KB, Flash <= 32KB語言JavaScriptPython地域海外中國維護情況停止維護持續維護開發對象懂得前端的人員也可以接手嵌入式應用開發需要熟悉python語言開發難度一般一般使用情況UI廠商都是用,柿餅,ACE相對較少。

概述

  • 傳統的嵌入式單片機開發基本上形式如下圖:
  • 該流程對於功能單一或者功能變更極少的場景是比較友好的,但是對於設備應用層變更比較多或者公板方案開發應用的場景,上述場景顯的有些累贅。那麼有什麼方式可以解決呢??
  • 對於設備應用層變更比較多或者公板方案開發應用的場景,可能因為應用層稍微修改一下就要出固件版本驗證,這對於版本管理,時間周期,固件質量都是比較不友好的。那麼我們如何避免這些問題??
  • 那麼有什麼方式呢??答案是有的,如:使用動態模塊或者膠水語言(Jerryscript,PikaScript)
  • 動態模塊:它更多的是一個 ELF 格式加載器,把單獨編譯的一個 elf 文件的代碼段,數據段加載到內存中,並對其中的符號進行解析,綁定到導出的 API 地址上。因為也獨立於固件編譯,支持動態加載。不過需要編譯一份支持動態模塊執行的固件。
  • 膠水語言(JerryScript,PikaScript):其實就是腳本語言,應用將以腳本語言的形式存在,通過動態加載腳本語言執行。不過固件需要對應膠水語言的執行引擎。
  • 上述兩種方式都是可以使固件跟應用分離,是的應用的變更不會引起固件的變更,這對於固件的穩定性來說更加有保障。只需要測試單獨的應用程式。
  • 動態模塊相對於膠水語言來說,明顯優勢不高,對比:
    動態模塊膠水語言
    API問題運行固件需要特殊處理,需要將API導出通過對應的引擎編寫API導出模塊應用形式應用程式需要通過固件編譯出對應的ELF文件膠水語言無需編譯,直接可通過對應引擎加載運行
  • 很明顯,作者傾向於膠水來改變開發模式,那麼使用哪種膠水語言呢??目前輕量級的膠水語言,有JerryScript,PikaScript。我們該如何選擇??

JerryScriptPikaScript資源占用RAM <= 64KB, Flash <= 200KBRAM <= 4KB, Flash <= 32KB語言JavaScriptPython地域海外中國維護情況停止維護持續維護開發對象懂得前端的人員也可以接手嵌入式應用開發需要熟悉python語言開發難度一般一般使用情況UI廠商都是用,柿餅,ACE相對較少

  • 兩種膠水語言各有各的優勢,我的選擇是根據使用場景,開發人員的角度,所以選擇JerryScript來解決我開發的困擾及問題。

JerryScript

物聯網設備在CPU性能和內存空間方面皆存在嚴格受限,在使用V8引擎這類大型引擎時難免存在諸多不便。在此背景下,JerryScript引擎誕生了。JerryScript是由三星開發的一款炙手可熱的輕量級引擎,其目的是讓JavaScript開發者能夠更好地構建物聯網應用, JerryScript是一個輕量級的JavaScript引擎,用於資源受限的設備,如微控制器。它可以在RAM小於64KB、快閃記憶體小於200KB的設備上運行。

  • JerryScript的主要特徵有:
  1. 完全符合ECMAScript 5.1標準;
  2. 為ARM Thumb-2編譯時,二進位大小為160K;
  3. 針對低內存消耗進行了高度優化;
  4. 以C99編寫,以實現最大的便攜性;
  5. 快照支持將JavaScript原始碼預編譯為字節代碼;
  6. 成熟的C API,易於嵌入應用程式。

JerryScript使用

目前很多UI廠商都在基於JerryScript作為引擎搭建UI框架,比如像RT-THREAD,OpenHarmony等廠商。而且JerryScript被默認作為第三方組件的形式存在。所以我將以RT-THREAD作為我的開發環境描述JavaScript如何在單片機中運行。

以字符串形式加載JS語法


  1. RT-THREAD中已經擁有JerryScript軟體包,所以我們需要下載對應軟體包即可:

  1. RT-THREAAD的JerryScript已經適配好了,如console列印等,所以我們也不用關心,直接使用。需要包含兩個頭文件:#include <jerryscript.h>和#include <jerry_util.h>
  2. JerryScript引擎啟動流程(初始化):
int main(void)
{
    /* JERRY_ENABLE_EXTERNAL_CONTEXT */
    jerry_port_set_default_context(jerry_create_context(PKG_JMEM_HEAP_SIZE * 1024, context_alloc, NULL));

    /* Initialize engine */
    jerry_init(JERRY_INIT_EMPTY);

    js_util_init();
    return RT_EOK;
}
  1. 因為我們還沒搭建文件系統所以不能存放XXX.js文件,我們先通過字符串的形式模擬文件內容。
char *script_test = 
        " var rice = \"Rice JerryScript\"\r\n"
        " console.log(\"hello!!\", rice);\r\n"
        " console.log(\"hello JerryScript run ok!!\"); \r\n";

void js_parse_test(void)
{
    jerry_value_t parsed_code = jerry_parse (NULL, 0, (jerry_char_t *)script_test,
        rt_strlen (script_test), JERRY_PARSE_NO_OPTS);
    if (jerry_value_is_error(parsed_code))
    {
        rt_kprintf("jerry parse failed!\n");
    }
    else
    {
        jerry_value_t ret2 = jerry_run(parsed_code);
        rt_kprintf("%s : jerry_run ret=%d\n", __func__, ret2);
    }
}
MSH_CMD_EXPORT(js_parse_test, js_parse_test);
  1. 編譯運行結果:

以文件的形式加載JS語法

  1. 需要增加文件系統及Ymodem,其中文件系統用來存放js文件,Ymodem用於把文件傳輸。
  • 增加文件系統組件:
  • 增加Ymodem組件:
  1. 文件系統掛載,我使用的板子有spi flash,所以文件系統直接掛載到此flash中:
int mnt_init(void)
{  
    if (dfs_mount("W25Q256", "/", "elm", 0, 0) == 0)
    {
        LOG_I("W25Q256 mount successful!");
    }
    else
    {
        LOG_E("W25Q256 mount failed!");
        dfs_mkfs("elm", "W25Q256");
        if (dfs_mount("W25Q256", "/", "elm", 0, 0) == 0)
        {
            LOG_I("W25Q256 mount successful!");
        }
    }
    return 0;
}
INIT_ENV_EXPORT(mnt_init);
  1. 編寫JS應用文件--rice.js
  • rice.js文件內容:
var rice = "Rice JerryScript";

console.log("hello!!", rice);
console.log("hello JerryScript run ok!!");
  1. 通過Ymodem傳輸到板子中,我使用的串口工具--XShell,它自帶Ymodem組件,所以可以直接傳輸,流程:
  • 在串口中斷輸入ry,使單片機進入Ymodem接收模式:
  • 然後選擇Ymodem發送文件:
  1. 編寫使用文件運行JS應用的代碼:
void js_parse_test(void)
{
    int fd = -1, fileSize = 0;
    char *fileContent = NULL;

    fd = open("/rice.js", O_RDONLY, 0777);
    if(fd < 0) {
        rt_kprintf("Open %s failed", "/rice.js");
        return;
    } else {
        fileSize = lSEEK(fd, 0, SEEK_END);
        lseek(fd, 0, SEEK_SET);
        fileContent = (char *)rt_malloc(fileSize); 
        read(fd, fileContent, fileSize);
        close(fd);
        fd = -1;
  }
 
 jerry_value_t parsed_code = jerry_parse((const jerry_char_t *)"/rice.js", (size_t)strlen("/rice.js"), 
                                               (const jerry_char_t *)fileContent, (size_t)fileSize, 
                                               JERRY_PARSE_STRICT_MODE);
    if (jerry_value_is_error(parsed_code))
    {
        rt_kprintf("jerry parse failed!\n");
    }
    else
    {
        jerry_value_t ret = jerry_run(parsed_code);
        rt_kprintf("%s : jerry_run ret=%d\n", __func__, ret);
    }                   
}
MSH_CMD_EXPORT(js_parse_test, js_parse_test);
  1. 編譯運行結果:

總結

  1. 採用膠水語言,可以減少對固件的修改,應用的變更不會導致固件的變更,而且修改方便快捷。
  2. 通過JavaScript,嵌入式研發人員,也慢慢變成類前後端開發模式,這樣職責更加清晰。
  3. JavaScript的運行如上,下一篇將講解C接口方法如何提供給JavaScript應用使用。
關鍵字: