GPIO, Pandaboard et temps réel – 3 – Lecture sur des entrées

Publié par cpb
Mai 21 2012

GPIO, Pandaboard et temps réel - 3Dans les articles précédents, nous avons vu comment accéder aux sorties GPIO de la Pandaboard depuis l’espace utilisateur (premier article) et depuis l’espace noyau (second article) Linux et Xenomai. Nous allons maintenant nous intéresser à la lecture de l’état des entrées GPIO.

Lecture depuis l’espace utilisateur

La plupart des broches du port d’extension de la Pandaboard sont affectées par défaut en entrée. Ceci pour des raisons de sécurité : elles se trouvent ainsi dans un état dit de « haute impédance » dans lequel on peut sans rique leur appliquer une tension.

Si au contraire une broche était configurée par défaut en sortie, elle aurait une tension définie et l’application par erreur de la tension opposée déclencherait un court-circuit fatal au micro-processeur.

Notez que l’application d’une tension sur une broche GPIO de la Pandaboard est par définition risquée. En confondant la broche 1 (+1.8 V) du connectecteur d’extension et sa broche 2 (+5V), j’ai appliqué par erreur une tension de +5V sur une entrée ne tolérant que +1.8 V, ce qui s’est soldé par la destruction immédiate du processeur OMAP. Soyez donc très prudents dans vos essais ; je décline toute responsabilité s’il arrive malheur à vos cartes suite aux expériences décrites ici.

 Nous allons utiliser la même broche (numéro 10 du connecteur A) que celle employée lors des articles précédents. Naturellement nous pourrions en choisir une autre en nous reportant au manuel de référence de la Pandaboard. C’est ce que nous ferons dans le prochain article car nous aurons besoin simultanément d’une entrée et d’une sortie.

Connecteur d'extension A de la Pandaboard

Avant d’appliquer une tension sur cette broche nous allons vérifier sa direction. Comme nous l’avons vu dans le premier article, nous allons accéder à notre GIO depuis l’interface /sys.

$ ssh root@192.168.5.152
root@192.168.5.152's password:
[Panda]# cd /sys/class/gpio/
[Panda]# ls
export       gpio62       gpiochip128  gpiochip32   gpiochip96
gpio1        gpiochip0    gpiochip160  gpiochip64   unexport
[Panda]# echo 138 > export 
[Panda]# ls
export       gpio138      gpiochip0    gpiochip160  gpiochip64   unexport
gpio1        gpio62       gpiochip128  gpiochip32   gpiochip96
[Panda]# cat gpio138/direction
in
[Panda]#

La broche est bien configurée en entrée. Si elle était dirigée en sortie (après l’une des expériences des précédents articles par exemple), il suffirait d’un « echo in > gpio138/direction » pour rectifier son sens. Lisons sa valeur :

[Panda]# cat gpio138/value
0
[Panda]#

Lorsqu’aucune tension n’est appliquée sur cette broche d’entrée, celle-ci se trouve automatiquement ramenée à la masse par une résistance de rappel interne du processeur OMAP. Le sens du rappel (pull-up si la broche est implicitement raccordée à la tension d’alimentation ou pull-down si elle est ramenée à la masse) est initialisé par le bootloader U-boot. Voici un extrait du fichier u-boot-2011.06/board/ti/panda/panda.h dans lequel nous voyons que la broche 10 du connecteur J3 (dont la fonction principale est SPI1_CS1) est tirée par un pull-down (PTD) alors que la broche 4 (GPIO_140) par exemple dispose d’un pull-up (PTU).

#ifndef _PANDA_H_
#define _PANDA_H_

#include <asm/io.h>
#include <asm/arch/mux_omap4.h>

const struct pad_conf_entry core_padconf_array[] = {
  [...]
  {MCSPI1_CS0, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M0)}, /* mcspi1_cs0  */
  {MCSPI1_CS1, (PTD | IEN | OFF_EN | OFF_PD | OFF_IN | M3)}, /* mcspi1_cs1  */
  {MCSPI1_CS2, (PTU | OFF_EN | OFF_OUT_PTU | M3)},           /* gpio_139    */
  {MCSPI1_CS3, (PTU | IEN | M3)},                            /* gpio_140    */
  {UART3_CTS_RCTX, (PTU | IEN | M0)},                        /* uart3_tx    */
  [...]
};
[...]

Le noyau Linux peut intervenir (au moment de son initialisation) sur le paramétrage des GPIO mais il considère généralement que ce travail a été effectué par le bootloader et les laisse dans leurs états précédents. Vérifions si le GPIO 140 est bien tiré vers la tension d’alimentation.

[Panda]# echo 140 > export
[Panda]# cat gpio140/direction
in
[Panda]# cat gpio140/value
1
[Panda]# echo 140 > unexport
[Panda]#

Appliquons à présent une valeur sur la broche 10. Pour cela je la mets en contact avec la broche numéro 1 du même port (+1.8V). En faisant attention à ne pas confondre avec la broche 2 pour ne pas recommencer la même mésaventure ! Pour s’en assurer, nous pouvons vérifier si la tension entre notre broche d’alimentation (2) et celle de masse (28) est bien égale à 1,8V et surtout pas à 5V.

Vérification de la tension d'alimentation GPIOMise en contact de la broche 10 et de la broche 1…

Contact entre broches 1 et 10 de la Pandaboard

Nous pouvons mesurer à présent la valeur d’entrée du GPIO.

[Panda]# cat gpio138/value
1
[Panda]#

Si nous déconnectons les deux broches et relions la broche 10 avec la masse (28), nous retrouvons la valeur précédente.

[Panda]# cat gpio138/value
0
[Panda]#

Notons que nous pouvons inverser le sens logique affectée à la tension électrique. La broche 10 est toujours reliée à la masse.

[Panda]# cat gpio138/active_low
0
[Panda]#

L’entrée n’est pas considérée comme active (1) au niveau bas (0V). Inversons ceci.

[Panda]# echo 1 > gpio138/active_low 
[Panda]# cat gpio138/active_low 
1
[Panda]# cat gpio138/value 
1
[Panda]#

Maintenant l’entrée est active si une tension nulle y est appliquée et inactive sinon. Connectons la broche 10 au +1.8V et vérifions.

[Panda]# cat gpio138/value
0
[Panda]#

Je rétablis l’état initial pour la suite.

[Panda]# echo 0 > gpio138/active_low 
[Panda]# cat gpio138/value 
1
[Panda]#

Nous savons comment lire l’état d’une entrée GPIO depuis l’espace utilisateur. Notre manipulation faite depuis la ligne de commande pourrait très bien être réalisée à l’identique depuis un script ou une application en C par exemple.

Lecture depuis le kernel

Il est bien entendu possible d’utiliser l’API offerte par le noyau Linux pour lire l’état d’une entrée GPIO depuis l’espace noyau. Voici un petit exemple provenant des exemples de mon cours « Écriture de drivers pour Linux » que j’ai simplifié pour ne conserver que le sens « lecture ».

entree-gpio.c :
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/version.h>
#include <asm/uaccess.h>

  static int gpio_in = 138; // Par defaut : broche 10
  module_param(gpio_in, int, 0644);

  static dev_t          exemple_dev;
  static struct cdev    exemple_cdev;
  static struct class * exemple_class = NULL;

  static int exemple_read(struct file * filp, char * buffer, size_t length, loff_t * offset);

  static struct file_operations fops_exemple = {
    .owner   =  THIS_MODULE,
    .read    =  exemple_read,
  };

static int __init exemple_init (void)
{
  int err;

  if ((err = gpio_request(gpio_in, THIS_MODULE->name)) != 0) {
    return err;
  }

  if ((err = gpio_direction_input(gpio_in)) != 0) {
    gpio_free(gpio_in);
    return err;
  }

  if ((err = alloc_chrdev_region(& exemple_dev, 0, 1, THIS_MODULE->name)) < 0) {
    gpio_free(gpio_in);
    return err;
  }

  exemple_class = class_create(THIS_MODULE, "classe_exemple");
  if (IS_ERR(exemple_class)) {
    unregister_chrdev_region(exemple_dev, 1);
    gpio_free(gpio_in);
    return -EINVAL;
  }

  device_create(exemple_class, NULL, exemple_dev, NULL, THIS_MODULE->name);

  cdev_init(& exemple_cdev, & fops_exemple);

  if ((err = cdev_add(& exemple_cdev, exemple_dev, 1)) != 0) {
    device_destroy(exemple_class, exemple_dev);
    class_destroy(exemple_class);
    unregister_chrdev_region(exemple_dev, 1);
    gpio_free(gpio_in);
    return err;
  }
  return 0;
}

static void __exit exemple_exit (void)
{
  cdev_del(& exemple_cdev);
  device_destroy(exemple_class, exemple_dev);
  class_destroy(exemple_class);
  unregister_chrdev_region(exemple_dev, 1);
  gpio_free(gpio_in);
}

static int exemple_read(struct file * filp, char * buffer, size_t length, loff_t * offset)
{
  char chaine[32];
  int lg;
  snprintf(chaine, 32, "%dn", gpio_get_value(gpio_in));
  lg = strlen(chaine) + 1;
  if (lg > length)
    return -ENOMEM;
  if (copy_to_user(buffer, chaine, lg) != 0)
    return -EFAULT;
  return lg;
}

module_init(exemple_init);
module_exit(exemple_exit);
MODULE_LICENSE("GPL");

La compilation s’effectue comme dans l’article précédent avec une invocation de make qui précise la chaîne de compilation et l’emplacement des sources du noyau, aucune valeur par défaut n’étant précisée dans le Makefile.

$ make ARCH=arm CROSS_COMPILE=~/cross-panda/usr/bin/arm-linux- KERNEL_DIR=~/Projets/Panda/linux-2.6.38.8 modules
make -C /home/cpb/Projets/Panda/linux-2.6.38.8/ SUBDIRS=/home/cpb/Documents/Articles/Blog/article-2012-05-21  modules
[...]
make[1]: quittant le répertoire « /home/cpb/Projets/Panda/linux-2.6.38.8 »
$ scp entree-gpio.ko root@192.168.3.152:/root/
root@192.168.3.152's password:
entree-gpio.ko                                                100%  102KB 101.7KB/s   00:00
$

Connectons-nous sur la carte Pandaboard et consultons l’état du driver par l’intermédiaire du fichier spécial qui apparaît dans /dev lors du chargement du module.

$ ssh root@192.168.3.152
root@192.168.3.152's password:
[Panda]# /sbin/insmod /root/entree-gpio.ko
[Panda]# cat /dev/entree_gpio
0
0
0
0
...

L’entrée étant connectée à la masse, nous lisons (en boucle via la commande cat) la valeur 0. Raccordons la broche 10 à l’alimentation.

[...]
0
0
0
1
1
1
1
[...]

La valeur passe à 1. Nous pouvons réitérer l’expérience autant que voulu, puis nous arrêtons la lecture en pressant Contrôle-C.

1
1
1
1
  (Contrôle-C)
[Panda]#

Conclusion

Nous avons pu utiliser très facilement une entrée GPIO depuis l’espace utilisateur ou depuis le noyau Linux. Ceci ouvre la porte à la conception de systèmes interactifs réagissant à des événements extérieurs (robotique, domotique, etc.) Toutefois, dans la plupart de ces applications, il est important de réagir rapidement à ces événements. Il faut donc être prévenu dès qu’il y a un changement d’état sur une entrée. Pour cela on utilisera les interruptions GPIO qui permettront d’invoquer une routine spécifique dès qu’un signal sera détecté par le microprocesseur sur une brioche GPIO. Ce sera l’objet du prochain article

2 Réponses

  1. Yoann Sculo dit :

    Pauvre Pandaboard !! Mais c’était pour la bonne cause ! 🙂
    Sinon, le debugfs peut être aussi très pratique pour regarder les mux et les états des GPIO

    /sys/kernel/debug/omap_mux

    Ça peut être même très pratique pour changer leur état et bidouiller depuis le userspace

  2. cedric dit :

    simple question: pour evité les risque ne peut t’on pas intercalé un petit circuit dont le rôle serai de s’assuré du sens de comunication du port d’extention?
    jai en téte les compasant a led qui serve a isolé electriquement deux montage tout’en passant l’information (j’ai oublier le nom je recommence tout juste a me remettre a l’electronique^^ »’)
    il suffirai ensuite d’interdire les comunication en entré quand le port est configuré en sortie…
    enfin c’est juste une petite intuition
    ps: si vous avez une reponse sa m’interresse fortement ^^

URL de trackback pour cette page