Signes de vie d’un système embarqué

Publié par cpb
Juil 15 2011

Suite à une question posée par Chriss en commentaire d’un précédent article, j’ai eu envie de revenir rapidement sur un élément essentiel pour la mise au point d’un système embarqué : le signe de vie. Il s’agit simplement de faire effectuer par le système une tâche régulière, avec un effet facilement observable par l’utilisateur, afin de s’assurer du bon fonctionnement global du dispositif. Ce signe de vie peut prendre diverses formes : signal électrique visible à l’oscilloscope sur une broche de test de la carte, trame vide émise régulièrement sur un port réseau, compteur incrémenté périodiquement dans une zone de mémoire partagée, etc. Le signe de vie le plus simple à mettre en oeuvre sur un système embarqué est le clignotement d’une LED.

Premier exemple sur carte Pandaboard

La carte Pandaboard, qui a déjà fait l’objet de plusieurs articles, dispose de trois LEDs. Les deux premières se trouvent à côté du support pour mémoire MicroSD, la troisième est placée à côté du connecteur d’alimentation. Cette dernière n’est pas contrôlable par l’utilisateur, elle indique une surtension d’alimentation. Nous allons donc nous intéresser uniquement aux LEDs 1 et 2.

LEDs sur carte Pandaboard

La première méthode pour accéder à ces LEDs, et en conserver un contrôle complet, va consister à les piloter en programmant les broches GPIO (General Purpose Input Output) du processeur auxquelles les LEDs sont reliées. Nous démarrons la carte en utilisant un kernel Linux 3.0-rc7 compilé (avec ce fichier .config) sans support particulier pour les LEDs. Nous avons simplement activé

  • le support GPIO (dans le menu “Device Drivers”),
  • l’interface “/sys/class/gpio/” (dans le menu “GPIO Support” activé précédemment),
  • et le driver pour Texas Instruments TWL 4030 et ultérieurs.

Connectons-nous sur la carte Pandaboard, et examinons le contenu du répertoire /sys/class/gpio/.

[~]$ telnet 192.168.3.152
Trying 192.168.3.152...
Connected to 192.168.3.152.
Escape character is '^]'.
(none) login: root
Password:
BusyBox v1.18.4 (2011-06-22 12:27:46 CEST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
(panda)[root]$ cd /sys/class/gpio/
(panda)[gpio]$ ls
export       gpio62       gpiochip128  gpiochip32   gpiochip96
gpio1        gpiochip0    gpiochip160  gpiochip64   unexport
(panda)[gpio]$

Nous voyons que dans ce répertoire le noyau nous propose l’accès à certaines broches GPIO (1 et 62). Toutefois ce ne sont pas celles qui correspondent aux LEDs. En consultant les spécifications de notre carte (pages 48-49), nous voyons que les LEDs 1 et 2 sont connectées aux GPIO 7 et 8. Demandons au noyau d’exporter (c’est-à-dire de nous rendre accessible) la broche GPIO 8. Nous remarquons alors que le contenu du répertoire /sys/class/gpio/ est modifié.

(panda)[gpio]$ echo 8 > export 
(panda)[gpio]$ ls
export       gpio62       gpiochip0    gpiochip160  gpiochip64   unexport
gpio1        gpio8        gpiochip128  gpiochip32   gpiochip96
(panda)[gpio]$

L’entrée gpio8 a fait son apparition, il s’agit d’un sous-répertoire contenant des fichiers virtuels, dont les contenus représentent des paramètres internes du noyau. Voyons donc la direction par défaut de la broche GPIO 8.

(panda)[gpio]$ ls gpio8/
active_low  edge        subsystem   value
direction   power       uevent
(panda)[gpio]$ cat gpio8/direction 
in
(panda)[gpio]$

Le processeur initialise par défaut ses broches GPIO en entrée, pour des raisons de sécurité. Elles présentent alors un état de haute impédance, et les manipulations externes (mises à la masse ou au +5V, court-circuit, etc.) n’ont pas d’influence. Toutefois, pour piloter la LED 2, il faut basculer la broche en sortie.

(panda)[gpio]$ echo out > gpio8/direction 
(panda)[gpio]$

Nous pouvons alors allumer et étendre la LED 2 ainsi :

(panda)[gpio]$ echo 1 > gpio8/value
(panda)[gpio]$ echo 0 > gpio8/value
(panda)[gpio]$

Il devient alors facile de programmer un petit signe de vie en utilisant un script shell simple :

/usr/bin/heartbeat.sh
#! /bin/sh

echo 7 > /sys/class/gpio/export
echo 8 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio7/direction
echo out > /sys/class/gpio/gpio8/direction

while true
do
        echo 1 > /sys/class/gpio/gpio7/value
        usleep 150000
        echo 0 > /sys/class/gpio/gpio7/value
        usleep 100000
        echo 1 > /sys/class/gpio/gpio8/value
        usleep 150000
        echo 0 > /sys/class/gpio/gpio8/value
        usleep 100000
        usleep 500000
done

Lancé en arrière-plan dans le script de démarrage /etc/init.d/rcS ainsi

  /usr/bin/heartbeat.sh &

ce petit script nous permet de contrôler visuellement que le système est opérationnel, et que l’ordonnancement des processus fonctionne normalement (sans qu’une tâche temps-réel ne consomme tout le CPU disponible par exemple).

Deuxième exemple, sur carte IGEP

Sur la carte IGEP v2, que nous avons également abordée précédement, il existe deux LEDs bicolores. La couleur verte de l’une d’entre elles est programmable par l’I2C. Les trois autres couleurs sont accessibles via les GPIO 26, 27, et 28. À noter : lorsque les couleurs rouge et verte de la même LED sont allumées simultanément, la couleur visible est jaune orangée. On peut donc obtenir un signe de vie visuellement plus riche en jouant sur les changements de couleur.

Voici un petit programme C qui assure un scintillement permanent des LEDs et remplit un rôle similaire au premier script ci-dessus.

sdv-igepv2.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
	int i;
	int compteur = 0;
	int fd[3];
	char buffer[80];

	fd[0] = open("/sys/class/gpio/gpio26/value", O_WRONLY);
	fd[1] = open("/sys/class/gpio/gpio27/value", O_WRONLY);
	fd[2] = open("/sys/class/gpio/gpio28/value", O_WRONLY);
	if ((fd[0] < 0) || (fd[1] < 0) || (fd[2] < 0)) {
		perror("open");
		exit(EXIT_FAILURE);
	}
	while(1) {
		for (i = 0; i < 3; i ++) { 			sprintf(buffer, "%d", (compteur >> i) & 1);
			write(fd[i], buffer, 2);
		}
		compteur ++;
		compteur %= 8;
		usleep(100000);
	}
}

Troisième exemple, avec les triggers du noyau Linux

Le kernel Linux nous offre une interface pour accéder aux LEDs de notre système, et propose même des triggers, c’est-à-dire des comportements prédéfinis pour leur clignotement. Pour que le noyau puisse accéder aux LEDs et nous offrir un moyen de les contrôler, il est nécessaires de sélectionner les options suivantes lors de sa compilation (cliquez sur l’aperçu ci-dessous). Dans le sous-menu “LED Support” du menu “Device Drivers”.

make ARCH=arm menuconfig

Vous pouvez utiliser directement ce fichier de configuration .config pour Linux 3.0-rc7 sur Pandaboard.

Dans cette configuration, nous ajoutons le support pour les LEDs pilotées par un GPIO, ainsi que la configuration spécifique à la plateforme. Cette configuration se trouve dans le fichier arch/arm/mach-omap2/board-omap4panda.c des sources du kernel.

[...]
static struct gpio_led gpio_leds[] = {
        {
                .name                   = "pandaboard::status1",
                .default_trigger        = "heartbeat",
                .gpio                   = 7,
        },
        {
                .name                   = "pandaboard::status2",
                .default_trigger        = "mmc0",
                .gpio                   = 8,
        },
};

static struct gpio_led_platform_data gpio_led_info = {
        .leds           = gpio_leds,
        .num_leds       = ARRAY_SIZE(gpio_leds),
};
[...]

On y retrouve bien les numéros de GPIO que nous avons utilisés plus tôt. Après compilation du noyau et reboot de la carte, nous trouvons une nouvelle interface dans /sys.

(panda)[root]$ cd /sys/class/
(panda)[class]$ ls
backlight     display       graphics      i2c-dev       leds          misc          net           rtc           scsi_host     ubi           vtconsole
bdi           firmware      hwmon         input         mdio_bus      mmc_host      power_supply  scsi_device   spi_master    usb_device
block         gpio          i2c-adapter   lcd           mem           mtd           regulator     scsi_disk     tty           vc
(panda)[class]$

Allons voir le contenu de cette classe LEDs.

(panda)[class]$ cd leds/
(panda)[leds]$ ls
pandaboard::status1  pandaboard::status2
(panda)[leds]$ ls pandaboard::status1/
brightness      device          max_brightness  power           subsystem       trigger         uevent
(panda)[leds]$

Nous y trouvons deux sous-répertoires, un pour chaque LED, avec les noms observés plus haut dans le code source du noyau. Le pseudo-fichier trigger indique l’évenement qui déclenche le changement d’état de la LED.

(panda)[leds]$ cat pandaboard::status1/trigger
none nand-disk mmc0 mmc1 timer [heartbeat] gpio default-on
(panda)[leds]$

Par défaut il s’agit du trigger heartbeat, qui simule un battement de coeur, dont la fréquence augmente en fonction de la charge du système, mais on peut en choisir d’autres :

  • none nous donne l’accès direct à la LED depuis l’entrée brightness du sous-répertoire,
  • nand-disk fait scintiller la LED lors de l’accès à la mémoire Flash interne (inutilisée sur mon système),
  • mmc0 et mmc1 signalent les accès aux partitions de la carte MicroSD, rappelant les LEDs d’accès aux disques durs,
  • timer allume et éteint la LED périodiquement : elle reste allumée pendant la durée en millisecondes indiquée dans delay_on puis s’éteint pendant delay_off millisecondes,
  • gpio a un comportement proche de celui de none, l’entrée brightness permettant d’allumer ou d’éteindre à volonté la LED.
  • default-on: allumer la LED dès le boot.

Voici par exemple la configuration pour une LED clignotant à 10 Hz, et dont la durée d’allumage est de 10 ms à chaque cycle.

(panda)[leds]$ echo timer > pandaboard::status2/trigger
(panda)[leds]$ echo 10 > pandaboard::status2/delay_on
(panda)[leds]$ echo 90 > pandaboard::status2/delay_off
(panda)[leds]$

Conclusion

Nous voyons que les cartes embarquées sur lesquelles on fait couramment fonctionner Linux nous permettent aisément de disposer d’un petit signe de vie en employant des LEDs. Bien sûr ces LEDs peuvent servir à d’autres utilisations (indication de l’état d’un serveur, de la disponibilité des ressources, etc.). L’avantage d’un signe de vie programmé dans l’espace utilisateur sur celui intégré dans le kernel, est de nous indiquer réellement si le système est utilisable pour les applications, et pas seulement le bon fonctionnement de l’espace noyau. J’en emploie également sur les systèmes embarquant des applications temps-réel, afin de vérifier facilement si CPU est surchargé ou s’il reste du temps processeur pour les tâches temps-partagé.

URL de trackback pour cette page