Zephyr 0S est un système d’exploitation temps réel que l’on peut utiliser sur de petits microcontrôleurs pour réaliser des tâches relativement simples avec un comportement temporel précis.
Nous allons l’étudier pendant quelques articles en nous appuyant sur des modèles de microcontrôleurs répandus, peu coûteux et faciles à se procurer comme le Raspberry Pi Pico.
Attention : il s’agit bien du « Raspberry Pi Pico » et pas du « Raspberry Pi Pico 2 » sorti il y a quelques semaines. Ce dernier est architecturé autour d’un microcontrôleur différent et n’est pas encore supporté par Zephyr Os au moment de la rédaction de cet article.
–
Introduction
Zephyr OS
Les microprocesseurs et microcontrôleurs sont omniprésents dans l’informatique industrielle, dans l’Internet des objets (IOT) et même dans la plupart des équipements électroniques que l’on rencontre au quotidien. Lorsque la composante « informatique » d’un équipement est suffisamment importante, on délègue le travail à un microprocesseur sur lequel on fait tourner un système d’exploitation (Operating System – OS) comme Linux embarqué.
Lorsque le travail logiciel est relativement simple, répétitif et ne nécessite pas de grosse capacité de calcul ou de stockage on se tourne vers un microcontrôleur. Les microcontrôleurs existent dans des gammes très variées, de la famille des PIC à celle des STM32 en passant par les AVR, LPC, etc. Cela offre un choix très large pour le concepteur d’un circuit.
En contrepartie, les méthodes de programmation pour accéder aux fonctionnalités offertes par un microcontrôleur varient sensiblement d’un modèle à l’autre. Il est difficile d’écrire du code portable d’un microcontrôleur à un autre, car ils n’offrent pas de méthodes de développement généralisées.
Mais heureusement des efforts ont été réalisés pour offrir un socle commun, permettant de disposer d’une interface de programmation (Application Programming Interface – API) standard et riche. Il s’agit tout simplement de doter les microcontrôleurs d’un OS sur lequel on peut appuyer le développement spécifique. Plusieurs systèmes de ce type ont été développés : µC/OS, Contiki, RIOT, etc. Nous allons nous intéresser à un OS pour microcontrôleurs qui semblent de plus en plus actif aujourd’hui : Zephyr ® . Il s’agit d’une marque déposée par la Linux Foundation qui gère aujourd’hui ce projet, initialement créé par Wind River.
Raspberry Pico
Depuis 2012, la Fondation Raspberry Pi a produit de nombreux modèles de « Single Board Computer » , petits ordinateurs monocartes nécessitant simplement l’ajout d’un clavier, d’un écran, d’une alimentation et d’une carte micro-SD pour offrir toutes les fonctionnalités d’un ordinateur classique. Ce sont tous les modèles 1B/B+/A, 2B, 3B/B+/A, 4B/A, 400, 5 ainsi que les petits Zero 1/W/2W.
Un modèle très différent a été produit en 2021 : le Raspberry Pi Pico. Il n’est pas conçu autour d’un microprocesseur mais autour d’un micro-contrôleur (RP2040) spécifiquement conçu pour l’occasion, avec 264Ko de RAM et 2Mo de mémoire flash intégrée. Une seconde version, Raspberry Pi Pico 2, disponible depuis août 2024, est construite autour d’un nouveau micro-contrôleur (RP2350) et dispose de 4Mb de mémoire flash. Elle n’est pas encore supportée par Zephyr.
Malgré leur ressemblance, il est important de ne pas confondre le Raspberry Pi Zero (sur lequel on installe un système Linux normal et des applications classiques) et le Raspberry Pi Pico qui ne peut contenir qu’un OS minimal comme Zephyr et un volume de code assez réduit.
Là où les Rasberry Pi classiques (y compris le Zero) peuvent faire penser à un petit PC, le Pico est beaucoup plus proche de la famille des Arduino.
–
Préparations préliminaires
Je vais préparer sur un PC tournant sous Linux un fichier contenant l’OS Zephyr et un petit code d’exemple, pour pouvoir ensuite l’installer sur un Raspberry Pi Pico.
Environnement de travail
Je crée un répertoire Zephyr-lab
dans mon arborescence pour stocker tous les éléments nécessaires au développement pour Zephyr.
Python
Nous allons installer plusieurs outils Python spécifiques à Zephyr (par exemple l’outil de build west
). Il est préférable de créer un environnement Python virtuel pour éviter de « polluer » notre installation Python globale.
[Zephyr-Lab]$ sudo apt install -y python3-venv
[Zephyr-Lab]$ python3 -m venv .venv
[Zephyr-Lab]$ source .venv/bin/activate
(.venv)[Zephyr-Lab]$
Le préfixe (.venv)
au début du prompt du shell nous indique que nous utilisons cet environnement virtuel. Pour le quitter il suffit d’utiliser la commande deactivate
. Bien sûr il faudra réactiver l’environnement (avec source .venv/bin/activate
dans ce répertoire) à chaque fois que nous lancerons un shell pour travailler avec Zephyr.
Outil de build
Nous installons l’outil de compilation de Zephyr. Il s’appelle west
(avant d’être un OS pour microcontrôleurs, le Zéphyr est un vent qui souffle de l’ouest) et servira à encadrer les appels à cmake
.
(.venv)[Zephyr-Lab]$ pip install west
[...]
SDK de Zephyr
Le SDK (Software Development Kit) est l’ensemble de la toolchain (chaîne de cross-compilation, permettant de générer du code pour le processeur cible) et des fichiers headers permettant d’accéder aux services de Zephyr.
Je fais mes développements sur un PC Linux, je choisis donc le SDK en conséquence. Il en existe en effet plusieurs versions :
zephyr-sdk-0.16.8_linux-x86_64.tar.xz
pour Linux x86/64 (par exemple un PC Linux),
zephyr-sdk-0.16.8_linux-aarch64.tar.xz
pour Linux Arm 64 (par exemple un Raspberry Pi 4 ou 5),
zephyr-sdk-0.16.8_macos-x86_64.tar.xz
pour MacOS sur x86/64,
zephyr-sdk-0.16.8_macos-x86_64.tar.xz
pour MacOS sur Arm 64,
zephyr-sdk-0.16.8_windows-x86_64.7z
pour Windows sur x86/64.
Le téléchargement se fait à partir du dépôt Gihub de zephyrproject-rtos
.
Une fois la toolchain décompressée, un script setup.sh
vient configurer ses composants et enregistrer le package Cmake du SDK
(.venv)[Zephyr-lab]$ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.8/zephyr-sdk-0.16.8_linux-x86_64.tar.xz
[...]
(.venv)[Zephyr-lab]$ tar xf zephyr-sdk-0.16.8_linux-x86_64.tar.xz
(.venv)[Zephyr-lab]$ cd zephyr-sdk-0.16.8/
(.venv)[zephyr-sdk-0.16.8]$ ./setup.sh
Zephyr SDK 0.16.8 Setup
** NOTE **
You only need to run this script once after extracting the Zephyr SDK
distribution bundle archive.
Install host tools [y/n]? y
Register Zephyr SDK CMake package [y/n]? y
Installing host tools ...
All done.
Press any key to exit ...
(.venv)[zephyr-sdk-0.16.8]$ cd ..
(.venv)[Zephyr-lab]$ rm zephyr-sdk-0.16.8_linux-x86_64.tar.xz
(.venv)[Zephyr-lab]$
Lors de l’installation du SDK, un package Cmake a été enregistré pour trouver l’accès à la toolchain :
(.venv)[zephyr-sdk-0.16.8]$ ls -l ~/.cmake/packages/Zephyr-sdk/
total 4
-rw-rw-r-- 1 cpb cpb 51 août 7 17:09 d901fd4ba9b608aa29d7f5c12d60fef3
(.venv)[zephyr-sdk-0.16.8]$ cat ~/.cmake/packages/Zephyr-sdk/d901fd4ba9b608aa29d7f5c12d60fef3
/home/Builds/Lab/Zephyr-lab/zephyr-sdk-0.16.8/cmake
(.venv)[zephyr-sdk-0.16.8]$
–
Exemple de base de Zephyr
Installation du kernel
Nous initialisons un répertoire de build nommé zephyr-project
. Pour cela nous utilisons la commande west init
. Celle-ci va cloner un repository Github contenant entre autres un manifest
listant les autres dépôts à cloner.
(.venv)[Zephyr-lab]$ west init zephyr-project
=== Initializing in /home/Builds/Lab/Zephyr-lab/zephyr-project
--- Cloning manifest repository from https://github.com/zephyrproject-rtos/zephyr
Cloning into '/home/Builds/Lab/Zephyr-lab/zephyr-project/.west/manifest-tmp'...
remote: Enumerating objects: 1088209, done.
remote: Counting objects: 100% (231/231), done.
remote: Compressing objects: 100% (138/138), done.
remote: Total 1088209 (delta 101), reused 156 (delta 89), pack-reused 1087978
Receiving objects: 100% (1088209/1088209), 650.48 MiB | 25.04 MiB/s, done.
Resolving deltas: 100% (821755/821755), done.
Updating files: 100% (36170/36170), done.
--- setting manifest.path to zephyr
=== Initialized. Now run "west update" inside /home/Builds/Lab/Zephyr-lab/zephyr-project.
(.venv)[Zephyr-lab]$
Le répertoire créé contient un sous-répertoire avec la configuration de west
et un second avec les sources du kernel Zephyr :
(.venv)[Zephyr-lab]$ ls -a zephyr-project/
. .. .west zephyr
(.venv)[Zephyr-lab]$ ls -a zephyr-project/.west/
. .. config
(.venv)[Zephyr-lab]$ ls -a zephyr-project/zephyr/
. doc Kconfig.zephyr share
.. drivers kernel snippets
arch dts lib soc
boards .editorconfig LICENSE submanifests
.checkpatch.conf .git .mailmap subsys
.clang-format .gitattributes MAINTAINERS.yml tests
cmake .github misc VERSION
CMakeLists.txt .gitignore modules version.h.in
.codecov.yml .gitlint README.rst west.yml
CODE_OF_CONDUCT.md include samples .yamllint
CODEOWNERS Kconfig scripts zephyr-env.cmd
CONTRIBUTING.rst Kconfig.constants SDK_VERSION zephyr-env.sh
(.venv)[Zephyr-lab]$
Comme la commande west init
nous y invitait, nous allons exécuter west update
pour mettre à jour tous les modules nécessaires (cela rappelle un peu la commande git submodule update
).
(.venv)[Zephyr-lab]$ cd zephyr-project/
(.venv)[zephyr-project]$ west update
=== updating acpica (modules/lib/acpica):
--- acpica: initializing
Initialized empty Git repository in /home/Builds/Lab/Zephyr-lab/zephyr-project/modules/lib/acpica/.git/
[...]
* [new tag] inital-pr -> inital-pr
* [new tag] old-structure -> old-structure
HEAD is now at 75d0880 tests: Update mps2 board name
HEAD is now at 75d0880 tests: Update mps2 board name
(.venv)[zephyr-project]$
[...]
Successfully installed Deprecated-1.2.14 MarkupSafe-2.1.5 Pillow-10.4.0 PyGithub-2.3.0 anytree-2.12.1 appdirs-1.4.4 arrow-1.2.3 astroid-3.2.4 bz-1.0 canopen-2.3.0 capstone-4.0.2 cbor-1.0.0 cbor2-5.6.4 certifi-2024.7.4 cffi-1.17.0 charset-normalizer-3.3.2 clang-format-18.1.8 click-8.1.3 cmsis-pack-manager-0.5.3 colorlog-6.8.2 coverage-7.6.1 cryptography-43.0.0 dill-0.3.8 exceptiongroup-1.2.2 gcovr-7.2 gitlint-0.19.1 gitlint-core-0.19.1 graphviz-0.20.3 grpcio-1.65.4 grpcio-tools-1.65.4 idna-3.7 imgtool-2.1.0 importlib-metadata-8.2.0 importlib-resources-6.4.0 iniconfig-2.0.0 intelhex-2.3.0 intervaltree-3.1.0 isort-5.13.2 jinja2-3.1.4 junit2html-31.0.2 junitparser-3.1.2 lark-1.1.9 libusb-package-1.0.26.2 lpc_checksum-3.0.0 lxml-5.2.2 mccabe-0.7.0 mock-5.1.0 msgpack-1.0.8 mypy-1.11.1 mypy-extensions-1.0.0 natsort-8.4.0 pathspec-0.12.1 platformdirs-4.2.2 pluggy-1.5.0 ply-3.11 prettytable-3.10.2 progress-1.6 protobuf-5.27.3 psutil-6.0.0 pycparser-2.22 pyelftools-0.31 pygments-2.18.0 pyjwt-2.9.0 pylink-square-1.2.1 pylint-3.2.6 pynacl-1.5.0 pyocd-0.36.0 pyserial-3.5 pytest-8.3.2 python-can-4.4.2 python-magic-0.4.27 pyusb-1.2.1 regex-2024.7.24 requests-2.32.3 sh-1.14.3 sortedcontainers-2.4.0 tabulate-0.9.0 tomli-2.0.1 tomlkit-0.13.0 typing-extensions-4.12.2 urllib3-2.2.2 wcwidth-0.2.13 wrapt-1.16.0 yamllint-1.35.1 zcbor-0.8.1 zipp-3.19.2
Nous terminons la préparation du répertoire en installant les modules Python supplémentaires réclamés par Zephyr :
(.venv)[zephyr-project]$ pip install -r zephyr/scripts/requirements.txt
[...]
Compilation pour Raspberry Pi Pico
Le dossier du kernel Zephyr que nous avons installé plus haut contient un sous-répertoire samples/
avec plusieurs exemples des possibilités de l’OS.
(.venv)[zephyr-project]$ cd zephyr/
(.venv)[zephyr]$ ls samples/
application_development fuel_gauge sample_definition_and_criteria.rst
arch hello_world sensor
basic index.rst shields
bluetooth kernel subsys
boards modules synchronization
classic.rst net sysbuild
cpp philosophers tfm_integration
drivers posix userspace
Nous allons nous intéresser aux plus simples des exemples, dans le sous-répertoire basic/
:
(.venv)[zephyr]$ ls samples/basic/
basic.rst blinky_pwm custom_dts_binding hash_map rgb_led sys_heap
blinky button fade_led minimal servo_motor threads
L’équivalent pour les systèmes embarqués du classique « Hello World » s’appelle blinky
, c’est le clignotement d’une led.
Nous pouvons demander à west
de compiler cet exemple en lui précisant via son option -b
le nom du microcontrôleur concerné rpi_pico
. Pour connaître la liste des cartes connues par Zephyr, voir le lien ci-dessous.
(.venv)[zephyr]$ west build -b rpi_pico samples/basic/blinky
-- west build: generating a build system
Loading Zephyr default modules (Zephyr base).
-- Application: /home/Builds/Lab/Zephyr-lab/zephyr-project/zephyr/samples/basic/blinky
[...]
[2/2] Linking ASM executable boot_stage2
[134/134] Linking C executable zephyr/zephyr.elf
Memory region Used Size Region Size %age Used
BOOT_FLASH: 256 B 256 B 100.00%
FLASH: 15432 B 2096896 B 0.74%
RAM: 3968 B 264 KB 1.47%
IDT_LIST: 0 GB 32 KB 0.00%
Generating files from /home/Builds/Lab/Zephyr-lab/zephyr-project/zephyr/build/zephyr/zephyr.elf for board: rpi_pico
Converted to uf2, output size: 31744, start address: 0x10000000
Wrote 31744 bytes to zephyr.uf2
(.venv)[zephyr]$
En sortie du build, un message nous annonce la création d’un fichier .uf2
.
Le format UF2 (pour “USB Flashing Format” ), développé par Microsoft est prévu pour flasher des microcontrôleurs en utilisant l’interface “mass storage” .
Le Raspberry Pi Pico possède une telle interface, il suffit de le connecter au PC avec un câble de connexion USB/micro-USB en maintenant son bouton “Bootsel” pressé pendant le banchement. Sur le PC une interface est détectée au bout de cinq secondes environ, semblable à une clé USB habituelle. C’est probablement la méthode la plus simple pour flasher un microcontrôleur (nous en verrons d’autres dans les prochains articles).
Cinq secondes après avoir raccordé mon Raspberry Pico à mon PC de développement, je le vois apparaître dans mon arborescence :
(.venv) [zephyr]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
[...]
sdd 8:48 1 128M 0 disk
└─sdd1 8:49 1 128M 0 part /media/cpb/RPI-RP2
[...]
(.venv)[zephyr]$ ls /media/cpb/RPI-RP2/
INDEX.HTM INFO_UF2.TXT
Les fichiers présents sur le Pico sont des documentations nous indiquant que c’est bien l’emplacement où nous devons copier le fichier .uf2
. Il faut à présent trouver ce fichier dans l’arborescence de Zephyr :
(.venv)[zephyr]$ find . -name '*.uf2'
./build/zephyr/zephyr.uf2
Tout logiquement, le fichier se trouve dans le sous-dossier build/
.
Je copie le fichier, et dès que la copie est terminée, le microcontrôleur se réinitialise et commence à exécuter le code.
(.venv)[zephyr]$ cp build/zephyr/zephyr.uf2 /media/cpb/RPI-RP2/
(.venv)[zephyr]$
Interface série
Nous étudierons dans le prochain article la structure d’un répertoire pour une application. Voyons juste le fichier samples/basic/blinky/src/main.c
:
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
/* 1000 msec = 1 sec */
#define SLEEP_TIME_MS 1000
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
/*
* A build error on this line means your board is unsupported.
* See the sample documentation for information on how to fix this.
*/
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void)
{
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) {
return 0;
}
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) {
return 0;
}
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) {
return 0;
}
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
k_msleep(SLEEP_TIME_MS);
}
return 0;
}
Outre le clignotement de la LED, nous pouvons apercevoir, dans la boucle principale une ligne « printf("LED state...");
» qui indique que ce programme affiche un message sur sa sortie standard.
Suivant l’état d’un paramètre de configuration, Zephyr redirige la sortie standard vers l’interface série principale du microcontrôleur (la plupart des microcontrôleurs ont plusieurs interfaces séries). Pour le Raspberry Pi Pico, cette interface est l‘UART0 dont les connecteurs sont TX
sur la broche 1, et RX
sur la broche 2. En outre nous avons une masse GND
sur la broche 3, nous pouvons donc utiliser un petit adaptateur USB-Série comme pour le port série du Raspberry Pi classique.
Attention toutefois, sur les Raspberry Pi habituel (1B, 2B, 3B, 3B+, 4, 5, etc.) l’ordre des broches est GND
–TX
–RX
alors qu’avec le Pico c’est GND
–RX
–TX
.
Une fois la connexion physique établie, je lance sur mon PC le terminal série minicom
(dont la sortie est facile à capturer dans le cadre de cet article), mais il est possible d’utiliser de nombreuses variantes putty
, tio
, picocom
, gtkterm
, cutecom
, etc.
(.venv)[zephyr]$ minicom
Welcome to minicom 2.8
OPTIONS: I18n
Port /dev/ttyUSB0, 07:46:09
Press CTRL-A Z for help on special keys
Après avoir branché l’alimentation du Raspberry Pico, j’observe sur la sortie :
*** Booting Zephyr OS build v3.7.0-711-gd590c1867284 ***
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
LED state: ON
LED state: OFF
Sources
- Liste des cartes supportées par Zephyr OS : https://docs.zephyrproject.org/latest/boards/index.html
–
Conclusion
Nous voyons que l’installation et l’utilisation de Zephyr sur un microcontrôleur est assez simple, l’outil west
offre une interface simple pour configurer, compiler et installer le code sur la cible. Nous n’avons pas utilisé west
pour installer le code ci-dessus sur le Raspberry Pico car ce dernier offre une méthode beaucoup plus simple, mais nous l’utiliserons dans un article à venir.
–
Nous avons compilé un exemple tout fait, sans modifier quoique ce soit. Nous verrons dans le prochain article comment développer notre propre code et le compiler avec Zephyr.
–
Si vous préférez assister à une session de formation sur « Zephyr OS » en mode présentiel ou distanciel, ou disposer d’une assistance technique personnalisée pour vos développement n’hésitez pas à me contacter.
Je pense qu’il y a une petite coquille apreès l’install du sdk.
ls -l ~/.cmake/packages/
c’est plutôt
ls -l ~/.cmake/packages/Zephyr-sdk/
Exact, merci. Je vais corriger la ligne