По всему тексту «SDK» относится к Raspberry Pi Pico SDK. Исходный код, включенный в документацию, защищен авторскими правами © 2020 Raspberry Pi (Trading) Ltd ( RPTL) под лицензией 3-Clause BSD.
RPTL предоставляет пользователям разрешение на использование РЕСУРСОВ исключительно в сочетании с продуктами Raspberry Pi. Любые другие виды использования РЕСУРСОВ запрещены.
RPTL и автор этого вольного перевода не несут за Ваши действия ни какой ответственности и не дают ни каких гарантий.
Окружение Micro Python
Python — это самый быстрый способ начать работу со встроенным программным обеспечением на Raspberry Pi Pico. Это руководство посвящено официальному порту MicroPython для плат на базе микроконтроллеров RP2040.
MicroPython — это реализация Python 3 для микроконтроллеров и небольших встраиваемых систем.
Поскольку Micro Python очень эффективен, а RP2040 разработан с непропорциональным объемом системной памяти и вычислительной мощности по сравнению с его ценой, MicroPython является хорошим инструментом для разработки доступных, во всех отношениях, встраиваемых систем.
Для исключительно требовательных частей программного обеспечения вы можете использовать SDK (описанный в документах «Начало работы с Raspberry Pi Pico» и «Raspberry Pi Pico C/C++ SDK» ) или внешний модуль C, добавленный в вашу прошивку MicroPython, чтобы выжать последнюю каплю производительности. Что касается любого другого проекта, MicroPython берет на себя большую часть тяжелой работы и позволяет вам сосредоточиться на написании кода, который повышает ценность вашего проекта. Библиотеки ускоренных вычислений с плавающей запятой во встроенном ПЗУ RP2040 автоматически используются вашим кодом Python, поэтому арифметическая производительность должна быть достаточно высокой.
Большая часть аппаратного обеспечения на кристалле доступна через стандартный машинный модуль, поэтому существующие проекты MicroPython можно портировать без особых проблем. Второе ядро процессора доступно через модуль _thread.
RP2040 имеет уникальное аппаратное обеспечение, которое вы не найдете в других микроконтроллерах, а программируемая система ввода-вывода (PIO) является ярким примером этого. PIO — это универсальная аппаратная подсистема, которая позволяет создавать новые интерфейсы ввода-вывода и запускать их на высокой скорости. В модуле rp2 вы найдете исчерпывающую библиотеку PIO, которая позволяет вам писать новые программы PIO по приглашению MicroPython и взаимодействовать с ними в режиме реального времени, чтобы разрабатывать интерфейсы для новых или необычных аппаратных средств (или даже если вы просто хотите добавить несколько дополнительных последовательных портов).
MicroPython реализует весь синтаксис Python 3.4 (включая исключения, with, yield from и т. д, а также asinc /await ключевые слова из Python 3.5). Предоставляются следующие основные типы данных: str (включая базовую поддержку Unicode),.bytes, bytearray, tuple, list, dict, set, frostset, array.array, collections.namedtuple, классы и экземпляры. К встроенным модулям относятся sys, time, struct и т. д. Обратите внимание, что для типов данных и модулей реализована только часть функций Python 3 . MicroPython может выполнять сценарии в текстовой исходной форме (файлы с расширением py) или из предварительно скомпилированного байт-кода, в обоих случаях либо из файловой системы на устройстве, либо «замороженных» в исполняемом файле MicroPython.
Бинарный файл MicroPython для RP2040
Готовый двоичный файл с последней прошивкой MicroPython доступен в документации в разделе Micro Python. Самый быстрый способ получить MicroPython — загрузить готовый бинарный файл с расширением uf2.
Если вы не можете или не хотите использовать готовый файл, например, если вы хотите разработать модуль на Си для MicroPython — вы можете следовать инструкциям в Разделе 1.3, чтобы получить исходный код для MicroPython, который вы можно использовать для создания собственного бинарного файла прошивки MicroPython.
Установка MicroPython на Raspberry Pi Pico
Raspberry Pi Pico имеет режим BOOTSEL для загрузки прошивки через порт USB. Удерживая кнопку BOOTSEL в момент включения платы, вы переводите ее в специальный режим, в котором она отображается как запоминающее устройство USB.
Сначала убедитесь, что ваш Raspberry Pi Pico не подключен к какому-либо источнику питания: отсоедините кабель micro USB, если он подключен, и отсоедините любые другие провода, которые могут подавать питание на плату, например, через контакт VSYS или VBUS. Теперь, удерживая нажатой кнопку BOOTSEL, подключите кабель micro USB (другой его конец должен быть подключен к компьютеру).
На компьютере должен появиться диск с именем RPI-RP2. Перетащите файл прошивки MicroPython.uf2 на этот диск. Это запишет прошивку MicroPython на флэш-память вашего Raspberry Pi Pico.
Для загрузки файла UF2 во флэш-память потребуется несколько секунд. По завершении загрузки файла плата автоматически перезагрузится, в результате чего диск RPI-RP2 исчезнет и загрузится в MicroPython.
По умолчанию MicroPython ничего не делает при первой загрузке. Он ждет, пока вы введете свои инструкции.
В главе 2 показано, как вы можете подключиться к микропрограмме MicroPython, установленной на вашей плате. Вы можете прочитать дальше, чтобы узнать, как можно создать собственный файл прошивки MicroPython из исходного кода.
В документе «Начало работы с Raspberry Pi Pico» есть подробные инструкции по настройке Raspberry Pi Pico.
Режим BOOTSEL и загрузка файлов UF2, если у вас возникли проблемы. Также есть раздел, посвященный загрузке файлов ELF с помощью отладчика, на случай, если ваша плата не имеет простого способа входа в BOOTSEL или вы хотите отладить разрабатываемый модуль MicroPython C.
Замечание: Если вы не следуете этим инструкциям на Raspberry Pi Pico, возможно, у вас нет кнопки BOOTSEL. Если это так, вам следует проверить, есть ли какой-либо другой способ заземления контакта CS флэш-памяти, например, перемычка, чтобы сообщить RP2040 о переходе в режим BOOTSEL при загрузке. Если такого метода нет, вы можете загрузить код с помощью интерфейса Serial Wire Debug.
Сборка MicroPython из исходного кода
Готовый двоичный файл, который можно загрузить из раздела документации MicroPython, должен подойти для большинства случаев использования, но вы можете собрать собственную прошивку MicroPython из исходного кода, если хотите настроить ее низкоуровневые аспекты.
Совет: Если вы уже загрузили и установили готовый файл MicroPython UF2, вы можете сразу перейти к главе 2, чтобы начать использовать свою плату.
Важно! В этих инструкциях по получению и сборке MicroPython предполагается, что вы используете ОС Raspberry Pi, работающую на Raspberry Pi 4, или эквивалентный дистрибутив Linux на основе Debian, работающий на другой платформе.
Рекомендуется создать каталог /home/pi/pico, чтобы хранить в нем все проверки, связанные с pico.
Затем клонируйте git-репозиторий micropython. Эти инструкции позволят получить последнюю версию исходного кода.
$ git clone -b master https://github.com/micropython/micropython.git
После завершения загрузки исходный код MicroPython должен находиться в новом каталоге с именем micropython. Репозиторий MicroPython также содержит указатели (субмодули) на определенные версии библиотек, необходимые для запуска на конкретной платы, как SDK в случае RP2040. Нам также нужно получить их явно:
$ cd micropython
$ git submodule update --init -- lib/pico-sdk lib/tinyusb
Замечание: Следующие инструкции предполагают, что вы используете Raspberry Pi Pico. Некоторые детали могут отличаться, если вы собираете прошивку для другой платы на базе RP2040. Поставщик платы должен подробно описать любые дополнительные шаги, необходимые для создания микропрограммы для этой конкретной платы. Версия, которую мы создаем здесь, является довольно общей, но могут быть некоторые отличия, такие как размещение последовательного порта по умолчанию на разных контактах или включение дополнительных модулей для управления оборудованием этой платы.
Чтобы собрать порт RP2040 MicroPython, вам потребуется установить некоторые дополнительные инструменты. Для создания проектов вам понадобится CMake, кроссплатформенный инструмент, используемый для создания программного обеспечения, и GNU Embedded Toolchain for Arm, который превращает исходный код MicroPython C в двоичную программу, которую могут понять процессоры RP2040. build-essential — это набор инструментов, необходимых для сборки кода, родного для вашей собственной машины — он необходим для некоторых внутренних инструментов в MicroPython и SDK. Вы можете установить все это через apt из командной строки. Все, что вы уже установили, будет игнорироваться apt.
$ sudo apt update
$ sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
Сначала нам нужно загрузить специальный инструмент для сборок MicroPython, который поставляется с исходным кодом:
чип. $ cd ports/rp2
$ make
Если все прошло хорошо, появится новый каталог с именем build (ports/rp2/build относительно каталога micropython), который содержит бинарные файлы новой прошивки. Наиболее важными из них являются: firmware.uf2 Бинарный файл UF2, который можно перетащить на диск RPI-RP2, который появляется, когда ваш Raspberry Pi Pico находится в режиме BOOTSEL. Бинарный файл прошивки, который вы можете загрузить со страницы документации, представляет собой файл UF2, потому что его проще всего установить.
firmware.elf Двоичный файл другого типа, который может быть загружен отладчиком (например, gdb с openocd) через порт отладки SWD RP2040. Это полезно для отладки собственного модуля C, добавленного вами в MicroPython, или самого базового интерпретатора MicroPython. Фактическое двоичное содержимое такое же, как и в файле firmware.uf2.
Вы можете заглянуть в свой новый файл firmware.uf2 с помощью picotool, см. Приложение B в книге «Начало работы с Raspberry Pi Pico», чтобы узнать, как использовать picotool, например,
$ picotool info -a build/firmware.uf2
File /home/ pi/pico/micropython/ports/rp2/build/firmware.uf2: Название информации о программе : Версия MicroPython: v1.13-288-g3ce8f14e0 Особенности: Поддержка потоков USB REPL замороженные модули : _boot, rp2, ds18x20, onewire, uasyncio, uasyncio/core, uasyncio/event, uasyncio/funcs, uasyncio/lock, uasyncio/stream . Информация о фиксированном выводе MicroPython отсутствует Информация о сборке Версия SDK: 1.0.0 pico_board: дата сборки pico: 21 января 2021 г. Атрибуты сборки: MinSizeRel
Подключение к MicroPython REPL
Когда MicroPython загружается в первый раз, он будет ждать, пока вы подключитесь и скажете ему, что делать. Вы можете загрузить файл .py со своего компьютера на плату, но более быстрый способ взаимодействия с ним — это так называемый цикл readevaluate-print или REPL (часто произносится как «рябь»).
Чтение MicroPython ждет, пока вы наберете текст, а затем нажмите клавишу ввода.
Оценка Все, что вы ввели, интерпретируется как код Python и запускается немедленно.
Печать Любые результаты последней введенной вами строки распечатываются, чтобы вы могли их прочитать.
Цикл Вернуться к началу — запросить еще одну строку кода.
Есть два способа подключения к этому REPL, чтобы вы могли обмениваться данными с прошивкой MicroPython на вашей плате:
через USB и через последовательный порт UART на Raspberry Pi Pico GPIO.
Подключение с Raspberry Pi через USB
Прошивка MicroPython оснащена виртуальным последовательным портом USB, доступ к которому осуществляется через разъем micro USB на Raspberry Pi Pico. Ваш компьютер должен заметить этот последовательный порт и указать его как символьное устройство, скорее всего, /dev/ttyACM0.
Замечание: Вы можете запустить ls /dev/tty*, чтобы вывести список своих последовательных портов. Их может быть довольно много, но серийный номер USB MicroPython будет начинаться с /dev/ttyACM. Если вы сомневаетесь, отсоедините разъем micro USB и посмотрите, какой из них исчез. Если вы ничего не видите, попробуйте перезагрузить Raspberry Pi.
Вы можете установить minicom для доступа к последовательному порту:
sudo apt install minicom
а затем открыть его следующим образом:
minicom -o -D /dev/ttyACM0
Где -D /dev/ttyACM0 указывает minicom на последовательный USB-порт MicroPython, а флаг -o по существу означает «просто сделайте это». Не нужно беспокоиться о скорости передачи данных, так как это виртуальный последовательный порт.
Добро пожаловать в minicom 2.7.1
ПАРАМЕТРЫ: I18n
Дата компиляции Aug 13 2017, 15:25:34.
Port /dev/ttyACM0, 13:50:29
Нажмите CTRL-A Z для получения подсказки по клавишам
Нажмите клавишу Enter (Ввод) в терминале, где вы открыли minicom. Вы должны увидеть это:
>>>
Это подсказка. MicroPython хочет, чтобы вы что-то ввели и сказали, что делать.
Если вы нажмете CTRL-D на клавиатуре, когда терминал minicom находится в фокусе, вы должны увидеть сообщение, похожее на это:
MPY: soft reboot
MicroPython v1.18 on 2022-01-17; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>>
Комбинация клавиш CTRL-D перезагружает MicroPython. Вы можете сделать это в любое время. При перезагрузке MicroPython распечатает сообщение, в котором будет точно указано, какая версия прошивки запущена и когда она была собрана. Ваш номер версии будет отличаться от показанного здесь.
Подключение к Raspberry Pi через UART
Внимание! REPL через UART по умолчанию отключен.
Порт MicroPython для RP2040 по умолчанию не предоставляет REPL через порт UART. Однако это значение по умолчанию можно
изменить в исходном файле ports/rp2/mpconfigport.h. Если вы хотите использовать REPL через UART, вам придется собрать
MicroPython самостоятельно, подробности см. в Разделе 1.3.
Загрузите исходный код MicroPython и в ports/rp2/mpconfigport.h измените MICROPY_HW_ENABLE_UART_REPL на 1
, чтобы включить его.
#define MICROPY_HW_ENABLE_UART_REPL (1) // полезно, если нет USB
Затем продолжайте следовать инструкциям в разделе 1.3, чтобы создать собственную прошивку MicroPython UF2.
Это позволит получить доступ к REPL через порт UART через два контакта GPIO. Настройки по умолчанию для UART
взяты из C SDK.
Таблица 1. Настройки UART
по умолчанию в
MicroPython
Функция По умолчанию
UART_BAUDRATE 115 200
UART_BITS 8
UART_STOP 1
UART0_TX Pin 0
UART0_RX Pin 1
UART1_TX Pin 4
UART1_RX Pin 5
Этот альтернативный интерфейс удобен, если у вас есть проблемы с USB, если у вас нет свободных портов USB , или если вы используете
какую-либо другую плату на базе RP2040, которая не имеет открытого USB-разъема.
ПРИМЕЧАНИЕ
Первоначально он занимает периферийное устройство UART0 на RP2040. Периферийное устройство UART1 можно бесплатно использовать в коде Python
в качестве второго UART.
Следующее, что вам нужно сделать, это включить последовательный порт UART на Raspberry Pi. Для этого запустите raspi-config,
$ sudo raspi-config ,
перейдите в «Параметры интерфейса» → «Последовательный» и выберите «Нет» в ответ на вопрос «Хотите ли вы, чтобы оболочка входа была доступна через
последовательный порт?» и «Да» на вопрос «Хотите ли вы включить аппаратное обеспечение последовательного порта?». Вы должны увидеть что-то похожее
на рисунок 1.
Рисунок 1. Включение
последовательного UART с помощью
raspi-config на
Raspberry Pi.
Оставив raspi-config, вы должны выбрать «Да» и перезагрузить Raspberry Pi, чтобы включить последовательный порт.
Затем вы должны подключить Raspberry Pi и Raspberry Pi Pico вместе со следующим сопоставлением:
Raspberry Pi Raspberry Pico
GND GND
GPIO15 (UART_RX0) GPIO0 (UART0_TX)
GPIO14 (UART_TX0) GPOI1 (UART0_RX)
ВАЖНО
RX совпадает с TX и TX соответствует RX. Вы не должны соединять два противоположных контакта TX или два
контакта RX вместе. Это связано с тем, что MicroPython должен прослушивать канал, который передает Raspberry Pi, и наоборот.
См. рис. 2.
Рисунок 2. Raspberry Pi 4 и Raspberry Pi Pico с UART0, соединенные вместе.
затем подключитесь к плате с помощью minicom, подключенного к /dev/serial0,
$ minicom -b 115200 -o -D /dev/serial0
Если вы нажмете клавишу ввода, MicroPython должен ответить, предложив вам ввести дополнительные данные:
>>>
Подключение с Mac
Пока вы используете последнюю версию macOS, например Catalina, драйверы уже должны быть загружены. В противном случае см
. веб-сайт производителя драйверов микросхем FTDI. Затем вы должны использовать программу терминала для подключения к Serial-over-USB
(USB CDC). Последовательный порт будет отображаться как /dev/tty.usbmodem0000000000001
Если у вас еще не установлена программа терминала, вы можете установить minicom с помощью Homebrew,
$ brew install minicom
и подключиться к плате, как показано ниже.
$ minicom -b 115200 -o -D /dev/tty.usbmodem00000000000001
Raspberry Pi Pico Python SDK
2.3. Подключение с Mac 10
ПРИМЕЧАНИЕ
Можно также использовать другие терминальные приложения, такие как CoolTerm или Serial.
2.4. Скажите «Hello World»
. После подключения вы можете проверить, все ли работает, набрав Python «Hello World» в REPL,
>>> print («Hello, Pico!»)
Hello, Pico!
>>>
2.5. Мигание светодиодом
Встроенный светодиод на Raspberry Pi Pico подключен к контакту 25 GPIO. Вы можете включать и выключать его из REPL. Когда
вы увидите приглашение REPL, введите следующее:
>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
Машинный модуль используется для управления встроенным оборудованием. Это стандартно для всех портов MicroPython, и вы можете узнать
больше об этом в документации MicroPython. Здесь мы используем его, чтобы взять под контроль GPIO, поэтому мы можем управлять им высоко
и низко.
Светодиод должен загореться. Вы можете снова отключить его с помощью
>>> led.value(0)
2.6. Что дальше?
На этом этапе у вас должен быть установлен MicroPython на вашей плате, и вы должны протестировать свои настройки, введя короткие программы
в приглашение, чтобы распечатать вам текст и помигать светодиодом.
Вы можете прочитать следующую главу, посвященную особенностям MicroPython на RP2040 и ее отличиям от
других платформ. В главе 3 также есть несколько коротких примеров различных API, предлагаемых для взаимодействия с оборудованием.
Вы можете узнать, как настроить интегрированную среду разработки (IDE) в главе 4, чтобы вам не приходилось вводить
программы построчно.
Вы можете погрузиться прямо в Приложение А, если хотите приступить к подключению проводов к макетной плате.
Порт RP2040
В настоящее время поддерживаются следующие функции:
• REPL через USB и UART (на GP0/GP1).
• Файловая система размером 1600 КБ с использованием littlefs2 на встроенной флэш-памяти. (Размер по умолчанию для Raspberry Pi Pico)
• Модуль utime с функциями сна и тиков.
• модуль убинации.
• машинный модуль с некоторыми базовыми функциями.
◦
класс machine.Pin.
◦
Класс machine.Timer.
◦
класс machine.ADC.
◦
классы machine.I2C и machine.SoftI2C.
◦
классы machine.SPI и machine.SoftSPI.
◦
класс machine.WDT.
◦
Класс machine.PWM.
◦
класс machine.UART.
• Модуль для конкретной платформы rp2.
◦
Библиотека аппаратного доступа PIO
◦
Программный ассемблер PIO
◦ Доступ для
чтения/записи Raw flash
• Многоядерная поддержка предоставляется через стандартный модуль _thread
• Ускоренная арифметика с плавающей запятой с использованием библиотеки ROM RP2040 и аппаратного разделителя (используется автоматически)
Документация по MicroPython доступна по адресу https:/ /docs.micropython.org. Например, машинный модуль,
который можно использовать для доступа ко многим аппаратным средствам RP2040, является стандартным, и вы найдете много необходимой информации
в онлайн-документации для этого модуля.
В этой главе дается очень краткий обзор некоторых аппаратных API с примерами кода, которые вы можете ввести в
REPL (глава 2) или загрузите на плату с помощью среды разработки, установленной на вашем компьютере (глава 4).
3.1. Вечное мигание светодиода (таймер)
В главе 2 мы увидели, как можно использовать класс machine.Pin для включения и выключения светодиода, управляя GPIO высоким и низким уровнем.
>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
>>> led.value(1)
>>> led.value(0)
Это, мягко говоря, довольно запутанный способ включения и выключения света. Выключатель света работал бы лучше. Класс
machine.Timer, который использует аппаратный таймер RP2040 для запуска обратных вызовов через регулярные промежутки времени, экономит много времени на вводе, если
мы хотим, чтобы свет включался и выключался неоднократно, тем самым обеспечивая наш уровень автоматизации от «механического переключателя».
«555 таймер».
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/blink/blink.py Строки 1–9
1 из пина импорта машины, Таймер
2
3 led = Pin(25, Pin. OUT)
4 tim = Timer()
5 def tick(timer):
6 global led
7 led.toggle()
8
9 tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick)
Ввод этой программы в REPL приведет к тому, что светодиод начнет мигать, но снова появится подсказка:
>>>
Созданный нами таймер будет работать в фоновом режиме с указанным интервалом, мигая светодиодом. Приглашение MicroPython
все еще работает на переднем плане, и мы можем ввести дополнительный код или запустить больше таймеров.
UART
ПРИМЕЧАНИЕ
REPL через UART по умолчанию отключен. См. Раздел 2.2 для получения подробной информации о том, как включить REPL через UART.
Пример использования циклического соединения UART0 с UART1.
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/uart/loopback/uart.py Строки 1–15
1 из машинного импорта UART, контакт
2 импорта времени
3
4 uart1 = UART (1, скорость = 9600, tx = контакт (8), rx = контакт (9))
5
6 uart0 = UART (0, скорость = 9600, tx = контакт (0), rx = контакт (1))
7
8 txData = b'hello world\n\r'
9 uart1.write(txData)
10 time.sleep(0.1)
11 rxData = bytes()
12 while uart0.any() > 0:
13 rxData += uart0.read(1)
14
15 print(rxData.decode('utf-8'))
Дополнительные сведения, включая схему подключения, см. в Приложении A.
3.3. АЦП
Аналого-цифровой преобразователь (АЦП) измеряет некоторый аналоговый сигнал и кодирует его как цифровое число. АЦП на
RP2040 измеряет напряжения.
АЦП имеет две ключевые характеристики: разрешение, измеряемое в цифровых битах, и количество каналов, или количество аналоговых сигналов, которые он
может принимать и преобразовывать одновременно. АЦП на RP2040 имеет разрешение 12 бит, что означает, что он может преобразовывать
аналоговый сигнал в цифровой сигнал в виде числа в диапазоне от 0 до 4095, хотя это обрабатывается в MicroPython,
преобразованном в 16-битное число в диапазоне от 0 до 4095. 65 535, чтобы он вел себя так же, как АЦП на других
микроконтроллерах MicroPython.
Всего RP2040 имеет пять каналов АЦП, четыре из которых выведены на микросхемы GPIO: GP26, GP27, GP28 и GP29. На
Raspberry Pi Pico первые три из них выведены на контакты GPIO, а четвертый можно использовать для измерения
напряжения VSYS на плате.
Пятый входной канал АЦП подключен к датчику температуры, встроенному в RP2040.
Вы можете указать, какой канал АЦП вы используете, по номеру вывода, например,
adc = machine.ADC(26) # Подключиться к GP26, который является каналом 0
, или по каналу,
adc = machine.ADC(4) # Подключиться к внутренней температуре sensor
adc = machine.ADC(0) # Подключение к каналу 0 (GP26)
Пример чтения четвертого канала аналого-цифрового преобразователя (АЦП), подключенного к внутреннему
датчику температуры:
Pico MicroPython Примеры: https://github. com/raspberrypi/pico-micropython-examples/tree/master/adc/temperature.py Строки 1–14
1 импорт машины
2 импорт utime
3
4 sensor_temp = machine.ADC(4)
5 convert_factor = 3.3 / (65535)
6
7 while True:
8 read = sensor_temp.read_u16() * convert_factor
9
10 # Датчик температуры измеряет напряжение Vbe смещенного биполярного диода, подключенного к
пятому каналу АЦП
11 # Обычно Vbe = 0,706 В при 27 градусах Цельсия, с наклоном -1,721 мВ (0,001721) на градус.
12 температура = 27 - (чтение - 0,706)/0,001721
13 печать (температура)
14 utime.sleep(2)
3.4. Прерывания
Вы можете установить IRQ следующим образом:
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/irq/irq.py Строки 1–4
1 из импорта машины Pin
2
3 p2 = Pin(2, Pin.IN, Pin.PULL_UP)
4 p2.irq(лямбда-вывод: print("IRQ с флагами:", pin.irq().flags()), Pin.IRQ_FALLING)
Он должен что-то распечатать, когда GP2 имеет спадающий фронт.
3.5. Многоядерная поддержка
Пример использования:
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/multicore/multicore.py Строки 1–12
1 время импорта, _thread, машина
2
3 задача определения ( n, задержка):
4 led = machine.Pin(25, machine.Pin.OUT)
5 for i in range(n):
6 led.high()
7 time.sleep(delay)
8 led.low()
9 time .sleep(delay)
10 print('done')
11
12 _thread.start_new_thread(task, (10, 0.5))
В любой момент времени может быть запущен/выполнен только один поток, потому что нет RTOS, есть только второе ядро. GIL не
включен, поэтому и core0, и core1 могут одновременно запускать код Python, осторожно используя блокировки для общих данных.
3.6.
Пример использования I2C :
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/i2c/i2c.py Строки 1–11
1 из пина импорта машины, I2C
2
3 i2c = I2C( 0, scl=Pin(9), sda=Pin(8), freq=100000)
4 i2c.scan()
5 i2c.writeto(76, b'123')
6 i2c.readfrom(76, 4)
7
8 i2c = I2C(1, scl=Pin(7), sda=Pin(6), freq=100000)
9 i2c.scan()
10 i2c.writeto_mem(76, 6, b'456')
11 i2c.readfrom_mem(76, 6, 4)
I2C можно построить без указания частоты, если просто хочется все по умолчанию.
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/i2c/i2c_without_freq.py Строки 1–3
1 из машинного импорта I2C
2
3 i2c = I2C(0) # по умолчанию SCL=Pin(9), SDA=Pin(8), freq=400000
ПРЕДУПРЕЖДЕНИЕ
Возможны некоторые ошибки при чтении/записи адресов устройств, которые не отвечают, аппаратное обеспечение в
некоторых случаях блокируется.
Таблица 2. Выводы I2C по
умолчанию Функция
Частота I2C по умолчанию 400 000
I2C0 SCL, контакт 9
I2C0 SDA, контакт 8
I2C1 SCL, контакт 7
I2C1 SDA, контакт 6
3.7.
Пример использования SPI :
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/spi/spi.py Строки 1–11
1 из машинного импорта SPI
2
3 spi = SPI(0)
4 spi = SPI(0, 100_000)
5 spi = SPI(0, 100_000, полярность=1, фаза=1)
6
7 spi.write('test')
8 spi.read(5)
9
10 buf = bytearray(3 )
11 spi.write_readinto('out', buf)
ПРИМЕЧАНИЕ
Выбор микросхемы должен управляться отдельно с помощью machine.Pin.
Таблица 3. Выводы SPI по
умолчанию Функция По умолчанию
SPI_BAUDRATE 1 000 000
SPI_POLARITY 0
SPI_PHASE 0
SPI_BITS 8
SPI_FIRSTBIT MSB
SPI0_SCK Вывод 6
SPI0_MOSI, контакт 7
SPI0_MISO, контакт 4
SPI1_SCK, контакт 10
SPI1_MOSI, контакт 11SPI1_MISO, контакт 8
PWM
Пример использования PWM для затухания светодиода:
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pwm/pwm_fade.py Строки 1–25
1 # Пример использования PWM для затухания светодиод.
2
3 время импорта
4 из штырька импорта машины, ШИМ
5
6
7 # Создание объекта ШИМ со светодиодом на штыре (25).
8 pwm = PWM(Pin(25))
9
10 # Установить частоту ШИМ.
11 pwm.freq(1000)
12
13 # Включение и выключение светодиода несколько раз.
14 режим = 0
15 направление = 1
16 для _ в диапазоне (8 * 256):
17 режим += направление
18 если режим > 255:
19 режим = 255
20 направление = -1
21 elif режим < 0:
22 долг = 0
23 направление = 1
24 pwm.duty_u16(долг * долг)
25 time.sleep(0,001)
Поддержка PIO
Текущая поддержка позволяет вам определять блоки Programmable IO (PIO) Assembler и использовать их в периферийном устройстве PIO.
Дополнительную документацию по PIO можно найти в главе 3 технического описания RP2040 и главе 4 книги Raspberry Pi
Pico C/C++ SDK . .
Raspberry Pi Pico MicroPython представляет новый декоратор @rp2.asm_pio вместе с классом rp2.PIO. Определение программы
PIO и конфигурация конечного автомата, разделенные на 2 логические части:
• Определение программы, включая количество используемых контактов и являются ли они входными/выходными. Это входит в определение @rp2.asm_pio
. Это близко к тому, что инструмент pioasm из SDK сгенерировал бы из файла .pio (но здесь все
определено в Python).
• Экземпляр программы, который устанавливает частоту конечного автомата и к каким выводам привязывается. Они устанавливаются
при настройке SM для запуска определенной программы.
Цель состояла в том, чтобы программа могла быть определена один раз, а затем легко создавать экземпляры несколько раз (при необходимости) с разными
GPIO. Другая цель заключалась в том, чтобы упростить выполнение основных задач, не утяжеляя слишком много
конфигураций PIO/SM.
Пример использования, чтобы мигать встроенным светодиодом, подключенным к GPIO 25,
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_blink.py Строки 1–28
1 время импорта
2 импорт rp2
3 импорт из машины Pin
4
5 # Определить мерцание программа. У него есть один GPIO для привязки к инструкции set, который является
выходным контактом.
6 # Используйте много задержек, чтобы мигание было видно невооруженным глазом.
7 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
8 def blink():
9 wrap_target()
10 set(pins, 1) [31]
11 nop() [31]
12 nop() [31]
13 nop () [31]
14 nop() [31]
15 set(pins, 0) [31]
16 nop() [31]
17 nop() [31]
18 nop() [31]
19 nop() [31]
20 обернуть()
21
22 # Создание конечного автомата с программой мигания на частоте 2000 Гц с привязкой набора к выводу (25)
(светодиод на плате rp2)
23 sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(25) ))
24
25 # Запустить конечный автомат на 3 секунды. Светодиод должен мигать.
26 sm.active(1)
27 time.sleep(3)
28 sm.active(0)
или через явный exec.
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_exec.py Строки 1–27
1 # Пример использования PIO для включения светодиода с помощью явного exec.
2 #
3 # Демонстрация:
4 # - использование set_init и set_base
5 # - использование StateMachine.exec
6
7 время импорта
8 from machine import Pin
9 import rp2
10
11 # Определить пустую программу, которая использует один установленный вывод.
12 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
13 def prog():
14 pass
15
16
17 # Создайте StateMachine, привязав вывод (25) к заданному выводу.
18 sm = rp2.StateMachine(0, prog, set_base=Pin(25))
19
20 # Включить установленный вывод с помощью инструкции exec.
21 sm.exec("set(pins, 1)")
22
23 # Переход в спящий режим на 500 мс.
24 времени сна (0,5)
25
26 # Отключить set pin командой exec.
27 sm.exec("set(pins, 0)")
Несколько замечаний:
• Вся конфигурация программы (например, автозагрузка) выполняется в декораторе @asm_pio.
В конструкторе StateMachine задаются только выводы частоты и базы .
• [n] используется для задержки, .set(n) используется для бокового набора
• Ассемблер автоматически определяет, используется ли боковой набор везде или только в нескольких инструкциях, и
автоматически устанавливает бит SIDE_EN
Идея состоит в том, что для 4 наборов контакты (in, out, set, sideset, исключая jmp), которые могут быть подключены к машине состояний,
для каждого набора необходимо настроить следующее:
1. базовый GPIO
2. количество последовательных GPIO
3. начальное направление GPIO (вход или выход)
4. начальное значение GPIO (высокое или низкое)
В дизайне Python API для PIO эти 4 элемента разделены на «декларацию» (элементы 2-4) и «экземпляр» ( пункт 1).
Другими словами, программа пишется с элементами 2-4, фиксированными для этой программы (например, драйвер WS2812 будет иметь 1 выходной контакт)
, а элемент 1 может свободно изменяться без изменения программы (например, к какому контакту подключен WS2812).
Таким образом, в декораторе @asm_pio вы объявляете элементы 2-4, а в конструкторе StateMachine вы указываете, какой базовый вывод использовать
(элемент 1). Это позволяет легко определить одну программу и запускать ее несколько раз на разных выводах (вы не можете
действительно изменить пункты 2-4 для другого экземпляра той же программы, это не имеет смысла делать).
И одно и то же ключевое слово arg (в случае с sideset_pins) используется как для объявления, так и для создания экземпляра, чтобы
показать, что они связаны.
Чтобы объявить несколько выводов в декораторе (количество, то есть пункт 2 выше), вы используете кортеж/список значений. И каждый элемент в
кортеже/списке указывает элементы 3 и 4. Например:
1 @asm_pio(set_pins=(PIO.OUT_LOW, PIO.OUT_HIGH, PIO.IN_LOW), sideset_pins=PIO.OUT_LOW)
2 def foo():
3 ....
4
5 sm = StateMachine(0, foo, freq=10000, set_pins=Pin(15), sideset_pins=Pin(22))
В этом примере:
• к SM подключено 3 вывода set, и их начальное состояние (установленное при создании StateMachine):
низкий уровень на выходе, высокий уровень на выходе, низкий уровень на входе (используется для открытого стока)
• имеется 1 вывод sideset, начальное состояние Низкий выходной сигнал
• 3 установочных контакта начинаются с вывода (15)
• 1 боковой вывод начинается с вывода (22)
Причина наличия констант OUT_LOW, OUT_HIGH, IN_LOW и IN_HIGH заключается в том, что значение вывода и направление автоматически
устанавливаются перед запуск программы PIO (вместо того, чтобы тратить впустую слова инструкций, чтобы выполнить set(pindirs, 1) и т. д. в начале).
IRQ
Существует поддержка PIO IRQ, например
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_irq.py Строки 1–25
1 время импорта
2 импорт rp2
3
4 @rp2.asm_pio()
5 def irq_test ():
6 wrap_target()
7 nop() [31]
8 nop() [31]
9 nop() [31]
10 nop() [31]
11 irq(0)
12 nop() [31]
13 nop( ) [31]
14 nop() [31]
15 nop() [31]
16 irq(1)
17 wrap()
18
19
20 rp2.PIO(0).irq(лямбда pio: print(pio.irq(). flags()))
21
22 sm = rp2.StateMachine(0, irq_test, freq=2000)
23 sm.active(1)
24 time.sleep(1)
25 sm.active(0)
Пример программы, которая мигает с частотой 1 Гц и вызывает IRQ с частотой 1 Гц для печати текущей метки времени в миллисекундах
. Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_1hz.py Lines 1 - 33
1 # Пример использования PIO для мигания светодиода и поднятия IRQ с частотой 1 Гц.
2
3 время импорта
4 из машины импортировать Pin
5 импортировать rp2
6
7
8 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
9 def blink_1hz():
10 # Циклы: 1 + 1 + 6 + 32 * (30 + 1 ) = 1000
11 irq(rel(0))
12 set(pins, 1)
13 set(x, 31) [5]
14 label("delay_high")
15 nop() [29]
16 jmp(x_dec, "delay_high" )
17
18 # Циклы: 1 + 7 + 32 * (30 + 1) = 1000
19 set(pins, 0)
20 set(x, 31) [6]
21 label("delay_low")
22 nop() [29]
23 jmp (x_dec, "delay_low")
24
25
26 # Создайте StateMachine с помощью программы blink_1hz, выводящей сигнал на вывод (25).
27 sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
28
29 # Настроить обработчик IRQ для печати метки времени в миллисекундах.
30 sm.irq(lambda p: print(time.ticks_ms()))
31
32 # Запускаем StateMachine.
33 sm.active(1)
или дождаться смены контакта и поднять IRQ.
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_pinchange.py Строки 1–46
1 # Пример использования PIO для ожидания смены контакта и вызова IRQ.
2 #
3 # Демонстрирует:
4 # - перенос PIO
5 # - инструкция ожидания PIO, ожидающая на входном контакте
6 # - инструкция PIO irq в режиме блокировки с относительным номером IRQ
7 # - установка вывода in_base для StateMachine
8 # - установка обработчика прерываний для StateMachine
9 # - создание экземпляров 2x StateMachine с одной и той же программой и разными выводами
10
11 время импорта
12 из машины импорт Pin
13 импорт rp2
14
15
16 @rp2.asm_pio()
17 def wait_pin_low():
18 wrap_target()
19
20 wait(0, pin, 0)
21 irq(block, rel(0) )
22 wait(1, pin, 0)
23
24 wrap()
25
26
27 def handler(sm):
28 # Вывести временную метку (обтекание) и объект конечного автомата.
29 print(time.ticks_ms(), sm)
30
31
32 # Создать экземпляр StateMachine(0) с помощью программы wait_pin_low на выводе(16).
33 pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
34 sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16)
35 sm0.irq(обработчик)
36
37 # Создать экземпляр StateMachine(1) с помощью программы wait_pin_low на выводе(17).
38 pin17 = Pin(17, Pin.IN, Pin.PULL_UP)
39 sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17)
40 sm1.irq(handler)
41
42 # Запускаем StateMachine.
43 sm0.active(1)
44 sm1.active(1)
45
46 # Теперь, когда на выводе (16) или выводе (17) установлен низкий уровень, в REPL будет напечатано сообщение.
Светодиод WS2812 (NeoPixel)
В то время как светодиод WS2812 (NeoPixel) может управляться с помощью следующей программы,
примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_ws2812.py Lines 1 - 52
1 # Пример использования PIO для управления набором светодиодов WS2812.
2
3 массив импорта, время
4 импорт из машины Pin
5 import rp2
6
7 # Настройте количество светодиодов WS2812.
8 NUM_LEDS = 8
9
10
11 @rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True,
pull_thresh=24)
12 def ws2812():
13 T1 = 2
14 T2 = 5
15 T3 = 3
16 wrap_target()
17 label("битовый цикл")
18 out(x, 1) .side(0) [T3 - 1]
19 jmp(not_x, "do_zero") .side(1) [T1 - 1]
20 jmp("bitloop") .side(1) [T2 - 1]
21 label("do_zero")
22 nop() .side(0) [T2 - 1]
23 wrap()
24
25
26 # Создаем StateMachine с помощью программы ws2812, выводя данные на Pin(22).
27 sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22))
28
29 # Запускаем StateMachine, он будет ждать данных в своем FIFO.
30 sm.active(1)
31
32 # Отображение шаблона на светодиодах с помощью массива значений RGB светодиодов.
33 ar = array.array("I", [0 for _ in range(NUM_LEDS)])
34
35 # Цвета цикла.
36 для i в диапазоне (4 * NUM_LEDS):
38 r = j * 100 // (NUM_LEDS - 1)
39 b = 100 - j * 100 // (NUM_LEDS - 1)
40, если j != i % NUM_LEDS:
41 r >>= 3
42 b >>= 3
43 ар[j] = г << 16 | b
44 sm.put(ar, 8)
45 time.sleep_ms(50)
46
47 # Затухание.
48 для i в диапазоне (24):
49 для j в диапазоне (NUM_LEDS):
50 ar[j] >>= 1
51 sm.put(ar, 8)
52 time.sleep_ms(50)
UART TX
Пример UART TX, примеры
Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_uart_tx.py Строки 1–42
1 # Пример использования PIO для создания UART TX интерфейс
2
3 из машинного импорта Контакт
4 из rp2 импортировать PIO, StateMachine, asm_pio
5
6 UART_BAUD = 115200
7 PIN_BASE = 10
8 NUM_UARTS = 8
9
10
11 @asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO .SHIFT_RIGHT)
12 def uart_tx(): 13 # Блок
с отменой подтверждения TX до тех пор, пока данные не будут доступны
17 # Сдвинуть 8 бит данных, 8 циклов выполнения на бит
18 label("bitloop")
19 out(pins, 1) [6]
20 jmp(x_dec, "bitloop"
) 1 для pull())
22 nop() .side(1) [6]
23
24
25 # Теперь мы добавляем 8 UART TX на контакты с 10 по 17. Используйте одинаковую скорость передачи для всех из них.
26 uarts = []
27 для i в диапазоне (NUM_UARTS):
28 sm = StateMachine(
29 i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin
(PIN_BASE + i)
30)
31 sm.active(1)
32 uarts.append(sm)
33
34 # Мы можем печатать символы с каждого UART, помещая их в TX FIFO
35 def pio_uart_print(sm, s):
36 for c in s:
37 sm.put(ord(c))
38
39
40 # Напечатать отдельное сообщение от каждого UART
41 for i, u in enumerate(uarts):
42 pio_uart_print(u, "Привет от UART {}! \n".format(i))
ПРИМЕЧАНИЕ
. Вам необходимо указать начальное состояние вывода OUT в вашей программе, чтобы иметь возможность передать сопоставление OUT вашему
экземпляру SM, даже если в этой программе это избыточно, поскольку сопоставления перекрываются.
SPI
Пример SPI.
Примеры Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_spi.py Строки
1–49 1 импорт rp2
2 из машинного импорта Pin
3
4 @rp2.asm_pio(out_shiftdir= 0, autopull=True, pull_thresh=8, autopush=True, push_thresh=8,
sideset_init=(rp2.PIO.OUT_LOW, rp2.PIO.OUT_HIGH), out_init=rp2.PIO.OUT_LOW)
5 def spi_cpha0():
6 # Примечание X должен быть предварительно инициализирован установочным кодом перед первым байтом, мы перезагружаем после отправки
каждого байта
7 # Обычно это делается с помощью exec(), но в этом случае он находится в памяти инструкций и
запускается только один раз
8 set(x, 6)
9 # Фактическое тело программы следует за
10 wrap_target()
11 pull(ifempty) .side(0x2) [1]
12 label("bitloop")
13 out(pins, 1) .side(0x0) [1]
14 in_(pins, 1) .side(0x1)
15 jmp(x_dec, "bitloop") .side(0x1)
16
17 out(pins, 1) .side(0x0)
18 set(x, 6) .side(0x0) # Обратите внимание, что это можно заменить на mov x, y для
программируемого размера кадра
19 in_(pins, 1) .side(0x1)
20 jmp(not_osre, "bitloop") .side(0x1) # Fallthru, если TXF пуст
21
22 nop() .side(0x0) [1] # CSn back porch
23 wrap()
24
25
26 class PIOSPI:
27
28 def __init__ (self, sm_id, pin_mosi, pin_miso, pin_sck, cpha=False, cpol=False, freq
=1000000):
29 assert(not(cpol или cpha))
30 self._sm = rp2.StateMachine(sm_id, spi_cpha0, freq=4*freq, sideset_base=Pin(
pin_sck), out_base=Pin(pin_mosi), in_base=Pin(pin_sck))
31 self._sm.active(1)
32
33 # Обратите внимание, что этот код эффектно умрет, потому что мы не опустошаем RX FIFO
34 def write_blocking(wdata):
35 for b in wdata:
36 self._sm.put(b << 24)
37
38 def read_blocking(n):
39 data = []
40 для i в диапазоне (n):
41 data.append(self._sm.get() & 0xff)
42 возвращаемые данные
43
44 def write_read_blocking(wdata):
45 rdata = []
46 для b в wdata :
47 self._sm.put(b << 24)
48 rdata.append(self._sm.get() & 0xff)
49 вернуть rdata
ПРИМЕЧАНИЕ
. Эта программа SPI поддерживает программируемые размеры кадров (за счет сохранения значения перезагрузки для счетчика X в регистре Y)
, но в настоящее время это нельзя использовать, поскольку порог автозагрузки связан с программой, а не с
созданием экземпляра SM.
PWM
Пример PWM, примеры
Pico MicroPython: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_pwm.py Строки 1–43
1 # Пример использования PIO для PWM и затухания яркость светодиода
2
3 из машинного импорта Pin
4 из rp2 импортировать PIO, StateMachine, asm_pio
5 из time import sleep
6
7
8 @asm_pio(sideset_init=PIO.OUT_LOW)
9 def pwm_prog():
10 pull(noblock) .side( 0) 11 mov(x ,
osr) # Хранить самые последние данные извлечения в X для повторного использования noblock , "пропустить") 15 nop() .side(1) 16 метка ("пропустить")
17 jmp(y_dec, "pwmloop")
18
19
20 class PIOPWM:
21 def __init__(self, sm_id, pin, max_count, count_freq):
22 self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin (pin))
23 # Используйте exec() для загрузки максимального количества в ISR
24 self._sm.put(max_count)
25 self._sm.exec("pull()")
26 self._sm.exec("mov(isr, osr)")
27 self._sm.active(1)
28 self._max_count = max_count
29
30 def set(self, value):
31 # Минимальное значение равно -1 (полностью отключить), 0 на самом деле по-прежнему дает
значение узкого импульса 32 = макс(значение, -1)
33 значение = мин(значение, self._max_count)
34 self._sm.put(значение)
35
36
37 # Контакт 25 является светодиодом на платах Pico
38 pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000)
39
40 while True:
41 для i в диапазоне (256):
42 pwm. установить (я ** 2)
43 сна (0,01)
Использование pioasm
Помимо написания кода PIO, встроенного в скрипт MicroPython, вы можете использовать инструмент pioasm из C/C++ SDK для
создания файла Python.
$ pioasm -o python input (output)
Для получения дополнительной информации о pioasm см. книгу Raspberry Pi Pico C/C++ SDK, в которой рассказывается о C/C++ SDK.
Использование интегрированной
среды разработки (IDE)
Порт MicroPython для Raspberry Pi Pico и других плат на базе RP2040 работает с широко используемыми
средами разработки.
4.1. Использование Thonny
Пакеты Thonny доступны для Linux, MS Windows и macOS. После установки использование среды разработки Thonny
одинаково на всех трех платформах. Последнюю версию Thonny можно загрузить с thonny.org.
В качестве альтернативы, если вы работаете с Raspberry Pi, вам следует установить Thonny с помощью apt из командной строки,
$ sudo apt install thonny
The MicroPython
Environment
Python is the fastest way to get started with embedded software on Raspberry Pi Pico. This book is about the official
MicroPython port for RP2040-based microcontroller boards.
MicroPython is a Python 3 implementation for microcontrollers and small embedded systems. Because MicroPython is
highly efficient, and RP2040 is designed with a disproportionate amount of system memory and processing power for
its price, MicroPython is a serious tool for embedded systems development, which does not compromise on
approachability.
For exceptionally demanding pieces of software, you can fall back on the SDK (covered in Getting started with
Raspberry Pi Pico and Raspberry Pi Pico C/C++ SDK), or an external C module added to your MicroPython firmware, to
wring out the very last drop of performance. For every other project, MicroPython handles a lot of heavy lifting for you,
and lets you focus on writing the code that adds value to your project. The accelerated floating point libraries in
RP2040’s on-board ROM storage are used automatically by your Python code, so you should find arithmetic
performance quite snappy.
Most on-chip hardware is exposed through the standard machine module, so existing MicroPython projects can be ported
without too much trouble. The second processor core is exposed through the _thread module.
RP2040 has some unique hardware you won’t find on other microcontrollers, with the programmable I/O system (PIO)
being the prime example of this: a versatile hardware subsystem that lets you create new I/O interfaces and run them at
high speed. In the rp2 module you will find a comprehensive PIO library which lets you write new PIO programs at the
MicroPython prompt, and interact with them in real time, to develop interfaces for new or unusual pieces of hardware
(or indeed if you just find yourself wanting an extra few serial ports).
MicroPython implements the entire Python 3.4 syntax (including exceptions, with, yield from, etc., and additionally async
/await keywords from Python 3.5). The following core datatypes are provided: str (including basic Unicode support),
bytes, bytearray, tuple, list, dict, set, frozenset, array.array, collections.namedtuple, classes and instances. Builtin modules
include sys, time, and struct, etc. Note that only a subset of Python 3 functionality is implemented for the data types and
modules.
MicroPython can execute scripts in textual source form (.py files) or from precompiled bytecode, in both cases either
from an on-device filesystem or "frozen" into the MicroPython executable.
Getting MicroPython for RP2040
Pre-built Binary
A pre-built binary of the latest MicroPython firmware is available from the MicroPython section of the
documentation.
The fastest way to get MicroPython is to download the pre-built release binary from the Documentation pages. If you
can’t or don’t want to use the pre-built release — for example, if you want to develop a C module for MicroPython — you
can follow the instructions in Section 1.3 to get the source code for MicroPython, which you can use to build your own
MicroPython firmware binary.
Installing MicroPython on Raspberry Pi Pico
Raspberry Pi Pico has a BOOTSEL mode for programming firmware over the USB port. Holding the BOOTSEL button
when powering up your board will put it into a special mode where it appears as a USB Mass Storage Device. First make
sure your Raspberry Pi Pico is not plugged into any source of power: disconnect the micro USB cable if plugged in, and
disconnect any other wires that might be providing power to the board, e.g. through the VSYS or VBUS pin. Now hold
down the BOOTSEL button, and plug in the micro USB cable (which hopefully has the other end plugged into your
computer).
A drive called RPI-RP2 should pop up. Go ahead and drag the MicroPython firmware.uf2 file onto this drive. This
programs the MicroPython firmware onto the flash memory on your Raspberry Pi Pico.
It should take a few seconds to program the UF2 file into the flash. The board will automatically reboot when finished,
causing the RPI-RP2 drive to disappear, and boot into MicroPython.
By default, MicroPython doesn’t do anything when it first boots. It sits and waits for you to type in further instructions.
Chapter 2 shows how you can connect with the MicroPython firmware now running on your board. You can read on to
see how a custom MicroPython firmware file can be built from the source code.
The Getting started with Raspberry Pi Pico book has detailed instructions on getting your Raspberry Pi Pico into
BOOTSEL mode and loading UF2 files, in case you are having trouble. There is also a section going over loading ELF
files with the debugger, in case your board doesn’t have an easy way of entering BOOTSEL, or you would like to debug a
MicroPython C module you are developing.
NOTE
If you are not following these instructions on a Raspberry Pi Pico, you may not have a BOOTSEL button. If this is the
case, you should check if there is some other way of grounding the flash CS pin, such as a jumper, to tell RP2040 to
enter the BOOTSEL mode on boot. If there is no such method, you can load code using the Serial Wire Debug
interface.
Building MicroPython From Source
The prebuilt binary which can be downloaded from the MicroPython section of the documentation should serve most
use cases, but you can build your own MicroPython firmware from source if you’d like to customise its low-level
aspects.
TIP
If you have already downloaded and installed a prebuilt MicroPython UF2 file, you can skip ahead to Chapter 2 to
start using your board.
IMPORTANT
These instructions for getting and building MicroPython assume you are using Raspberry Pi OS running on a
Raspberry Pi 4, or an equivalent Debian-based Linux distribution running on another platform.
It’s a good idea to create a pico directory to keep all pico-related checkouts in. These instructions create a pico directory
at /home/pi/pico.
$ cd ~/
$ mkdir pico
$ cd pico
Raspberry Pi Pico Python SDK
1.2. Installing MicroPython on Raspberry Pi Pico 4
Then clone the micropython git repository. These instructions will fetch the latest version of the source code.
$ git clone -b master https://github.com/micropython/micropython.git
Once the download has finished, the source code for MicroPython should be in a new directory called micropython. The
MicroPython repository also contains pointers (submodules) to specific versions of libraries it needs to run on a
particular board, like the SDK in the case of RP2040. We need to explicitly fetch these too:
$ cd micropython
$ git submodule update --init -- lib/pico-sdk lib/tinyusb
NOTE
The following instructions assume that you are using a Raspberry Pi Pico. Some details may differ if you are building
firmware for a different RP2040-based board. The board vendor should detail any extra steps needed to build
firmware for that particular board. The version we’re building here is fairly generic, but there might be some
differences like putting the default serial port on different pins, or including extra modules to drive that board’s
hardware.
To build the RP2040 MicroPython port, you’ll need to install some extra tools. To build projects you’ll need CMake, a
cross-platform tool used to build the software, and the GNU Embedded Toolchain for Arm, which turns MicroPython’s C
source code into a binary program RP2040’s processors can understand. build-essential is a bundle of tools you need
to build code native to your own machine — this is needed for some internal tools in MicroPython and the SDK. You can
install all of these via apt from the command line. Anything you already have installed will be ignored by apt.
$ sudo apt update
$ sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
First we need to bootstrap a special tool for MicroPython builds, that ships with the source code:
$ make -C mpy-cross
We can now build the port we need for RP2040, that is, the version of MicroPython that has specific support for our
chip.
$ cd ports/rp2
$ make
If everything went well, there will be a new directory called build (ports/rp2/build relative to the micropython directory),
which contains the new firmware binaries. The most important ones are:
firmware.uf2 A UF2 binary file which can dragged onto the RPI-RP2 drive that pops up once your Raspberry Pi
Pico is in BOOTSEL mode. The firmware binary you can download from the documentation page
is a UF2 file, because they’re the easiest to install.
firmware.elf A different type of binary file, which can be loaded by a debugger (such as gdb with openocd) over
RP2040’s SWD debug port. This is useful for debugging either a native C module you’ve added to
MicroPython, or the MicroPython core interpreter itself. The actual binary contents is the same
as firmware.uf2.
You can take a look inside your new firmware.uf2 using picotool, see the Appendix B in the Getting started with
Raspberry Pi Pico book for details of how to use picotool, e.g.
$ picotool info -a build/firmware.uf2
File /home/pi/pico/micropython/ports/rp2/build/firmware.uf2:
Program Information
name: MicroPython
version: v1.13-288-g3ce8f14e0
features: USB REPL
thread support
frozen modules: _boot, rp2, ds18x20, onewire, uasyncio, uasyncio/core,
uasyncio/event, uasyncio/funcs, uasyncio/lock, uasyncio/stream
binary start: 0x10000000
binary end: 0x10038be4
embedded drive: 0x100a0000-0x10200000 (1408K): MicroPython
Fixed Pin Information
none
Build Information
sdk version: 1.0.0
pico_board: pico
build date: Jan 21 2021
build attributes: MinSizeRel
Connecting to the
MicroPython REPL
When MicroPython boots for the first time, it will sit and wait for you to connect and tell it what to do. You can load a .py
file from your computer onto the board, but a more immediate way to interact with it is through what is called the readevaluate-print loop, or REPL (often pronounced similarly to "ripple").
Read MicroPython waits for you to type in some text, followed by the enter key.
Evaluate Whatever you typed is interpreted as Python code, and runs immediately.
Print Any results of the last line you typed are printed out for you to read.
Loop Go back to the start — prompt you for another line of code.
There are two ways to connect to this REPL, so you can communicate with the MicroPython firmware on your board:
over USB, and over the UART serial port on Raspberry Pi Pico GPIOs.
Connecting from a Raspberry Pi over USB
The MicroPython firmware is equipped with a virtual USB serial port which is accessed through the micro USB
connector on Raspberry Pi Pico. Your computer should notice this serial port and list it as a character device, most likely
/dev/ttyACM0.
TIP
You can run ls /dev/tty* to list your serial ports. There may be quite a few, but MicroPython’s USB serial will start
with /dev/ttyACM. If in doubt, unplug the micro USB connector and see which one disappears. If you don’t see
anything, you can try rebooting your Raspberry Pi.
You can install minicom to access the serial port:
$ sudo apt install minicom
and then open it as such:
$ minicom -o -D /dev/ttyACM0
Where the -D /dev/ttyACM0 is pointing minicom at MicroPython’s USB serial port, and the -o flag essentially means "just do
it". There’s no need to worry about baud rate, since this is a virtual serial port.
Press the enter key a few times in the terminal where you opened minicom. You should see this:
>>>
This is a prompt. MicroPython wants you to type something in, and tell it what to do.
If you press CTRL-D on your keyboard whilst the minicom terminal is focused, you should see a message similar to this:
MPY: soft reboot
MicroPython v1.13-422-g904433073 on 2021-01-19; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>>
This key combination tells MicroPython to reboot. You can do this at any time. When it reboots, MicroPython will print
out a message saying exactly what firmware version it is running, and when it was built. Your version number will be
different from the one shown here.
Connecting from a Raspberry Pi using UART
WARNING
REPL over UART is disabled by default.
The MicroPython port for RP2040 does not expose REPL over a UART port by default. However this default can be
changed in the ports/rp2/mpconfigport.h source file. If you want to use the REPL over UART you’re going to have to build
MicroPython yourself, see Section 1.3 for more details.
Go ahead and download the MicroPython source and in ports/rp2/mpconfigport.h change MICROPY_HW_ENABLE_UART_REPL to 1
to enable it.
#define MICROPY_HW_ENABLE_UART_REPL (1) // useful if there is no USB
Then continue to follow the instructions in Section 1.3 to build your own MicroPython UF2 firmware.
This will allow the REPL to be accessed over a UART port, through two GPIO pins. The default settings for UARTs are
taken from the C SDK.
Table 1. Default UART
settings in
MicroPython
Function Default
UART_BAUDRATE 115,200
UART_BITS 8
UART_STOP 1
UART0_TX Pin 0
UART0_RX Pin 1
UART1_TX Pin 4
UART1_RX Pin 5
This alternative interface is handy if you have trouble with USB, if you don’t have any free USB ports, or if you are using
some other RP2040-based board which doesn’t have an exposed USB connector
NOTE
This initially occupies the UART0 peripheral on RP2040. The UART1 peripheral is free for you to use in your Python code
as a second UART.
The next thing you’ll need to do is to enable UART serial on the Raspberry Pi. To do so, run raspi-config,
$ sudo raspi-config
and go to Interfacing Options → Serial and select "No" when asked "Would you like a login shell to be accessible over
serial?" and "Yes" when asked "Would you like the serial port hardware to be enabled?". You should see something like
Figure 1.
Figure 1. Enabling a
serial UART using
raspi-config on
the Raspberry Pi.
Leaving raspi-config you should choose "Yes" and reboot your Raspberry Pi to enable the serial port.
You should then wire the Raspberry Pi and the Raspberry Pi Pico together with the following mapping:
Raspberry Pi Raspberry Pi Pico
GND GND
GPIO15 (UART_RX0) GPIO0 (UART0_TX)
GPIO14 (UART_TX0) GPOI1 (UART0_RX)
IMPORTANT
RX matches to TX, and TX matches to RX. You mustn’t connect the two opposite TX pins together, or the two RX
pins. This is because MicroPython needs to listen on the channel that the Raspberry Pi transmits on, and vice versa.
See Figure 2
Figure 2. A Raspberry Pi 4 and the Raspberry Pi Pico with UART0 connected together.
then connect to the board using minicom connected to /dev/serial0,
$ minicom -b 115200 -o -D /dev/serial0
If you press the enter key, MicroPython should respond by prompting you for more input:
>>>
Connecting from a Mac
So long as you’re using a recent version of macOS like Catalina, drivers should already be loaded. Otherwise see the
manufacturers' website for FTDI Chip Drivers. Then you should use a Terminal program to connect to Serial-over-USB
(USB CDC). The serial port will show up as /dev/tty.usbmodem0000000000001
If you don’t already have a Terminal program installed you can install minicom using Homebrew,
$ brew install minicom
and connect to the board as below.
$ minicom -b 115200 -o -D /dev/tty.usbmodem0000000000001
Raspberry Pi Pico Python SDK
2.3. Connecting from a Mac 10
NOTE
Other Terminal applications like CoolTerm or Serial can also be used.
2.4. Say "Hello World"
Once connected you can check that everything is working by typing a Python "Hello World" into the REPL,
>>> print("Hello, Pico!")
Hello, Pico!
>>>
2.5. Blink an LED
The on-board LED on Raspberry Pi Pico is connected to GPIO pin 25. You can blink this on and off from the REPL. When
you see the REPL prompt enter the following,
>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
The machine module is used to control on-chip hardware. This is standard on all MicroPython ports, and you can read
more about it in the MicroPython documentation. Here we are using it to take control of a GPIO, so we can drive it high
and low. If you type this in,
>>> led.value(1)
The LED should turn on. You can turn it off again with
>>> led.value(0)
2.6. What next?
At this point you should have MicroPython installed on your board, and have tested your setup by typing short programs
into the prompt to print some text back to you, and blink an LED.
You can read on to the next chapter, which goes into the specifics of MicroPython on RP2040, and where it differs from
other platforms. Chapter 3 also has some short examples of the different APIs offered to interact with the hardware.
You can learn how to set up an integrated development environment (IDE) in Chapter 4, so you don’t have to type
programs in line by line.
You can dive straight into Appendix A if you are eager to start connecting wires to a breadboard.
The RP2040 Port
Currently supported features include:
• REPL over USB and UART (on GP0/GP1).
• 1600 kB filesystem using littlefs2 on the on-board flash. (Default size for Raspberry Pi Pico)
• utime module with sleep and ticks functions.
• ubinascii module.
• machine module with some basic functions.
◦
machine.Pin class.
◦
machine.Timer class.
◦
machine.ADC class.
◦
machine.I2C and machine.SoftI2C classes.
◦
machine.SPI and machine.SoftSPI classes.
◦
machine.WDT class.
◦
machine.PWM class.
◦
machine.UART class.
• rp2 platform-specific module.
◦
PIO hardware access library
◦
PIO program assembler
◦
Raw flash read/write access
• Multicore support exposed via the standard _thread module
• Accelerated floating point arithmetic using the RP2040 ROM library and hardware divider (used automatically)
Documentation around MicroPython is available from https://docs.micropython.org. For example the machine module,
which can be used to access a lot of RP2040’s on-chip hardware, is standard, and you will find a lot of the information
you need in the online documentation for that module.
This chapter will give a very brief tour of some of the hardware APIs, with code examples you can either type into the
REPL (Chapter 2) or load onto the board using a development environment installed on your computer (Chapter 4).
3.1. Blinking an LED Forever (Timer)
In Chapter 2 we saw how the machine.Pin class could be used to turn an LED on and off, by driving a GPIO high and low.
>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
>>> led.value(1)
>>> led.value(0)
This is, to put it mildy, quite a convoluted way of turning a light on and off. A light switch would work better. The
machine.Timer class, which uses RP2040’s hardware timer to trigger callbacks at regular intervals, saves a lot of typing if
we want the light to turn itself on and off repeatedly, thus bringing our level of automation from "mechanical switch" to
"555 timer".
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/blink/blink.py Lines 1 - 9
1 from machine import Pin, Timer
2
3 led = Pin(25, Pin.OUT)
4 tim = Timer()
5 def tick(timer):
6 global led
7 led.toggle()
8
9 tim.init(freq=2.5, mode=Timer.PERIODIC, callback=tick)
Typing this program into the REPL will cause the LED to start blinking, but the prompt will appear again:
>>>
The Timer we created will run in the background, at the interval we specified, blinking the LED. The MicroPython prompt
is still running in the foreground, and we can enter more code, or start more timers.
UART
NOTE
REPL over UART is disabled by default. See Section 2.2 for details of how to enable REPL over UART.
Example usage looping UART0 to UART1.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/uart/loopback/uart.py Lines 1 - 15
1 from machine import UART, Pin
2 import time
3
4 uart1 = UART(1, baudrate=9600, tx=Pin(8), rx=Pin(9))
5
6 uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
7
8 txData = b'hello world\n\r'
9 uart1.write(txData)
10 time.sleep(0.1)
11 rxData = bytes()
12 while uart0.any() > 0:
13 rxData += uart0.read(1)
14
15 print(rxData.decode('utf-8'))
For more detail, including a wiring diagram, see Appendix A.
3.3. ADC
An analogue-to-digital converter (ADC) measures some analogue signal and encodes it as a digital number. The ADC on
RP2040 measures voltages.
An ADC has two key features: its resolution, measured in digital bits, and its channels, or how many analogue signals it
can accept and convert at once. The ADC on RP2040 has a resolution of 12-bits, meaning that it can transform an
analogue signal into a digital signal as a number ranging from 0 to 4095 – though this is handled in MicroPython
transformed to a 16-bit number ranging from 0 to 65,535, so that it behaves the same as the ADC on other MicroPython
microcontrollers.
RP2040 has five ADC channels total, four of which are brought out to chip GPIOs: GP26, GP27, GP28 and GP29. On
Raspberry Pi Pico, the first three of these are brought out to GPIO pins, and the fourth can be used to measure the VSYS
voltage on the board.
The ADC’s fifth input channel is connected to a temperature sensor built into RP2040.
You can specify which ADC channel you’re using by pin number, e.g.
adc = machine.ADC(26) # Connect to GP26, which is channel 0
or by channel,
adc = machine.ADC(4) # Connect to the internal temperature sensor
adc = machine.ADC(0) # Connect to channel 0 (GP26)
An example reading the fourth analogue-to-digital (ADC) converter channel, connected to the internal temperature
sensor:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/adc/temperature.py Lines 1 - 14
1 import machine
2 import utime
3
4 sensor_temp = machine.ADC(4)
5 conversion_factor = 3.3 / (65535)
6
7 while True:
8 reading = sensor_temp.read_u16() * conversion_factor
9
10 # The temperature sensor measures the Vbe voltage of a biased bipolar diode, connected to
the fifth ADC channel
11 # Typically, Vbe = 0.706V at 27 degrees C, with a slope of -1.721mV (0.001721) per degree.
12 temperature = 27 - (reading - 0.706)/0.001721
13 print(temperature)
14 utime.sleep(2)
3.4. Interrupts
You can set an IRQ like this:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/irq/irq.py Lines 1 - 4
1 from machine import Pin
2
3 p2 = Pin(2, Pin.IN, Pin.PULL_UP)
4 p2.irq(lambda pin: print("IRQ with flags:", pin.irq().flags()), Pin.IRQ_FALLING)
It should print out something when GP2 has a falling edge.
3.5. Multicore Support
Example usage:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/multicore/multicore.py Lines 1 - 12
1 import time, _thread, machine
2
3 def task(n, delay):
4 led = machine.Pin(25, machine.Pin.OUT)
5 for i in range(n):
6 led.high()
7 time.sleep(delay)
8 led.low()
9 time.sleep(delay)
10 print('done')
11
12 _thread.start_new_thread(task, (10, 0.5))
Only one thread can be started/running at any one time, because there is no RTOS just a second core. The GIL is not
enabled so both core0 and core1 can run Python code concurrently, with care to use locks for shared data.
3.6. I2C
Example usage:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/i2c/i2c.py Lines 1 - 11
1 from machine import Pin, I2C
2
3 i2c = I2C(0, scl=Pin(9), sda=Pin(8), freq=100000)
4 i2c.scan()
5 i2c.writeto(76, b'123')
6 i2c.readfrom(76, 4)
7
8 i2c = I2C(1, scl=Pin(7), sda=Pin(6), freq=100000)
9 i2c.scan()
10 i2c.writeto_mem(76, 6, b'456')
11 i2c.readfrom_mem(76, 6, 4)
I2C can be constructed without specifying the frequency, if you just want all the defaults.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/i2c/i2c_without_freq.py Lines 1 - 3
1 from machine import I2C
2
3 i2c = I2C(0) # defaults to SCL=Pin(9), SDA=Pin(8), freq=400000
WARNING
There may be some bugs reading/writing to device addresses that do not respond, the hardware seems to lock up in
some cases.
Table 2. Default I2C
pins Function Default
I2C Frequency 400,000
I2C0 SCL Pin 9
I2C0 SDA Pin 8
I2C1 SCL Pin 7
I2C1 SDA Pin 6
3.7. SPI
Example usage:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/spi/spi.py Lines 1 - 11
1 from machine import SPI
2
3 spi = SPI(0)
4 spi = SPI(0, 100_000)
5 spi = SPI(0, 100_000, polarity=1, phase=1)
6
7 spi.write('test')
8 spi.read(5)
9
10 buf = bytearray(3)
11 spi.write_readinto('out', buf)
NOTE
The chip select must be managed separately using a machine.Pin.
Table 3. Default SPI
pins Function Default
SPI_BAUDRATE 1,000,000
SPI_POLARITY 0
SPI_PHASE 0
SPI_BITS 8
SPI_FIRSTBIT MSB
SPI0_SCK Pin 6
SPI0_MOSI Pin 7
SPI0_MISO Pin 4
SPI1_SCK Pin 10
SPI1_MOSI Pin 11SPI1_MISO Pin 8
PWM
Example of using PWM to fade an LED:
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pwm/pwm_fade.py Lines 1 - 25
1 # Example using PWM to fade an LED.
2
3 import time
4 from machine import Pin, PWM
5
6
7 # Construct PWM object, with LED on Pin(25).
8 pwm = PWM(Pin(25))
9
10 # Set the PWM frequency.
11 pwm.freq(1000)
12
13 # Fade the LED in and out a few times.
14 duty = 0
15 direction = 1
16 for _ in range(8 * 256):
17 duty += direction
18 if duty > 255:
19 duty = 255
20 direction = -1
21 elif duty < 0:
22 duty = 0
23 direction = 1
24 pwm.duty_u16(duty * duty)
25 time.sleep(0.001)
PIO Support
Current support allows you to define Programmable IO (PIO) Assembler blocks and using them in the PIO peripheral,
more documentation around PIO can be found in Chapter 3 of the RP2040 Datasheet and Chapter 4 of the Raspberry Pi
Pico C/C++ SDK book.
The Raspberry Pi Pico MicroPython introduces a new @rp2.asm_pio decorator, along with a rp2.PIO class. The definition of
a PIO program, and the configuration of the state machine, into 2 logical parts:
• The program definition, including how many pins are used and if they are in/out pins. This goes in the @rp2.asm_pio
definition. This is close to what the pioasm tool from the SDK would generate from a .pio file (but here it’s all
defined in Python).
• The program instantiation, which sets the frequency of the state machine and which pins to bind to. These get set
when setting a SM to run a particular program.
The aim was to allow a program to be defined once and then easily instantiated multiple times (if needed) with different
GPIO. Another aim was to make it easy to do basic things without getting weighed down in too much PIO/SM
configuration.
Example usage, to blink the on-board LED connected to GPIO 25,
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_blink.py Lines 1 - 28
1 import time
2 import rp2
3 from machine import Pin
4
5 # Define the blink program. It has one GPIO to bind to on the set instruction, which is an
output pin.
6 # Use lots of delays to make the blinking visible by eye.
7 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
8 def blink():
9 wrap_target()
10 set(pins, 1) [31]
11 nop() [31]
12 nop() [31]
13 nop() [31]
14 nop() [31]
15 set(pins, 0) [31]
16 nop() [31]
17 nop() [31]
18 nop() [31]
19 nop() [31]
20 wrap()
21
22 # Instantiate a state machine with the blink program, at 2000Hz, with set bound to Pin(25)
(LED on the rp2 board)
23 sm = rp2.StateMachine(0, blink, freq=2000, set_base=Pin(25))
24
25 # Run the state machine for 3 seconds. The LED should blink.
26 sm.active(1)
27 time.sleep(3)
28 sm.active(0)
or via explicit exec.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_exec.py Lines 1 - 27
1 # Example using PIO to turn on an LED via an explicit exec.
2 #
3 # Demonstrates:
4 # - using set_init and set_base
5 # - using StateMachine.exec
6
7 import time
8 from machine import Pin
9 import rp2
10
11 # Define an empty program that uses a single set pin.
12 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
13 def prog():
14 pass
15
16
17 # Construct the StateMachine, binding Pin(25) to the set pin.
18 sm = rp2.StateMachine(0, prog, set_base=Pin(25))
19
20 # Turn on the set pin via an exec instruction.
21 sm.exec("set(pins, 1)")
22
23 # Sleep for 500ms.
24 time.sleep(0.5)
25
26 # Turn off the set pin via an exec instruction.
27 sm.exec("set(pins, 0)")
Some points to note,
• All program configuration (eg autopull) is done in the @asm_pio decorator. Only the frequency and base pins are set
in the StateMachine constructor.
• [n] is used for delay, .set(n) used for sideset
• The assembler will automatically detect if sideset is used everywhere or only on a few instructions, and set the
SIDE_EN bit automatically
The idea is that for the 4 sets of pins (in, out, set, sideset, excluding jmp) that can be connected to a state machine,
there’s the following that need configuring for each set:
1. base GPIO
2. number of consecutive GPIO
3. initial GPIO direction (in or out pin)
4. initial GPIO value (high or low)
In the design of the Python API for PIO these 4 items are split into "declaration" (items 2-4) and "instantiation" (item 1).
In other words, a program is written with items 2-4 fixed for that program (eg a WS2812 driver would have 1 output pin)
and item 1 is free to change without changing the program (eg which pin the WS2812 is connected to).
So in the @asm_pio decorator you declare items 2-4, and in the StateMachine constructor you say which base pin to use
(item 1). That makes it easy to define a single program and instantiate it multiple times on different pins (you can’t
really change items 2-4 for a different instantiation of the same program, it doesn’t really make sense to do that).
And the same keyword arg (in the case about it’s sideset_pins) is used for both the declaration and instantiation, to
show that they are linked.
To declare multiple pins in the decorator (the count, ie item 2 above), you use a tuple/list of values. And each item in the
tuple/list specified items 3 and 4. For example:
1 @asm_pio(set_pins=(PIO.OUT_LOW, PIO.OUT_HIGH, PIO.IN_LOW), sideset_pins=PIO.OUT_LOW)
2 def foo():
3 ....
4
5 sm = StateMachine(0, foo, freq=10000, set_pins=Pin(15), sideset_pins=Pin(22))
In this example:
• there are 3 set pins connected to the SM, and their initial state (set when the StateMachine is created) is: output
low, output high, input low (used for open-drain)
• there is 1 sideset pin, initial state is output low
• the 3 set pins start at Pin(15)
• the 1 sideset pin starts at Pin(22)
The reason to have the constants OUT_LOW, OUT_HIGH, IN_LOW and IN_HIGH is so that the pin value and dir are automatically
set before the start of the PIO program (instead of wasting instruction words to do set(pindirs, 1) etc at the start).
IRQ
There is support for PIO IRQs, e.g.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_irq.py Lines 1 - 25
1 import time
2 import rp2
3
4 @rp2.asm_pio()
5 def irq_test():
6 wrap_target()
7 nop() [31]
8 nop() [31]
9 nop() [31]
10 nop() [31]
11 irq(0)
12 nop() [31]
13 nop() [31]
14 nop() [31]
15 nop() [31]
16 irq(1)
17 wrap()
18
19
20 rp2.PIO(0).irq(lambda pio: print(pio.irq().flags()))
21
22 sm = rp2.StateMachine(0, irq_test, freq=2000)
23 sm.active(1)
24 time.sleep(1)
25 sm.active(0)
An example program that blinks at 1Hz and raises an IRQ at 1Hz to print the current millisecond timestamp,
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_1hz.py Lines 1 - 33
1 # Example using PIO to blink an LED and raise an IRQ at 1Hz.
2
3 import time
4 from machine import Pin
5 import rp2
6
7
8 @rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
9 def blink_1hz():
10 # Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
11 irq(rel(0))
12 set(pins, 1)
13 set(x, 31) [5]
14 label("delay_high")
15 nop() [29]
16 jmp(x_dec, "delay_high")
17
18 # Cycles: 1 + 7 + 32 * (30 + 1) = 1000
19 set(pins, 0)
20 set(x, 31) [6]
21 label("delay_low")
22 nop() [29]
23 jmp(x_dec, "delay_low")
24
25
26 # Create the StateMachine with the blink_1hz program, outputting on Pin(25).
27 sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
28
29 # Set the IRQ handler to print the millisecond timestamp.
30 sm.irq(lambda p: print(time.ticks_ms()))
31
32 # Start the StateMachine.
33 sm.active(1)
or to wait for a pin change and raise an IRQ.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_pinchange.py Lines 1 - 46
1 # Example using PIO to wait for a pin change and raise an IRQ.
2 #
3 # Demonstrates:
4 # - PIO wrapping
5 # - PIO wait instruction, waiting on an input pin
6 # - PIO irq instruction, in blocking mode with relative IRQ number
7 # - setting the in_base pin for a StateMachine
8 # - setting an irq handler for a StateMachine
9 # - instantiating 2x StateMachine's with the same program and different pins
10
11 import time
12 from machine import Pin
13 import rp2
14
15
16 @rp2.asm_pio()
17 def wait_pin_low():
18 wrap_target()
19
20 wait(0, pin, 0)
21 irq(block, rel(0))
22 wait(1, pin, 0)
23
24 wrap()
25
26
27 def handler(sm):
28 # Print a (wrapping) timestamp, and the state machine object.
29 print(time.ticks_ms(), sm)
30
31
32 # Instantiate StateMachine(0) with wait_pin_low program on Pin(16).
33 pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
34 sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16)
35 sm0.irq(handler)
36
37 # Instantiate StateMachine(1) with wait_pin_low program on Pin(17).
38 pin17 = Pin(17, Pin.IN, Pin.PULL_UP)
39 sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17)
40 sm1.irq(handler)
41
42 # Start the StateMachine's running.
43 sm0.active(1)
44 sm1.active(1)
45
46 # Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL.
WS2812 LED (NeoPixel)
While a WS2812 LED (NeoPixel) can be driven via the following program,
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_ws2812.py Lines 1 - 52
1 # Example using PIO to drive a set of WS2812 LEDs.
2
3 import array, time
4 from machine import Pin
5 import rp2
6
7 # Configure the number of WS2812 LEDs.
8 NUM_LEDS = 8
9
10
11 @rp2.asm_pio(sideset_init=rp2.PIO.OUT_LOW, out_shiftdir=rp2.PIO.SHIFT_LEFT, autopull=True,
pull_thresh=24)
12 def ws2812():
13 T1 = 2
14 T2 = 5
15 T3 = 3
16 wrap_target()
17 label("bitloop")
18 out(x, 1) .side(0) [T3 - 1]
19 jmp(not_x, "do_zero") .side(1) [T1 - 1]
20 jmp("bitloop") .side(1) [T2 - 1]
21 label("do_zero")
22 nop() .side(0) [T2 - 1]
23 wrap()
24
25
26 # Create the StateMachine with the ws2812 program, outputting on Pin(22).
27 sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22))
28
29 # Start the StateMachine, it will wait for data on its FIFO.
30 sm.active(1)
31
32 # Display a pattern on the LEDs via an array of LED RGB values.
33 ar = array.array("I", [0 for _ in range(NUM_LEDS)])
34
35 # Cycle colours.
36 for i in range(4 * NUM_LEDS):
37 for j in range(NUM_LEDS):
38 r = j * 100 // (NUM_LEDS - 1)
39 b = 100 - j * 100 // (NUM_LEDS - 1)
40 if j != i % NUM_LEDS:
41 r >>= 3
42 b >>= 3
43 ar[j] = r << 16 | b
44 sm.put(ar, 8)
45 time.sleep_ms(50)
46
47 # Fade out.
48 for i in range(24):
49 for j in range(NUM_LEDS):
50 ar[j] >>= 1
51 sm.put(ar, 8)
52 time.sleep_ms(50)
UART TX
A UART TX example,
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_uart_tx.py Lines 1 - 42
1 # Example using PIO to create a UART TX interface
2
3 from machine import Pin
4 from rp2 import PIO, StateMachine, asm_pio
5
6 UART_BAUD = 115200
7 PIN_BASE = 10
8 NUM_UARTS = 8
9
10
11 @asm_pio(sideset_init=PIO.OUT_HIGH, out_init=PIO.OUT_HIGH, out_shiftdir=PIO.SHIFT_RIGHT)
12 def uart_tx():
13 # Block with TX deasserted until data available
14 pull()
15 # Initialise bit counter, assert start bit for 8 cycles
16 set(x, 7) .side(0) [7]
17 # Shift out 8 data bits, 8 execution cycles per bit
18 label("bitloop")
19 out(pins, 1) [6]
20 jmp(x_dec, "bitloop")
21 # Assert stop bit for 8 cycles total (incl 1 for pull())
22 nop() .side(1) [6]
23
24
25 # Now we add 8 UART TXs, on pins 10 to 17. Use the same baud rate for all of them.
26 uarts = []
27 for i in range(NUM_UARTS):
28 sm = StateMachine(
29 i, uart_tx, freq=8 * UART_BAUD, sideset_base=Pin(PIN_BASE + i), out_base=Pin
(PIN_BASE + i)
30 )
31 sm.active(1)
32 uarts.append(sm)
33
34 # We can print characters from each UART by pushing them to the TX FIFO
35 def pio_uart_print(sm, s):
36 for c in s:
37 sm.put(ord(c))
38
39
40 # Print a different message from each UART
41 for i, u in enumerate(uarts):
42 pio_uart_print(u, "Hello from UART {}!\n".format(i))
NOTE
You need to specify an initial OUT pin state in your program in order to be able to pass OUT mapping to your SM
instantiation, even though in this program it is redundant because the mappings overlap.
SPI
An SPI example.
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_spi.py Lines 1 - 49
1 import rp2
2 from machine import Pin
3
4 @rp2.asm_pio(out_shiftdir=0, autopull=True, pull_thresh=8, autopush=True, push_thresh=8,
sideset_init=(rp2.PIO.OUT_LOW, rp2.PIO.OUT_HIGH), out_init=rp2.PIO.OUT_LOW)
5 def spi_cpha0():
6 # Note X must be preinitialised by setup code before first byte, we reload after sending
each byte
7 # Would normally do this via exec() but in this case it's in the instruction memory and is
only run once
8 set(x, 6)
9 # Actual program body follows
10 wrap_target()
11 pull(ifempty) .side(0x2) [1]
12 label("bitloop")
13 out(pins, 1) .side(0x0) [1]
14 in_(pins, 1) .side(0x1)
15 jmp(x_dec, "bitloop") .side(0x1)
16
17 out(pins, 1) .side(0x0)
18 set(x, 6) .side(0x0) # Note this could be replaced with mov x, y for
programmable frame size
19 in_(pins, 1) .side(0x1)
20 jmp(not_osre, "bitloop") .side(0x1) # Fallthru if TXF empties
21
22 nop() .side(0x0) [1] # CSn back porch
23 wrap()
24
25
26 class PIOSPI:
27
28 def __init__(self, sm_id, pin_mosi, pin_miso, pin_sck, cpha=False, cpol=False, freq
=1000000):
29 assert(not(cpol or cpha))
30 self._sm = rp2.StateMachine(sm_id, spi_cpha0, freq=4*freq, sideset_base=Pin(
pin_sck), out_base=Pin(pin_mosi), in_base=Pin(pin_sck))
31 self._sm.active(1)
32
33 # Note this code will die spectacularly cause we're not draining the RX FIFO
34 def write_blocking(wdata):
35 for b in wdata:
36 self._sm.put(b << 24)
37
38 def read_blocking(n):
39 data = []
40 for i in range(n):
41 data.append(self._sm.get() & 0xff)
42 return data
43
44 def write_read_blocking(wdata):
45 rdata = []
46 for b in wdata:
47 self._sm.put(b << 24)
48 rdata.append(self._sm.get() & 0xff)
49 return rdata
NOTE
This SPI program supports programmable frame sizes (by holding the reload value for X counter in the Y register)
but currently this can’t be used, because the autopull threshold is associated with the program, instead of the SM
instantiation.
PWM
A PWM example,
Pico MicroPython Examples: https://github.com/raspberrypi/pico-micropython-examples/tree/master/pio/pio_pwm.py Lines 1 - 43
1 # Example of using PIO for PWM, and fading the brightness of an LED
2
3 from machine import Pin
4 from rp2 import PIO, StateMachine, asm_pio
5 from time import sleep
6
7
8 @asm_pio(sideset_init=PIO.OUT_LOW)
9 def pwm_prog():
10 pull(noblock) .side(0)
11 mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock
12 mov(y, isr) # ISR must be preloaded with PWM count max
13 label("pwmloop")
14 jmp(x_not_y, "skip")
15 nop() .side(1)
16 label("skip")
17 jmp(y_dec, "pwmloop")
18
19
20 class PIOPWM:
21 def __init__(self, sm_id, pin, max_count, count_freq):
22 self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin))
23 # Use exec() to load max count into ISR
24 self._sm.put(max_count)
25 self._sm.exec("pull()")
26 self._sm.exec("mov(isr, osr)")
27 self._sm.active(1)
28 self._max_count = max_count
29
30 def set(self, value):
31 # Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse
32 value = max(value, -1)
33 value = min(value, self._max_count)
34 self._sm.put(value)
35
36
37 # Pin 25 is LED on Pico boards
38 pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000)
39
40 while True:
41 for i in range(256):
42 pwm.set(i ** 2)
43 sleep(0.01)
Using pioasm
As well as writing PIO code inline in your MicroPython script you can use the pioasm tool from the C/C++ SDK to
generate a Python file.
$ pioasm -o python input (output)
For more information on pioasm see the Raspberry Pi Pico C/C++ SDK book which talks about the C/C++ SDK
Using an Integrated
Development Environment (IDE)
The MicroPython port to Raspberry Pi Pico and other RP2040-based boards works with commonly used development
environments.
4.1. Using Thonny
Thonny packages are available for Linux, MS Windows, and macOS. After installation, using the Thonny development
environment is the same across all three platforms. The latest release of Thonny can be downloaded from thonny.org
Alternatively if you are working on a Raspberry Pi you should install Thonny using apt from the command line,
$ sudo apt install thonny