![](/themes/connect/images/creative-commons.png)
Le développement d'applications embarquées devient de plus en plus populaire et accessible. La conjonction de deux facteurs principaux semble expliquer cela : l'arrivée de plateformes de développement à bas coût comme l'Arduino ou autre BeagleBone et Raspberry Pi et l'utilisation massive de logiciels libres sur ces matériels. Nous vous proposons la (re-)découverte d'un logiciel libre destiné aux cibles enfouies qui vous permettra d'accélérer vos développements : Lepton.
1. Introduction
Les objets connectés sont considérés par beaucoup d'observateurs et d'acteurs, spécialistes ou non, comme l'un des prochains eldorados de l'électronique grand public. Ils seraient ubiquitaires, parfois utiles, mais nul doute qu'ils déferleront dans notre quotidien [IEA]. La vision d'ensemble dans laquelle s'intégrera l'objet connecté est la suivante.
Cette image décrit l'intégration d'un objet connecté dans son environnement d'utilisation le plus complet.
Dans cette optique, l'objet connecté peut-être vu comme le premier maillon s'intégrant dans un environnement quelconque. Le mobile sera utilisé comme agrégateur d'informations et donneur d'ordres « local ». L'entité « big-data » se chargeant de brasser et « contextualiser » les données grâce à la puissance de machines en réseau. Chaque entité agissant et rétro-agissant entre elles pour affiner et déterminer les besoins ciblés par les fournisseurs de services et les utilisateurs.
Qui dit objet connecté suppose silicium et donc unité de traitement. Les différents fondeurs de l'industrie (STMicroelectronics, Atmel, Freescale entre autres) proposent plusieurs gammes de produits. Ceux-ci s'étendent des simples plateformes 8 bits comprenant de simples contrôleurs de communication synchrones aux processeurs 64 bits dopés d'accélérateurs matériels en tout genre (unité de traitement graphique, modules de cryptographie, traitement de signal…).
Qui dit objet connecté suppose silicium et donc logiciel. Plusieurs types d'acteurs fournissent différentes combinaisons. Celles-ci vont du simple micrologiciel mono-tâche en passant par le système d'exploitation temps-réel (Real-Time Operating System - RTOS) jusqu'au système d'exploitation complet géré par un environnement de développement proposant tous les outils nécessaires à la mise au point et à l'intégration du système sur le matériel.
Dans cet article, nous vous proposons de (re)découvrir Lepton [OS_LEPTON], un système d'exploitation temps réel libre POSIX adapté aux cibles enfouies.
1.1 Petit rappel (ou radotage)
Lepton (qui signifie léger en grec) peut se voir comme le socle de développement commun des objets connectés à ressources limitées. Nous définissons les systèmes à ressources limitées ou cibles enfouies comme ceux ou celles possédant quelques Mio de mémoire flash et quelques centaines ou dizaines de Kio de mémoire vive.
Pour répondre à cette exigence, Lepton respecte en grande partie le standard POSIX permettant ainsi le portage rapide d'applications et librairies en provenance du monde UNIX.
Il s'adresse principalement aux processeurs embarqués de la famille ARM sur lesquels l'exécution de GNU/Linux ou ucLinux nécessite l'ajout de mémoire externe. Par processeurs enfouis nous entendons particulièrement la plate-forme cortex-M [CORTEXM].
Lepton est déjà déployé et utilisé sur les appareils de mesure des marques Chauvin-Arnoux [CA6117], Metrix [MT5022] et Enerdis. Celles-ci ont supporté son développement et permis la libération de son code source depuis 2011. Actuellement la société o10ée [O10EE] fournit divers services autour du système d'exploitation comme le portage sur un matériel particulier ou l'adaptation de fonctionnalités sur une plate-forme matérielle déjà supportée.
Oscilloscope portable Metrix doté de Lepton comme cœur logiciel.
Lepton est ouvert par nature pour permettre les contributions qui profitent à tous et suffisamment fermé pour laisser la maîtrise à chacun de ses spécificités et fonctionnalités particulières. La licence MPL [MPL] est utilisée pour permettre l'enrichissement des composants « standards » comme le noyau, les piles IP, les piles graphiques par exemple, et garantir la création et le portage de pilotes de périphériques, de librairies et d'applications particuliers.
Les objectifs principaux de Lepton sont :
- le développement de briques logicielles ;
- l'accueil de briques logicielles ;
- la réutilisation et l'amélioration de ces briques logicielles ;
- la mutualisation des ressources afin de fournir ces briques logicielles.
1.2 Architecture générale
Lepton est un système d'exploitation libre portable, modulaire et compréhensible. L'ensemble des briques impliquées dans la construction d'une application se reposant sur Lepton est représenté par le schéma suivant :
Vue générale de l'architecture de Lepton (image o10ée).
Une application Lepton au sens large est composée de processus, au sens UNIX du terme, qui contiennent plusieurs unités de traitement appelées threads. Ce découpage logique permet une organisation modulaire et favorise la réutilisation de briques fonctionnelles. Le terme logique provient du fait qu'à l'exécution chaque processus ne dispose pas de son propre espace d'adressage. En effet, les processeurs de la famille cortex-M ne possèdent pas de MMU et donc pas de mécanisme de mémoire virtuelle.
La portabilité fonctionnelle est assurée par la couche POSIX du kernel. Afin de disposer de la multitude et de la richesse des applications et des bibliothèques du monde UNIX, Lepton permet de porter et créer des programmes et bibliothèques utilisant la norme POSIX. Toutes les fonctions de la norme ne sont pas disponibles (fort heureusement pour l'empreinte mémoire), mais les plus communes sont présentées pour faciliter et garder l'approche « applicative ». Il est ainsi possible de créer des processus grâce aux appels système vfork et exec, contrairement à d'autres systèmes similaires comme RTEMS ou Riot.
Concrètement, ce sont plus de 150 fonctions de la norme POSIX qui sont à disposition du développeur pour concevoir son application embarquée qui fournissent :
- la création et la synchronisation des processus et des threads ;
- l'accès aux périphériques de manière simple et standard ;
- la notion de streams afin de maximiser la réutilisation logicielle ;
- le support du réseau et de l'API BSD socket (LWIP [LWIP]) ;
- des systèmes de fichiers ;
- des bibliothèques graphiques ;
- des services réseau tels qu'un serveur web et un serveur FTP.
La partie droite du schéma montre 3 composants considérés comme des RTOS à part entière :
- embOS [EMBOS] ;
- eCos [ECOS] ;
- freeRTOS [FREERTOS].
Le premier est propriétaire et est développé par la société Segger. Les seconds sont libres et supportés par diverses sociétés commerciales. Contrairement à freeRTOS, eCos fournit un mécanisme puissant de gestion de « paquets » permettant une approche plus modulaire. freeRTOS dispose lui d'un cœur simple, ce qui se matérialise par une mise en œuvre plus aisée. Lors de la construction d'une application Lepton, un des 3 RTOS est utilisé afin de fournir les mécanismes basiques de synchronisation tels que l'ordonnancement des tâches ou la gestion des délais. Dans cette vision, Lepton peut être considéré comme une distribution qui enrichit ou fournit une couche POSIX. Un noyau comme celui de Riot pourrait être « Leptonizé ».
1.3 Architecture du cœur logiciel
Cet article se concentre principalement sur l'utilisation de Lepton sur la famille samd20 du fondeur ATMEL. Cette cible est supportée par la combinaison Lepton/freeRTOS. L'architecture du noyau utilisée répond au schéma suivant :
Ensemble des composants constituant le noyau Lepton fonctionnant sur le cœur at91samd20.
Les composants les plus proches du matériel sont :
- CMSIS pour Cortex Microcontroler Software Interface Standard [CMSIS]. Ce module est une couche logicielle indépendante des fondeurs, définie par ARM, permettant un accès aux registres de la famille cortex-M. Lepton utilise la librairie CMSIS pour la déclaration des exceptions et interruptions par défaut. Les deux premières entrées de ce tableau correspondent à l'adresse de la pile pour le code de démarrage ainsi qu'au point d'entrée du micrologiciel.
- ASF signifie Atmel Software Framework [ASF]. Cette librairie est fournie par ATMEL et permet la gestion des différents contrôleurs du cœur. Elle ne repose pas a priori sur la CMSIS et facilite la configuration de certains modules matériels comme le port série (USART) ou les entrées/sorties (GPIO).
- freeRTOS est un RTOS libre, populaire et fiable. Il possède une empreinte mémoire plus faible qu'eCos pour fournir les mécanismes basiques d'ordonnancement comme la gestion des tâches ou les mécanismes de synchronisation tels que l'exclusion mutuelle ou les sémaphores.
Cette vision d'ensemble donnée, nous pouvons désormais construire et déployer une application Lepton basique sur cible.
1.4 Carte d'évaluation samd20 Xplained Pro
Le microcontrôleur que nous allons vous faire découvrir provient du fondeur ATMEL très connu dans le monde des amateurs (et même des professionnels) pour les microcontrôleurs AVR équipant les cartes Arduino (désormais Genuino).
La famille de microcontrôleurs SAMD20 est une cousine de la carte Arduino Zero [ARDUINO_Z] reposant quant à elle sur une SAMD21. Ces 2 familles de microcontrôleurs, très proches, utilisent un processeur ARM Cortex-M0+ affichant la plus basse consommation de l'architecture ARM.
Carte d'évaluation ATMEL SAMD20 XPlained Pro proche parente de l'Arduino Zero.
La carte d' évaluation SAMD20 Xplained Pro est disponible à cette adresse [SAMD20] pour un prix de 40 euros. Elle dispose d'un processeur Cortex-M0+ fonctionnant à 48 MHz qui embarque une mémoire flash de 256 Kio et une mémoire vive interne de 32 Kio.
Les périphériques disponibles pour ce type de microcontrôleurs sont assez classiques :
- compteurs et timers ;
- convertisseurs analogique/numérique et numérique/analogique ;
- interfaces de communication synchrones et asynchrones (USART, SPI, TWI).
Un module de débogage intégré est aussi présent et évite l'achat d'une sonde JTAG externe.
2. Utilisation basique
2.1 Prérequis
Comme certains d'entre nous ont pu le remarquer, le service d'hébergement Google Code sera arrêté à la fin de cette année. La migration de code ayant été proposé vers GitHub, entre autres, Lepton utilise désormais Git comme gestionnaire de version et non plus Mercurial.
Avant de cloner le dépôt, nous allons procéder à l'installation de certaines dépendances pour construire une application se basant sur Lepton. La machine hôte utilisée étant une Debian Jessie, le gestionnaire de paquets apt est utilisé. Dans un terminal, tapons :
# aptitude install build-essential libexpat-dev libgtkgl2.0-dev screen gdb git
L'image finale générée s'exécutera sur un processeur cortex-M0+. Cela implique l'utilisation d'une chaîne de compilation croisée pour ARM. D'excellents tutoriels existent et montrent comment créer sa chaîne de compilation aux petits oignons. Nous avons pris le parti de récupérer une chaîne de compilation précompilée générée par ARM [LAUNCHPAD]. La version de gcc utilisée est la 4.8.4. Choisissons un répertoire pour stocker la chaîne de compilation et décompressons-la :
$ tar -jxf gcc-arm-none-eabi-4_8-2014q2-20140609-linux.tar.bz2
Ajoutons le chemin des exécutables (compilateur, assembleur, éditeur de liens, outils divers) dans notre variable PATH :
$ export PATH="/path/of/cross/toolchain/bin:$PATH"
Tous les composants nécessaires à la construction d'un système Lepton sont maintenant disponibles. Nous pouvons cloner le dépôt dans le répertoire de notre choix à l'aide de la commande suivante :
$ git clone https://github.com/lepton-distribution/lepton.git
Afin de simplifier la gestion des chemins pour la construction, un lien symbolique est créé dans notre répertoire HOME. Cela permet aussi de disposer de plusieurs arbres Lepton n'importe où sur le système de fichiers de la machine hôte. Rendons-nous dans le répertoire du dépôt et tapons :
$ cd /path/of/lepton/repository/lepton
$ sh tauon_ln.sh
La variable LEPTON sera utilisée pour désigner le répertoire racine de Lepton.
2.2 Installation
La configuration de Lepton s'effectue au moyen d'un fichier texte au format XML propre à chaque application. Ce fichier définit entre autres :
- la taille du rootfs ;
- le nombre de descripteurs de fichiers maximum autorisés pour le système ;
- les pilotes de périphérique ;
- les « pseudo-binaires » embarqués.
Le fichier de configuration XML est lu par un exécutable, mklepton, qui génère des fichiers source et d'en-tête qui seront inclus dans l'image finale. Le processus de construction d'une application se basant sur Lepton peut être décrit par ce schéma :
Ce schéma décrit l'ensemble des composants « internes » impliqués dans la construction d'une application se basant sur Lepton.
Les fichiers créés par mklepton sont insérés dans l'arbre de construction de Lepton. Pour la cible qui nous concerne, les fichiers sont copiés dans $LEPTON/sys/root/src/kernel/core/arch/cortexm. Ainsi à un instant donné une seule application est disponible pour une architecture particulière.
La première étape consiste à construire le binaire mklepton pour générer les fichiers de configuration liés à notre cible at91samd20. Pour cela, rendons-nous dans le répertoire $LEPTON/tools/config et tapons :
$ scons BUILD_MKLEPTON=True
Le binaire produit par cette commande se situe dans $LEPTON/tools/bin.
Nous pouvons maintenant fournir au binaire de configuration, le fichier contenant les différents paramètres et ajustements propres à l'application et à la cible choisie. Le répertoire $LEPTON/sys/user/ contient les applications/projets utilisateur complets :
- fichiers source ;
- fichier de construction ;
- fichier de configuration.
Un mécanisme de lien symbolique permet aussi d'ajouter un projet utilisateur situé n'importe où sur le système de fichiers. Le projet qui nous concerne pour faire fonctionner Lepton sur at91samd20 est $LEPTON/sys/user/tauon_sampleapp. Le répertoire etc contient le fichier de configuration qui nous intéresse et il se nomme mkconf_tauon_sampleapp_at91samd20.xml. L'utilisation de ce fichier se fait par les commandes suivantes :
$ cd $LEPTON/tools/bin
$ ./mklepton_gnu.sh -t cortexm_lepton ~/tauon/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
Le script substitue juste la variable HOME au chemin effectif. L'argument -t permet de définir le type de cible souhaitée. La sortie de cette commande (plutôt verbeuse) indique l'ensemble des paramètres définis pour une application. Elle doit se terminer par le message :
[...]
mklepton for cortexm_lepton success :)!
La compilation de Lepton peut désormais s'effectuer. L'utilitaire de construction utilisé est SCons [SCONS]. Il permet de décrire la compilation de fichiers source, la création de librairies statiques et la génération de l'image finale à l'aide du langage Python. Le répertoire prj/scons du projet tauon_sampleapp contient notamment les fichiers :
- Sconscript qui fournit les fichiers propres à l'application que l'on souhaite créer ;
- SConstruct qui est un lien symbolique vers le fichier global de construction de Lepton. C'est ce fichier qui sert de point d'entrée à SCons ;
- at91samd20_opts.py définit les options de compilation propres à chaque projet ainsi que l'activation de fonctionnalités non gérées par mklepton. On peut choisir d'inclure une pile IP ou quel « cœur » choisir entre eCos et freeRTOS.
Des classes Python propres à Lepton ont été créées pour faciliter l'insertion de librairies ou de fichiers source. Il est nécessaire d'étendre la variable PYTHONPATH. Rien de plus simple que :
$ export PYTHONPATH="$HOME/tauon/sys/root/prj/scons/common/module:$PYTHONPATH"
Pour lancer la compilation de Lepton, déplaçons-nous dans le répertoire $LEPTON/sys/user/tauon_sampleapp/prj/scons et tapons :
$ scons -c --targetfile=at91samd20_opts.py && scons -Q bin --targetfile=at91samd20_opts.py
La première commande est l'équivalent du make clean. La seconde lance la compilation et copie l'image finale dans le répertoire $LEPTON/sys/user/tauon_sampleapp/bin. L'argument --targetfile permet de sélectionner la cible et les options souhaitées pour l'image finale. Le binaire final se nomme tauon_at91samd20.elf.
Dans le monde des systèmes enfouis, il est toujours recommandé d'avoir une vue globale du micrologiciel utilisé. L'anatomie du firmware peut-être révélé par la commande binutils arm-none-eabi-size.
$ cd $LEPTON/sys/user/tauon_sampleapp/bin
$ arm-none-eabi-size --format=s -x tauon_at91samd20.elf
tauon_at91samd20.elf :
section size addr
.text 0x162d0 0x0
.ARM.exidx 0x8 0x162d0
.relocate 0x44c 0x20000000
.bss 0x7590 0x2000044c
.stack 0x404 0x200079dc
.debug_info 0x74d7e 0x0
.debug_abbrev 0xeb20 0x0
.debug_loc 0x2468a 0x0
.debug_aranges 0x2300 0x0
.debug_ranges 0x26b0 0x0
.debug_macro 0x1bf4e 0x0
.debug_line 0x30bf3 0x0
.debug_str 0x668ca 0x0
.comment 0x70 0x0
.ARM.attributes 0x32 0x0
.debug_frame 0x5c64 0x0
Total 0x183aa1
Deux espaces d'adressage sont identifiables dans le binaire :
- 0x0 qui correspond à la mémoire flash et aux sections .text et .ARM8.exidx. Les constantes qui correspondent normalement à la section .rodata sont aussi incluses dans la section .text.
- 0x20000000 est utilisée pour les données en lecture/écriture ainsi que pour la pile de démarrage et le tas. Il représente la mémoire vive.
Le fichier ~/tauon/sys/root/lib/freertos/arch/at91samd20/target_freertos.ld fournit toutes les informations nécessaires à l'organisation mémoire du firmware.
L'exploitation du fichier ELF sur la cible ne peut se faire directement. En effet, il n'existe pas de programme démarrage spécifique tel qu'un « bootloader » permettant de charger chaque section en mémoire (en tout cas pour celles se situant en mémoire vive). Il serait toujours possible d'en écrire un gérant le XIP (eXecute In Place), mais ceci consommerait un peu plus de notre précieuse flash. D'autre part, en observant la taille de l'ELF, on remarque qu'il est plus important que la taille de flash disponible.
Plusieurs solutions existent :
- transformer le fichier ELF dans un format binaire directement « flashable » sur la cible et utiliser un programme dédié pour cette écriture ;
- passer par un moniteur JTAG tel OpenOCD qui identifie les sections utiles de l'ELF et les écrit sur le support non volatile.
Dans un premier temps, nous allons utiliser la première méthode qui ne nécessite pas de configuration particulière et qui est plus simple à mettre en place pour une introduction. edbg [EDBG] est un programme utilisateur GNU/Linux qui permet d'écrire sur la flash d'une carte at91samd20 en passant par l'interface de Debug disponible par USB. En lançant la commande :
$ lsusb -v -d 03eb:2111
Quatre interfaces sont énumérées :
- 2 ports série, un virtuel (EDBG Virtual COM Port) et un modem (CDC-ACM) ;
- une de débogage (EDBG CMSIS-DAP) ;
- une passerelle (EDBG Data Gateway).
edbg va simplement utiliser la libudev pour transmettre les commandes à l'interface EDBG CMSIS-DAP et envoyer les ordres d'écriture.
Récupérons ce projet en nous plaçant dans un répertoire de notre choix :
$ cd /path/to/edbg
$ git clone https://github.com/ataradov/embedded.git
Un patch est nécessaire pour augmenter la taille de la flash supportée et la passer de 128 Kio à 256 Mio.
diff --git a/edbg/edbg.c b/edbg/edbg.c
index a12d663..52582dc 100644
--- a/edbg/edbg.c
+++ b/edbg/edbg.c
@@ -160,7 +160,11 @@ typedef struct
// The real ID must be read from the device I/O-space.
static target_t targets[] =
{
+#if 0
{ 0x0bc11477, "SAMD20J18", 0, 131072, 64, 4096, 256 },
+#else
+ { 0x0bc11477, "SAMD20J18", 0, 262144, 64, 4096, 256 },
+#endif
{ 0, "", 0, 0, 0, 0, 0 },
};
Une fois le patch appliqué, la compilation se lance à l'aide d'un classique make clean && make.
Nous disposons désormais de l'utilitaire de « flashage », mais le binaire généré n'a pas subi de transformation. Le format choisi est le BIN. Il permet de structurer les instructions et les données d'un programme de manière contiguë. Transformons le binaire généré à l'aide de la commande binutils arm-none-eabi-objcopy.
$ cd $LEPTON/sys/user/tauon_sampleapp/bin
$ arm-none-eabi-objcopy -O binary tauon_at91samd20.elf tauon_at91samd20.bin
Le fichier at91samd20.bin a une taille bien plus raisonnable : 91940 octets.
L'écriture en flash se lance à l'aide de la commande suivante et produit :
$ cd $LEPTON/sys/user/tauon_sampleapp/bin
$ /path/to/edbg/edbg -b -p -f tauon_at91samd20.bin
Debugger: ATMEL EDBG CMSIS-DAP ATML1873040200013000 01.0F.00DB (S)
Target: SAMD20J18 (0x0bc11477)
Programming........................................................................................................................................................................................................................................................................................................................................................................... done.
L'application Lepton écrite fournit une invite de commandes sur le port série déclaré en /dev/ttyACM0 sur notre distribution GNU/Linux ; la vitesse de communication est 115200. N'importe quel moniteur série fera l'affaire pour avoir accès au shell de Lepton. Pour ceux qui utilise screen, on utilise la commande suivante :
$ screen /dev/ttyACM0 115200
Après l'appui sur le bouton RESET, la sortie du moniteur série devrait afficher :
lepton (tauon) posix os
$Revision: 1.3 $ $Date: 2009-06-18 13:43:22 $
version 4.0.0.0 kernel compilation date: Jun 3 2015 - 21:03:05
Thu Jan 01 00:00:00 1970
type ctrl-x: .init script not run or any key to continue
lepton start!
lepton shell
lepton#2$
3. Hello blink
Tout système d'exploitation « moderne » fournit un mécanisme d'abstraction permettant d'accéder au matériel. La partie visible de tous les développeurs est appelée en général userspace. Elle permet de développer une application sans (trop) se soucier de sa portabilité entre 2 architectures matérielles proches, voire complètement différentes.
Dans cette partie, nous allons créer une application Lepton pilotant la LED0 disponible sur notre chère samd20. Cette application sera composée :
- d'un binaire qui ouvrira le fichier associé au pilote de périphérique de LED et enverra les ordres d'allumage et d'extinction ;
- d'un pilote de périphérique se chargeant d'initialiser correctement les entrées/sorties (en fait une seule) et gérant l'état haut et bas en fonction d'une donnée provenant de l'application utilisateur.
3.1 Programme utilisateur
La première étape consiste à ajouter le binaire à notre image finale. Éditons le fichier XML situé dans $LEPTON/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xmlet ajoutons les lignes suivantes :
--- a/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
+++ b/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
@@ -103,6 +103,11 @@
<bin name="cat" stack="1280" priority="100" timeslice="1" />
</binaries>
+<binaries src_path="bin" dest_path="bin">
+ <!-- led test binary-->
+ <bin name="tstleds" stack="1280" priority="100" timeslice="1" />
+</binaries>
+
<files>
La valeur de pile est empirique et nous ne disposons pas encore d'utilitaire permettant de la dimensionner d'une façon précise. La technique du canari pourrait être utilisée dans un premier temps pour déterminer la valeur maximale de pile atteinte pour un « processus ». Les autres valeurs correspondent à la priorité et au quantum de temps alloué à chaque thread de notre processus.
Afin que le binaire fasse partie de l'image finale générée, il faut à nouveau lancer l'exécutable de configuration mklepton.
$ cd $LEPTON/tools/bin
$ ./mklepton_gnu.sh -t cortexm_lepton ~/tauon/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
On peut remarquer l'ajout de notre binaire tstleds à la sortie du mklepton .
creat /usr/sbin/cat
mkdir /usr/bin
creat /usr/bin/tstleds
mkdir /usr/sbin
mkdir /usr/bin
Il ne reste plus qu'à écrire le code source de notre programme utilisateur et à inclure ce fichier source à l'arbre de construction. Ajoutons les lignes suivantes au fichier $LEPTON/sys/user/tauon_sampleapp/prj/scons/SConscript
--- a/sys/user/tauon_sampleapp/prj/scons/SConscript
+++ b/sys/user/tauon_sampleapp/prj/scons/SConscript
@@ -65,6 +65,10 @@ if tauon_build_envs.envs_map['DEFAULT']['TARGET_SUFFIX'].find('k60n512')>=0:
TauonSource(tauon_sampleapp_src_dir+'tstleds.c'),
])
+#simple led application for samd20
+if tauon_build_envs.envs_map['DEFAULT']['TARGET_SUFFIX'].find('at91samd20')>=0:
+ tauon_sampleapp_src_list.extend([TauonSource(tauon_sampleapp_src_dir+'tstleds_samd20.c')])
+
#add general option for LWIP
if tauon_build_envs.envs_map['DEFAULT']['LWIP']:
tauon_build_envs.envs_map['DEFAULT'].Append(CPPDEFINES='USE_LWIP')
Le lecteur attentif remarquera que le nom du processus défini ne correspond pas au nom de fichier source choisi, soit testleds_samd20.c. En effet, seul le nom de la fonction définissant le point d'entrée du processus compte réellement. Ce mécanisme est expliqué ici [OS_LEPTON].
Le code du programme utilisateur est relativement simple. Il ouvre le fichier de périphérique correspondant à la LED et obtient un descripteur de fichier. Ce dernier sera utilisé pour écrire l'état de la LED (0 éteinte - 1 allumée). La fonction ioctl aurait pu être utilisée, mais pourquoi faire compliqué quand on peut se simplifier la vie (:. Le fichier de pilote de périphérique de LED est ouvert en écriture exclusive non bloquante. Cette dernière option évite d'attendre une interruption en provenance de la LED. En effet, celle-ci est vue comme une sortie ne générant aucun événement. Ci-après le code (sans les fichiers d'en-tête) :
int tstleds_main(int argc,char* argv[]) {
int fd=-1;
unsigned char buf=0;
if((fd = open("/dev/led", O_WRONLY|O_NONBLOCK, 0)) < 0) {
printf("(EE) can't open /dev/led\r\n");
return -1;
}
printf("begin blink loop\r\n");
while(1) {
buf = !buf;
if (write(fd, &buf, 1) < 0) {
printf("(EE) write fail\r\n");
break;
}
usleep(1000000);
}
close(fd);
return 0;
}
Nous pouvons déjà vérifier la présence de notre binaire sur la cible. Après avoir flashé notre image, tapons sur le prompt :
lepton#2$ ls -l /usr/bin
ls /usr/bin
-rwxrwxrwx 8 Jun 09 2015 tstleds
total 1
En lançant le binaire, le message d'erreur apparaît.
lepton#2$ tstleds
(EE) can't open /dev/led
En effet, en consultant le répertoire des pilotes de périphériques aucun fichier led n'existe.
lepton#2$ ls -l /dev
ls /dev
drwxrwxrwx 30 Jan 01 14:32 hd
crwxrwxrwx 0 Jan 01 14:32 null
brwxrwxrwx 0 Jan 01 14:32 proc
crwxrwxrwx 0 Jan 01 14:32 fifo
brwxrwxrwx 0 Jan 01 14:32 board
crwxrwxrwx 0 Jan 01 14:32 ttys3
total 6
Il ne nous reste plus qu'à remédier à ce problème.
3.2 Pilote de périphérique
Tout comme le programme utilisateur, il est nécessaire de déclarer notre pilote de LED dans le fichier de configuration $LEPTON/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml. Cette déclaration se fait à l'aide des lignes suivantes.
--- a/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
+++ b/sys/user/tauon_sampleapp/etc/mkconf_tauon_sampleapp_at91samd20.xml
@@ -72,6 +72,8 @@
<dev name="dev_cmsis_itm0_map" use="off"/>
<!-- /dev/debug -->
<dev name="dev_os_debug_map" use="off"/>
+ <!-- /dev/led -->
+ <dev name="dev_at91samd20_xplained_pro_led_map" use="on"/>
</devices>
</target>
La présence de ce pilote dans l'image finale est révélée par la ligne coloriée en rouge.
xml target=cortexm_lepton
make devices:/home/shiby/tauon/sys/root/src/kernel/core/arch/cortexm/dev_mkconf.c ...
use devices dev_at91samd20_xplained_pro_board_map
use devices dev_at91samd20_uart_s3_map
use devices dev_at91samd20_xplained_pro_led_map
make devices:/home/shiby/tauon/sys/root/src/kernel/core/arch/cortexm/dev_mkconf.c ok!
xml end target: ok!
La définition de la structure du pilote de LED se fait dans le fichier source. 2 choix s'offrent à nous :
- intégrer le pilote dans l'arborescence générale des pilotes de la samd20 situé dans $LEPTON/sys/root/src/kernel/dev/arch/cortexm/at91samd20 ;
- laisser le pilote dans le projet utilisateur.
C'est cette dernière option que nous allons utiliser par souci de simplicité.
Commençons par déclarer le fichier source lié à notre pilote de LED dans le fichier de construction du projet tauon_sampleapp.
--- a/sys/user/tauon_sampleapp/prj/scons/SConscript
+++ b/sys/user/tauon_sampleapp/prj/scons/SConscript
@@ -65,6 +65,11 @@ if tauon_build_envs.envs_map['DEFAULT']['TARGET_SUFFIX'].find('k60n512')>=0:
TauonSource(tauon_sampleapp_src_dir+'tstleds.c'),
])
+#simple led application for samd20
+if tauon_build_envs.envs_map['DEFAULT']['TARGET_SUFFIX'].find('at91samd20')>=0:
+ tauon_sampleapp_src_list.extend([TauonSource(tauon_sampleapp_src_dir+'tstleds_samd20.c'),
+ TauonSource(tauon_sampleapp_src_dev_dir+'board_atmel_at91samd20_xplained_pro/dev_at91samd20_xplained_pro/dev_at91samd20_xplained_pro_led.c')])
+
#add general option for LWIP
if tauon_build_envs.envs_map['DEFAULT']['LWIP']:
tauon_build_envs.envs_map['DEFAULT'].Append(CPPDEFINES='USE_LWIP')
Nous pouvons désormais écrire le code du pilote de périphérique dans le fichier $LEPTON.
[headers]
/*===========================================
Global Declaration
=============================================*/
const char dev_at91samd20_xplained_pro_led_name[]="led\0";
static int dev_at91samd20_xplained_pro_led_load(void);
static int dev_at91samd20_xplained_pro_led_open(desc_t desc, int o_flag);
static int dev_at91samd20_xplained_pro_led_load(void);
static int dev_at91samd20_xplained_pro_led_open(desc_t desc, int o_flag);
static int dev_at91samd20_xplained_pro_led_close(desc_t desc);
static int dev_at91samd20_xplained_pro_led_write(desc_t desc, const char* buf,int cb);
static int dev_at91samd20_xplained_pro_led_ioctl(desc_t desc,int request,va_list ap);
dev_map_t dev_at91samd20_xplained_pro_led_map={
dev_at91samd20_xplained_pro_led_name,
S_IFCHR,
dev_at91samd20_xplained_pro_led_load,
dev_at91samd20_xplained_pro_led_open,
dev_at91samd20_xplained_pro_led_close,
__fdev_not_implemented, //isset_read
__fdev_not_implemented, //isset_write
__fdev_not_implemented, //read
dev_at91samd20_xplained_pro_led_write,
__fdev_not_implemented, //seek
__fdev_not_implemented //ioctl
};
#define LED0_GPIO PIN_PA14
/*===========================================
Implementation
=============================================*/
/*-------------------------------------------
| Name:dev_at91samd20_xplained_pro_led_load
| Description:
| Parameters:
| Return Type:
| Comments:
| See:
---------------------------------------------*/
int dev_at91samd20_xplained_pro_led_load(void){
return 0;
}
/*-------------------------------------------
| Name:dev_at91samd20_xplained_pro_led_open
| Description:
| Parameters:
| Return Type:
| Comments:
| See:
---------------------------------------------*/
int dev_at91samd20_xplained_pro_led_open(desc_t desc, int o_flag){
/*
* configure PA14 GPIO to manage yellow led
*/
struct port_config gpio_config_led;
port_get_config_defaults(&gpio_config_led);
gpio_config_led.direction = PORT_PIN_DIR_OUTPUT;
port_pin_set_config(LED0_GPIO, &gpio_config_led);
return 0;
}
/*-------------------------------------------
| Name:dev_at91samd20_xplained_pro_led_close
| Description:
| Parameters:
| Return Type:
| Comments:
| See:
---------------------------------------------*/
int dev_at91samd20_xplained_pro_led_close(desc_t desc){
return 0;
}
/*-------------------------------------------
| Name:dev_at91samd20_xplained_pro_led_write
| Description:
| Parameters:
| Return Type:
| Comments:
| See:
---------------------------------------------*/
int dev_at91samd20_xplained_pro_led_write(desc_t desc, const char* buf,int size){
if(size < 0) {
return -1;
}
/*
* if value == 0, turn off led
* else turn ON
*/
if(buf[0] == 0) {
port_pin_set_output_level(LED0_GPIO, true);
}
else {
port_pin_set_output_level(LED0_GPIO, false);
}
return size;
}
/*============================================
| End of Source : dev_at91samd20_xplained_pro_led.c
==============================================*/
Nous n'allons pas commenter tout le mécanisme de déclaration de pilote qui est très bien expliqué dans l'article suivant [LEPTON_DET]. Nous allons plutôt nous concentrer sur les 3 items surlignés en rouge :
- S_IFCHR permet de définir un périphérique en mode caractère. Il serait tout à fait possible de définir la LED comme un périphérique bloc, mais cela aurait peu de sens (peut-on se déplacer dans le flux d'une LED ?) ;
- la fonction dev_at91samd20_xplained_pro_led_open sera appelée par la fonction du programme utilisateur open. Elle va configurer l'entrée/sortie connectée à la LED en utilisant la librairie ASF fournie par ATMEL. Aucune précaution n'est prise quant à l'utilisation par plusieurs programmes utilisateur de la LED, mais il serait tout à fait possible de gérer le fait que le périphérique soit déjà ouvert et dans ce cas renvoyer une erreur ;
- dev_at91samd20_xplained_pro_led_write est exécuté par l'appel à la fonction write. Elle se charge de changer l'état de l'entrée/sortie connectée à la LED. Les entrées/sorties sont câblées en logique inversée.
On peut flasher le programme sur la carte et voir le pilote de LED dans le système de fichiers.
lepton#2$ ls -l /dev
ls /dev
drwxrwxrwx 30 Jan 01 00:00 hd
crwxrwxrwx 0 Jan 01 00:00 null
brwxrwxrwx 0 Jan 01 00:00 proc
crwxrwxrwx 0 Jan 01 00:00 fifo
brwxrwxrwx 0 Jan 01 00:00 board
crwxrwxrwx 0 Jan 01 00:00 ttys3
crwxrwxrwx 0 Jan 01 00:00 led
En exécutant à nouveau le programme utilisateur, la console affiche :
lepton#2$ tstleds
begin blink loop
et la LED clignote (:.
3.3 Mise au point (si problèmes)
Lors du développement d'un micrologiciel embarqué, il est indispensable de disposer d'un moyen de débogage efficace et fiable. La recherche de bogues à l'aide d'un affichage ou du clignotement d'une LED atteint vite ses limites.
Dans le monde du logiciel libre, le logiciel OpenOCD [OOCD] est utilisé pour permettre le débogage à l'aide d'une sonde JTAG. Sur notre carte, nous disposons d'une sonde JTAG intégrée (EDBG CMSIS-DAP) que nous pouvons piloter à l'aide de l'entrée micro-USB.
Dans l'arborescence du projet utilisateur tauon_sampleapp, nous disposons d'un fichier de configuration pour connecter le moniteur JTAG à notre carte. En disposant d'un OpenOCD supérieur à la version 0.8, on peut initier une connexion à l'aide de :
$ openocd -f ~/tauon/sys/user/tauon_sampleapp/etc/scripts/cmsis-dap_at91samd20.cfg
Open On-Chip Debugger 0.8.0-dirty (2014-08-10-11:59)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : only one transport option; autoselect 'cmsis-dap'
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: Interface Initialised (SWD)
adapter speed: 500 kHz
adapter_nsrst_delay: 100
cortex_m reset_config sysresetreq
Info : CMSIS-DAP: FW Version = 01.0F.00DB
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1
Info : DAP_SWJ Sequence (reset: 50+ '1' followed by 0)
Info : CMSIS-DAP: Interface ready
Info : clock speed 500 kHz
Info : IDCODE 0x0bc11477
Info : at91samd20j18.cpu: hardware has 4 breakpoints, 2 watchpoints
Pour lancer une session de débogage, il suffit de se déplacer dans le répertoire $LEPTON/tauon/sys/user/tauon_sampleapp/bin et de taper dans une autre console :
% arm-none-eabi-gdb -x ../etc/scripts/gdb_script_at91samd20_cmsis_dap.gdb tauon_at91samd20.elf
GNU gdb (GNU Tools for ARM Embedded Processors) 7.6.0.20140529-cvs
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/shiby/dev/tauon/github/lepton-distrib/sys/user/tauon_sampleapp/bin/tauon_at91samd20.elf...done.
0x00000000 in exception_table ()
force hard breakpoints
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x81000000 pc: 0x000000d4 msp: 0x20007dd8
===== arm v7m registers
(0) r0 (/32): 0x0001663C
(1) r1 (/32): 0x200033D8
(2) r2 (/32): 0x200079DC
(3) r3 (/32): 0x200033D0
(4) r4 (/32): 0x2000044C
(5) r5 (/32): 0x20000474
(6) r6 (/32): 0x00000000
(7) r7 (/32): 0xA5A5A5A5
(8) r8 (/32): 0xA5A5A5A5
(9) r9 (/32): 0xA5A5A5A5
(10) r10 (/32): 0xA5A5A5A5
(11) r11 (/32): 0xA5A5A5A5
(12) r12 (/32): 0xA5A5A5A5
(13) sp (/32): 0x20007DD8
(14) lr (/32): 0x000015B7
(15) pc (/32): 0x000000D4
(16) xPSR (/32): 0x81000000
(17) msp (/32): 0x20007DD8
(18) psp (/32): 0x20001BC0
(19) primask (/1): 0x00
(20) basepri (/8): 0x00
(21) faultmask (/1): 0x00
(22) control (/2): 0x00
===== Cortex-M DWT registers
(23) dwt_ctrl (/32)
(24) dwt_cyccnt (/32)
(25) dwt_0_comp (/32)
(26) dwt_0_mask (/4)
(27) dwt_0_function (/32)
(28) dwt_1_comp (/32)
(29) dwt_1_mask (/4)
(30) dwt_1_function (/32)
Si nous souhaitons placer un point d'arrêt sur le point d'entrée de notre programme utilisateur tstleds, rien de plus simple que :
(gdb) b tstleds_main
(gdb) c
Lors de l'exécution du programme, on obtient :
Breakpoint 3, tstleds_main (argc=1, argv=0x20002838 <ucHeap+8808>)
at /home/shiby/tauon/sys/user/tauon_sampleapp/src/tstleds_samd20.c:63
63 int fd=-1;
Conclusion (la suite)
Cet article a donné les éléments principaux permettant de créer une application à faible empreinte mémoire sur un processeur cortex-M0+. Les briques logicielles utilisées se reposent sur Lepton qui fournit des mécanismes présents sur des systèmes d'exploitation POSIX comme la compartimentation logique utilisateur/noyau.
Plusieurs autres cartes utilisant les processeurs cortex-M sont en cours de portage notamment celles du fondeur STMicroelectronics et sa gamme de STM32.
Au niveau logiciel, plusieurs axes de développement se dégagent :
- l'utilisation de la protection mémoire fournie par la MPU ;
- la mise à disposition de primitives cryptographiques ;
- la création d'un cadre logiciel gérant la consommation énergétique ;
- l'intégration de piles protocolaires comme MQTT.
J'espère en tout cas que cette présentation vous a ouvert de nouveaux horizons de programmation et que la simplicité d'utilisation et de déploiement de Lepton pourra être un atout pour vos prochains projets sur cibles enfouies.
Remerciements
Vifs remerciements aux personnes de Chauvin-Arnoux, Enerdis et Metrix, simple lecteur
Bibliographie
[IEA] More data less energy
[OS_LEPTON] Jean-Jacques Pitrolle, Lepton, un système d'exploitation temps réel pour les systèmes enfouis, 2013, http://connect.ed-diamond.com/Open-Silicium/OS-007/Lepton-un-systeme-d-exploitation-temps-reel-pour-les-systemes-enfouis2
[CORTEXM] http://www.arm.com/products/processors/cortex-m/
[CA6117] http://www.chauvin-arnoux.com/en/produit/
[MT5022] http://www.handscope.chauvin-arnoux.com/fr/accueil.aspx
[O10EE] http://o10ee.com/
[MPL] https://www.mozilla.org/MPL/1.1/
[LWIP] https://savannah.nongnu.org/projects/lwip/
[EMBOS] https://www.segger.com/embos.html
[ECOS] http://ecos.sourceware.org/
[FREERTOS] http://www.freertos.org/
[CMSIS] http://www.arm.com/products/processors/cortex-m/
[ASF] http://asf.atmel.com/docs/latest/
[LAUNCHPAD] https://launchpad.net/gcc-arm-embedded
[SCONS] http://www.scons.org/
[EDBG] https://github.com/ataradov/embedded.git
[LEPTON_DET] Philippe Le Boulanger, Lepton une approche détaillée, 2014
[OOCD] http://openocd.sourceforge.net/
[ARDUINO_Z] http://www.amazon.com/Arduino-Zero-Pro-Cortex-M0-SAMD21/dp/B00US73ZIO