Перейти к содержанию

Калькулятор делителя PIO RP2040 и RP2350

Задайте частоту, с которой программа PIO должна выдавать биты или периоды, и сколько тактов автомата занимает один бит, — калькулятор посчитает дробный делитель 16.8 так же, как pico-sdk, покажет фактическую частоту, значение регистра SMx_CLKDIV и дрожание от дробной части и выдаст код настройки.

Исходные данные

Гц

битов, импульсов или периодов в секунду

команды плюс их задержки [n] за один бит или период

МГц

· · МГц

Результат

Делитель
15,625 = 15 + 160/256
нужен 15,625; INT 15, FRAC 160
Регистр SMx_CLKDIV
0x000FA000INT << 16 | FRAC << 8
Фактическая частота
800 кГц+0 % к заданной
Такт автомата
8 МГцодин такт — 125 нс

Делитель дробный: автомат делает такт то через 15, то через 16 тактов системы, и только в среднем — через 15,625. Края сигнала дрожат на один такт clk_sys — 8 нс. Для UART это неважно, для тактового сигнала лучше подобрать целый делитель.

Код pico-sdk

// 800 kHz, 10 PIO cycles per period, clk_sys 125 MHz
float div = (float)clock_get_hz(clk_sys) / (800000.0f * 10);
sm_config_set_clkdiv(&c, div);   // INT 15, FRAC 160

// the same divisor, exactly:
sm_config_set_clkdiv_int_frac8(&c, 15, 160);

// or the register of a running state machine:
pio0->sm[0].clkdiv = 0x000FA000;   // INT << 16 | FRAC << 8

Расчёт выполнен по формулам и данным из источников, приведённых на этой странице, и не заменяет проверку: параметры компонентов у разных производителей и даже партий различаются. Перед применением сверяйте результат с документацией на конкретные детали.

Автоматы PIO в RP2040 и RP2350 работают от системной частоты clk_sys, но у каждого есть свой делитель: он не замедляет системный такт, а разрешает автомату делать шаг только в части тактов (даташит RP2040, разд. 3.5.5). Как устроены сами автоматы и их команды — в статье «PIO в RP2040».

Формула

Программа PIO выполняется с известным числом тактов: каждая команда — один такт плюс задержка в квадратных скобках, переход по .wrap тактов не тратит. Если один бит или один период сигнала занимает N тактов автомата, частота программы

f=fclk_sysD⋅N,D=INT+FRAC256.f = \frac{f_\text{clk\_sys}}{D \cdot N}, \qquad D = INT + \frac{FRAC}{256}.

Делитель D записывается в регистр SMx_CLKDIV: 16 бит целой части INT и 8 бит дробной FRAC, то есть от 1 до 65536 с шагом 1/256. Значение INT = 0 означает 65536, и FRAC тогда тоже должен быть нулём. В регистре INT стоит в битах 31…16, FRAC — в битах 15…8.

Как округляет pico-sdk

Функция sm_config_set_clkdiv() принимает делитель числом с плавающей точкой и переводит его в INT и FRAC функцией pio_calculate_clkdiv8_from_float. В pico-sdk 2.1.1 по умолчанию включено округление до ближайшей 1/256 (PICO_PIO_CLKDIV_ROUND_NEAREST): к делителю прибавляется 0,5/256, а затем дробная часть отбрасывается. Если собрать SDK с PICO_PIO_CLKDIV_ROUND_NEAREST=0, дробная часть округляется вниз — калькулятор считает оба варианта. Например, для UART 9600 бод при 125 МГц нужен делитель 1627,6042: с округлением FRAC = 155, без него — 154.

Системная частота по умолчанию в pico-sdk 2.1.1 — 125 МГц у RP2040 и 150 МГц у RP2350; с опцией PICO_USE_FASTEST_SUPPORTED_CLOCK RP2040 работает на 200 МГц. При другой частоте все делители надо пересчитать — поэтому в примерах Raspberry Pi делитель вычисляют в программе через clock_get_hz(clk_sys).

Примеры

ПрограммаТактов на бит или периодЧастотаДелитель при 125 МГц
Меандр из даташита RP2040412,5 МГц2,5 — INT 2, FRAC 128, регистр 0x00028000
WS2812 из pico-examples10 (3 + 3 + 4)800 кГц15,625 — INT 15, FRAC 160
UART из pico-examples8115 200 бод135,633 — INT 135, FRAC 162

Для WS2812 при 150 МГц у RP2350 делитель другой — 18,75, то есть INT 18 и FRAC 192.

Дробный делитель и дрожание

По даташиту дробная часть реализована дельта-сигма-модулятором: часть периодов делителя длится n тактов системы, часть — n + 1, и только в среднем получается заданное значение. В примере даташита с делителем 2,5 такты автомата идут через 2 и 3 такта clk_sys по очереди. Поэтому края сигнала дрожат на один период clk_sys — 8 нс при 125 МГц. Для UART такое дрожание несущественно: приёмник из pico-examples считывает каждый бит в его середине, на 8 тактах автомата от края. Для тактовых сигналов, видео и других мест, где важна форма каждого периода, подбирают целый делитель — меняя число тактов на бит задержками в программе или системную частоту.

Если частота слишком высока и делитель получается меньше 1, программу нужно сократить в тактах; если слишком низка и делитель больше 65536 — добавить задержки или цикл. Аппаратный ШИМ RP2040 с делителем 8.4 считает калькулятор ШИМ RP2040, выводы платы — в статье о Raspberry Pi Pico.

Частые вопросы

Как рассчитать делитель PIO для нужной частоты?

Посчитать, сколько тактов автомата занимает один бит или период программы, с учётом задержек [n] в командах, и поделить системную частоту на произведение частоты и этого числа тактов. Например, программа WS2812 из pico-examples тратит 10 тактов на бит: 125 МГц / (800 кГц · 10) = 15,625. Делитель записывается как INT = 15 и FRAC = 0,625 · 256 = 160.

Какой делитель нужен для UART 115 200 бод на PIO?

Программы UART из pico-examples тратят 8 тактов на бит, поэтому делитель — 125 МГц / (8 · 115 200) ≈ 135,634. С шагом 1/256 pico-sdk запишет INT = 135, FRAC = 162, и скорость получится 115 200,74 бод — ошибка меньше тысячной процента.

Почему дробный делитель PIO даёт дрожание?

По даташиту RP2040 дробный делитель работает как дельта-сигма: часть периодов длится n тактов системы, часть — n + 1, и только в среднем получается нужное значение. При делителе 2,5 такты автомата идут через 2 и 3 такта clk_sys по очереди. Для UART это незаметно, а для тактового сигнала или видео лучше подобрать целый делитель.

Какой наибольший делитель у PIO?

65536: в поле INT регистра SMx_CLKDIV 16 бит, и значение 0 по даташиту означает 65536, а дробная часть тогда должна быть нулевой. При 125 МГц и одном такте на период это около 1907 Гц; для более низких частот в программу добавляют задержки или цикл со счётчиком.

Источники

  1. Raspberry Pi. RP2040 Datasheet (build 2025-02-20) — 3.5.5 — делитель 16 + 8 бит с дельта-сигма для дробной части, от 1 до 65536 с шагом 1/256; при дробном делителе часть периодов удлиняется с n до n + 1 тактов системы; таблица 381 (SMx_CLKDIV) — частота = clk_sys / (INT + FRAC/256), INT = 0 означает 65536, тогда и FRAC = 0; разд. 3.2 — меандр из 4 тактов с делителем 2,5 (запись 2,5 · 2^16) даёт 12,5 МГц (эталон тестов); UART на PIO — делитель на 8 скоростей передачи; WS2812 — T1 + T2 + T3 = 3 + 3 + 4 такта на бит
  2. Raspberry Pi. pico-sdk 2.1.1: hardware/pio.h, pico/platform.h, hardware/platform_defs.h — pio_calculate_clkdiv8_from_float: делитель от 1 до 65536; при PICO_PIO_CLKDIV_ROUND_NEAREST (по умолчанию 1) прибавляется 0,5/256, затем INT — целая часть, FRAC — дробная · 256 с отбрасыванием; sm_config_set_clkdiv_int_frac8; clk_sys по умолчанию 125 МГц у RP2040 (200 МГц с PICO_USE_FASTEST_SUPPORTED_CLOCK) и 150 МГц у RP2350; лицензия BSD-3-Clause
  3. Raspberry Pi. pico-examples (sdk-2.1.1): pio/ws2812, pio/uart_tx, pio/uart_rx — делитель = clock_get_hz(clk_sys) / (частота · тактов на бит); WS2812 — 10 тактов на бит, 800 кГц (второй источник тестов); UART — 8 тактов на бит
  4. Raspberry Pi. RP2350 Datasheet — 11.5.5 и регистр SMx_CLKDIV — тот же делитель 16.8, INT = 0 означает 65536
Встроить калькулятор на свой сайт

Скопируйте код и вставьте его в HTML страницы, статьи или вики. Виджет бесплатный, без рекламы, счётчиков и cookie. Условие одно — ссылка на GAW под рамкой остаётся на месте. Подробнее о виджетах

<iframe src="https://gaw.ru/embed/rp2040-pio/" title="Калькулятор делителя PIO RP2040 и RP2350" width="100%" height="640" style="border:1px solid #e3e5e9;border-radius:8px" loading="lazy" allow="clipboard-write"></iframe>
<p style="margin:6px 0 0;font-size:14px">Калькулятор: <a href="https://gaw.ru/tools/rp2040-pio/">Калькулятор делителя PIO RP2040 и RP2350</a> — GAW.ru</p>
Автоподстройка высоты рамки

Если сайт разрешает скрипты, добавьте этот код один раз в любом месте страницы: рамка будет менять высоту вместе с результатом расчёта.

<script>window.addEventListener("message",function(e){if(e.origin!=="https://gaw.ru"||!e.data||e.data.type!=="gaw:embed-height")return;var f=document.querySelectorAll('iframe[src^="https://gaw.ru/embed/"]');for(var i=0;i<f.length;i++)if(f[i].contentWindow===e.source)f[i].style.height=e.data.height+"px"})</script>

Обновлено .

Нашли ошибку в расчёте?