Real-time BeagleBone: використання високошвидкісних висновків

Fast GPIO tutorial

Здрастуйте, шановні хабравчане! Давно вже я є читачем Хабра, але досі не міг знайти гідної теми для публікації. І ось, нарешті, гарненько прошерстів Хабр і GT, здивувався відсутності публікацій, присвячених функціональній підсистемі реального часу (PRU‐ICSS) лінійки процесорів SitaraTM фірми TI.

Найбільш популярною і доступною налагоджувальної платою з процесором AM335x є так званий «одноплатник» BeagleBone Black (White,Green). І саме наявність PRU робить BeagleBone найбільш кращим для використання в hardware-проектах порівняно з іншими бюджетними одноплатниками типу *Pi. Крім того, в деяких випадках BBB-PRU може досить ефективно замінити зв'язку ПК-МК-ПЛІС.

У даній статті наведено короткий огляд підсистеми PRU і режимів роботи високошвидкісних портів вводу/виводу, розглянуто покроковий приклад ініціалізації високошвидкісних портів виводу (Enhanced GPIO) та проведена оцінка їх продуктивності.

Введення
Відразу обмовлюся, що не буду детально зупинятися на характеристиках і параметрах самого BeagleBone, так як дані теми досить добре висвітлені в інтернеті, просто в кінці наведу найбільш корисні, на мій погляд, ресурси. А сконцентруюся безпосередньо на підсистемі PRU‐ICSS.

Аналогічні PRU рішення, з числа популярних, мною знайдені тільки для Intel Edison(до речі, tutorial на цю тему). Але за схожою ціною Edison поступається по продуктивності і характеристиками.

ВАЖЛИВО! Не всі описані далі режими роботи PRU і не в повному обсязі можливо реалізувати за допомогою BeagleBone із-за фізичних обмежень топології плати.

Значна частина наведених у публікації матеріалів є перекладом, адаптацією, модифікацією або комбінацією ресурсів, наведених в корисних джерелах в кінці статті.

Отже, що ж являє собою підсистема реального часу?

Огляд PRU‐ICSS
PRU-ICSS складається з двох 32-розрядних ядер, які мають RISC-архітектуру і працюють на частоті 200МГц. Кожне ядро має свою область пам'яті, а також спільну з Linux область пам'яті, може використовувати висновки загального призначення, розташовані на роз'ємах P8-P9, і формувати переривання.

PRU є важливим доповненням всієї платформи BeagleBone, що дозволяє забезпечувати підтримку програм з жорсткими часовими обмеженнями. Але варто відзначити, що PRU є апаратним прискорювачем, що дозволяє підвищити швидкодію Linux-додатків. На PRU можна покласти виконання окремих функцій і завдань, таких як реалізація програмних високошвидкісних протоколів передачі даних, у тому числі і нестандартних, або цифрової обробки сигналів датчиків в режимі реально часу. Також можна просто реалізувати додаткову апаратуру, наприклад шостий UART ttyO6.

Архітектура PRU
Не буду заглиблюватися в переклад мінлива, назву основні характеристики системи і прокоментую деякі слайди з презентаций і схеми з мінлива.

PRU + ARM Architecture

Основною перевагою PRU є короткий час доступу до локальних пам'яті і периферії. У тактах опорної частоти воно навіть нижче, ніж у підсистеми ARM. Більш докладний опис затримок запису/читання наведено тут.

Підсистема PRU включає в себе наступні блоки:

  • Два ядра PRU, кожне включає в себе:
    • 8KB пам'яті інструкцій;
    • 8KB пам'яті даних;

    • Високошвидкісний інтерфейс шини OCP для доступу до пам'яті і периферії ARM;
    • Порти введення/виводу (eGPIO) з підтримкою асинхронного послідовного захоплення і виведення;
    • Помножувач з можливістю накопичення (MAC);
  • Швидкодіюча тимчасова пам'ять (Scratchpad memory):
    • 3 блоки, в кожному 30 32-бітних регістрів;
    • Прямий доступ забезпечує можливість швидкої синхронізації між ядрами PRU;
  • Один контролер переривань (INTC):
    • Прийом до 64 зовнішніх подій;
    • 10 каналів переривань;

    • Апаратна підтримка подій;
  • Один комплект периферії для промислового Ethernet:
    • Один таймер з 10 подіями захоплення і 8 порівняння;
    • Два сигналу синхронізації;

    • Два 16-бітових сторожових таймера;
    • Цифрові порти введення/виводу;
  • 12KB пам'яті загального призначення;
  • Формування 16 програмних подій;
  • Один двухпортовий модуль Ethernet MII;
  • Один порт MDIO;
  • Один приймач UART c тактовою частотою 192МГц;
  • Один модуль захоплення (ECAP);
  • Підтримка гнучкого керування живленням;
PRUSS scheme

Тепер більш докладно розглянемо структуру швидкодіючих портів введення/виводу, що безпосередньо є темою наведеного нижче уроку і предметом дослідження.

Управління портами вводу і виводу здійснюється за допомогою регістрів R31 і R30 відповідно. Примітно, що регістр R31 також використовується для формування системних переривань. Таким чином, запис у R31 генерує переривання, а читання з регістра повертає інформацію про стан портів вводу (GPI) і контролер переривань (INTC).

Fast GPIO

Висока швидкість портів вводу/виводу забезпечується прямим доступом PRU, на відміну від ядра ARM, у якої доступ до GPIO здійснюється через кілька рівнів з'єднань.

Режими роботи GPIO

Режими задаються шляхом встановлення відповідних бітів в конфігураційному регістрі CFG. Пряме включення є режимом за замовчуванням і не вимагає додаткових налаштувань.

Порти введення (GPI — R31) мають 4 режими роботи:

  • Пряме включення:

    • Регістр PRU<n>_DATAIN (pru<n>_r31_status[16:0]) підключений безпосередньо до відповідного PRU<n>
    • Кожен PRU має незалежні порти введення, таким чином, їх загальна кількість становить 34;


    • Схема прямого включення GPI
  • Паралельний захоплення 16-ти біт:

    • Регістр PRU<n>_DATAIN (pru<n>_r31_status[16:0]) захоплює дані щодо позитивного або негативного фронту тактової частоти, формованої в регістрі PRU<n>_CLOCK (pru<n>_r31_status[16]);

    • Схема паралельного захоплення
  • Сдвиговый 28-бітний регістр вводу:

    • Регістр PRU<n>_DATAIN (pru<n>_r31_status[0]) здійснює захоплення з подальшим зрушенням;
    • Частота вибірки визначається відповідним значенням предделителей в конфігураційному регістрі CGF;


    • Схема сдвигового регістра
  • Режим MII_RT:

    • Регістр mii_rt_r31_status [29:0] знаходиться під управлінням модуля MII_RT;
Порти виводу (GPO — R30) мають 2 режими роботи:

  • Пряме включення:

    • Регістр PRU<n>_DATAOUT (pru<n>_r30[15:0]) підключений безпосередньо до відповідного PRU<n>
    • Кожен PRU має незалежні порти виводу, таким чином, їх загальна кількість становить 32;


    • Схема прямого включення GPO
  • Сдвиговый регістр виводу:

    • Регістр PRU<n>_DATAOUT (pru<n>_r30[0]) здійснює виведення з подальшим зрушенням по позитивному фронту тактової частоти, формованої в регістрі PRU<n>_CLOCK (pru<n>_r30[1]);
    • Частота вибірки визначається відповідним значенням предделителей в конфігураційному регістрі CGF;

    • В даному режимі реалізується подвійна буферизація з допомогою 16-бітних тіньових регістрів gpo_sh0 і gpo_sh1;
    • Кожен тіньової регістр має незалежний сигнал управління завантаженням pru<n>_r30[29:30] (PRU<n>_LOAD_GPO_SH [0:1]);
    • Видача значень починається після установки біта pru<n>_r30[31](PRU<n>_ENABLE_SHIFT) 1;
    • Якщо не оновлювати значення тіньових регістрів, то буде продовжуватися циклічна видача встановлених значень;
    • Видача значень припиняється після скидання біта pru<n>_r30[31](PRU<n>_ENABLE_SHIFT) в 0;

    • Схема сдвигового регістра
Варто зауважити, що PRU може також звертатися до звичайних портів вводу/виводу і іншої периферії ядра ARM через шину OCP, але це займе більше часу.

Розробка під PRU
Для створення програми під PRU необхідні наступні дії:

  1. Встановити PRU‐ICSS package (якщо не встановлено);
  2. Створити опис дерева пристроїв використовуваної периферії і PRU, скомпілювати і завантажити його;
  3. Написати програму для PRU (*.p) і скомпілювати її (*.bin);
  4. Написати програму завантаження та контролю виконання(*.c) для програми PRU і скомпілювати її;
PRU‐ICSS package служить засобом завантаження додатків з Linux в PRU і складається з двох секцій: низькорівневий драйвер ядра і власні бібліотеки. Низкоуровненвый драйвера ядра (uio_pru) забезпечує взаємодію ядра PRU c користувача бібліотекою PRUSSDRV і бере на себе функції харчування PRU, ініціалізації тактирования PRU, виділення пам'яті для PRU і реєстрації переривань PRU. Бібліотека PRUSSDRV, в свою чергу, дозволяє запускати і зупиняти PRU, забезпечує доступ PRU до периферії і зовнішньої пам'яті, керує перериваннями PRU. Також до складу PRU‐ICSS package входить компілятор асемблера PRU — pasm.

Програмний стек PRU‐ICSS package:

PRU‐ICSS package

Таким чином, програма *.c під Linux за допомогою функцій бібліотеки PRUSSDRV завантажує виконавчий файл (*.bin) в PRU, створює спільні області пам'яті, посилає і приймає переривання.

Окремо варто згадати, що для PRU фірма TI випустила відладчик prudebug і компілятор C.

Компактний і інформативне опис набору інструкцій можна знайти здесь.

Області можливого застосування
Як вже зазначалося раніше, основним завданням PRU є розвантаження основного ядра ARM шляхом виконання завдань, обмежених за часом. Такими завданнями можуть є реалізація протоколів передачі даних або блоків цифрової обробки сигналів. Так на BeagleBone PRU можна реалізувати до 25 каналів ШИМ або 4 додаткових програмних UART. Найбільш успішними проектами, що демонструють всю міць PRU, є BeagleLogic, MachineKit і LEDscape.

Приклади використання PRU-cape:

PRU‐cape
Установка PRU
Перейдемо, нарешті, до практичної частини.

Отже, буду експериментувати на наявній платі Beaglebone Black ревізії A5C. Використовую образ Linux bone-debian-7.8-lxde-4gb-armhf-2015-03-01-4gb.img.xz, отриманих з зовнішньої SD картки пам'яті. Для доступу до BBB використовую SSH PuTTY, для обміну файлами WinSCP.

root@beaglebone:~# uname -a
Linux beaglebone 3.8.13-bone70 #1 SMP Fri Jan 23 02:15:42 UTC 2015 armv7l GNU/Linux

В останніх версіях ядра для програмування PRU за замовчуванням використовується фреймворк remoteproc, але я його ще не освоїв, тому будемо використовувати бібліотеку PRUSSDRV, описану раніше. Щоб це стало можливо, включаємо модуль:

root@beaglebone:~# modprobe uio_pruss

Переконуємося, що інші необхідні компоненти встановлені
root@beaglebone:~# ls -a /usr/include | grep pruss
pruss_intc_mapping.h
prussdrv.h

root@beaglebone:~# ls -a /usr/lib | grep pruss
libprussdrv.a
libprussdrv.so
libprussdrvd.a
libprussdrvd.so

root@beaglebone:~# pasm
PRU Assembler Version 0.86
Copyright © 2005-2013 by Texas Instruments Inc.
Usage: pasm [-V#EBbcmLldz] [-Idir] [-Dname=value] [-Cname] InFile [OutFileBase]
...

root@beaglebone:~# lsmod | grep pru
uio_pruss 4066 0


Виконуємо попередні налаштування, прописавши необхідні змінні середовища в автозавантаження:

echo "export SLOTS=/sys/devices/bone_capemgr.*/slots" >> ~/.profile
#echo "export SLOTS=/sys/devices/platform/bone_capemgr/slots" >> ~/.profile #для вресий ядра від 4
echo "export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins" >> ~/.profile
echo "export PINGROUPS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pingroups" >> ~/.profile
source .profile

Відключення HDMI/EMMC

Майже вся периферія PRU виведена на порти, основною функцією яких є HDMI/EMMC. Тому для успішної взаємодії PRU з зовнішнім світом необхідно відключити ініціалізацію HDMI/EMMC.

Для використовуваного способу робиться це досить просто — потрібно лише раскомментровать потрібну строчку в /boot/uEnv.txt. Тому редагуємо його
nano /boot/uEnv.txt
, щоб в результаті вийшло так:

...
##Disable HDMI/eMMC
cape_disable=capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN,BB-BONE-EMMC-2G
...

GPIO:direct mode
Скориставшись інформативними таблицями роз'ємів P8 і P9, можна виявити, що в BeagleBone доступно 8 швидкодіючих портів виводу для PRU0 і 14 — для PRU1. Як описано раніше, необхідно попередньо відключити HDMI/EMMC і сконфігурувати висновки у потрібний режим. Так як хочеться вичавити побільше з BBB, будемо працювати з PRU1 і конфігурувати висновки pr1_pru1_pru_r30[0:13]. Також у нас ще залишається один незадіяний вхід pr1_pru1_pru_r31_16, розташований на роз'ємі P9.26. Його використовуємо для запуску програми з зовнішньої кнопочці.

Оптимальним прикладом демонстрації роботи портів загального призначення PRU, на мою думку, буде реалізація функції програмованої апаратної затримки по зовнішній події. Затримку будемо передавати з допомогою користувацької програми під linux через загальну область пам'яті. PRU безпосередньо реалізує апаратну затримку і виводить її значення, а саме молодші 14 біт, доступні висновки.

Таким чином, вимальовується наступний алгоритм програми:

  1. Запускаємо власну програму з зазначенням бажаної затримки в якості аргументу;
  2. Програма ініціалізує PRU, передає значення затримки і запускає підпрограму PRU;
  3. Підпрограма PRU чекає зовнішнього події (натискання кнопки);
  4. Після виявлення події відраховує задану затримку;
  5. Повідомляє власну програму про завершення допомогою переривання;
  6. Користувальницька програма отримує переривання про завершення роботи підпрограми;
  7. Після чого деактивує PRU і завершується сама;
Direct GPO device tree фоліо
Отже, для реалізації задуманої програми, необхідно налаштувати 14 портів BBB на висновок і 1 порт на введення, а також безпосередньо запустити підсистему PRU.

На основі прикладів з github'а склав наступний опис дерева пристроїв:

PRU_DGPO-00A0.dts
/dts-v1/;
/plugin/;

/ {
compatible = "ti,beaglebone", "ti,beaglebone-black";

/* identification */
part-number = "PRU_DGPO";
version = "00A0";

/* state the resources this cape uses */
exclusive use =
/* PRU1 Direct Output */
"P8.20", /* pru1: pr1_pru1_pru_r30_13 */
"P8.21", /* pru1: pr1_pru1_pru_r30_12 */
"P8.28", /* pru1: pr1_pru1_pru_r30_10 */
"P8.27", /* pru1: pr1_pru1_pru_r30_8 */
"P8.30", /* pru1: pr1_pru1_pru_r30_11 */
"P8.29", /* pru1: pr1_pru1_pru_r30_9 */
"P8.40", /* pru1: pr1_pru1_pru_r30_7 */
"P8.39", /* pru1: pr1_pru1_pru_r30_6 */
"P8.42", /* pru1: pr1_pru1_pru_r30_5 */
"P8.41", /* pru1: pr1_pru1_pru_r30_4 */
"P8.44", /* pru1: pr1_pru1_pru_r30_3 */
"P8.43", /* pru1: pr1_pru1_pru_r30_2 */
"P8.46", /* pru1: pr1_pru1_pru_r30_1 */
"P8.45", /* pru1: pr1_pru1_pru_r30_0 */

/* PRU1 Direct Input */
"P9.26", /* pru1: pr1_pru1_pru_r31_16 */

/* the hardware ip uses */
"pru1";

fragment@0 {
target = <&am33xx_pinmux>;
__overlay__ {

pru_pru_pins: pinmux_pru_pru_pins { // The PRU pin modes
pinctrl-single,pins = <
0x084 0x0D /* lcd_pclk.pr1_pru1_pru_r30_13, MODE5 | OUTPUT | PRU */
0x080 0x0D /* lcd_pclk.pr1_pru1_pru_r30_12, MODE5 | OUTPUT | PRU */
0x0e8 0x0D /* lcd_pclk.pr1_pru1_pru_r30_10, MODE5 | OUTPUT | PRU */
0x0e0 0x0D /* lcd_vsync.pr1_pru1_pru_r30_8, MODE5 | OUTPUT | PRU */
0x0ec 0x0D /* lcd_ac_bias_en.pr1_pru1_pru_r30_11, MODE5 | OUTPUT | PRU */
0x0e4 0x0D /* lcd_hsync.pr1_pru1_pru_r30_9, MODE5 | OUTPUT | PRU */
0x0bc 0x0D /* lcd_data7.pr1_pru1_pru_r30_7, MODE5 | OUTPUT | PRU */
0x0b8 0x0D /* lcd_data6.pr1_pru1_pru_r30_6, MODE5 | OUTPUT | PRU */
0x0b4 0x0D /* lcd_data5.pr1_pru1_pru_r30_5, MODE5 | OUTPUT | PRU */
0x0b0 0x0D /* lcd_data4.pr1_pru1_pru_r30_4, MODE5 | OUTPUT | PRU */
0x0ac 0x0D /* lcd_data3.pr1_pru1_pru_r30_3, MODE5 | OUTPUT | PRU */
0x0a8 0x0D /* lcd_data2.pr1_pru1_pru_r30_2, MODE5 | OUTPUT | PRU */
0x0a4 0x0D /* lcd_data1.pr1_pru1_pru_r30_1, MODE5 | OUTPUT | PRU */
0x0a0 0x0D /* lcd_data0.pr1_pru1_pru_r30_0, MODE5 | OUTPUT | PRU */

0x180 0x36 /* uart1_rxd.pr1_pru1_pru_r31_16, MODE6 | INPUT | PRU */
>;
};
};
};

fragment@1 {
target = <&ocp>;
__overlay__ {
test_helper: helper {
compatible = "bone-pinmux-helper";
pinctrl-names = "default";
pinctrl-0 = <&pru_pru_pins>;
status = "okay";
};
};
};

fragment@2 { // Enable the PRUSS
target = <&pruss>;
__overlay__ {
status = "okay";
};
};
};


Далі цей файл необхідно скомпілювати, скопіювати в /lib/firmware і завантажити:

echo "Compiling the overlay from .dts to .dtbo"
dtc -O dtb -o PRU_DGPO-00A0.dtbo -b 0 -@ PRU_DGPO-00A0.dts

echo "Copy PRU_DGPO-00A0.dtbo to /lib/firmware"
cp PRU_DGPO-00A0.dtbo /lib/firmware

echo "Loading overlay:"
sh -c "echo 'PRU_DGPO' > $SLOTS"

Потім варто перевірити процес завантаження.

В успішному разі повинно бути так:
root@beaglebone:~# dmesg | tail
[12566.485091] bone-capemgr bone_capemgr.9: slot #7: generic override
[12566.485149] bone-capemgr bone_capemgr.9: bone: Using override eeprom data at slot 7
[12566.485197] bone-capemgr bone_capemgr.9: slot #7: 'Override Board Name,00A0,Override Manuf,PRU_DGPO'
[12566.485506] bone-capemgr bone_capemgr.9: slot #7: Requesting part number/version based 'PRU_DGPO-00A0.dtbo
[12566.485554] bone-capemgr bone_capemgr.9: slot #7: Requesting firmware 'PRU_DGPO-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[12566.492347] bone-capemgr bone_capemgr.9: slot #7: dtbo 'PRU_DGPO-00A0.dtbo' loaded; converting to live tree
[12566.494050] bone-capemgr bone_capemgr.9: slot #7: #3 фоліо
[12566.555682] bone-capemgr bone_capemgr.9: slot #7: Applied #3 фоліо.

root@beaglebone:~# cat $SLOTS
0: 54:PF---
1: 55:PF---
2: 56:PF---
3: 57:PF---
4: ff:P-O-- Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
5: ff:P-O-- Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
6: ff:P-O-- Bone-Black-HDMIN,00A0,Texas Instrument,BB-BONELT-HDMIN
7: ff:P-O-L Override Board Name,00A0,Override Manuf,PRU_DGPO

root@beaglebone:~# cat $PINS | grep 00d
pin 32 (44e10880) 0000000d pinctrl-single
pin 33 (44e10884) 0000000d pinctrl-single
pin 40 (44e108a0) 0000000d pinctrl-single
pin 41 (44e108a4) 0000000d pinctrl-single
pin 42 (44e108a8) 0000000d pinctrl-single
pin 43 (44e108ac) 0000000d pinctrl-single
pin 44 (44e108b0) 0000000d pinctrl-single
pin 45 (44e108b4) 0000000d pinctrl-single
pin 46 (44e108b8) 0000000d pinctrl-single
pin 47 (44e108bc) 0000000d pinctrl-single
pin 56 (44e108e0) 0000000d pinctrl-single
pin 57 (44e108e4) 0000000d pinctrl-single
pin 58 (44e108e8) 0000000d pinctrl-single
pin 59 (44e108ec) 0000000d pinctrl-single

root@beaglebone:~# cat $PINS | grep 036
pin 96 (44e10980) 00000036 pinctrl-single

Щоб детальніше розібратися з настройками дерева пристроїв останніх релізів Debian, рекомендую звернутися сюди.

Взагалі древо пристроїв в Linux — це окрема тема, яка вимагає глибокого розгляду. Зацікавлені можуть почати знайомство з Device Tree тут, здесь.

Користувальницька програма
Як сказано раніше, основними завданнями користувальницької програми є:

  • Ініціалізація PRU;
  • Обмін даними з PRU через загальну область пам'яті;
  • Запуск підпрограми PRU;
  • Формування і обробка переривань і подій PRU;
Parallel_output.c :

#include < stdio.h>
#include <stdlib.h>
#include <prussdrv.h>
#include <pruss_intc_mapping.h>

#define PRU_NUM 1 // using PRU1 for these examples

int main (int argc, char* argv[])
{
unsigned int ret;

if(getuid()!=0){
printf("You must run this program as root. Exiting.\n");
exit(EXIT_FAILURE);
}

if(argc!=2) {
printf("Usage is Parralel_output and integer number of delay \n");
printf(" e. g. ./Parralel_output 100\n");
return 2;
}

char *p;
unsigned int cyc = (unsigned int) strtol(argv[1], &p, 10);
printf("Delay for %d cycles\n", cyc);

// Initialize structure used by prussdrv_pruintc_intc
// PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h
tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;

// Allocate and memory initialize
prussdrv_init ();
ret = prussdrv_open(PRU_EVTOUT_0);
if (ret)
{
printf("prussdrv_open open failed\n");
return (ret);
}

// Map PRU's interrupts
prussdrv_pruintc_init(&pruss_intc_initdata);

// Write a number of cycles into PRU1 Data RAM0
prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM , 0, &cyc, 4);

// Load and execute the PRU program on the PRU
prussdrv_exec_program (PRU_NUM, "./Parallel_output.bin");

// Wait for event completion from PRU, returns the number PRU_EVTOUT_0
int n = prussdrv_pru_wait_event (PRU_EVTOUT_0);
printf("PRU program completed, event number %d.\n", n);

// Disable PRU and close memory mappings
prussdrv_pru_disable(PRU_NUM);
prussdrv_exit ();
return EXIT_SUCCESS;
}

Компілюємо:

gcc Parallel_output.c -o Parallel_output -lpthread -lprussdrv

Підпрограма PRU
Для написання підпрограми PRU використовується асемблер PASM. Підпрограма виконується на одному з двох ядер PRU незалежно від Linux. Синхронізація PRU і Linux здійснюється за допомогою подій і переривань.

Крім описаного вище алгоритму додамо в підпрограму ще кілька інструкцій, щоб оцінити час їх виконання.

Parallel_output.p:

.origin 0 // start of program in memory PRU
.entrypoint START // program entry point (for a debugger)

#define PRU0_R31_VEC_VALID 32 // allows notification of program completion
#define PRU_EVTOUT_0 3 // the event that number is sent back

START:
WBC r31.t16 // wait bit clear - i.e., button press
// Toggle 4 times Parallel output pins
MOV r30, 0xffff
MOV r30, 0x0000

MOV r30, 0xffff
MOV r30, 0x0000

LBCO r30, C24, 0, 4 // load PRU1 Data RAM into r30 (use c24 const addr)

CYCLE:
SUB r30, r30, 1 // Decrement REG30 by 1 - i.e., parallel output current value on pins
QBNE CYCLE, r30, 0 // Loop to CYCLE, unless REG30=0

END: // notify the calling app that finished
MOV R31.b0, PRU0_R31_VEC_VALID | PRU_EVTOUT_0
HALT // halt the pru program

Компілюємо:

pasm -b Parallel_output.p

Запуск PRU і оцінка продуктивності
Для того щоб оцінити швидкість виконання інструкцій PRU, буду використовувати осцилограф Tektronix MSO4032 350MHz з 16-канальним логічним аналізатором, які мають дозвіл 2 нс.

Фото системи в зборі



Нарешті запускаємо програму:

root@beaglebone:/home/debian/Desktop/Direct-GPO# ./Parallel_output 15
Delay for 15 cycles
PRU program completed, event number 1.

Так як цикл затримки включає в себе 2 інструкції (SUB і QBNE), кожна з 5нс, то апаратна затримка буде складати arg*10нс. Для вищенаведеного прикладу затримка повинна становити 150 нс. Вимірювання проводяться без урахування тестових початкових інструкцій.

Для наочності інструкції стягнуто осциллограмму:

osc

Як видно, час виконання інструкції MOV в межах похибки, обумовленої дозволом логічного аналізатора — 2нс, і цілком вписывется в заявлені 5нс. Задіяні всі 14 висновків і 1 введення, як і заплановано.

Виконання інструкції WBC — очікування події — займає ~25 нс, але цей час постійно і його не складе труднощів врахувати при необхідності.

Далі приведені осцилограми для різних значень аргументів, вимірювання затримки проводиться за допомогою курсорів осцилографа.

Осцилограма для затримки в 150 нс
root@beaglebone:~# ./Parallel_output 15



Осцилограма для затримки в 15 мкс
root@beaglebone:~# ./Parallel_output 1500



Осцилограма для затримки в 159,23 мкс
root@beaglebone:~# ./Parallel_output 15923



15923 — просто рандомное число, близьке до 2^14, щоб задіяти всі висновки.
hex2dec('3E32') = 15922.






Підсумки
Таким чином, за допомогою вищеописаного прикладу продемонстровані можливості BeagleBone PRU в частині формування сигналів з роздільною здатністю до 5нс на 14 висновки одночасно, розглянуті основні принципи управління і конфігурації PRU, а також програмна модель PRU.

Вихідні коди можна знайти на тут.

Корисні джерела
Значна частина прикладів і принципів роботи взята з матеріалів Derek Molloy:

Величезне спасибі йому за це!

Цікавий сайт, присвячений застосуванню BBB в CNC і не тільки.
→ Підключення інтернет на BBB (раз, два, три).
Репозиторій device tree overlay для BB.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.