Le Raspberry Pi offre quelques possibilités d’entrées-sorties directes en utilisant les broches GPIO présentes sur son connecteur P1. Elles ne sont pas très nombreuses (une dizaine) mais cela peut suffire pour des petits projets interactifs nécessitant d’interroger des capteurs tout-ou-rien ou de valider des actionneurs.
Nous pouvons utiliser ces GPIO de différentes façons, depuis l’espace utilisateur ou depuis le noyau. Voyons-en rapidement les principaux aspects…
Les broches GPIO
Sur le connecteur P1 du Raspberry Pi, nous pouvons trouver plusieurs broches consacrées aux entrées-sorties GPIO. Celles-ci peuvent être configurées individuellement en entrées ou en sorties numériques. Attention, la tension appliquée sur une borne d’entrée doit rester inférieure à 3.3 V.
Les GPIO directement accessibles sont les suivantes.
| Broche | GPIO |
| 3 | 0 (rev.1) ou 2 (rev.2) |
| 5 | 1 (rev.1) ou 3 (rev.2) |
| 7 | 4 |
| 11 | 17 |
| 12 | 18 |
| 13 | 21 (rev.1) ou 27 (rev.2) |
| 15 | 22 |
| 16 | 23 |
| 18 | 24 |
| 22 | 25 |
On peut remarquer que certaines broches (3, 5 et 13) ont changé d’affectations au gré des versions du Raspberry Pi, aussi évitera-t-on de les employer pour garder un maximum de portabilité aux applications.
Accès depuis l’espace utilisateur
L’accès simple, depuis le shell – ou tout autre programme de l’espace utilisateur – peut se faire très aisément grâce au système de fichiers /sys.
/ # cd /sys/class/gpio/ /sys/class/gpio # ls export gpiochip0 unexport
Demandons l’accès au GPIO 24 (broche 18).
/sys/class/gpio # echo 24 > export /sys/class/gpio # ls export gpio24 gpiochip0 unexport /sys/class/gpio # cd gpio24/ /sys/devices/virtual/gpio/gpio24 # ls active_low direction edge subsystem uevent value /sys/devices/virtual/gpio/gpio24 # cat direction in
Sortie de signal
Par défaut, les broches GPIO sont dirigées en entrée. Inversons le sens du 24, puis regardons sa valeur.
/sys/devices/virtual/gpio/gpio24 # echo out > direction /sys/devices/virtual/gpio/gpio24 # cat value 0
Effectivement, le voltmètre confirme qu’il n’y a pas de tension sur la broche.
Modifions l’état de la sortie
/sys/devices/virtual/gpio/gpio24 # echo 1 > value
Vérifions la tension.
Re-basculons la sortie à zéro.
/sys/devices/virtual/gpio/gpio24 # echo 0 > value /sys/devices/virtual/gpio/gpio24 #
Lecture d’état
Demandons à présent l’accès à la broche 16 (Gpio 23)
# cd /sys/class/gpio/ /sys/class/gpio # echo 23 > export /sys/class/gpio # ls export gpio23 gpio24 gpiochip0 unexport /sys/class/gpio # cd gpio23/ /sys/devices/virtual/gpio/gpio23 # cat direction in
Je relie la broche 16 à la broche 20 (GND), puis je lis la valeur d’entrée.
/sys/devices/virtual/gpio/gpio23 # cat value 0
Je relie à présent la broche 16 à la broche 17 (+3.3V)
/sys/devices/virtual/gpio/gpio23 # cat value 1
Retour à nouveau sur la broche 20 (GND).
/sys/devices/virtual/gpio/gpio23 # cat value 0 /sys/devices/virtual/gpio/gpio23 #
Accès depuis le kernel
Lectures et écritures
Nous pouvons écrire un petit module pour accéder en lecture et écriture aux mêmes broches. Dans le module ci-dessous un timer à 8Hz fait clignoter la sortie sur la broche 18 seulement si la broche 16 est mise à 1 (+3.3V).
rpi-gpio-1.c #include <linux/module.h> #include <linux/timer.h> #include <linux/gpio.h> #include <linux/fs.h> // Sortie sur broche 18 (GPIO 24) #define RPI_GPIO_OUT 24 // Entree sur broche 16 (GPIO 23) #define RPI_GPIO_IN 23 static struct timer_list rpi_gpio_1_timer; static void rpi_gpio_1_function (unsigned long unused) { static int value = 1; value = 1 - value; if (gpio_get_value(RPI_GPIO_IN) == 0) value = 0; gpio_set_value(RPI_GPIO_OUT, value); mod_timer(& rpi_gpio_1_timer, jiffies+ (HZ >> 3)); } static int __init rpi_gpio_1_init (void) { int err; if ((err = gpio_request(RPI_GPIO_IN,THIS_MODULE->name)) != 0) return err; if ((err = gpio_request(RPI_GPIO_OUT,THIS_MODULE->name)) != 0) { gpio_free(RPI_GPIO_IN); return err; } if ((err = gpio_direction_input(RPI_GPIO_IN)) != 0) { gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); return err; } if ((err = gpio_direction_output(RPI_GPIO_OUT,1)) != 0) { gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); return err; } init_timer(& rpi_gpio_1_timer); rpi_gpio_1_timer.function = rpi_gpio_1_function; rpi_gpio_1_timer.data = 0; // non utilise rpi_gpio_1_timer.expires = jiffies + (HZ >> 3); add_timer(& rpi_gpio_1_timer); return 0; } static void __exit rpi_gpio_1_exit (void) { del_timer(& rpi_gpio_1_timer); gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); } module_init(rpi_gpio_1_init); module_exit(rpi_gpio_1_exit); MODULE_LICENSE("GPL");
La compilation se fait avec le fichier Makefile suivant. Les lignes KERNEL_DIR et CROSS_COMPILE indiquent respectivement l’emplacement du répertoire de compilation du noyau pour le Raspberry Pi et le préfixe pour la toolchain sur mon système. Il faut les adapter à votre environnement.
Makefile ifneq (${KERNELRELEASE},) obj-m += rpi-gpio-1.o else ARCH ?= arm KERNEL_DIR ?= ~/linux-3.2.27 CROSS_COMPILE ?= /usr/local/cross/rpi/bin/arm-linux- MODULE_DIR := $(shell pwd) CFLAGS := -Wall all: modules modules: ${MAKE} -C ${KERNEL_DIR} ARCH=${ARCH} CROSS_COMPILE=${CROSS_COMPILE} SUBDIRS=${MODULE_DIR} modules clean: rm -f *.o .*.o .*.o.* *.ko .*.ko *.mod.* .*.mod.* .*.cmd rm -f Module.symvers Module.markers modules.order rm -rf .tmp_versions endif
Après compilation et transfert sur le Raspberry Pi, nous chargeons le module et observons à l’aide d’un analyseur logique les deux broches 16 (canal 2) et 18 (canal 1), tandis qu’un signal de +3.3V est envoyé sur la broche 16.
Durant la période (mise en évidence par un trait rouge) où la broche 16 se voit appliquer une tension de +3.3V, nous voyons bien une oscillation de la sortie sur la broche 18.
Interruptions
À présent nous allons traiter les interruptions déclenchées par une entrée GPIO. Le module ci-dessous installe un handler d’interruption pour le GPIO 23 (broche 16). À chaque déclenchement de l’interruption, notre handler basculera l’état de la broche de sortie 18 (GPIO 24).
rpi-gpio-2.c #include <linux/interrupt.h> #include <linux/module.h> #include <linux/gpio.h> // Sortie sur broche 18 (GPIO 24) #define RPI_GPIO_OUT 24 // Entree sur broche 16 (GPIO 23) #define RPI_GPIO_IN 23 static irqreturn_t rpi_gpio_2_handler(int irq, void * ident) { static int value = 1; gpio_set_value(RPI_GPIO_OUT, value); value = 1 - value; return IRQ_HANDLED; } static int __init rpi_gpio_2_init (void) { int err; if ((err = gpio_request(RPI_GPIO_OUT, THIS_MODULE->name)) != 0) return err; if ((err = gpio_request(RPI_GPIO_IN, THIS_MODULE->name)) != 0) { gpio_free(RPI_GPIO_OUT); return err; } if ((err = gpio_direction_output(RPI_GPIO_OUT,1)) != 0) { gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); return err; } if ((err = gpio_direction_input(RPI_GPIO_IN)) != 0) { gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); return err; } if ((err = request_irq(gpio_to_irq(RPI_GPIO_IN), rpi_gpio_2_handler, IRQF_SHARED | IRQF_TRIGGER_RISING, THIS_MODULE->name, THIS_MODULE->name)) != 0) { gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); return err; } return 0; } static void __exit rpi_gpio_2_exit (void) { free_irq(gpio_to_irq(RPI_GPIO_IN), THIS_MODULE->name); gpio_free(RPI_GPIO_OUT); gpio_free(RPI_GPIO_IN); } module_init(rpi_gpio_2_init); module_exit(rpi_gpio_2_exit); MODULE_LICENSE("GPL");
Le second module ayant été ajouté dans le Makefile, nous pouvons le compiler de la même façon que le précédent. Transférons-le sur le Raspberry Pi, puis chargeons-le dans le kernel.
/ # cat /proc/interrupts
CPU0
3: 362 ARMCTRL BCM2708 Timer Tick
32: 419 ARMCTRL dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
52: 0 ARMCTRL BCM2708 GPIO catchall handler
65: 20 ARMCTRL ARM Mailbox IRQ
66: 1 ARMCTRL VCHIQ doorbell
77: 123 ARMCTRL bcm2708_sdhci (dma)
83: 18 ARMCTRL uart-pl011
84: 317 ARMCTRL mmc0
FIQ: usb_fiq
Err: 0
/ # insmod rpi-gpio-2.ko
/ # cat /proc/interrupts
CPU0
3: 434 ARMCTRL BCM2708 Timer Tick
32: 463 ARMCTRL dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
52: 0 ARMCTRL BCM2708 GPIO catchall handler
65: 28 ARMCTRL ARM Mailbox IRQ
66: 1 ARMCTRL VCHIQ doorbell
77: 124 ARMCTRL bcm2708_sdhci (dma)
83: 119 ARMCTRL uart-pl011
84: 326 ARMCTRL mmc0
193: 0 GPIO rpi_gpio_2
FIQ: usb_fiq
Err: 0
/ #
Nous voyons que la ligne d’interruption (numéro 193) est apparue dans /proc/interrupts. Pour l’instant aucune interruption ne s’est déclenchée. Connectons sur cette entrée un Générateur-Basse-Fréquence, qui lui envoie un signal carré [0, +3.3V] de 2.5kHz environ.
Aussitôt un signal carré apparaît sur la broche de sortie, avec une fréquence moitié du précédent. Nous pouvons le vérifier avec un oscilloscope.

Nous pouvons également vérifier dans /proc/interrupts que le nombre d’interruptions 193 traitées progresse régulièrement.
/ # cat /proc/interrupts
CPU0
3: 646 ARMCTRL BCM2708 Timer Tick
32: 617 ARMCTRL dwc_otg, dwc_otg_pcd, dwc_otg_hcd:usb1
52: 29791 ARMCTRL BCM2708 GPIO catchall handler
65: 60 ARMCTRL ARM Mailbox IRQ
66: 1 ARMCTRL VCHIQ doorbell
75: 1 ARMCTRL
77: 124 ARMCTRL bcm2708_sdhci (dma)
83: 187 ARMCTRL uart-pl011
84: 342 ARMCTRL mmc0
193: 29792 GPIO rpi_gpio_2
FIQ: usb_fiq
Err: 0
/ #
En zoomant sur le point de déclenchement, nous mesurons la durée de prise en compte de l’interruption.
La durée entre la montée du signal d’entrée et la réponse du handler est d’environ 7 micro-secondes, ce qui est tout à fait correct pour cette gamme de microprocesseur.
Conclusion
L’accès aux GPIO du port d’extension du Raspberry Pi est très simple, tant depuis l’espace utilisateur que depuis le noyau. Il existe d’autres broches permettant des entrées-sorties GPIO, mais elles ont une autre fonctionnalité par défaut (RS-232, SPI, etc.).
Il serait également intéressant d’accéder aux GPIO depuis un driver RTDM pour Xenomai. Ceci fera l’objet d’un prochain article.
Pour en savoir plus
Si vous souhaitez approfondir l’écriture de drivers pour Linux – notamment pour les GPIO du Raspberry Pi -, je vous propose de vous retrouver en session de formation « Écriture de drivers et programmation noyau Linux » chez Logilin.





Article fort intéressant, merci beaucoup.
je suis en train de tester un RPI et maintenant j’aimerais bien implémenter la connexion RS232 du RPI pour commencer à bien jouer.
Si vous avez des références sur le sujet, je suis preneur.
Ben j’ai pas cherché bien loin, je vois que vous avez fait un article dessus, je vais y jeter un œil
merci
Bonjour,
Est-ce que cet article fait référence à une utilisation de des GPIO de PI avec un noyau temps-réel ou un noyau simple ?
merci
Pour l’instant un noyau classique, mais je vais essayer l’utilisation avec RTDM et Xenomai rapidement.
Cet article est très intéressant mais je ne suis pas sûr d’avoir vraiment tout compris au niveau des interruptions. Est ce qu’il existe un lien physique sur le raspberry pi entre le GPIO et le microprocesseur pour déclencher des interruptions? Ou bien la routine écrite en C est elle purement logicielle pour surveiller le changement d’état d’une broche du GPIO.
En tout cas merci pour les explications, plein de choses a faire avec ce Raspberry…
–
Guy
Bonjour Guy,
L’interruption est bien déclenchée directement par le changement d’état de la broche du GPIO. C’est l’APIC (Advanced Programmable Interrupt Controller) inclus dans le processeur qui détecte les variations de niveaux sur ces broches et envoie une demande d’interruption. Suivant sa configuration, il peut ainsi notifier le CPU d’un front électrique montant, descendant, d’un niveau haut ou bas. Toutes ces configurations ne sont peut-être pas accessibles sur l’APIC du Raspberry Pi, je n’ai pas encore vérifié.
Bonjour,
Merci pour l’article. J’ai cependant deux questions…
Quelle est la valeur de la constante HZ ? Et où est définie jiffies ?
Par ailleurs, si l’on souhaite avoir un timer à 8Hz, ne faut-il pas écrire HZ <> 3 ?
Peut-être me trompé-je lourdement puisque je n’ai aucune expérience dans l’écriture de pilotes, notamment sous Linux…
Merci d’avance !
Mon commentaire a légèrement été formaté…
Je voulais dire HZ << 8.
Et encore une erreur…
Mon dernier mot sera : HZ << 3.
Toutes mes excuses.
Bonjour,
La constante
HZest définie dans un fichier d’en-tête du kernel (<asm/param.h>si je me souviens bien) et contient le nombre de ticks par secondes (même sur un système tickless).La variable
jiffies(<linux/jiffies.h>) contient le nombre de ticks écoulés depuis le boot.Donc
jiffies+HZsignifie « maintenant + une seconde » (ou plutôt en français : « dans une seconde » ) etjiffies+HZ/8oujiffies+HZ>>3signifie « dans un huitième de seconde »Le timer se reprogramme ainsi régulièrement en demandant à être à nouveau invoqué dans un huitième de seconde.
Au temps pour moi, merci beaucoup pour l’explication claire et concise !
Bonjour
Je suis en train de réaliser un projet personnel avec ma mini2440 le noyau image que j’ai récupéré ne me permet pas d’accéder au répertoire sys/class/gpio/. J’aimerais connaître la procédure à faire lors de la compilation du noyau afin d’avoir accès au répertoire gpio. Un tutoriel ou un lien internet expliquant la démarche serait la bienvenue.
Cordialement.
problème résolu
Bonjour
Pour exploiter les GPIO du Raspberry Pi en domotique j’ai réalisé une carte de protection pour évité tout retour d’alimentation qui nuirai à la santé du Rasp, si cela peut vous interesser http://blueberry4pi.com/2013/02/01/carte-de-protection-pour-les-gpio-du-raspberry/
Intéressant, le schéma est-il disponible ?
Hello Christophe !
« high » n’est-il pas supposé mettre la broche en pull up et rien d’autre?
Ca semble passer son etat d’entré en sortie.
# echo in > direction
# cat direction
# in
# echo high > direction
# cat directon
# out
Normalement apres:
# echo high > direction
seule « value » devrait passer a ’1′ (si broche « en l’air ») mais « direction » devrait rester en « in » ?!
Help !
Inscrire «
high» dans le fichierdirectiona pour effet de basculer la broche en sortie (si elle ne l’était pas déjà) et la place à l’état1(+3.3V).Y écrire «
low» ou «out» bascule la broche en sortie et la met à 0.Écrire «
in» dansdirectionpasse la broche en entrée.En fait, il ne faut pas considérer «
high» comme « haute impédance », mais comme « sortie au niveau haut ».NB: le code correspondant est visible dans les sources du kernel, fichier
drivers/gpio/gpiolib.c, fonctiongpio_direction_store().Ah ok.
Il n’y a donc aucun moyen de « tirer » la broche a 3.3V ou GND autrement que via une resistance externe ?
Je bricole juste qq chose pour faire un « shutdown -h » avec un poussoir & Bash.
Je ne suis pas sûr que cela soit possible avec le Raspberry Pi.
J’avais réalisé ce genre de chose sur une Pandaboard via le système de fichier
debugfs(voir cet article), mais je n’ai pas encore regardé sur le Raspberry et je doute que cela soit possible.C’est un bon article, mais c’est dommage que tu ne fournisses pas les sources qui t’on permis d’arrive jusque-là.
Par exemple : on aimerait savoi d’où vient la table des correspondance broches/GPIO en début d’article. Ca permettrait à tes lecteurs d’avoir d’autres pistes à fouiller.
Hello
J’ai juste inclus j’ai des erreurs:
—————-
gcc -I/usr/include -I/usr/src/linux/include -Wall test.c -o test
In file included from /usr/src/linux/include/linux/list.h:4:0,
from /usr/src/linux/include/linux/module.h:9,
from test.c:8:
/usr/src/linux/include/linux/types.h:13:2: warning: #warning « Attempt to use kernel headers from user space, see http://kernelnewbies.org/KernelHeaders » [-Wcpp]
…
etc..
—————-
J’ai regardé les sources de wiringPi [http://wiringpi.com] vite fait, les fonctions C proposer semblent a premiere vue acceder au E/S par le biais de fichier (/sys/../value, etc..) est ce aussi ce qui ce passe derriere l’exemple que tu proposes?
Un article, « GPIOs from scratch for newbies », serait bienvenu !
Il ne faut pas faire de compilation des modules du noyau depuis la ligne de commande, il vaut mieux utiliser un Makefile qui va chercher les éléments de configuration et les fichiers d’en-tête du kernel.
Il y en a un présenté dans l’article, il faut juste configurer correctement KERNEL_DIR.
Les accès depuis l’espace utilisateur en début d’article se font en effet par l’intermédiaire de /sys.
J’ai trouvé mon bonheur en attendant d’etre plus degourdi.
« C library for Broadcom BCM 2835 as used in Raspberry Pi »
http://www.airspayce.com/mikem/bcm2835/
C’est moins « usine à gaz » que WiringPi.
Les executables doivent evidement se faire en root (acces a /dev/mem ). Pourtant si je fais un chmod +s dessus ca ne change rien; alors qu’avec « avrdude » cette manip me permet de le lancer en simple utilisateur (/dev/bus/usb je crois).
Que donne un «
ls -l <executable>» (pour voir les droits et le propriétaire) ?Ok!! merci ! Effectivement le proprietaire etait resté un simple utilisateur ! Pfff… Le pire est que j’ai un « alias ls=’ls -lrt’ » mais ca ne m’a pas empeché de ne rien voir.
Bizarrement quand on change le nom du « group », le flag ‘s’ de l’user disparait. Bah !