GPIO, Pandaboard et temps réel – 1 – Sorties depuis l’espace utilisateur

Publié par cpb
Mai 09 2012

GPIO Pandaboard et temps-réel
Les GPIO (General Purpose Input Output) sont des broches du microprocesseur permettant de réaliser des opérations d’entrée-sortie électriques programmables. Chaque broche peut être affectée en entrée ou en sortie par programmation et utilisée aisément pour communiquer avec des périphériques externes.

La Pandaboard utilise un processeur Texas OMAP4430, construit autour d’un coeur Cortex A9. La documentation techique de l’OMAP4430 (plus de 5500 pages…) précise que le processeur dispose de six modules GPIO, chacun offrant 32 lignes d’entrées-sorties. Sur les 192 ports GPIO théoriquement présents, une bonne partie est déjà employée pour la carte elle-même mais quelques uns sont accessibles à l’utilisateur.

Connecteurs d’extension

Nous trouvons sur la Pandaboard deux connecteurs utlisables pour des expérimentations et extensions futures. Numérotés J3 et J6, ils sont également repérés par les libellés “Expansion Connector A” et “Expansion Connector B” comme on peut le voir sur la photo ci-contre.Connecteurs d'extension de la Pandaboard.

Le détail des broches disponibles est fourni dans le manuel de référence de la Pandaboard (pages 43 et 44), certaines d’entre elles ayant une signification pour le système.

 

Par exemple, dans le tableau ci-dessous nous pouvons remarquer que la broche numéro 10 est associée à deux fonctionnalités : un signal Chip Select pour une communication avec une autre carte ou une entrée sortie GPIO numéro 138.

GPIO du connecteur A

Certaines broches ont des affectations figées car elles sont reliées à des composants sur la carte, mais d’autres peuvent être utilisées pour réaliser des entrées sorties personnalisées.

Nous remarquons la présence sur la broche numéro 1 d’un signal d’alimentation +1.8 V que nous pourrons utiliser pour l’envoyer sur les entrées de notre choix (dans le prochain article) et de la masse du signal sur les broches 27 et 28.

Attention, les opérations sur ce connecteur sont très risquées car elles se répercutent directement sur les broches du processeur, sans protection. Je décline donc toute responsabilité si une manipualtion décrite ici sonne le glas de votre Pandaboard. Pour vous convaincre encore, sachez que j’ai grillé une carte Pandaboard (enfin, un processeur OMAP4 mais c’est le principal) simplement en confondant la broche 2 (alimentation +5V) et la broche 1 (alimentation +1.8V) pour envoyer un signal sur une entrée GPIO. Donc : prudence et concentration sont de rigueur !

 Nous allons donc souder sur le connecteur quelques fils afin d’accéder facilement aux broches 1, 10 et 28.

Broches soudées sur connecteur d'extensionLa broche 1 de chaque connecteur (en haut à gauche lorsqu’on le regarde de face) est repérée par un carré, la 2 se trouve en dessous, la 3 à droite du 1, la 4 en dessous de la 3 et ainsi de suite.

Naturellement, la soudure se fait plutôt sur la face opposée, où les numérotations sont inversées.

Soudure au verso du connecteur d'extensionPour cet article, nous n’utiliserons pas l’alimentation (broche 1), mais j’ai profité de l’occasion pour la souder en même temps les deux autres.

Accès aux GPIO depuis l’espace utilisateur avec le shell

Démarrons notre carte en utilisant un système fait maison, comme décrit dans les articles numéro 1, numéro 2, numéro 3, numéro 4 et numéro 5 d’une précédente série. Puis connectons depuis un PC distant en utilisant le protocole SSH.

[~]$ ssh root@192.168.5.152
root@192.168.5.152's password:
[Panda]#

Le prompt du shell sur la Pandaboard est préfixé par “[Panda]” pour éviter les confusions avec le système hôte.

Vérifions pour commencer la tension sur la broche 10 avec un simple voltmètre.

GPIO en sortie à 0V

La tension est nulle, mais la broche est par défaut affectée en entrée (avec une haute impédance). Nous allons modifier cela.

[Panda]# cd /sys/class/gpio/
[Panda]# ls
export       gpio62       gpiochip128  gpiochip32   gpiochip96
gpio1        gpiochip0    gpiochip160  gpiochip64   unexport
[Panda]#

Par défaut, le port GPIO 138 n’est pas accessible depuis le pseudo système de fichier /sys/, mais nous pouvons le réclamer ainsi.

[Panda]# echo 138 > export 
[Panda]# ls
export       gpio138      gpiochip0    gpiochip160  gpiochip64   unexport
gpio1        gpio62       gpiochip128  gpiochip32   gpiochip96
[Panda]#

Le port 138 est maintenant accessible vérifions son sens de fonctionnement.

[Panda]# cd gpio138/
[Panda]# cat direction
in
[Panda]#

Il est affecté en entrée par sécurité au boot. Basculons-le en sortie.

[Panda]# echo out > direction 
[Panda]# cat direction 
out
[Panda]# cat value
0
[Panda]#

La valeur inscrite sur le port est 0, ce qui ne nous surprend pas puisque la tension sur la borne est nulle. Modifions la valeur.

[Panda]# echo 1 > value 
[Panda]#

Le voltmètre affiche maintenant 1,8 V (la tension de référence pour les GPIO, à ne pas dépasser pour les entrées !).

GPIO en sortie à 1,8 V.

Écrivons à nouveau sur le port.

[Panda]# echo 0 > value 
[Panda]#

Le voltmètre affiche à nouveau 0.00. Libérons le port GPIO pour terminer cette expérience.

[Panda]# cd /sys/class/gpio/
[Panda]# echo 138 > unexport 
[Panda]#

Application des GPIO avec un script shell

Nous savons écrire une valeur sur une broche de sortie depuis la ligne de commande, automatisons cela dans un petit script shell qui va faire osciller la broche 10.

/usr/local/bin/oscillateur-gpio.sh :
#! /bin/sh

GPIO=138  # Broche 10 du port "Expansion A"

cd /sys/class/gpio

echo ${GPIO} > export

cd gpio${GPIO}

echo out > direction

while true
do
	echo 1 > value
	usleep 1000
	echo 0 > value
	usleep 1000
done

Le script réalise de petites pause de 1000 microsecondes, soit une milliseconde après chaque changement d’état de la broche.

Connectons un oscilloscope à la place du voltmètre pour voir évoluer le signal.

Oscilloscope sur port de sortie GPIONous apercevons sur l’écran un signal périodique qui alterne entre 0 et 1,8V. Toutefois, en regardant de plus près, les durées que nous avions indiquées dans le script ne sont pas respectées.

Oscillateur GPIO par script shellLe signal change de niveau toutes les 3,3 millisecondes environ et non pas à chaque milliseconde. Il s’agit de la granularité du usleep qui pose problème.Nous pouvons améliorer ceci en utilisant un petit programme en C qui réalise le même travail mais s’appuie directement sur la libC en passant outre les latences dûes au shell et aux utilitaires système (issus de la Busybox en l’occurrence).

oscillateur-gpio-user.c :
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static volatile int exit_loop = 0;

void handler_signal(int unused)
{
    exit_loop = 1;
}

int main(int argc, char * argv[])
{
    int value = 1;
    char path[256];
    int gpio = 138;
    FILE * fp;

    // En argument on peut preciser un GPIO different de 138
    if ((argc > 2) || ((argc == 2) && (sscanf(argv[1], "%d", & gpio) != 1))) {
        fprintf(stderr, "usage: %s [gpio]n", argv[0]);
        exit(EXIT_FAILURE);
    }

    // Exporter le GPIO dans le systeme de fichiers
    snprintf(path, 256, "/sys/class/gpio/export");
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "%dn", gpio);
    fclose(fp);

    // Intercepter Controle-C pour finir proprement
    signal(SIGINT, handler_signal);

    // Basculer le GPIO en sortie
    snprintf(path, 256, "/sys/class/gpio/gpio%d/direction", gpio);
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "outn");
    fclose(fp);

    // Ecrire alternativement la valeur du GPIO
    snprintf(path, 256, "/sys/class/gpio/gpio%d/value", gpio);
    if ((fp = fopen(path, "w")) == NULL) {
        perror(path);
        exit(EXIT_FAILURE);
    }
    while (! exit_loop) {
        fprintf(fp, "%dn", value);
        fflush(fp);
        value = 1 - value;
        usleep(1000);
    }

    // De-exporter le GPIO du systeme de fichiers
    if ((fp = fopen("/sys/class/gpio/unexport", "w")) == NULL) {
        perror("/sys/class/gpio/unexport");
        exit(EXIT_FAILURE);
    }
    fprintf(fp, "%dn", gpio);
    fclose(fp);
    return EXIT_SUCCESS;
}

Après compilation (en utilisant un cross-compiler comme indiqué dans cet article), nous transférons le code sur la cible.

$ ~/cross-panda/usr/bin/arm-linux-gcc -Wall -o oscillateur-gpio-user oscillateur-gpio-user.c
$ scp oscillateur-gpio-user root@192.168.5.152:/root/
root@192.168.5.152's password:
oscillateur-gpio-user                                               100% 6270     6.1KB/s   00:00
$

Sur la cible nous lançons le programme (et nous pouvons l’arrêter avec Contrôle-C).

[Panda]# /root/oscillateur-gpio-user 
    (Contrôle-C)
[Panda]#

Le signal est plus précis, nous pourrions calibrer la durée des sommeils pour avoir exactement une milliseconde, ou utiliser un timer logiciel comme ceux founis par setitimer().

Oscillateur GPIO par programme C - Temps partagé

Toutefois, sur l’écran de l’oscilloscope, le signal n’est pas très stable, ses fronts montants et descendants tremblent en permanence, signe classique d’un comportement lié à l’ordonnancement en temps partagé.

Pour améliorer la précision nous pouvons lancer le programme en ordonnancement temps réel souple avec chrt.

[Panda]# chrt -f99 /root/oscillateur-gpio-user
    (Contrôle-C)
[Panda]#

Oscillateur GPIO par programme C - Temps réel souple

Le signal est beaucoup plus stable, mais il y a encore des petits “sauts” de temps à autres. Avec un oscilloscope à mémoire, nous pourrions voir apparaître parfois des fronts décalés d’une durée assez significative.

Conclusion

Afin d’améliorer la précision d’un signal, il est nécessaire d’adopter une approche temps réel. Ceci peut se réaliser de différentes manières. Dans le prochain article nous verrons comment gérer les GPIO depuis l’espace kernel en écrivant un module pour le noyau Linux standard, puis nous nous rapprocherons du temps réel strict en pilotant les GPIO depuis un module kernel de Xenomai, avec l’API RTDM.

URL de trackback pour cette page