Расширение языка C для MicroPython на Raspberry Pi Pico

Это руководство о том, как написать и запустить базовое расширение на языке C для MicroPython на Raspberry Pi Pico. Я расскажу о причинах расширения MicroPython, о том, как написать модуль расширения, как его скомпилировать и как установить на Pico. Если вы хотите прочитать больше об этой теме, пожалуйста, обратитесь к разделу https://docs.micropython.org/en/latest/develop/extendingmicropython.html

Введение

Зачем расширять MicroPython?

Причин, по которым вам может понадобиться расширение MicroPython, может быть несколько. Поскольку это урезанная версия Python, MicroPython не очень хорошо справляется с тяжелыми вычислительными задачами (как и Python). С другой стороны, язык C действительно хорошо подходит для таких задач. Расширение C для Micropython позволяет выполнять такие вычисления на языке C и использовать результаты в MicroPython. Это может ускорить некоторые процессы на порядок.

Вторая причина может заключаться в том, что вы хотите взаимодействовать с каким-либо оборудованием, использующим API на языке C, из MicroPython. 

Типы расширений MicroPython C

Существует несколько различных способов расширения MicroPython: внешние модули на языке C и собственный машинный код в файлах .mpy.

Чтобы запустить внешний модуль на языке C, необходимо перекомпилировать его в микропрограмму MicroPython. Это довольно сложный процесс, поэтому в данном руководстве мы остановимся на втором варианте. Если вас заинтересовал этот подход, вы можете прочитать о нем подробнее на этих веб-страницах:                https://docs.micropython.org/en/latest/develop/cmodules.html                      https://www.raspberrypi.org/forums/viewtopic.php?t=300352

Файл .mpy - это файл, который хранит скомпилированный собственный машинный код и может быть скомпонован динамически. Это делает его гораздо более гибким, поскольку вам не нужно перекомпилировать всю прошивку MicroPython.

Напишите модуль расширения

Написание модуля расширения - это самая простая часть расширения MicroPython. Я покажу вам, как это сделать, на простом примере. Для начала мы создадим новый каталог под названием фактфиб. Откройте новое окно терминала, перейдите в новый каталог и создайте новый файл с именем factfib.c. Если вы создали каталог на Raspberry Pi под Dekstop, команда выглядит следующим образом.

cd ~/Desktop/factfib

touch factfib.c

Открыть factfib.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 каждая переменная является объектом. Поэтому в Си мы представляем их как mp_obj_t. Но программа на языке Си может работать только с переменными типа Си, поэтому нам нужно преобразовать объект в тип Си. Это называется Marshalling, и MicroPython предоставляет функции для извлечения любого C-типа из объекта.

Эта веб-страница Оливера Робсона очень полезна для маршалинга https://mpy-c-gen.oliverrobson.tech/. Кроме того, вы можете найти примеры маршалинга в документации MicroPython https://docs.micropython.org/en/latest/develop/natmod.html. Я советую вам прочитать эту статью, если вам нужны дополнительные объяснения.

Постройте пристройку

Мы собираем расширение в файл .mpy с помощью Makefile. Но сначала нам нужно настроить несколько вещей. Убедитесь, что ваш терминал все еще открыт в фактфиб каталог. Затем клонируйте репозиторий 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'

Эти установки займут некоторое время, но если вы хотите сэкономить время и знаете, что делаете, вам нужно только клонировать директории /py и /tools из репозитория micropython и поместить их в папку micropython папку внутри фактфиб папку (пропустите вторую команду).

Пока идет установка, мы можем использовать это время для написания 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. Откройте его и в разделе Architecture configuration в armv7m найдите переменную CFLAGS (это должна быть строка 64). Измените -mcpu=cortex-m3 на -mcpu=cortex-m0 и сохраните файл.

chnge -mcpu=cortex-m3 to -mcpu=cortex-m0

Затем, после того как все будет установлено, выполните

make

и фактфиб.мпи будет создан файл.

Установите и протестируйте расширение

Последний шаг - установка и тестирование. Для этого мы используем файловую систему, которую Micropython создает на Raspberry Pi Pico. Сначала нам нужно прошить Pico прошивкой MicroPython. Самый простой способ сделать это - использовать Thonny IDE (предустановленную на Raspbian/Raspberry Pi OS).

Если вы не используете Raspberry Pi для программирования Pico или если вы никогда раньше не использовали MicroPython на Pico, обратитесь к руководству по началу работы с MicroPython от Raspberry Pi. 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/

Закройте Thonny после прошивки Pico прошивкой MicroPython и установите 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 файлы на Pico, но я продемонстрирую, как использовать интерпретатор MicroPython на minicom.

Поэтому сначала нажмите 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

Поздравляем! Вы создали свое первое расширение MicroPython C для Pico! Следующий шаг - научиться писать и компилировать более сложные модули. Хороший способ начать - проверить, как работает маршаллинг списков и массивов (см. веб-страницу "Маршаллинг" выше).

Если у вас возникли вопросы по поводу этой заметки, не стесняйтесь написать комментарий.

8 комментариев

  1. PeterB Ноябрь 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);
    return mp_obj_new_int(rslt);
    }

  2. XMC.pl Июнь 1, 2022 в 3:31 дп

    Как настроить joomla так, чтобы она могла получать данные из mysql?

  3. Lynggaard Январь 8, 2023 в 8:09 пп

    Я пытался сделать вышеописанное, но разные вещи не работают так, как описано:

    - Я следовал инструкциям выше, получил micropython, изменил makefile как описано
    - Я делаю (но получаю файл factfib.native.mpy вместо factfib.mpy)
    - Я копирую файл factfib.native.mpy в pico как factfib.mpy
    (Я попробовал rshell, но получил "timed out or error in transfer to remote: b"", но я смог скопировать его с помощью Thonny)
    - Я могу импортировать модуль (в thonny или rshell - одно и то же) без ошибок
    - Я пытаюсь запустить "ff.fibonacci(5)", но затем получаю:

    " Traceback (последний последний вызов):
    Файл "", строка 1, в
    AttributeError: объект 'module' не имеет атрибута 'fibonacci'".

    Что я делаю не так?

    • paopao69 Март 22, 2023 в 5:59 пп

      В конце концов, вам удалось заставить его работать?

  4. Paulo Февраль 9, 2024 в 1:44 дп

    Теперь вы можете компилировать под armv6, поэтому просто измените эту строку в Makefile и не нужно менять код micropython
    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);
    return mp_obj_new_int(rslt);
    }

    • Adam Февраль 9, 2024 в 9:22 дп

      Спасибо за информацию, Пауло!

  5. Dr_Phil Май 24, 2024 в 7:36 дп

    Я следовал всем инструкциям и в итоге получил собранный файл facfib.mpy. Я использовал
    ARCH = armv6m
    При загрузке в Pico я получаю эту ошибку:

    ValueError: несовместимый файл .mpy

    Для моего Pico требуются следующие значения mpy:
    версия mpy: 6
    mpy sub-version: 2
    Флаги mpy: -march=armv6m

    Проверка первых двух байтов файла дает версию 6 и подверсию 6.
    Есть ли способ исправить это, пожалуйста?

Комментировать