当前位置: 首页 > news >正文

[APM32F4] 跟随大神 Fabrice Bellard 的脚步,把 MicroQuickJS 移植到 APM32F427

MicroQuickJS 在 APM32F427 的移植示例

这几天,国内开源新闻都在报道一个新的开源项目:MicroQuickJS。

该项目是传奇人物 Fabrice Bellard 的开源新作品,是一款专为嵌入式系统设计的高效 JavaScript 引擎。详见 mquickjs 仓库 → https://github.com/bellard/mquickjs

这名工程师的传奇经历,大家自行搜搜膜拜。今天带来的小作文是把这个引擎移植到 APM32F427 并跑一个简单的 Demo,话不多说,这就开始。

移植目标

先定个小目标——点灯:

复制
  1. varnumber=12;
  2. varkey2_pinname ="PD9";
  3. // 基本数学运算
  4. number+=8;
  5. console.log("The answer is "+number);
  6. console.log("Sin(PI/4) = "+Math.sin(Math.PI /4));
  7. // 板载 GPIO 示例
  8. varled2 =newAPM32F4xx_Pin("PF0");
  9. varled3 =newAPM32F4xx_Pin("PF1");
  10. varkey2 =newAPM32F4xx_Pin(key2_pinname);
  11. varts =Date.now();
  12. varled3_level =0;
  13. led2.mode ="OUT"
  14. led3.mode ="OUT"
  15. key2.mode ="IN"
  16. led3.level = led3_level;
  17. console.log("LED2 mode: "+ led2.mode);
  18. console.log("LED3 mode: "+ led3.mode);
  19. console.log("KEY2 mode: "+ key2.mode);
  20. // 按键回调函数
  21. varcb =function(pin, level) {
  22. console.log("Interrupt! PIN: "+ pin +", LEVEL: "+ level);
  23. if(led3_level ==0) {
  24. led3_level =1;
  25. }else{
  26. led3_level =0;
  27. }
  28. console.log("LED3: "+ led3_level);
  29. led3.level = led3_level;
  30. }
  31. // 注册按键回调
  32. APM32F4xx_Pin.int_register(key2_pinname, cb);
  33. // 闪灯
  34. number=5;
  35. while(number>0) {
  36. ts =Date.now();
  37. led2.level =1;
  38. while(Date.now() - ts <500);
  39. ts =Date.now();
  40. led2.level =0;
  41. while(Date.now() - ts <500);
  42. number-=1;
  43. console.log(":: "+number+", "+ key2.level);
  44. }
  45. console.log("Blinked!")


功能就看上面 js 文件的注释,不再赘述。

移植要点
先创建一个 APM32F427 的基础工程,实现:

  • 串口,对接 printf。
  • GPIO,实现 LED 控制及按键中断



参考移植的代码仓库 board\board_apm32f427z_tiny.c 文件。

第二步是 mquickjs 的移植。

从 mquickjs 代码仓库克隆代码,放在工程 mquickjs 文件夹,按以下说明修改。

  • mquickjs 仓库所需修改文件:
  • example.c
  • example_stdlib.c 和 mqjs_stdlib.c
  • mquickjs.h
  • 使用 mquickjs 源码自带的工具生成
  • mquickjs_atom.h
  • example_stdlib.h



example.c

这个文件实现了板级驱动与 js 引擎的对接。

例如创建 led 的类 APM32F4xx_Pin 的构造函数 js_apm32f4_gpio_constructor:

复制
  1. staticJSValuejs_apm32f4_gpio_constructor(JSContext *ctx, JSValue *this_val,intargc,
  2. JSValue *argv)
  3. {
  4. uint32_tpin;
  5. APM32F4_GPIO_Data *d;
  6. JSValue obj;
  7. if(!(argc & FRAME_CF_CTOR))
  8. returnJS_ThrowTypeError(ctx,"must be called with new");
  9. argc &= ~FRAME_CF_CTOR;
  10. if(argc <1)
  11. {
  12. returnJS_ThrowTypeError(ctx,"Pin-name must be not empty");
  13. }
  14. JSCStringBuf buf;
  15. constchar*str;
  16. size_tlen;
  17. str = JS_ToCStringLen(ctx, &len, argv[0], &buf);
  18. pin = board_pin_from_name(str);
  19. if(pin == INVALID_PIN_NUMBER)
  20. {
  21. returnJS_ThrowTypeError(ctx,"Invalid Pin-name");
  22. }
  23. obj = JS_NewObjectClassUser(ctx, JS_CLASS_APM32F4_GPIO);
  24. d =malloc(sizeof(*d));
  25. d->pin_number = pin;
  26. JS_SetOpaque(ctx, obj, d);
  27. returnobj;
  28. }


对应的 js 代码:

复制
  1. varled2 =newAPM32F4xx_Pin("PF0");


example.c 还包含了 example_stdlib.h 这个文件,但是这个文件不是手写的,也不能随意复制,而只能使用 mquickjs 自带的工具自动生成。还有 mquickjs_atom.h 也是类似的方式生成。

两个头文件对应两个可执行程序,都是需要在主机(电脑)上编译执行,可以参考 mquickjs\Makefile 文件内容。

编译这两个工具需要 make.exe 和 gcc.exe,请自行搜索安装。我的移植仓库中提供了一个 build_stdlib.bat 批处理文件可以生成工具,并生成这两个头文件。

example_stdlib.c 和 mqjs_stdlib.c

example_stdlib.c 这个文件末尾会包含 mqjs_stdlib.c,用于生成上述工具使用,不需要编译到单片机程序里

这(两)个文件主要是定义 JS 环境下的类,主要内容是:

复制
  1. staticconstJSPropDef js_apm32f4_gpio_proto[] = {
  2. JS_CGETSET_DEF("level", js_apm32f4_pin_level_get, js_apm32f4_pin_level_set ),
  3. JS_CGETSET_DEF("mode", js_apm32f4_pin_mode_get, js_apm32f4_pin_mode_set ),
  4. JS_PROP_END,
  5. };
  6. staticconstJSPropDef js_apm32f4_gpio[] = {
  7. JS_CFUNC_DEF("int_register",2, js_apm32f4_gpio_int_register ),
  8. JS_PROP_END,
  9. };
  10. staticconstJSClassDef js_apm32f4_gpio_class = JS_CLASS_DEF("APM32F4_Pin",1, js_apm32f4_gpio_constructor, JS_CLASS_APM32F4_GPIO, js_apm32f4_gpio, js_apm32f4_gpio_proto,NULL, js_apm32f4_gpio_finalizer);


js_apm32f4_gpio_class 定义了一个名为 APM32F4_Pin 的 JS 类:

  • 具备level和mode两个属性,并且两个属性均可以 set/get
  • 一个用于注册中断的函数int_register



在 js 里可以这样使用:

复制
  1. led2.mode ="OUT";
  2. led3.level =1;
  3. led3.level =0;
  4. console.log("LED2 Mode: "+ led2.mode );


mquickjs.h

这个文件需要注释这一部分:

复制
  1. //#if INTPTR_MAX >= INT64_MAX
  2. //#define JS_PTR64 /* pointers are 64 bit wide instead of 32 bit wide */
  3. //#endif


注释掉的原因是强制使用 32-bit 平台,而不是自动判断。

自动判断会出问题是因为:自动生成 example_stdlib.h 和 mquickjs_atom.h 的工具是在 PC 上编译和运行,采用自动判断的结果是 64-bit,生成的头文件也是使用于 64-bit 与 MCU 的运行环境不同。为了兼顾 MCU 所以只能强制在 PC 上也是 32-bit。

js 文件的存储
当前的 MCU 没有移植文件系统,为了也能相对灵活的运行 js 脚本,在 Flash 里划出了最高 16KiB 空间把 js 文件直接存在这个空间中。如果需要更换 js 脚本,可以使用 jFlash 把 js 脚本写入这个空间。

在工程里,可以通过分散加载文件实现空间划分,可以通过 .incbin 汇编伪指令把文件直接打包到固件中,以便提供“内置”的 js 文件。

分散加载文件示例。把文件放在 0x08000000 ~ 0x080FBFFF 存放固件,0x080FC000 以上存 js 文件:

LR_IROM1 0x08000000 0x080FC000 { ; load region size_region
ER_IROM1 0x08000000 0x000FC000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00060000 { ; RW data
.ANY (+RW +ZI)
}
}

LR_IROM2 0x080FC000 0x4000 {
JS_IMAGE 0x080FC000 0x4000 {
*(.js_image)
}
}
C 文件中使用 .incbin 汇编伪指令把文件直接打包到固件中:

复制
  1. __attribute__((__used__, section(".js_image")))
  2. voidram_iamge(void)
  3. {
  4. __asm(".incbin "t.js"");
  5. __asm(".incbin "zero.bin"");
  6. }


程序中直接使用如下的指令获取文件内容:

复制
  1. constchar * js_txt = (constchar *)&Image$$JS_IMAGE$$Base;


其中:t.js 是合法的 js 文件;zero.bin 是4个字节的“零”文件,是为了保证读取 0x080FC000 开始的字符串的"\0" 结尾。

编译运行
keil 工程中所需的 C 文件有:



除了 example.c 与 mquickjs 相关的有4个:cutils.c, dtoa.c, libm.c, mquickjs.c。

js 运行时会实例化类,所以依赖动态内存管理,所以也需要一定 heap 空间;同时栈空间也不能太小。我的设置是:



编译,下载,观察走起~

预期现象是:

复位后 LED2 闪烁 5 次后熄灭
按 KEY2 会切换 LED3

串口助手截图:
红色箭头是 bsp 部分的输出内容,律师框的部分是 js 脚本的输出。



视频为证:




最后
虽然本文介绍的是一个简单的示例,但是也证明 js 环境确实运行起来了。还针对 APM32F427 的 GPIO 设计了一个简单的类可供实例化并运行起来,麻雀虽小五脏俱全。

MicroQuickJS 还有更多值得深挖的点,大家都用起来呀~

能用js写的最终都会用js写


移植的 demo 放在代码仓库 → https://gitee.com/quincyzh/mquickjs_apm32f427

欢迎点评~



---------------------
作者:wangqy_ic
链接:https://bbs.21ic.com/icview-3501521-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

http://www.jsqmd.com/news/374497/

相关文章:

  • 基于PLC的群控电梯控制系统设计与仿真
  • 深挖 TBD 核心进阶点:深度学习匹配(目标关联的“智能指纹”)
  • 基于单片机的电子音乐门铃系统设计
  • 2026有机肥造粒机哪家好?口碑厂家一览无余,环保粉土机/药材粉碎机/锯末粉碎机/有机肥造粒机,造粒机厂家找哪家 - 品牌推荐师
  • 分析婚礼策划公司,AND婚礼性价比与品质兼具怎么样 - mypinpai
  • 基于LabVIEW的音效电路设计
  • 2026年山东电液伺服数控折弯机排名,哪家的价格合理 - 工业品网
  • 闭眼入!全网爆红的AI论文写作软件 —— 千笔·专业学术智能体
  • 基于PLC控制的机械手研究和设计
  • 电源使能信号:RTQ6362 EN/UVLO 启动 / 停止电压参数设计指南
  • 提取浓缩装置服务商靠谱吗,该如何选择合适的品牌 - 工业品牌热点
  • 山东一卡通还能回收吗?新手必读的回收指南! - 团团收购物卡回收
  • 聊聊广州知名的红点概念设计奖申请公司,哪家性价比高 - 工业设备
  • 基于C51单片机单片机的水温控制系统
  • 共话安徽口碑好的冷却塔喷头企业,诚信的冷却塔喷头源头厂家怎么选 - myqiye
  • 2026年永辉超市卡回收价格一览表清晰版 - 京回收小程序
  • 2026年京津冀商业空间施工企业排名,展卓装饰靠谱之选 - 工业设备
  • IC697CGR772处理器单元模块
  • 2026年全国不锈钢厂家权威榜单 实力靠谱厂家甄选 适配多行业工程及定制需求 - 深度智识库
  • IC697ALG441模拟输入扩展器模块
  • Redis真的是单线程吗?
  • Nodejs+vue+ElementUI框架学生读书笔记共享平台设计与实现
  • IC697BEM741总线控制器
  • Nodejs+vue+ElementUI框架农村养殖管理系统设计与实现
  • 探讨平舆的驾校培训,平舆驾校全科目自主训练有保障 - 工业品牌热点
  • 【计算机基础】-27-RT-Thread-idle线程如何计算自己的执行时间?
  • IC697BEM761总线控制器卡
  • 企业级指标中台 API/JDBC 架构选型四步法
  • Redis6为何引入多线程?
  • 双相钢法兰供应商分析,助您做出明智选择,不锈钢法兰/非标法兰/变压器法兰/不锈钢管板/船用法兰,双相钢法兰公司找哪家 - 品牌推荐师