MoonBit支持国产芯片开发--性能媲美C

MoonBit支持国产芯片开发–性能媲美C

在 ESP32-C3 上实现生命游戏

过去,我们曾在文章《硬件实现:在ESP32-C6单片机上运行MoonBit WASM-4小游戏》中,展示了如何通过 WebAssembly (WASM) 将 MoonBit 程序移植到物理硬件,初步探索其在嵌入式领域的潜力。
而如今,随着 MoonBit Native 后端 的正式发布: MoonBit 支持 Native 后端,MoonBit 迈出了关键一步:无需再依赖 WebAssembly 作为中间层,代码可以直接以原生形式运行在嵌入式硬件之上
这项进步不仅显著提升了性能和资源利用效率,也为 MoonBit 深度整合进嵌入式与物联网生态、直接控制硬件设备,奠定了坚实基础。

为了具体展示 MoonBit Native 在嵌入式开发中的优势,本文将通过一个经典实例——在乐鑫 ESP32-C3 芯片(或其 QEMU 仿真环境)上实现“康威生命游戏”——来进行探讨。

  • 生命游戏是一个非常经典的细胞自动机,在一个二维网格上模拟细胞的生死演化。每个细胞的状态(存活或死亡)根据其周围 8 个邻居的状态,在离散的时间步中同时更新。基本规则包括:存活细胞在邻居过少(<2)或过多(>3)时死亡,在邻居适中(2或3)时继续存活;死亡细胞在正好有 3 个存活邻居时“复活”。
  • ESP32-C3 是一款流行的低成本、低功耗 RISC-V 微控制器,资源相对有限。生命游戏对计算和内存访问有一定要求,使其成为检验嵌入式系统性能和编程语言效率的理想模型。我们的目标是利用 MoonBit 编写生命游戏的核心逻辑,并将其运行结果显示出来。不仅支持在连接了 ST7789 LCD 的真实 ESP32-C3 硬件上运行,也支持在开发机上通过 QEMU 仿真环境运行,以方便开发和验证。

准备工作

在开始之前,请确保你的系统中安装了乐鑫官方提供的链接: ESP IDF 开发框架。请参考链接: ESP官方文档,当前支持的ESP-IDF(乐鑫物联网开发框架)版本为 v5.4.1。若希望使用 QEMU 仿真,请参考乐鑫官方文档中的链接: QEMU模拟器章节安装相关工具链。

本文使用MoonBit native后端生成C代码,并且借助链接: moonbit-esp32 包将MoonBit项目打包为静态链接库,以嵌入到标准的ESP-IDF项目中。 moonbit-esp32 库的核心功能体现在其 components 包中。这个目录扮演着关键的桥梁角色,专门负责提供 MoonBit 语言到 ESP-IDF中各种核心组件功能的绑定。具体来说,该目录下包含了多个子模块,例如 gpio (通用输入输出)、 spi(串行外设接口)、 lcd(液晶显示屏控制,包含通用接口和特定驱动如 ST7789 的封装)、 task(封装 FreeRTOS 任务管理)以及 qemu_lcd(针对 QEMU 仿真环境的 LCD 接口)。每一个子模块都封装了对应的 ESP-IDF C API,允许开发者使用类型安全、更符合 MoonBit 语言习惯的方式来直接操作 ESP32 的硬件外设和系统服务,从而将 MoonBit 的现代语言特性带入底层嵌入式开发。

本文相关代码位于 链接 仓库中的 game-of-life 子目录中。

注意:本文所述的开发与测试流程仅在 macOS 系统上验证通过,其他操作系统用户可能需要根据平台差异自行进行调整。

生命游戏逻辑的实现

相关代码位于game.mbt 文件中。

  1. 首先我们定义DEAD 定义为 0ALIVE 定义为 Int16 类型的-1 。这里定义ALIVE-1 而不是1 是一个巧妙的优化:在 RGB565 颜色格式下,00x0000)恰好代表黑色,而 -10xFFFF )恰好代表白色。这样,在将游戏状态数据传输给 LCD 时,可以省去颜色转换的步骤。
  2. 网格大小由ROWSCOLS (均为 240) 定义。cells 使用一个一维 FixedArray 全局变量来存储整个二维网格的当前状态。FixedArray 是固定大小的数组,大小为 112.5KB(240 * 240 * 2 / 1024)。因为ESP32-C3可用内存约有320KB,且并不是连续的,所以next数组没有定义成完整的下一个状态副本,而是定义为一个 3 行滚动缓冲区,大小约为1.4KB。我们通过模运算循环使用这三行空间来存储计算中的下一代状态,并采用延迟提交策略将计算完成的旧行写回cells ,以降低内存使用峰值。

const DEAD : Int16 = 0
const ALIVE : Int16 = -1pub const ROWS : Int = 240
pub const COLS : Int = 240let cells : FixedArray[Int16] = FixedArray::make(ROWS * COLS, 0)
let next : FixedArray[Int16] = FixedArray::make(3 * COLS, 0)

在计算下一个状态时,核心代码如下:

let live_neighbors = live_neighbor_count(row, col)
let next_cell = match (cell, live_neighbors) {(ALIVE, _..<2) => DEAD  // 优雅地用范围模式匹配邻居数小于2的情况(ALIVE, 2 | 3) => ALIVE // 使用或模式, 将邻居数为2或3的情况合并在一个分支(ALIVE, 3..<_) => DEAD  // 用范围模式匹配邻居数大于3的情况(DEAD, 3) => ALIVE(otherwise, _) => otherwise // 必须考虑所有情况, 编译器会进行穷尽性检查
}

这是实现生命游戏规则的核心,充分展现了 MoonBit 模式匹配的强大和优雅。生命游戏的规则被近乎直译地映射到 MoonBit 的 match 模式匹配语句中,代码清晰、直观且不易出错。

此外,在需要性能的场景,MoonBit提供了对数组的unsafe操作。这在性能关键路径上消除了数组边界检查的开销,是嵌入式开发中为了榨取极致性能的常用手段,但需要开发者确保逻辑的正确性。

最后,对比 QEMU(仿真)版本和 ST7789(硬件)版本的 main.mbt 文件。
与 QEMU 虚拟 LCD 面板相关的逻辑位于 game-of-life/qemu/src/main/main.mbt,关键代码摘录如下:

let panel = @qemu_lcd.esp_lcd_new_rgb_qemu(@game.COLS, @game.ROWS, BPP_16)..reset!()..initialize!()
@game.init_universe()
for i = 0; ; i = i + 1 {let start = esp_timer_get_time()panel.draw_bitmap!(0, 0, @game.ROWS, @game.COLS, cast(@game.get_cells()))@game.tick()let end = esp_timer_get_time()println("tick \\\\{i} took \\\\{end - start} us")
}

这段代码借助MoonBit ESP32 binding,首先初始化一个用于 QEMU 仿真的虚拟 LCD 面板,并对其进行复位和初始化设置。这段 MoonBit 代码利用了级联运算符 (..) 和错误处理(!)。

  • 首先创建 panel 对象。
  • 接着,..reset!() 使用级联运算符,在 panel 上调用 reset 方法;由于 reset 函数的返回类型可能包含错误,末尾的 ! 表示:如果 reset 成功,则继续执行;如果 reset 引发了一个错误,! 会立即将该错误重新抛出,中断后续操作。
  • 同理,..initialize!() 只有在 reset! 成功后才会执行,并且同样带有自动错误传播的 !。这种组合使得对同一对象执行一系列可能失败的操作时,代码既简洁(避免重复写 panel)又安全(错误自动向上传播)。
  • 接着,它初始化生命游戏的世界状态。
  • 最后,代码进入一个无限循环:在每次循环中,它将当前的游戏状态绘制到虚拟 LCD 上,然后计算并更新游戏到下一代状态,同时测量并打印出每次绘制和更新操作所花费的时间。

与 ST7789 LCD 相关的逻辑位于 game-of-life/st7789/src/main/main.mbt,关键代码摘录如下:

let spi_config : @spi.SPI_BUS_CONFIG = { ... }
@spi.spi_bus_initialize(@spi.SPI2_HOST, spi_config, SPI_DMA_CH_AUTO) |> ignore...let panel = @lcd.esp_lcd_new_panel_st7789(io_handle~,reset_gpio_num=PIN_RST,rgb_ele_order=@lcd.BGR,bits_per_pixel=16,)..reset!()..initialize!()..config!(on_off=true, sleep=false, invert_color=true)
@game.init_universe()
for i = 0; ; i = i + 1 {let start = esp_timer_get_time()panel.draw_bitmap!(0, 0, @game.ROWS, @game.COLS, cast(@game.get_cells()))@game.tick()let end = esp_timer_get_time()println("tick \\\\{i} took \\\\{end - start} us")
}
  • 这段代码首先定义了与物理 ST7789 LCD 连接所需的 GPIO 引脚和 SPI 时钟速度等常量。
  • 接着,它执行了一系列硬件初始化步骤:配置并启动 SPI 总线,创建基于 SPI 的 LCD IO 句柄,然后使用特定驱动初始化 ST7789 面板并设置其显示状态(复位、初始化、开启显示、唤醒、颜色反转)。
  • 完成硬件准备后,它初始化生命游戏的状态。
  • 最后,程序进入一个无限循环,在循环中不断将当前游戏画面绘制到物理 LCD 屏幕上,计算游戏的下一代状态,并测量打印每次循环所需的时间。

此外,读者可以注意到,MoonBit支持标签参数。无论是在 esp_lcd_new_panel_st7789 的调用中(如reset_gpio_num=PIN_RST, rgb_ele_order=...),还是在后续链式调用的 ..config!(on_off=true, ...) 中,都明确地将值与其参数名称关联起来。这种方式极大地提高了代码的可读性和自文档性,使得参数的意图一目了然,并且也无需关心参数顺序。

我们可以发现:生命游戏的计算 (@game.tick()) 和主循环结构完全相同。主要的区别在于与显示设备的交互层。ST7789 版本需要进行详细的物理硬件配置(定义 GPIO 引脚、配置 SPI 总线、初始化特定 LCD 驱动);而 QEMU 版本则直接与仿真环境提供的虚拟 LCD 接口交互,初始化过程相对简单。

运行实例代码

您可以通过克隆示例代码仓库来亲自体验:

git clone <https://github.com/moonbit-community/moonbit-esp32-example.git>
  1. QEMU 版本
    为了方便读者不借助硬件复现,我们提供了QEMU版本,代码位于 game-of-life/qemu 目录,在确保环境配置无误后,运行以下命令便可看到结果。33.1 FPS (每帧约 30.2 ms)
cd game-of-life/qemu
moon install
make set-target esp32c3
make update-deps
make build
make qemu

以下为运行效果

MoonBit 支付国产芯片-Part1

此外,我们还提供了一份使用C实现的代码,位于 game-of-life/qemu-c 目录,测试结果表明,使用C实现的生命游戏每帧的计算时间与MoonBit版本相同,均为30.1ms左右。

  1. ST7789 版本

此版本在真实的 ESP32-C3 开发板和 ST7789 LCD 上运行。实测帧率约 27.1 FPS (每帧约 36.9ms)。

cd game-of-life/st7789
moon install
make set-target esp32c3
make build
make flash monitor

此外,我们还提供了一份使用C实现的代码,位于 game-of-life/st7789-c 目录,测试结果表明,使用C实现的生命游戏每帧的计算时间与 MoonBit 版本几乎相同,为36.4ms。

对于具体的测试方法,详见链接: 仓库

总结

通过在 ESP32-C3 上运行生命游戏的实例,我们展示了 MoonBit 的 Native 后端在嵌入式开发中的应用。MoonBit生命游戏代码经过优化,能到达与C几乎相同的速度。同时,MoonBit 的模式匹配等现代语言特性有助于提升代码的可读性和开发体验。结合其与 ESP-IDF 等生态系统的无缝集成能力,MoonBit 为ESP32嵌入式开发提供了一种将原生级执行效率与现代化开发体验相结合的高效解决方案。

New to MoonBit?

  • Download MoonBit.
  • Explore MoonBit Beginner’s Guide.
  • Play with MoonBit Language Tour.
  • Check out MoonBit Docs.
  • Join our Discord community.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/77959.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/77959.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【RAG 框架部署】LangChain-Chatchat (原 Langchain-ChatGLM) + Ollama

目录 前言 一、什么是RAG&#xff1f; 二、环境准备和Ollama搭建 1、conda虚拟环境配置 2、Ollama搭建 三、LangChain-Chatchat搭建 1、框架安装 2、文件配置 3、初始化知识库 4、启动Langchan-Chatchat 前言 由于LangChain-Chatchat的 0.3.0 版本已修改为支持不同模…

python对接马来西亚股票完整代码

StockTV全球股票数据API对接实战&#xff1a;构建智能金融分析系统 一、StockTV API核心功能解析 StockTV作为覆盖200国家证券市场的数据平台&#xff0c;其API提供三大核心模块的对接能力&#xff1a; 市场列表查询 - 获取指定国家的股票基础数据个股详情检索 - 查询实时行情…

普通IT的股票交易成长史--20250430晚

声明&#xff1a;本文章的内容只是自己学习的总结&#xff0c;不构成投资建议。文中观点基本来自yt站Andylee&#xff0c;美股Alpha姐&#xff0c;综合自己的观点得出。感谢他们的无私分享。 送给自己的话&#xff1a; 仓位就是生命&#xff0c;绝对不能满仓&#xff01;&…

windows 下 oracle 数据库的备份与还原

1、备份 创建备份出来的文件存放的位置。 创建目录对象&#xff0c;在数据库中创建一个目录对象&#xff0c;该对象指向文件系统中用于存储导出文件的实际目录&#xff08; sql 命令&#xff0c;可以在 plsql 中执行&#xff09;。 -- 创建目录对象&#xff0c;\D:\Oracle19c\…

基于单片机的智能药盒系统

标题:基于单片机的智能药盒系统 内容:1.摘要 本文聚焦于基于单片机的智能药盒系统。背景方面&#xff0c;随着人口老龄化加剧&#xff0c;老年人按时准确服药问题愈发凸显&#xff0c;同时现代快节奏生活也使人们容易遗忘服药时间。目的是设计并实现一个能帮助人们按时、按量服…

“100% 成功的 PyTorch CUDA GPU 支持” 安装攻略

#工作记录 一、总述 在深度学习领域&#xff0c;PyTorch 凭借其灵活性和强大的功能&#xff0c;成为了众多开发者和研究者的首选框架。而 CUDA GPU 支持能够显著加速 PyTorch 的计算过程&#xff0c;大幅提升训练和推理效率。然而&#xff0c;安装带有 CUDA GPU 支持的 PyTor…

图数据库榜单网站

图数据库榜单 https://db-engines.com/en/ranking/graphdbms点击跳转

Android Jetpack Compose 面试题大全(2025最新整理)

基础概念 什么是 Jetpack Compose&#xff1f;它与传统 Android UI 开发有何不同&#xff1f; Compose 是 Android 的现代声明式 UI 工具包&#xff0c;使用 Kotlin 编写不同于传统的基于 View 和 XML 的 imperative 方式&#xff0c;Compose 使用声明式范式主要区别&#xff1…

添加了addResourceHandlers 但没用

B站黑马的视频 public class WebMvcConfig extends WebMvcConfigurationSupport { /** * 设置静态资源映射 * param registry */ Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("开始进…

STM32实现simpleFOC控制无刷电机

一、FOC基础知识学习 使用simpleFOC控制无刷电机前&#xff0c;需要大概了解一下相关知识&#xff0c;包括力矩控制、速度控制、位置控制的原理和它们之间的联系。 推荐学习资料&#xff1a; 教你写一个比SimpleFOC更好的电机库_哔哩哔哩_bilibili 《灯哥手把手教你写FOC算…

【数据结构】快慢指针

一、快慢指针的原理 定义&#xff1a; 快指针&#xff1a;每次移动两步 慢指针&#xff1a;每次移动一步 终止条件&#xff1a; 当快指针到达链表末尾时停止 事件复杂度&#xff1a; 始终为O(n),仅需依次遍历 空间复杂度&#xff1a; …

毕业论文 | 基于STM32的自动烟雾报警系统设计

基于STM32的烟雾报警系统 一、系统设计原理1. **系统架构**2. **工作原理**二、核心公式与算法1. **MQ-2传感器浓度计算**2. **温度传感器数据处理**3. **校准与滤波**三、关键代码实现1. **ADC初始化与数据读取(以MQ-2为例)**2. **报警逻辑与阈值设置**3. **EEPROM存储阈值*…

Android Gradle插件开发

文章目录 1. Gradle插件是什么2. 为什么需要插件3. 编写插件位置4. 编写插件5. 自定义插件扩展5.1 订阅扩展对象5.2 把扩展添加给Plugin并使用5.3 配置参数5.4 嵌套扩展5.4.1 定义扩展5.4.2 获取扩展属性5.4.3 使用5.4.4 执行5.4.5 输出 6. 编写在单独项目里6.1 新建Module6.2 …

PPIO X OWL:一键开启任务自动化的高效革命

2024年&#xff0c;仅凭一PPIO X OWL&#xff1a;一键开启任务自动化的高效革命篇技术论文&#xff0c;OWL的Github仓库便在24小时斩获了15k Star&#xff0c;成为2024年增速最快的多智能体协作框架&#xff0c;重新定义了任务自动化的效率边界。Camel AI团队开源全栈方案&…

分布式事务,事务失效,TC事务协调者

1. 概述 本方案书旨在解决分布式系统中事务一致性问题&#xff0c;重点阐述全局事务标识&#xff08;XID&#xff09;的传递与存储机制、事务协调者&#xff08;TC&#xff09;的设计与部署&#xff0c;以及分布式事务失效场景的应对策略。基于业界成熟框架&#xff08;如Seat…

2025年“深圳杯”数学建模挑战赛D题-法医物证多人身份鉴定问题

法医物证多人身份鉴定问题 小驴数模 犯罪现场法医物证鉴定是关系到国家安全、公共安全、人民生命财产安全和社会稳定的重大问题。目前法医物证鉴定依赖DNA分析技术不断提升。DNA检验的核心是STR&#xff08;Short Tandem Repeat&#xff0c;短串联重复序列&#xff09;分析技术…

Mysql查询异常【Truncated incorrect INTEGER value】

文章目录 异常原因分析1、数据类型不一致2、数据长度超长3、数据格式要正确 处理方案模拟案例创建表数据查询 异常 在执行MySQL的语句时&#xff0c;在控制台报错如下所示。 Data truncation: Truncated incorrect INTEGER value 原因分析 1、数据类型不一致 必须要保证数据…

WPF性能优化举例

WPF性能优化集锦 一、UI渲染性能优化 1. 虚拟化技术 ​​ListView/GridView虚拟化​​: <ListView VirtualizingStackPanel.IsVirtualizing="True"VirtualizingStackPanel.VirtualizationMode="Recycling"ScrollViewer.IsDeferredScrollingEnabled=…

C# 面向对象实例演示

C# 面向对象编程实例演示 一、基础概念回顾 面向对象编程(OOP)的四大基本特性&#xff1a; ​​封装​​ - 将数据和操作数据的方法绑定在一起​​继承​​ - 创建新类时重用现有类的属性和方法​​多态​​ - 同一操作作用于不同对象产生不同结果​​抽象​​ - 简化复杂系…

大连理工大学选修课——机器学习笔记(3):KNN原理及应用

KNN原理及应用 机器学习方法的分类 基于概率统计的方法 K-近邻&#xff08;KNN&#xff09;贝叶斯模型最小均值距离最大熵模型条件随机场&#xff08;CRF&#xff09;隐马尔可夫模型&#xff08;HMM&#xff09; 基于判别式的方法 决策树&#xff08;DT&#xff09;感知机…