«« sommaire   

IV.3 – Personnaliser le support du matériel

Christophe BLAESS - juillet 2023

La démarche pour la création d'un système embarqué est généralement la suivante :

L'utilisation de Yocto Project, telle que nous l'avons vue jusqu'ici, permettra de mener à bien la seconde phase du projet, c'est-à-dire l'implémentation sur le kit de développement du module SOM. En effet la plupart des fabricants de modules proposent un layer pour Yocto Project regroupant les recettes nécessaires au fonctionnement sur leur kit de développement.

Nous allons nous intéresser dans cette séquence à l'étape ultérieure : le support de périphériques non intégrés dans le kit de développement (par exemple des convertisseurs analogiques-numériques, des capteurs de température, de lumière, de présence…).

Pour supporter un périphérique non présent dans la configuration du kit de développement du SOM, deux étapes sont nécessaires :

Configuration du noyau

Le noyau (kernel) Linux assure la mise à disposition des ressources matérielles pour les applications. C'est lui qui contient les pilotes (drivers) de périphériques qui communiquent avec les équipements autour du processeur. Mais il contient également beaucoup d'autres éléments : systèmes de fichiers, protocoles réseau, gestion de la mémoire, mécanisme d'ordonnancement…

Le noyau comporte des milliers d'options de configuration, que nous pouvons ajuster par l'intermédiaire d'un menu. Les options que l'on modifie le plus souvent en préparant un système embarqué concernent des drivers qu'il faut intégrer dans le noyau. Malheureusement pour notre expérimentation, l'essentiel des drivers susceptibles de nous intéresser sont déjà inclus dans la configuration du noyau fournie par le layer pour Raspberry Pi.

Nous allons donc modifier des options qui ne sont pas directement intéressantes pour nous, mais qui ont des effets visibles depuis la ligne de commande :

Nous allons tout d'abord vérifier que ces options sont bien absentes d'un noyau compilé avec les options par défaut :

Poky (Yocto Project Reference Distro) 4.0.11 mybox ttyS0

mybox login: root
Password: (linux)
root@mybox:~# uname  -a
Linux mybox 5.15.34-v7l #1 SMP Tue Apr 19 19:21:26 UTC 2022 armv7l armv7l armv7l GNU/Linux

Pas de mot-clé «PREEMPT» dans le résultat de uname. Vérifions les triggers disponibles pour la led :

root@mybox:~# cat  /sys/class/leds/led0/trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kb
d-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-sh
iftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight g
pio cpu cpu0 cpu1 cpu2 cpu3 default-on input panic actpwr mmc1 [mmc0] rf
kill-any rfkill-none rfkill0 rfkill1
root@mybox:~# 

Le trigger sélectionné est encadré par des crochets ; c'est «mmc0». Dans la liste des triggers disponibles, aucune trace de «activity».

La configuration du kernel ressemble un peu à celle de Busybox que nous avons vue dans la séquence précédente (plus exactement Busybox utilise le mécanisme de configuration mis au point pour le noyau Linux). Nous appelons bitbake avec la même option «-c menuconfig» que précédemment. Toutefois le nom de la recette est particulier : comme nous ne connaissons pas le nom exact de la recette gérant le kernel, et que cela peut varier en fonction de la machine cible, de la version de Yocto project, etc. on utilise un nom générique «virtual/kernel» qui sera traduit par bitbake :

[build-rpi]$ bitbake  -c menuconfig  virtual/kernel

Une nouvelle fenêtre (ou un nouvel onglet de terminal) s'ouvre. Je vous encourage à parcourir quelques menus pour vous familiariser avec la configuration du kernel Linux, notamment le menu «Device Drivers». Pour avoir un peu d'explication sur une option, on peut appuyer sur «?».

Deux options ont retenu notre attention.

La première est dans le menu «General Setup» (figure IV.3-1)…

… sous-menu «Preemption Model» (figure IV.4-2)…

… sélectionner l'option «Preemptible Kernel (Low-Latency Desktop)» (figure IV.4-3).

La seconde option se trouve en ouvrant le menu «Device Drivers» (figure IV.4-4)…

… en descendant jusqu'au sous-menu «LED Support» (figure IV.4-5)…

… puis le sous-menu «LED Trigger Support» (figure IV.4-6)…

… il faut activer (en pressant «Y») l'option «LED activity Trigger» (figure IV.4-7).

On quitte ensuite le menu en demandant à sauvegarder la nouvelle configuration.

Comme lors de la configuration de Busybox, nous demandons à bitbake de nous préparer un fragment de configuration regroupant les options qui diffèrent des valeurs par défaut :

[build-rpi]$ bitbake  -c diffconfig  virtual/kernel
Config fragment has been dumped into:
  /home/Builds/Lab/Yocto-lab/builds/build-rpi/tmp/work/raspberrypi4-poky-linux-gnueabi/linux-raspberrypi/1_5.15.34+gitAUTOINC+e1b976ee4f_0086da6acd-r0/fragment.cfg
NOTE: Tasks Summary: Attempted 300 tasks of which 299 didn't need to be rerun and all succeeded.

Et nous appelons recipetool pour qu'il crée l'extension de recette nécessaire en intégrant le fragment dont nous lui fournissons le chemin.

[build-rpi]$ recipetool  appendsrcfile  -w  ../../layers/meta-my-layer/  virtual/kernel   tmp/work/raspberrypi4-poky-linux-gnueabi/linux-raspberrypi/1_5.15.34+gitAUTOINC+e1b976ee4f_0086da6acd-r0/fragment.cfg

À l'issue de cette opération, nous voyons qu'un nouveau sous-répertoire «recipes-kernel» est apparu dans notre layer et qu'il contient une extension de recette pour le noyau :

[build-rpi]$ ls  ../../layers/meta-my-layer/
conf  COPYING.MIT  README  recipes-core  recipes-custom  recipes-example  recipes-kernel  recipes-support 

[build-rpi]$ ls  ../../layers/meta-my-layer/recipes-kernel/
linux

[build-rpi]$ ls  ../../layers/meta-my-layer/recipes-kernel/linux/
linux-raspberrypi  linux-raspberrypi_%.bbappend

[build-rpi]$ ls  ../../layers/meta-my-layer/recipes-kernel/linux/linux-raspberrypi
fragment.cfg

[build-rpi]$ 

Avant de relancer un build de l'image nous forçons l'effacement de la recette «virtual/kernel» pour éviter les warnings de bitbake :

[build-rpi]$ bitbake  -c  clean  virtual/kernel 
 [...]

[build-rpi]$ bitbake  my-image
 [...]

Une fois la compilation terminée, et l'image installée sur le Raspberry Pi, nous allons vérifier le résultat de nos modifications :

Poky (Yocto Project Reference Distro) 4.0.11 mybox ttyS0

mybox login: root
Password: (linux)

root@mybox:~# uname  -a
Linux mybox 5.15.34-v7l #1 SMP PREEMPT Tue Apr 19 19:21:26 UTC 2022 armv7l armv7
l armv7l GNU/Linux

Notre noyau est bien marqué comme préemptible. Voyons la configuration de la led verte, et les triggers disponibles :

root@mybox:~# cat /sys/class/leds/led0/trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftl
ock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctr
lllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 cpu2 c
pu3 activity default-on input panic actpwr mmc1 [mmc0] rfkill-any rfkill-none rf
kill0 rfkill1

Le trigger «activity» est apparu. C'est toujours «mmc0» qui est sélectionné par défaut, mais nous pouvons en changer :

root@mybox:~# echo  activity  >  /sys/class/leds/led0/trigger
root@mybox:~# cat  /sys/class/leds/led0/trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftl
ock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctr
lllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 cpu2 c
pu3 [activity] default-on input panic actpwr mmc1 mmc0 rfkill-any rfkill-none rf
kill0 rfkill1

La led verte clignote avec un bref flash toutes les secondes, le processeur est très faiblement chargé. Lançons une petite boucle active :

root@mybox:~# while  true  ;  do  :  ;  done

Instantanément, la led clignote plus intensément. Elle revient à son motif initial dès que l'on presse Contrôle-C. Si on lance plusieurs instances de cette boucle en parallèle (en faisant suivre le «done» d'un «&»), la led est quasiment allumée en permanence.

Nous voyons que les modifications de configuration que nous avons apportées au noyau Linux sont bien visibles sur notre cible. Dans un projet réel, il s'agit le plus souvent d'activer des drivers, des protocoles réseau, etc. qui ne le sont pas dans la configuration par défaut.

Modification du Device Tree

Lorsque le noyau démarre, il n'a que très peu d'informations sur le système sur lequel il s'exécute. Il doit déterminer de nombreux paramètres concernant le processeur (nombre de cœurs et fréquence, type et adresse du contrôleur d'interruption, nombre et adresses des timers, etc.), la mémoire (quantité disponible, adresses, etc.), les bus de communication avec les périphériques, les périphériques effectivement présents…

Sur un PC par exemple, l'ensemble de ces informations est maintenu par le BIOS que l'on peut partiellement configurer avec le menu Setup à la mise sous tension.

Sur l'architecture ARM — largement majoritaire pour les systèmes embarqués — il n'y a pas de BIOS et le paramétrage doit être explicitement fourni au noyau. C'est le rôle du Device Tree.

Le Device Tree pour un système donné est renseigné manuellement dans un fichier avec l'extension «.dts» (Device Tree Source). Celui-ci peut hériter de paramètres génériques (concernant par exemple la famille de System On Chip) se trouvant dans des fichiers d'extention «.dtsi» (Device Tree Source Include).

Les fichiers «.dts» et «.dtsi» sont livrés avec le noyau Linux pour un grand nombre de plateformes (dont les Raspberry Pi).

On utilise ensuite un outil nommé «dtc» (Device Tree Compiler) pour obtenir une représentation binaire du Device Tree prête à être interprétée par le noyau. Cette image est inscrite dans un fichier d'extension «.dtb» (Device Tree Blob) qui sera chargé en mémoire par le bootloader et transmis ainsi au kernel.

Pour nous donner un but réaliste, nous allons ajouter un petit capteur de température qui communique avec le processeur par l'intermédiaire du bus i2c. J'ai choisi un capteur MCP9808 monté sur un module simple à connecter comme on le voit sur la figure IV.3-8.

Ce petit module peut facilement être installé sur une plaquette d'essai. J'ai utilisé une plaquette que nous employons en session de formation (le «RPI 5» sur la figure IV.3-9 est le numéro de la platine d'essai, pas celui d'un Raspberry Pi du futur !)

Sur certains bus (PCI, USB…) il est possible d'énumérer et d'identifier les périphériques présents. Sur d'autres bus (SPI, i2c…) ce n'est pas possible et il faut indiquer au noyau quels périphériques sont connectés, ainsi que leurs adresses de communication. On réalise traditionnellement cela dans le Device Tree.

Pendant la phase de prototypage avec un montage volant de ce type, on peut généralement se dispenser de modifier le Device Tree et agir sur les entrées bind dans l'arborescence /sys/. Nous allons imaginer que nous sommes à l'étape de validation d'une carte porteuse et que l'on souhaite ajuster précisément le Device Tree du module SOM.

Les connexions sont réalisées comme suit :

Les points importants pour le support dans le noyau sont :

Patch sur les sources du noyau

Nous allons faire un patch sur le noyau Linux en modifiant l'extrait de Device Tree concerné. Pour cela nous devons vérifier quelle version du noyau est compilée par Poky. La commande «uname -a» nous a déjà indiqué 5.10.31.

En cherchant dans le layer «meta-raspberrypi», nous trouvons une recette meta-raspberrypi/recipes-kernel/linux/linux-raspberrypi_5.15.bb contenant entre autres :

LINUX_VERSION ?= "5.15.34"
LINUX_RPI_BRANCH ?= "rpi-5.15.y"
 [...]
SRCREV_machine = "0086da6acd41600d47b87b05874f99704216426f"
 [...]
SRC_URI = " \
    git://github.com/raspberrypi/linux.git;name=machine;branch=${LINUX_RPI_BRANCH};protocol=https
 [...]

Voici les éléments qui nous intéressent :

Nous pouvons donc, dans un répertoire de travail, télécharger ces (volumineuses) sources et extraire le numéro de commit qui nous intéresse :

[build-rpi]$ git  clone  https://github.com/raspberrypi/linux.git  -b  rpi-5.15.y 
Clonage dans 'linux'...
remote: Enumerating objects: 10927264, done.
remote: Counting objects: 100% (144179/144179), done.
remote: Compressing objects: 100% (35526/35526), done.
Receiving objects: 100% (10927264/10927264), 3.54 GiB | 14.31 MiB/s, done.
Resolving deltas: 100% (9132857/9132857), done.
Updating files: 100% (74255/74255), done.

[build-rpi]$ cd  linux/

[linux]$ git  checkout  0086da6acd41600d47b87b05874f99704216426f
Updating files: 100% (6137/6137), done.
Note: switching to '0086da6acd41600d47b87b05874f99704216426f'.
  [...]

Nous avons les sources du noyau. Les fichiers sources de Device Tree se trouvent dans le sous-répertoire arch/arm/boot/dts/. Celui du Raspberry Pi 4 est nommé bcm2711-rpi-4-b.dtsbcm2711» faisant référence au System On Chip Broadcom 2711 utilisé dans cette version du Raspberry Pi).

En examinant le contenu du fichier, on trouve la section

  [...]
&i2c1 {
        pinctrl-names = "default";
        pinctrl-0 = <&i2c1_pins>;
        clock-frequency = <100000>;
}
  [...]

Pour un vrai projet industriel, on crée un nouveau fichier .dts pour représenter le projet. Ici, j'édite seulement le fichier original pour ajouter les lignes suivantes :

[linux]$ nano  arch/arm/boot/dts/bcm2711-rpi-4-b.dts
  [...]
&i2c1 {
        pinctrl-names = "default";
        pinctrl-0 = <&i2c1_pins>;
        clock-frequency = <100000>;

        status = "okay";
        temp@18 {
                compatible = "jedec,jc-42.4-temp";
                reg = <0x18>;
                #address-cells = <0x1>;
                #size-cells = <0x0>;
        };
};
 [...]

Notre propos n'est pas ici de détailler le contenu du Device Tree mais de voir comment intégrer nos modifications au build de Yocto Project. Pour produire un patch, le plus simple est de demander à git de s'en charger :

[linux]$ git  diff  >  ../001-add-i2c-device-in-dts.patch 

[linux]$ cd  ..

Je peux alors l'outil recipetool comme nous l'avions fait dans la séquence II.3 :

[build-rpi]$ recipetool  appendsrcfile  ../../layers/meta-my-layer/  virtual/kernel  001-add-i2c-device-in-dts.patch 

Il me reste à regénérer mon image (après avoir effacé le noyau précédemment compilé pour éviter les warnings) et à la charger sur le Raspberry Pi :

[build-rpi]$ bitbake  -c  clean  virtual/kernel 

[build-rpi]$ bitbake  my-image

Si tout se passe bien, je dois avoir dès le démarrage une nouvelle entrée dans le sous-répertoire «hwmon/» (Hardware Monitoring) de /sys/class :

Poky (Yocto Project Reference Distro) 4.0.11 mybox ttyS0

mybox login: root
Password: (linux)

root@mybox:~# ls  /sys/class/hwmon/
hwmon0  hwmon1 hwmon2

En effet, normalement le Raspberry Pi 4 n'a que deux entrées hwmon0 et hwmon1, dont les contenus sont les suivants :

root@mybox:~# ls  /sys/class/hwmon/hwmon0/
device  name  power  subsystem  temp1_input  uevent

root@mybox:~# ls  /sys/class/hwmon/hwmon1/
device  of_node  subsystem   temp1_crit_alarm  temp1_input  temp1_max_alarm  temp1_min        uevent
name    power    temp1_crit  temp1_crit_hyst   temp1_max    temp1_max_hyst   temp1_min_alarm

root@mybox:~# ls /sys/class/hwmon/hwmon2/
device  in0_lcrit_alarm  name  power  subsystem  uevent

L'entrée qui nous intéresse est la deuxième, qui a un contenu typique des capteurs de températures servant à surveiller des seuils. Voyons la température actuelle (en millidegrés Celsius) :

root@mybox:~# cat /sys/class/hwmon/hwmon1/temp1_input
22812

22,812 °C, température correcte pour un matin de fin juillet à Paris. Je pose le doigt quelques secondes sur le boîtier du capteur :

root@mybox:~# cat /sys/class/hwmon/hwmon1/temp1_input
28875

Je retire mon doigt et la température descend doucement :

root@mybox:~# cat /sys/class/hwmon/hwmon1/temp1_input
27312
root@mybox:~# cat /sys/class/hwmon/hwmon1/temp1_input
26875
root@mybox:~# cat /sys/class/hwmon/hwmon1/temp1_input
26187
root@mybox:~# 

Conclusion

Cette séquence nous aura permis d'intervenir dans deux points critiques de l'image produite par Yocto Project : le noyau Linux et le Device Tree. Aujourd'hui c'est une part importante du travail nécessaire pour supporter une nouvelle carte, celle qui concerne la communication avec le hardware.

Nous avons vu au long de ce cours en ligne de nombreux aspects de l'utilisation de Yocto Project pour produire une image au contenu parfaitement maitrisé.

J'espère que ces documents vous seront utiles. Les lecteurs souhaitant bénéficier d'un support technique pour leur projet, d'une formation spécifique (dans leurs locaux, adaptée à leur matériel personnel, etc.) ou qui préfèrent au contraire suivre un cours classique en format inter-entreprise pourront me retrouver chez Logilin.

Ce document est placé sous licence Creative Common CC-by-nc. Vous pouvez copier son contenu et le réemployer à votre gré pour une utilisation non-commerciale. Vous devez en outre mentionner sa provenance.

Le nom Yocto Project est une marque déposée par la Linux Foundation. Le présent document n'est en aucune façon approuvé par Yocto Project ou la Linux Foundation.

«« sommaire