Dans 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.
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.
Mise en contact de la broche 10 et de la broche 1…
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…
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
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 ^^