PIO в RP2040: программируемые автоматы ввода-вывода
PIO — главная особенность RP2040: восемь маленьких автоматов, которые выполняют свои программы и сами формируют сигналы на выводах с точностью до такта. На них делают UART, SPI, I²S, управление светодиодами WS2812, VGA и интерфейсы, которых нет в обычной периферии. Разбираем по даташиту Raspberry Pi устройство PIO, его девять команд и как считать частоту.
Обновлено Редакция GAW
PIO (programmable input/output) — блоки программируемого ввода-вывода RP2040. Это не процессор общего назначения и не ПЛИС, а несколько маленьких автоматов, оптимизированных под одну задачу: точно по тактам выставлять и читать уровни на выводах и передавать данные между выводами и процессором. Raspberry Pi в даташите перечисляет интерфейсы, которые на них делают: параллельные шины 8080 и 6800, I²C, I²S, SDIO, SPI, UART, вывод DPI и VGA через резистивный ЦАП.
Устройство
По даташиту в RP2040 два одинаковых блока PIO, в каждом четыре автомата (state machine) — всего восемь. Автоматы одного блока выполняют программы из общей памяти на 32 команды. У каждого автомата:
| Узел | Что делает |
|---|---|
| Два сдвиговых регистра, 32 бита | OSR — выдаёт биты на выводы, ISR — собирает биты с выводов; сдвиг в любую сторону на любое число бит |
| Два рабочих регистра X и Y, 32 бита | счётчики циклов и переменные |
| FIFO 4 × 32 бита в каждую сторону | очередь данных к процессору и от него; можно объединить в 8 × 32 в одну сторону |
| Дробный делитель частоты 16 + 8 бит | замедляет автомат на постоянный множитель |
| Отображение выводов | доступ к любым из 30 GPIO, независимо от других автоматов |
| Связь с DMA | до одного слова за такт |
Каждый автомат по площади кристалла сопоставим с обычным контроллером SPI или I²C, но его можно перепрограммировать под другой интерфейс на ходу. У RP2350 таких блоков три — 12 автоматов.
Команды
Команд всего девять, каждая длиной 16 бит:
| Команда | Действие |
|---|---|
| JMP | переход по адресу, безусловный или по условию: X или Y равен нулю, X или Y не ноль с уменьшением, X ≠ Y, уровень вывода, OSR не пуст |
| WAIT | ожидание уровня на выводе, GPIO или флага прерывания |
| IN | вдвинуть биты из выводов или регистров в ISR |
| OUT | выдвинуть биты из OSR на выводы или в регистры |
| PUSH | передать ISR в приёмный FIFO |
| PULL | взять слово из передающего FIFO в OSR |
| MOV | переслать данные между регистрами, с инверсией или перестановкой бит |
| IRQ | установить, сбросить или ждать флаг прерывания |
| SET | записать константу до 5 бит в выводы, их направление или регистры X и Y |
Один такт на команду. По даташиту каждая команда выполняется ровно за один такт, кроме тех, что явно ждут, как WAIT. Поэтому время работы программы считается точно.
Задержка и side-set. В каждой команде есть 5-битное поле. Его младшие биты задают задержку — до 31 дополнительного такта после команды. Старшие можно отдать под side-set: одновременно с основным действием команда выставляет константу ещё на нескольких выводах — так формируют тактовый сигнал рядом с данными без лишних команд. Сколько бит уйдёт на side-set, решает настройка автомата, и столько же меньше останется на задержку.
Переход без затрат. Директивы ассемблера .wrap_target и .wrap задают кольцо программы: после команды, отмеченной .wrap, автомат переходит на .wrap_target, не тратя такт на команду JMP.
Автопуш и автопулл. Автомат может сам обмениваться словами с FIFO, когда сдвиговый регистр заполнился или опустел, — программа становится короче.
Пример: меандр и расчёт частоты
Программа выдаёт на вывод меандр:
.program square
set pindirs, 1 ; pin as output
.wrap_target
set pins, 1 [31] ; high, then wait 31 more cycles
set pins, 0 [31] ; low, then wait 31 more cycles
.wrap
Каждая из двух команд в кольце занимает 1 + 31 = 32 такта, переход по .wrap — ноль. Период — 64 такта автомата, скважность ровно 50 %. Частота:
Делитель автомата — дробный, 16 целых и 8 дробных бит, поэтому частоту можно подогнать точно: так по даташиту настраивают скорость UART на PIO. Пример из даташита — меандр с периодом 4 такта и делителем 2,5 даёт 12,5 МГц. Делитель под нужную частоту и число тактов на бит, со значением регистра и кодом pico-sdk, считает калькулятор делителя PIO.
Как запустить: в C и C++ программу собирает ассемблер pioasm из Pico SDK — он превращает текст в заголовочный файл с массивом команд, который загружается в память PIO. В MicroPython, порт которого Raspberry Pi упоминает в даташите, программы PIO описывают прямо в коде на Python.
Когда нужен PIO
- Интерфейса нет в периферии. Светодиоды WS2812, третий UART, параллельная шина дисплея, нестандартный протокол датчика.
- Нужна точность до такта. Программа PIO не зависит от прерываний процессора: импульсы не плывут, когда ядро занято.
- Нужна разгрузка процессора. Вместе с DMA автомат передаёт поток данных на выводы без участия ядра — даташит приводит вывод изображения DPI на 360 Мбит/с при системной частоте 48 МГц.
Если же нужный интерфейс есть в аппаратной периферии — UART, SPI, I²C, ШИМ, — проще использовать её. Частоту аппаратного ШИМ считает калькулятор ШИМ RP2040, выводы платы — в статье о Raspberry Pi Pico, обзор чипов — в разделе RP2040 и RP2350.
Частые вопросы
Сколько автоматов PIO у RP2040?
Восемь: по даташиту Raspberry Pi в RP2040 два одинаковых блока PIO, в каждом четыре автомата (state machine). Автоматы одного блока делят память программ на 32 команды. У RP2350 блоков три, то есть 12 автоматов.
Какие команды есть в PIO?
Девять: JMP — переход, в том числе по условию, WAIT — ожидание уровня вывода или прерывания, IN и OUT — сдвиг битов в регистр и из регистра, PUSH и PULL — обмен с FIFO, MOV — пересылка между регистрами, IRQ — флаги прерываний, SET — запись константы в выводы или регистры. Каждая команда занимает 16 бит и выполняется за один такт.
Как рассчитать частоту сигнала, который формирует PIO?
Посчитать такты программы на один период и поделить системную частоту на это число и на делитель автомата. Каждая команда — один такт плюс указанная в ней задержка, переход по .wrap тактов не занимает. Например, программа из двух команд с задержкой 31 даёт период 64 такта: при 125 МГц и делителе 1 это около 1,95 МГц.
Можно ли использовать PIO из MicroPython?
Да. Raspberry Pi упоминает в даташите порт MicroPython для RP2040, и в нём есть модуль для программ PIO. Для C и C++ в Pico SDK есть ассемблер pioasm, который превращает текст программы в заголовочный файл с массивом команд.
Источники
- Raspberry Pi. RP2040 Datasheet (build-date 2025-02-20) — гл. 3 — 2 одинаковых блока PIO по 4 автомата, общая память программ на 32 команды; у автомата 2 сдвиговых и 2 рабочих 32-разрядных регистра, FIFO 4 × 32 бита в каждую сторону или 8 × 32 в одну, дробный делитель 16 + 8 бит, доступ к любым из 30 GPIO, DMA до слова за такт; интерфейсы: параллельные 8080 и 6800, I²C, I²S, SDIO, SPI, UART, DPI и VGA; 9 команд JMP, WAIT, IN, OUT, PUSH, PULL, MOV, IRQ, SET длиной 16 бит, каждая за один такт; 5-битное поле задержки и side-set, задержка до 31 такта; .wrap_target и .wrap — переход без затрат тактов; автопуш и автопулл; пример меандра на 12,5 МГц с делителем 2,5; разд. 3.6.2 — программа для светодиодов WS2812; ассемблер pioasm в SDK
- Raspberry Pi. RP2350 Datasheet — гл. 1 — 12 автоматов PIO в трёх блоках, до 48 GPIO в корпусе QFN-80
Нашли ошибку?
Смотрите также
- RP2040 и RP2350RP2040 и RP2350: ядра, память, GPIO, PIO, АЦП, 5 В на выводах, эрраты
- Делитель PIO RP2040INT и FRAC по частоте и тактам на бит, регистр CLKDIV, ошибка, код pico-sdk
- Raspberry Pi Pico40 контактов, функции GPIO, питание, АЦП, BOOTSEL и SWD
- ШИМ RP2040: TOP и делительTOP и делитель под частоту, коррекция фазы, уровень, слайс и канал, код Pico SDK
- Сдвиговый регистрПоследовательный вход, параллельные выходы, каскад
- DMAПрямой доступ к памяти без участия ядра
- UARTКадр 8N1, скорость и допуск, TX и RX, уровни, USB–UART, AVR и STM32