在Raspberry Pi Pico上为MicroPython提供C语言扩展

这是一份关于如何在Raspberry Pi Pico上编写和运行MicroPython的基本C语言扩展的指南。我将介绍扩展MicroPython的原因,如何编写扩展模块,如何编译以及如何在Pico上安装它。如果你想阅读更多关于这个主题的内容,请参考 https://docs.micropython.org/en/latest/develop/extendingmicropython.html

简介

为什么要扩展MicroPython?

你可能想扩展MicroPython,这可能有多种原因。由于MicroPython是Python的精简版,当涉及到计算量大的任务时(类似于Python),MicroPython的表现不是很好。另一方面,C语言确实很适合此类任务。Micropython的C语言扩展使你能够通过C语言进行这些计算,并在MicroPython中使用这些结果。这可以使一些程序的速度提高一个数量级。

第二个原因可能是,你想在MicroPython中连接一些使用C语言API的硬件。 

MicroPython C语言扩展的类型

有几种不同的方式来扩展MicroPython:外部C模块和.py文件中的本地机器代码。

要运行一个外部C模块,你需要把它重新编译到MicroPython固件中。这是一个相当复杂的过程,这就是为什么本指南将重点介绍第二种方法。如果你对这种方法感兴趣,你可以在这些网页上阅读更多信息。                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

.py是一个存储已编译的本地机器代码的文件,可以动态链接。这使得它更加灵活,因为你不需要重新编译整个MicroPython固件。

编写扩展模块

编写扩展模块是扩展MicroPython的最简单的部分。我将通过一个简单的例子来告诉你如何做。首先,我们将创建一个新的目录,名为 事实fib.打开一个新的终端窗口,cd进入新目录,并创建一个新的文件,名为 事实fib.c.如果你在Dekstop下的Raspberry Pi上创建了该目录,该命令看起来像这样。

cd ~/Desktop/factfib

touch factfib.c

开放式 事实fib.c 在你喜欢的文本编辑器中,输入以下代码:

#include "py/dynruntime.h

STATIC mp_int_t factorial_helper(mp_int_t x) {
   if (x < 1) {
      return 1;
   }
   return x*factorial_helper(x-1);
}

STATIC mp_obj_t factorial(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = factorial_helper(x);
   return rslt;
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(factorial_obj, factorial);

STATIC mp_int_t fibonacci_helper(mp_int_t x) {
   if (x <= 0) {
      return 0;
   }
   else if (x == 1) {
      return 1;
   }
   else {
      return (fibonacci_helper(x-1) + fibonacci_helper(x-2));
   }
}

STATIC mp_obj_t fibonacci(mp_obj_t x_obj) {
   mp_int_t x = mp_obj_get_int(x_obj);
   mp_int_t rslt = fibonacci_helper(x);
   return mp_obj_new_int(rslt);
}

STATIC MP_DEFINE_CONST_FUN_OBJ_1(fibonacci_obj, fibonacci);

mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {
   MP_DYNRUNTIME_INIT_ENTRY

   mp_store_global(MP_QSTR_factorial, MP_OBJ_FROM_PTR(&factorial_obj));
   mp_store_global(MP_QSTR_fibonacci, MP_OBJ_FROM_PTR(&fibonacci_obj));

   MP_DYNRUNTIME_INIT_EXIT
}

让我解释一下上面的代码。在MicroPython和Python中,每个变量都是一个对象。因此在C语言中,我们把它们表示为 mp_obj_t.但是C程序只能使用C类型的变量,这就是为什么我们需要将对象转化为C类型。这就是所谓的Marshalling,MicroPython提供了从一个对象中提取任何C类型的函数。

奥利弗-罗布森的这个网页对法警工作很有帮助。 https://mpy-c-gen.oliverrobson.tech/.此外,你可以在MicroPython文档中找到调集的例子 https://docs.micropython.org/en/latest/develop/natmod.html.如果你需要进一步解释,我建议你阅读这篇文章。

构建扩展

我们通过使用Makefile将扩展程序构建成一个.mpy文件。但我们需要先设置一些东西。确保你的终端仍然打开在 事实fib 目录。然后克隆MicroPython的GitHub仓库。

sudo apt-get install build-essential libreadline-dev libffi-dev git pkg-config gcc-arm-none-eabi libnewlib-arm-none-eabi

git clone --recurse-submodules https://github.com/micropython/micropython.git

pip3 install 'pyelftools>=0.25'

这些安装会花费一些时间,如果你想节省时间并且知道自己在做什么,你只需要从micropython仓库克隆/py和/tools目录,然后把它们放在一个 微软 文件夹内的 事实fib 文件夹(跳过第二个命令)。

在安装过程中,我们可以利用这段时间来编写Makefile。只需执行

touch Makefile

并在一个文本编辑器中打开新创建的文件。

MPY_DIR = micropython

MOD = factfib

SRC = factfib.c

ARCH = armv7m

include $(MPY_DIR)/py/dynruntime.mk

你可能已经注意到,我们将 ARCH 变量为 armv7m 虽然皮克是 armv6m.我们必须这样做,因为 armv6m 架构(在我写这篇文章的时候)不支持 dynruntime.mk 工具。

但幸运的是,我们仍然可以为Pico编译一个有效的扩展。我们只需要修改 dynruntime.mk 文件,它应该位于 micropython/py/dynruntime.mk。 打开它并在armv7m的架构配置下找到CFLAGS变量(应该是第64行)。改变 -mcpu=cortex-m3 至 -mcpu=cortex-m0 并保存该文件。

将-mcpu=cortex-m3改为-mcpu=cortex-m0

然后在一切安装完毕后,执行

make

和 事实fib.mpy 文件将被创建。

安装并测试该扩展

最后一步是安装和测试。为此我们利用Micropython在Raspberry Pi Pico上创建的文件系统。首先,我们需要用MicroPython固件闪过Pico。最简单的方法是使用Thonny IDE(预装在Raspbian/Raspberry Pi OS上)。

如果你没有使用Raspberry Pi为你的Pico编程,或者你以前从未在Pico上使用过MicroPython,那么请参考Raspberry Pi的MicroPython入门指南。 https://www.raspberrypi.org/documentation/rp2040/getting-started/#getting-started-with-micropython

如果你使用的是Windows,你应该看看这篇关于Raspberry Pi Pico和MicroPython在Windows上的博文。 https://picockpit.com/raspberry-pi/raspberry-pi-pico-and-micropython-on-windows/

在你用MicroPython固件刷新你的Pico后,关闭Thonny,并安装rshell。我们使用rshell来从命令行访问文件系统。

sudo pip3 install rshell
rshell --version

如果一切设置正确,这将打印出rhsell版本。然后拔掉pico的插头,再插上(不按BOOTSEL按钮),以确保没有任何东西在试图访问文件系统。现在我们需要执行

rshell -p /dev/ttyACM0 --buffer-size 512

cp factfib.mpy /pyboard/factfib.mpy

为了测试安装,你可以使用rshell或Thonny IDE来保存你的 .py 文件,但我将演示如何在minicom上使用MicroPython解释器。

因此,首先按 Ctrl+c 来离开rshell,然后通过以下方式安装minicom

sudo apt install minicom

现在我们可以访问Picos REPL(读取-评估-打印循环)。

minicom -o -D /dev/ttyACM0

三个结尾的角括号>>表示MicroPython解释器正在运行。现在我们可以导入我们的扩展并像往常一样使用它。

>>> import factfib as ff

>>> ff.factorial(5)

>>> 120

>>> ff.fibonacci(7)

>>> 13

恭喜你!你已经为Pico创建了第一个MicroPython C语言扩展!你已经为Pico创建了你的第一个MicroPython C语言扩展!下一步是学习如何编写和编译更复杂的模块。一个好的开始是测试列表和数据集的工作方式(见上面的 Marshalling 网页)。

如果你对这个帖子有任何疑问,欢迎写下评论。

8评论

  1. PeterB 在11 月 26, 2021在8:29 上午

    除了c程序中的错误外,它工作得很好。谢谢。

    STATIC mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj)。
    mp_int_t rslt = factorial_helper(x);
    返回mp_obj_new_int(rslt)。
    }

  2. XMC.pl 在6 月 1, 2022在3:31 上午

    如何配置可以从 mysql 获取数据的 joomla?

  3. Lynggaard 在1 月 8, 2023在8:09 下午

    我试着做了上述工作,但各种事情都不像描述的那样进行。

    - 我按照上面的指示,得到了micropython,按照描述修改了makefile文件
    - 我做了(但我得到的是factfib.native.mpy文件,而不是factfib.mpy)。
    - 我将 factfib.native.mpy 文件复制到 pico,作为 factfib.mpy。
    (我试着用rshell,但得到的结果是 "超时或传输到远程时出错:b"",但我可以用Thonny把它复制过来)
    - 我可以导入模块(在thonny或rshell中--相同的)而没有错误
    - 我试着运行 "ff.fibonacci(5)",但我得到了。

    "回溯(最近的一次调用)。
    文件"",第1行,在
    AttributeError: 'module' object has no attribute 'fibonacci'"

    我做错了什么?

    • paopao69 在3 月 22, 2023在5:59 下午

      你最终让它工作了吗?

  4. Paulo 在2 月 9, 2024在1:44 上午

    现在,您可以编译为 armv6 版本,因此只需修改 Makefile 中的这一行,而无需修改 microropython 代码
    ARCH = armv6m

    我在编译 factfib.c 时遇到了一些错误,请问修复方法是什么:

    // 缺少了最后的"......"。
    #include "py/dynruntime.h"

    // 将返回值转换为 mp_obj_t
    STATIC mp_obj_t factorial(mp_obj_t x_obj) {
    mp_int_t x = mp_obj_get_int(x_obj)。
    mp_int_t rslt = factorial_helper(x);
    返回mp_obj_new_int(rslt)。
    }

    • Adam 在2 月 9, 2024在9:22 上午

      感谢您提供的信息,保罗!

  5. Dr_Phil 在5 月 24, 2024在7:36 上午

    我按照所有说明进行了操作,最终生成了 facfib.mpy 文件。我使用了
    ARCH = armv6m
    加载到 Pico 时,我遇到了这个错误:

    ValueError:不兼容 .mpy 文件

    我的 Pico 需要以下 mpy 值:
    mpy 版本: 6
    mpy 子版本: 2
    mpy 标志:-march=armv6m

    通过检查文件的前两个字节,可以得到版本 6 和子版本 6。
    请问有什么办法可以纠正这种情况吗?

发表评论