Dans ce post, on va apprendre à faire clignoter une LED avec le microcontrôleur PRU 0 (il y en a deux). L’objectif est d’apprendre à lancer un programme en C dans le micro-contrôleur. Après on peut modifier le programme pour faire un tas d’autres choses. C’est une base.
PRU signifie « Programmable Real-time Unit ».
J’ai perso eu du mal à démarrer avec les PRUs dans ma BeagleBone Black . Beaucoup de documents sur le net. Mais beaucoup utilisent les « tree obverlays » et les slots qui eux n’existent plus dans les versions récentes de la BBB. J’ai fini par faire clignoter ma LED après trois soirées de boulot. C’est frustrant, mais maintenant j’ai à peu près compris la logique. C’est ce que j’expose ici, en espèrant que ça vous sera utile!
Mon approche est basée sur Remoteproc, qui signifie « remote processor », c-a-d processeur distant. C’est une approche, récente, pour programmer les PRUs. Il y en a trois: remoteproc0, remoteproc1, remoteproc2. Comme on le voit ci-dessous, remoteproc2 est occupé. Les deux autres sont utilisables et correspondent aux PRU 0 et 1.
Pour bien commencer, lancer une connexion ssh dans un terminal
ssh debian@beaglebone.home
Dans le log au démarrage:
dmesg | tail -100
[ 11.957862] remoteproc remoteproc0: 4a334000.pru is available
[ 11.958057] pru-rproc 4a334000.pru: PRU rproc node pru@4a334000 probed successfully
[ 11.973707] remoteproc remoteproc1: 4a338000.pru is available
[ 11.973893] pru-rproc 4a338000.pru: PRU rproc node pru@4a338000 probed successfully
[ 61.264026] remoteproc remoteproc2: wkup_m3 is available
[ 61.271194] remoteproc remoteproc2: powering up wkup_m3
[ 61.271222] remoteproc remoteproc2: Booting fw image am335x-pm-firmware.elf, size 217168
[ 61.275721] remoteproc remoteproc2: remote processor wkup_m3 is now up
Editer et Modifier/boot/uEnv.txt pour désactiver la fonction HDMI.
###Disable auto loading of virtual capes (emmc/video/wireless/adc).
disable_uboot_overlay_video=1
disable_uboot_overlay_audio=1
Puis re-démarrer: sudo reboot now
Attention, c’est utile (uniquement) si on doit utiliser les pins du header P8 qui sont partagés avec l’HDMI. Voir les liens vers des tableaux « PinMux modes » en fin de post.
Information sur ma configuration :
ma disto
cat /etc/dogtag
BeagleBoard.org Debian Buster IoT Image 2020-04-06
cat /etc/debian_version
10.3
uname -a
Linux beaglebone 4.19.94-ti-r42 #1buster SMP PREEMPT Tue Mar 31 19:38:29 UTC 2020 armv7l GNU/Linux
Commençons les choses sérieuses
Télécharger BeagleScope (licence GPL v2), qui nous fourni les includes et libs pour pouvoir compiler et faire l’édition de lien, avec le Makefile.
git clone https://github.com/ZeekHuge/BeagleScope.git
si besoin, installer git : sudo apt-get install git
cd BeagleScope/
cd examples/firmware_exmples/pru_blinky/
Il y a des erreurs dans deploy.sh, on va faire le boulot en ligne de commande.
cd PRU_gpioToggle
Remplacer le fichier PRU_gpioToggle.c par celui sur ma page perso (avec wget, ci-dessous). Le code active le pin 27 du P9 (pin P9_27).
mv PRU_gpioToggle.c PRU_gpioToggle.c.old
wget elaurent.benaroya.free.fr/wordpress/BBB/PRU_gpioToggle.c
Vous pouvez (devez) éditer ce code. Ce qui est à retenir :
| Le registre R30 est utilisé pour mettre à l’état haut ou l’état bas un pin configuré en sortie (pruout). |
| C’est un registre 32 bits. |
| On met alternativement le bit numéro 5 à 1 (état haut) et à 0 (état bas). |
| Le bit 5 du registre R30 correspond physiquement à P9_27. |
| Après avoir changé la valeur du bit 5 dans R30, on attend avec la fonction __delay_cycles pendant 100 millions de cycles, soit 1/2 secondes car le micro-controlleur fonctionne à 200 MHz. |
| Enfin, on boucle 60*5 fois, c’est dire pendant 5 minutes. |
| Ensuite, la commande __halt(); arrête le processus. |
Attention, l’inclusion du fichier resource_table_empty.h est importante bien que celui-ci ne fasse pas grand chose.
Note importante : le code que je fournis est adapté de celui : beaglebone-pru-c
Compilation
1) d’abord définir la variable d’environnement PRU_CGT:
Créer les liens symboliques, à ne faire qu’une seule fois!
sudo ln -s /usr/bin/clpru /usr/share/ti/cgt-pru/bin/clpru
sudo ln -s /usr/bin/lnkpru /usr/share/ti/cgt-pru/bin/lnkpr
Puis :
export PRU_CGT=/usr/share/ti/cgt-pru
L’export peut être placé dans votre ~/.bashrc pour ne pas avoir à s’en occuper à chaque connexion au BBB.
2) Compiler
make clean
make
3) Copier le binaire dans le firmware
cd gen
cp PRU_gpioToggle.out gpiotoggle-fw
sudo cp gpiotoggle-fw /lib/firmware
Note importante : dans les versions « anciennes » du système d’exploitation, le nom du programme chargé dans le firmware est fixé : « am335x-pru0-fw » pour le PRU0 et « am335x-pru1-fw » pour le PRU1. Il faut donc remplacer la commande ci-dessous par :
cd gen
cp PRU_gpioToggle.out am335x-pru0-fw
sudo cp am335x-pru0-fw /lib/firmware
Le schéma électronique consiste à placer une LED et une résistance de 500 ohms environ, en série, entre P9_27 et P9_1 (DGND, digital ground).
Utiliser une LED jaune, rouge ou orange du fait de la chute de tension (entre 2 et 2.8 V contre 3.2 V au moins pour une bleue), sachant que la tension de sortie des gpio est de 3.3V!

Avec un résistance de 500 ohms, on obtient environ un courant de (3.3-2.3)/500 = 2 mA. C’est acceptable. Attention, le courant maximum délivré par chaque pin du gpio est de l’ordre de 4 mA à 8 mA, selon le pin : voir discussion dans GPIO current capacity?. C’est beaucoup moins qu’un Arduino, attention!!!
Configurer le pin P9_27 en sortie digitale (pruout)
config-pin
GPIO Pin Configurator Usage: config-pin -c <filename> config-pin -l <pin> config-pin -q <pin> config-pin <pin> <mode>
config-pin -l P9.27
Available modes for P9_27 are: default gpio gpio_pu gpio_pd gpio_input qep pruout pruin
config-pin -q P9.27
Current mode for P9_27 is: default
config-pin P9.27 pruout
Current mode for P9_27 is: pruout
Lancer le programme sur le PRU0 (remoteproc0)
echo 'stop' > /sys/class/remoteproc/remoteproc0/state 2>/dev/null
echo "gpiotoggle-fw" > /sys/class/remoteproc/remoteproc0/firmware
echo 'start' > /sys/class/remoteproc/remoteproc0/state
La LED devrait se mettre à clignoter pendant 5 minutes.
Pour vérifier que les trois commandes ci-dessus se sont bien passées, on peut faire un dmesg. Si erreurs, problème!
dmesg | tail
[ 3105.812064] remoteproc remoteproc0: powering up 4a334000.pru
[ 3105.812358] remoteproc remoteproc0: Booting fw image gpiotoggle-fw, size 33556
[ 3105.812391] remoteproc remoteproc0: remote processor 4a334000.pru is now up
Note : comme indiqué ci-dessus, dans les versions plus anciennes de l’OS, le nom du programme dans le firmware est fixé, la commande convenable est donc :
echo "am335x-pru0-fw" > /sys/class/remoteproc/remoteproc0/firmware
Approfondissement et liens externes
Pour le plaisir de mettre une image des headers P8 et P9 (c’est vendeur) : 
Beaglebone Black Headers PinMux Modes
Les tableaux PinMux modes pour les headers P8 et P9
Celui-ci est très bien BeagleboneBlackPinMuxModes et les versions imprimables en pdf: P8 : P8 headers pdf et P9 : P9 headers pdf.
Dans la table du header P9, on a
| P9 pin | Offset | mode 5 | mode 6 |
| P9_27 | 0x9A4 | pr1_pru0_pru_r30_5 | pr1_pru0_pru_r31_5 |
Dans cet extrait de la table, le pin 27 du header P9 correspond à pr1_pru0_pru_r30_5, qui indique le PRU 0, le registre 30 (output) sur le bit no 5.
Le mode 6, c’est la même chose mais en input, sur le registre R31. J’écris le bit no 5 parce que la numérotation des bits dans un nombre, en binaire, commence à 0. Donc le pin no 5 à 1 correspond à 0b0010_0000 en écriture binaire (sur 8 bits). En langage C, ce nombre peut s’écire 1 << 5 .
Voici un autre tableau équivalent, avec stabilotage : P9 headers.
Les pins GPIO pour la BeagleBone Black physiquement : The BeagleBone Black GPIO Pins ou gpio très complet (les figures en bas de la page).
Programmation des PRUs en C
Technique et bas niveau, mais vraiment intéressant, codage en C pour les PRUs : Beaglebone PRU Code in C
Sur les Remoteproc Remoteproc wiki
Super projet, proche de ce que je veux faire et qui a pour but d’acquérir de l’audio par le PRU, en temps réel, et de le transmettre au CPU qui le traite ou le joue : Using the Beaglebone Green Programmable Real-Time Unit with the RemoteProc and Remote Messaging Framework to Capture and Play Data from an ADC, lien parent du repo github.
Note sur le temps-réel
Il est important de comprendre que les PRUs permettent des traitements temps réel, car ce sont des micro-controlleurs dont on maîtrise totalement le fonctionnement en particulier celui des interruptions. Les versions « standards » de Linux ne permettent pas le temps réel parce qu’il peut se passer plein de trucs externes pendant l’exécution d’un programme. Il ne faut pas confondre temps réel et faible latence. Temps réel signifie qu’on peut prédire le temps d’excution d’un programme – de manière déterministe – indépendemment de la latence.
L’incontournable zeekhuge
Ce lien suivant est assez proche ce que j’ai exposé : l’application « blinky » : ptp_blinky. C’est assez complémentaire en fait.
Concernant les PRUs en général : working_with_prus_beagleboneblack_introduction_to_prus.
Conclusion
Dans ce post, j’ai exposé en détail comment compiler et exécuter un programme sur une « Programmable Real-time Unit » (PRU) de la BeagleBone Black. Le programme fait simplement clignoter une LED. J’espère que cette introduction à la programmation des PRUs vous sera utile et surtout vous donnera envie de réaliser des applications plus complexes, utilisant le SPI ou l’ADC par exemple, ou encore les timers.
ET Voilà!!!