# mquickjs_apm32f427 **Repository Path**: quincyzh/mquickjs_apm32f427 ## Basic Information - **Project Name**: mquickjs_apm32f427 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-12-30 - **Last Updated**: 2026-01-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README MicroQuickJS 在 APM32F427 的移植示例 跟随大神 Fabrice Bellard 的脚步,把 MicroQuickJS 移植到 APM32F427。 MicroQuickJS 是传奇人物 Fabrice Bellard 的开源新作品,是一款专为嵌入式系统设计的高效 JavaScript 引擎。详见 mquickjs 仓库 → https://github.com/bellard/mquickjs 今天带来的小作文是把这个引擎移植到 APM32F427 并跑一个简单的 Demo,话不多说,这就开始。 # 移植目标 先定个小目标——点灯: ``` JS var number = 12; var key2_pinname = "PD9"; // 基本数学运算 number += 8; console.log("The answer is " + number); console.log("Sin(PI/4) = " + Math.sin(Math.PI / 4)); // 板载 GPIO 示例 var led2 = new APM32F4xx_Pin("PF0"); var led3 = new APM32F4xx_Pin("PF1"); var key2 = new APM32F4xx_Pin(key2_pinname); var ts = Date.now(); var led3_level = 0; led2.mode = "OUT" led3.mode = "OUT" key2.mode = "IN" led3.level = led3_level; console.log("LED2 mode: " + led2.mode); console.log("LED3 mode: " + led3.mode); console.log("KEY2 mode: " + key2.mode); // 按键回调函数 var cb = function(pin, level) { console.log("Interrupt! PIN: " + pin + ", LEVEL: " + level); if (led3_level == 0) { led3_level = 1; } else { led3_level = 0; } console.log("LED3: " + led3_level); led3.level = led3_level; } // 注册按键回调 APM32F4xx_Pin.int_register(key2_pinname, cb); // 闪灯 number = 5; while(number > 0) { ts = Date.now(); led2.level = 1; while (Date.now() - ts < 500); ts = Date.now(); led2.level = 0; while (Date.now() - ts < 500); number -= 1; console.log(":: " + number + ", " + key2.level); } console.log("Blinked!") ``` 功能就看上面 js 文件的注释,不再赘述。 # 移植要点 创建一个 APM32F427 的基础工程,实现: - 串口,对接 printf。 - GPIO,实现 LED 控制及按键中断 参考代码仓库 board\\board_apm32f427z_tiny.c 文件。 从 mquickjs 代码仓库克隆代码,放在工程 mquickjs 文件夹,按以下说明修改。 - mquickjs 仓库所需修改文件: 1. example.c 2. example_stdlib.c 和 mqjs_stdlib.c 3. mquickjs.h - 使用 mquickjs 源码自带的工具生成 1. mquickjs_atom.h 2. example_stdlib.h ## example.c 这个文件实现了板级驱动与 js 引擎的对接。 例如创建 led 的类 APM32F4xx_Pin 的构造函数 **js_apm32f4_gpio_constructor**: ``` C static JSValue js_apm32f4_gpio_constructor(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { uint32_t pin; APM32F4_GPIO_Data *d; JSValue obj; if (!(argc & FRAME_CF_CTOR)) return JS_ThrowTypeError(ctx, "must be called with new"); argc &= ~FRAME_CF_CTOR; if (argc < 1) { return JS_ThrowTypeError(ctx, "Pin-name must be not empty"); } JSCStringBuf buf; const char *str; size_t len; str = JS_ToCStringLen(ctx, &len, argv[0], &buf); pin = board_pin_from_name(str); if (pin == INVALID_PIN_NUMBER) { return JS_ThrowTypeError(ctx, "Invalid Pin-name"); } obj = JS_NewObjectClassUser(ctx, JS_CLASS_APM32F4_GPIO); d = malloc(sizeof(*d)); d->pin_number = pin; JS_SetOpaque(ctx, obj, d); return obj; } ``` 对应的 js 代码: ``` JS var led2 = new APM32F4xx_Pin("PF0"); ``` example.c 还包含了 **example_stdlib.h** 这个文件,但是这个文件不是手写的,也不能随意复制,而只能使用 mquickjs 自带的工具自动生成。此外,还有 **mquickjs_atom.h** 也是类似的方式生成。 两个头文件对应两个可执行程序,都是需要在主机(电脑)上编译执行,可以参考 mquickjs\\Makefile 文件内容。 编译这两个工具需要 make.exe 和 gcc.exe,请自行搜索安装。我推荐 **w64devkit** 这个工具,下载地址:https://github.com/skeeto/w64devkit/releases , 需要下载 32-bit 的那个套件:w64devkit-x86-\.7z.exe。 我的移植仓库中提供了一个 **build_stdlib.bat** 批处理文件可以生成工具,并生成这两个头文件。 ## example_stdlib.c 和 mqjs_stdlib.c **example_stdlib.c** 这个文件末尾会包含 **mqjs_stdlib.c**,用于生成上述工具使用,不需要编译到单片机程序里。 这(两)个文件主要是定义 JS 环境下的类,主要内容是: ``` C static const JSPropDef js_apm32f4_gpio_proto[] = { JS_CGETSET_DEF("level", js_apm32f4_pin_level_get, js_apm32f4_pin_level_set ), JS_CGETSET_DEF("mode", js_apm32f4_pin_mode_get, js_apm32f4_pin_mode_set ), JS_PROP_END, }; static const JSPropDef js_apm32f4_gpio[] = { JS_CFUNC_DEF("int_register", 2, js_apm32f4_gpio_int_register ), JS_PROP_END, }; static const JSClassDef 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 里可以这样使用: ``` js led2.mode = "OUT"; led3.level = 1; led3.level = 0; console.log("LED2 Mode: " + led2.mode ); ``` ## mquickjs.h 这个文件需要注释这一部分: ``` C // #if INTPTR_MAX >= INT64_MAX // #define JS_PTR64 /* pointers are 64 bit wide instead of 32 bit wide */ // #endif ``` 注释掉的原因是强制使用 32-bit 平台,而不是自动判断。 自动判断会出问题的原因是在于:自动生成 **example_stdlib.h** 和 **mquickjs_atom.h** 的工具是在 PC 上编译和运行,采用自动判断的结果是 64-bit,生成的头文件也是使用于 64-bit,与 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** 汇编伪指令把文件直接打包到固件中: ``` C __attribute__((__used__, section(".js_image"))) void ram_iamge(void) { __asm(".incbin \"t.js\""); __asm(".incbin \"zero.bin\""); } ``` 程序中直接使用如下的指令获取文件内容: ``` C const char * js_txt = (const char *)&Image$$JS_IMAGE$$Base; ``` t.js 是合法的 js 文件;zero.bin 是4个字节的“零”文件,是为了保证读取 0x080FC000 开始的字符串的"\0" 结尾。 # 编译运行 keil 工程中所需的C文件有: ![keil](./doc/fdf94d51ac9644818953b234f4b97526.png) js 运行时会实例化类,所以依赖动态内存管理,所以也需要一定 heap 空间;同时栈空间也不能太小。我的设置是: ![heap-stack.png](./doc/bc5c937622d34d64918bd459182f8d3a.png) 编译,下载,观察走起~ 预期现象是: - 复位后 LED2 闪烁 5 次后熄灭 - 按 KEY2 会切换 LED3 ![seial.png](./doc/a36d6e5b0032480ba5671dc0865946eb.png) 视频为证: ![GIF](./doc/show.gif) # 最后 虽然本文介绍的是一个简单的示例,但是也证明 js 环境确实运行起来了。还针对 APM32F427 的 GPIO 设计了一个简单的类可供实例化并运行起来,麻雀虽小五脏俱全。 > 能用js写的最终都会用js写 MicroQuickJS 还有更多值得深挖的点,大家都用起来呀~ 移植的 demo 放在代码仓库 → https://gitee.com/quincyzh/mquickjs_apm32f427 欢迎点评~