
Введение
В этой статье мы рассмотрим способы программирования платы прототипирования Raspberry Pi Pico (далее RPi Pico). Интерес к этой платформе у меня появился в основном из-за интересной возможности прошивки различных загрузчиков с помощью проводника методом drag & drop. Эта возможность появилась прежде всего благодаря встроенной поддержке USB Flashing Format (UF2) от Microsoft. Ниже представлены платформы, которые поддерживают этот формат прошивки. Среди них есть как классические Arduino UNO, RPi Zero, STM32F103, STM32F4, так и новые платы ESP32-S2. Можно сказать, что на данный момент это наиболее распространенные и перспективные платформы, что позволяет предположить: UF2 скоро может стать стандартом. Но только в чипе RP2040 из RPi Pico эта возможность внедрена на уровне железа.

Какие преимущества имеет эта платформа?
- Поддержка UF2 позволяет быстро менять загрузчики, что предполагает мультиплатформенность;
- Очень большая Flash-память 2 Мб, очень высокая тактовая частота 133 МГц;
- Из пунктов 1 и 2 автоматически вытекает, что мы можем использовать множество различных языков и сред программирования. Никакому Arduino это и не снилось, а в ESP32, на моей памяти, добавили лишь поддержку MicroPython;
- При использовании CircuitPython появляется возможность обращаться с flash-накопителем RPi Pico как с обычной флешкой, и использовать ее не только для прошивки, а в качестве места для хранения своих скриптов и конфигурационных файлов с доступом к ним через проводник. Мы также имеем возможность выполнять скрипты и сохранять файлы в память RPi Pico в одном и том же режиме - нет необходимости подключать RPi Pico к USB с зажатой кнопкой BOOTSEL;
- Невысокая стоимость плат. На данный момент от 210 до 420 рублей в зависимости от объема памяти.
Фирменный оригинал зеленого цвета 2020 года разработки имеет только один вариант с 2 Мб памяти, а китайские более поздние варианты черного цвета ничем не хуже, но имеют также модификации с 4, 8 и 16 Мб памяти. По идее память даже можно перепаять при желании, потому что она в обычном корпусе SOIC-8. Недостатком оригинальной платы является отсутствие кнопки Reset (из-за этого для перехода в режим boot приходится отключать плату от USB компьютера). В китайских вариантах она есть, но из коробки все равно не работает.
В данном обзоре я расскажу, как программировать RPi Pico на четырех языках: MicroPython, CircuitPython, Golang и Си. К слову, на Arduino ее программировать тоже можно, а также на jаvascript, Lua, FreeRTOS и даже Rust.
Программирование RPi Pico на MicroPython в среде Thonny
Установка MicroPython:
Идем на сайт MicroPython и скачиваем прошивку в формате UF2.
Зажимаем кнопку BOOTSEL на плате, подключаем плату к USB, отпускаем кнопку BOOTSEL. Если у вас Windows, то откроется проводник. А у меня Kubuntu, поэтому откроется предложение о том, что дальше делать. Я выбираю открыть проводник Dolphin, и вижу файл INFO_UF2.TXT. В нем содержится информация о загрузчике.
UF2 Bootloader v3.0
Model: Raspberry Pi RP2
Board-ID: RPI-RP2

Кстати, с возможностью выбора что делать связана одна неприятная особенность: том в Kubuntu не монтируется автоматически, и поэтому ваша среда программирования не увидит плату, если вы после ее подключения не откроете проводник, а затем не закроете его. Наверное, это поведение можно изменить, но я думаю, что так сделано в целях безопасности.
Установка Thonny:
Официальное руководство рекомендует использовать среду Thonny. Это довольно примитивная среда программирования, но по сравнению с PyCharm она хотя бы запустится на большинстве компьютеров.
Установить Thonny можно из exe, debian-пакета, с помощью pip3 и с помощью flatpak. В дистре у нас старая версия, установленное с помощью pip3 непонятно как потом запускать, поэтому я выбрал flatpak. Последнего у меня не было, и мне пришлось добавить поддержку flatpak в Discover.
sudo apt install flatpak

Потом в терминале для установки пакета набираем:
flatpak install org.thonny.Thonny
Дальше при попытке залить MicroPython на плату с помощью Thonny я столкнулся с несоответствием описанного в официальном руководстве с реальной жизнью: после выбора "Configure interpreter..." в правом нижнем углу основного окна в упрощенном режиме и нажатия на ссылку "install or update firmware" я не вижу "target device location" и "target device model", а кнопка "Установить" оказывается заблокированной.

Поэтому я просто кидаю rp2-pico-20220117-v1.18.uf2 в проводник. После этого том автоматически закрывается, и после перетыкания USB и перезагрузки в "FS Mode" я вижу порт /dev/ttyACM0 в Thonny.

После этого наша плата готова к работе. Можно делать в основном две вещи: исполнять код на плате "в прямом эфире", набивая его в терминал (Arduino нервно курит в сторонке). Например, здесь я вывел названия всех модулей MicroPython.

А также классическим способом писать скрипт и заливать его в плату с помощью кнопки "Выполнить". Лично мне MicroPython показался не очень интересным, потому что никаких особых преимуществ по сравнению с Arduino Framework он не дает. Из интересного могу выделить uasyncio (асинхронные возможности), ucollections (упорядоченные словари и всё такое), uctypes (ускорение критичных участков кода на python-подобном языке, которые транслируются в Си и компилируются), json (собственно, json), struct (упаковка байтов). Все эти возможности я так или иначе использовал в своей практике на других платформах.
Скрипт для простейшей мигалки у нас такой:
from machine import Pin, Timer
led = Pin(25, Pin.OUT)
timer = Timer()
def blink(timer):
led.toggle()
timer.init(freq=2.5, mode=Timer.PERIODIC, callback=blink)
Программирование RPi Pico на CircuitPython в средах Mu Editor и Thonny
Возможности CircuitPython показались мне гораздо более богатыми, поэтому я решил попробовать установить и его. Кроме описанных выше преимуществ в плане использования flash-памяти как накопителя CircuitPython предоставляет много модулей для работы с различной периферией: показометрами и аудио-девайсами, так как разрабатывается сейчас американской компанией Adafruit, клонами устройств которой заполнен весь алиэкспресс.

Для установки идем на официальный сайт CircuitPython и скачиваем прошивку в формате UF2, после чего в boot-режиме кидаем ее в проводник, как мы уже делали это ранее. После перезагрузки у нас flash-том будет называться не RPI-RP2, а CIRCUITPY. И на нем уже можно хранить файлы.

Файл boot_out.txt содержит в себе информацию о текущей прошивке:
Adafruit CircuitPython 7.2.5 on 2022-04-06; Raspberry Pi Pico with rp2040
Board ID:raspberry_pi_pico
По какой-то неведомой мне причине ребята из Adafruit рекомендуют использовать для программирования на CircuitPython среду Mu Editor. Должен сказать, что поддержка CircuitPython есть и в Thonny, поэтому не знаю зачем, но поставим.
Mu Editor на Linux проще всего установить из AppImage.
Просто скачиваем образ, делаем его исполняемым в свойствах файла и запускаем.

После запуска пойдет довольно долгая установка, которую лучше не прерывать. При следующих запусках того же файла программа открывается очень быстро.

Основное отличие Mu Editor от Thonny, на мой взгляд, заключается в том, что Mu Editor позволяет исполнять python-скрипты в автоматическом режиме после нажатия на кнопку "Сохранить", кнопка "Выполнить" отсутствует. Есть одна особенность: основной скрипт должен быть сохранен в файле с названием code.py. Файлы с другими названиями эта среда, судя по всему, запускать не умеет. Для просмотра результатов выполнения скрипта необходимо нажать на кнопку "Последовательный", чтобы вызвать консоль REPL. Чтобы продемонстрировать консоль, микроконтроллер у меня тут мигает и считает сколько раз мигнул.

import time
import board
import digitalio
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
x = 0
while True:
led.value = not led.value
time.sleep(0.8)
print(str(x))
Программирование RPi Pico на Golang в среде VSCode
На мой взгляд это наиболее сложная часть, потому что программирование на Go требует хоть какой-то предварительной настройки.
Устанавливаем Golang:
Скачиваем архив
Распаковываем архив в /usr/local/go
$ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.2.linux-amd64.tar.gz
Добавляем /usr/local/go/bin в переменную окружения PATH:
export PATH=$PATH:/usr/local/go/bin
Разработчики пишут, что ее можно добавить в $HOME/.profile or /etc/profile и перезагрузить компьютер, чтобы она перманентно сохранялась, а не обновлялась при каждом закрытии терминала. Не знаю, насколько правильно я это сделал, но вроде работает.
Добавил в файл ~/.profile по аналогии с тем, что там уже было:
if [ -d "/usr/local/bin" ] ; then
PATH="/usr/local/bin:$PATH"
fi
if [ -d "/usr/local/go/bin" ] ; then
PATH="/usr/local/go/bin:$PATH"
fi
Потом проверяем установку Golang:
$ go version
Установка tinyGo (поддержки нашей платы):
wget https://github.com/tinygo-org/tinygo/releases/download/v0.23.0/tinygo_0.23.0_amd64.deb
sudo dpkg -i tinygo_0.23.0_amd64.deb
Или устанавливаем пакет с помощью gDebi или QApt. dpkg лично я не рекомендую использовать, потому что потом ничего толком не удалишь.
Если не добавляли путь в $PATH в ~/.profile, то пишем в терминал:
export PATH=$PATH:/usr/local/bin
Проверяем установку TinyGo:
$ tinygo version
Ставим в VSCode расширения Go и TinyGo:

Добавляем целевую папку в VSCode и инициируем новый проект blinky в этой папке:
go mod init blinky
Добавляем с помощью меню VSCode файл main.go и копируем в него код
package main
import (
"machine"
"time"
)
func main() {
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
for {
led.Low()
time.Sleep(time.Millisecond * 500)
led.High()
time.Sleep(time.Millisecond * 500)
}
}
После того, как был инициирован проект Go или добавлен файл с расширением .go (точно не помню), расширение TinyGo должно автоматически подхватить этот проект, и снизу появится панелька.
Выбираем в левом нижнем углу target "pico":

Плата должна быть в режиме boot. Прошивать можно даже с залитым микропитоном. Дальше в терминале VS Code для заливки кода в плату пишем:
tinygo flash -target=pico main.go
Тут нас ждет уже известный нам прикол: после загрузки в режиме boot вы должны предварительно открыть диск RPI-RP2 в проводнике (у меня это Dolphin), и затем его закрыть. Иначе при попытке прошить будет валиться с воплями:
error: failed to flash /tmp/tinygo2433652575/main.uf2: unable to locate device: RPI-RP2

В случае успеха команда просто выполняется, и плата начинает мигать светодиодом.
На данной странице вы можете найти список Golang-пакетов, поддерживаемых TinyGo и известные ошибки. От себя напишу, что пакетов пока маловато, но сама возможность использовать Go очень прельщает. Я писал на этом языке веб-приложение на фреймворке BeeGo для дипломного проекта моей жены, и нахожу этот язык прекрасным и лаконичным. Основные плюсы Golang в том, что он является быстрым, компилируемым, и в нем можно использовать сишные библиотеки. Это хороший простой язык для американизированных приматов типа меня, которые не могут в сложные современные языки типа Rust.
Программирование RPi Pico на Си в PlatformIO
Программирование RPi Pico в PlatformIO я опишу в основном по материалам этой статьи со своими замечаниями.
Предполагаем, что PlatformIO как расширение для VSCode у нас уже установлено. Далее необходимо добавить поддержку RPi Pico:
В Platforms - Advanced Installation добавляем новую платформу Wizio-Pico.
https://github.com/Wiz-IO/wizio-pico


Далее в Platforms - Project - Create New Project добавляем новый проект.

Интересно, что у Wizio существует два варианта прошивки: через USB-порт (PICOPROBE) и уже известный нам по предыдущей моей статье программатор-отладчик CMSIS-DAP. Также есть выбор фреймворка: Baremetal (сишный фреймворк RP Pico) и Arduino Framework. Выбираем Baremetal.

После создания проекта файл platformio.ini необходимо отредактировать следующим образом:
[env:raspberry-pi-pico]
platform = wizio-pico
board = raspberry-pi-pico
framework = baremetal
upload_port = /media/alex/RPI-RP2/ ; directory for mass-storage
monitor_port = /dev/ttyACM0 ; directory for usb-over-serial
monitor_speed = 115200
board_build.bynary_type = copy_to_ram
build_flags =
-D PICO_STDIO_USB ; enable stdio over USB
build_unflags = -D PICO_STDIO_UART
После чего в файл main.c добавляем код мигалки:
#include "pico/stdlib.h"
const uint LED_PIN = 25;
int main() {
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
while (1) {
gpio_put(LED_PIN, 0);
sleep_ms(250);
gpio_put(LED_PIN, 1);
sleep_ms(1000);
}
}
На сях аналогично Python и Go - пока не откроешь RPI-RP2 в проводнике и не закроешь, попытка залить программу вызывает ошибку:
Configuring upload protocol...
AVAILABLE: cmsis-dap, picoprobe, uf2
CURRENT: upload_protocol = uf2
Uploading...
Converting to UF2 ( 0x10000000 )
Wrote 70656 bytes to /home/alex/Документы/PlatformIO/Projects/pico/.pio/build/raspberry-pi-pico/APPLICATION.uf2
Pico USB drive not found.
Успешная прошивка выглядит как-то так:

Заключение
В этой статье мы рассмотрели основные способы программирования Raspberry Pi Pico на разных языках и в различных средах. Безусловно, есть и другие варианты практически в любых сочетаниях. Например, можно программировать на Си непосредственно в VSCode, используя Raspberry Pi Pico SDK и компилятор GCC for arm-none-eabi, также есть установка SDK через скрипт. На мой взгляд эти способы являются более сложными и менее удобными. Возможно, кому-то привычнее работать непосредственно в VSCode, но я привык работать в PlatformIO, который предоставляет более удобный интерфейс для прошивки различных плат.
Я считаю, что будущее любительской электроники именно за этими платами, потому что они предоставляют гораздо более широкие возможности для применения альтернативных языков программирования. Си - это язык 1973 года рождения, в нем даже нет банальных удобных функций для работы со строками (в том числе в реализации AVR-GCC, хотя там есть несколько дополнительных функций, отчасти решающих эту проблему). Просто чтобы сформировать и вывести строку в терминал или на экран дисплея, приходится городить целый огород из специализированных функции, продумывать заранее, как ты будешь формировать строку, а также постоянно экономить память. В Arduino Framework проблема со строками решена, но, тем не менее, он остается довольно убогим и малоприменимым для более серьезных проектов. В таких языках как Python и Go множество различных встроенных возможностей, которые делают программирование на них удобным в отличие от Си. А проект CircuitPython вообще предоставляет невиданную доселе возможность прямого использования памяти как накопителя и выполнения кода прямо с этого накопителя из файла code.py. То есть редактируй код где хочешь, хоть в блокноте - записал файл на диск с помощью проводника, и код уже выполняется микроконтроллером. Это просто возможности будущего, которые уже есть в Raspberry Pi Pico.