Introduction 'a la plateforme Raspberry Pi

 
CONTINUER À LIRE
Introduction 'a la plateforme Raspberry Pi
Introduction à la plateforme Raspberry Pi
                                 — Partie 2 —
                          B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
                             Faculté des Sciences, Université de Mons
                                             10 décembre 2014

1     Objectifs de la séance
     L’objectif de cette séance est de permettre l’interaction entre la platine Raspberry Pi et le monde réel au
travers des ports d’entrées/sorties (GPIO). Plusieurs petits circuits électroniques simples seront réalisés sur
une platine d’expérimentation (breadboard). De courts programmes en shell, Python et/ou C seront écrits
afin de contrôler les entrées/sorties.
     Comme pour la première séance, la plateforme sera utilisée sans écran. Les composants électroniques
utilisés seront issus du SparkFun Inventor’s Kit 1 .

2     Entrées/sorties GPIO
     Le processeur ou plus exactement System-on-Chip (SoC) qui équipe la platine Raspberry Pi possède un
grand nombre de portes d’entrées/sorties. Celles-ci permettent au SoC d’interagir avec d’autres périphériques
ou circuits électroniques. Ces ports d’entrées/sorties sont souvent appelés GPIO pour General Purpose In-
put/Output. Le comportement de ces ports d’entrées/sorties est contrôlable par programme.
     Le SoC BCM2835 dispose de 54 lignes GPIO. Certaines sont accessibles au travers de l’un des connec-
teurs de la platine. Les broches GPIO accessibles dépendent de la version de la platine Raspberry Pi. Sur
les modèles A et B (rev.1 et 2), 17 GPIO sont accessibles au travers du connecteur P1 (26 broches). Sur les
modèles A+ et B+, 26 GPIOs sont accessibles au travers du connecteur J8 (40 broches).
     Les Tables 12, 11 et 10 renseignent sur l’identification des différentes broches des connecteurs GPIO.
Certaines des broches permettent d’accéder à l’alimentation électrique de la platine Raspberry Pi (3,3V ou
5V). Certaines broches sont marquées GND, il s’agit du point de potentiel de référence (0V). Les autres
broches sont étiquettées avec le numéro de broche GPIO et lorsque c’est possible par une fonction alterna-
tive. Sur la Table 11 par exemple, on peut y observer que les broches 8 et 10 du connecteur correspondent
aux GPIO 14 et 15 respectivement mais que ces broches peuvent également être utilisées pour une liaison
série (UART TxD et RxD). Lors de la première séance, ce sont ces broches que nous avons utilisées pour
y connecter un adaptateur USB/série afin d’obtenir une console.
     Une bonne référence pour obtenir de l’information sur les autres connecteurs ou sur un autre modèle
de platine est le site http://elinux.org/RPi_Low-level_peripherals.

2.1    Précautions et limites d’utilisation
      +   Attention, l’utilisation des broches d’entrées/sorties nécessite de respecter certaines limita-
          tions. Dans le cas contraire, la broche d’entrée/sortie voir le SoC peuvent être définitivement
          endommagés ! En particulier, ni la platine ni le SoC ne fournissent de mécanisme de protec-
          tion contre les surtensions ou les courants trop forts.

    1. https://www.sparkfun.com/products/retired/12060

                                                        1
Les contraintes suivantes sont valables pour le modèle B mais devraient être similaires pour les autres
modèles. Une broche d’entrée/sortie fonctionne comme une source de tension en 3,3V. Les broches ne
tolèrent pas une tension de 5V ! Le courant qui peut être tiré d’une broche est limité par plusieurs facteurs.
Le courant tiré d’une broche provient de l’alimentation USB (5V) puis est abaissé à 3V3 par un régulateur,
puis il passe par le SoC.
     — Limite par broche : 16mA (contrainte du SoC).
     — Limite totale 3V3 : 50mA (contrainte du régulateur 3V3). Cela donne un courant moyen maximum
         d’environ 2-3mA par broche.
     — Limite totale 5V : ? ? ?mA (contrainte de l’alimentation USB).
En résumé, tirer le moins de courant possible d’une broche d’entrées/sorties.

3     Application : contrôle d’une LED et d’un bouton poussoir
    Cette section décrit comment configurer une broche comme entrée ou sortie digitale et comment en
contrôler ou lire l’état. Afin de mettre en pratique ce contrôle, la platine Raspberry Pi sera utilisée d’une part
pour contrôler l’allumage d’une LED et d’autre part pour lire l’état d’un bouton poussoir. Les Figures 1a
et 1b présentent le schéma des circuits correspondants.
    Le circuit de commande de la LED est articulé autour de 2 résistances (R1 et R2), d’un transistor
bipolaire NPN (Q1) et d’une LED (LD1). La résistance R1 de 330 ohms limite le courant qui traverse la
LED 2 à environ 4mA. Le transistor permet de commander le circuit de la LED en tirant un courant faible
d’environ 0,3mA de la broche de sortie 3 .
    Le bouton poussoir est placé en série avec une résistance (R2) de façon à limiter le courant tiré du port
au cas où il serait incorrectement configuré en sortie et placé dans un état haut. Dans ce cas, le courant
sera limité à 10mA, ce qui reste dans les limites permises. La résistance R1 est une résistance de pull-up
optionnelle 4 . Lorsque le bouton poussoir est pressé, la tension présente sur la broche sera proche de 0V, ce
qui sera interpreté comme un niveau 0. Lorsque l’interrupteur est relâché, la résistance de pull-up du port
rendra la tension proche de 3,3V et donc d’un niveau haut.

    (a) Commande d’une LED avec une sortie digitale.                   (b) Capture d’un bouton poussoir avec une entrée digitale.

     La Figure 2a montre comment le circuit de contrôle de la LED peut être implémenté sur une plaquette
d’expérimentation (breadboard). Attention, il est important de pouvoir identifier les différentes broches de
la LED et du transistor. La LED a 2 broches : une anode et une cathode. La cathode est la broche la plus
courte. Sur le schéma elle située à la pointe de la flêche. Le transistor bipolaire a trois broches : une base,
un collecteur et un émetteur. Sur le schéma, les lettres ”b”, ”c” et ”e” ont été ajoutées pour faciliter l’iden-
tification des broches du transistor. Pour identifier les broches du transistor, il est nécessaire de consulter sa
fiche technique (datasheet). La Figure 2b montre un extrait de la fiche technique du transistor 2N2222 qui
permet d’en repérer les différentes broches.
    2. Les caractéristiques de la LED fournie avec l’Arduino SIK ne sont pas connues avec exactitude. Cependant, il est typique pour
une LED de ce type de ne pas supporter un courant supérieur à 20mA. La tension nécessaire aux bornes de la LED pour qu’elle
commence à conduire (forward voltage drop, Vf orward ) est égale à environ 2V pour une LED émettant dans le rouge. Le courant
qui traverse la LED peut être calculé simplement comme I = (3, 3V − Vf orward )/330Ω ≈ 4mA
    3. Le courant traversant la jonction base-émetteur du transistor est égal à I = (3, 3V − 0, 7V )/10kΩ ≈ 0, 26mA.
    4. Certains ports comportent déjà une résistance de pull-up externe. De plus, il est possible d’activer une résistance de pull-up
interne pour chaque port individuellement.

                                                                   2
(b) Broches du transis-
              (a) Commande d’une LED avec une sortie digitale.                  tor 2N2222.

4     Contrôle logiciel
     Une fois le circuit implémenté sur la plaquette d’expérimentation et relié à la platine Raspberry Pi, il
est nécessaire de s’intéresser à la partie logicielle du contrôle. Cette section discute de plusieurs moyens
de contrôler une entrée ou une sortie digitale et des performances de ceux-ci. Le premier moyen utilisé
sera au travers de fichiers virtuels au travers de l’API SysFS. Le second moyen sera via un programme
écrit en Python, grâce à l’API RPi.GPIO. Finalement, le troisième moyen sera via un programme écrit en
C qui accède directement aux registres du SoC contrôlant les entrées/sorties. Cette dernière approche est
plus complexe et rébarbative, mais elle permet d’une part d’obtenir les meilleures performances et d’autre
part de comprendre ce qu’il y a sous le capot. En effet, tous les autres moyens plus simples de contrôler les
entrées/sorties se reposent in fine sur l’utilisation des registres du SoC.
     A titre de comparaison, la Table 1 donne un aperçu des performances qu’il est possible d’obtenir pour
le contrôle d’une sortie digitale avec différentes API. Ce tableau a été obtenu par J. Pihlajamaa 5 . On
peut constater que l’accès direct aux registres permet de faire changer l’état d’une sortie digitale plusieurs
dizaines de fois par seconde alors qu’avec l’API SysFS, il n’est pas possible d’effectuer plus de quelques
milliers de changements par seconde.

         Language      Library                   Version / tested                        Square wave
         Shell         SysFS                     N.A. / July 3, 2012                            3.4 kHz
         Python        RPi.GPIO                  0.3.0 / August 1, 2012                          44 kHz
         Python        wiringPi                  github / August 14, 2012                        20 kHz
         C             /dev/mem + mmap           N.A. / July 3 and August 14, 2012          14-22 MHz
         C             BCM 2835                  1.3 ? / July 3, 2012                    4.7 - 5.1 MHz
         C             wiringPi                  not available / August 14, 2012         6.9 - 7.1 MHz
         Perl          BCM 2835                  1.0 / July 3, 2012                              35 kHz

            TABLE 1 – Comparaison des différentes API pour le contrôle d’une sortie digitale.

    5. http://codeandlife.com/2012/07/03/benchmarking-raspberry-pi-gpio-speed

                                                        3
4.1    Contrôle via sysFS
     Selon la philosophie UNIX, tout peut être contrôlé au travers de fichiers. Cette approche a été appliquée
aux entrées/sorties grâce à SysFS 6 . Il s’agit d’un système générique permettant à un programme utilisateur
d’accéder au travers du système de fichiers à de l’état maintenu par le noyau. En clair, des fichiers virtuels
apparaissent dans le système de fichiers, typiquement sous le chemin /sys. Comme illustré à la Figure 3,
lire ou écrire ces fichiers, au travers d’appels système standards tels que read et write, a pour effet de
lire ou écrire de l’état maintenu par le noyau.

                                 F IGURE 3 – Illustration de l’interface SysFS.

     Le contrôle des entrées/sorties digitales avec SysFS peut être réalisé au travers de deux groupes de
fichiers. Les premiers, situés sous /sys/class/gpio permettent de placer une broche d’I/O particulière
sous le contrôle de SysFS. Par défaut, aucune broche n’est sous le contrôle de SysFS. La Table 2 documente
les fichiers de ce groupe. En écrivant le numéro d’une broche d’entrée/sortie dans le fichier export, cette
broche passe sous le contrôle de SysFS. Il suffit d’écrire ce numéro dans le fichier unexport pour obtenir
l’effet inverse.

             File            Access     Description
             export           W         Pin number to export
             unexport         W         Pin number to remove

                           TABLE 2 – Fichiers SysFS sous /sys/class/gpio

     Lorsqu’une broche d’entrée/sortie passe sous le contrôle de SysFS, de nouveaux fichiers virtuels appa-
raissent dans le répertoire /sys/devices/virtual/gpio/gpion où le n final désigne le numéro
de la broche. Le fichier direction permet de configurer une broche comme entrée (in) ou comme sor-
tie (out). Le fichier value est utilisé pour lire ou écrire l’état de la broche. Le fichier edge permet de
capturer de manière asynchrone des événements de changement d’état d’une broche en entrée.

            File              Access     Description
            direction          R/W       Direction of pin : in or out
            edge               R/W       Event trigger : rising, falling, both or none
            value              R/W       State of pin : 0 or 1

               TABLE 3 – Fichiers SysFS sous /sys/devices/virtual/gpio/gpion

     L’exemple qui suit illustre l’utilisation de SysFS pour le contrôle de la broche 3 en sortie. La première
étape consiste à mettre GPIO 3 sous le contrôle de SysFS en écrivant la valeur 3 dans le fichier export.
  6. voir Ottawa Linux Symposium, 2005 - https://www.kernel.org/pub/linux/kernel/people/mochel/doc/
papers/ols-2005/mochel.pdf et https://www.kernel.org/doc/Documentation/gpio/sysfs.txt

                                                         4
La commande echo est utilisée pour afficher une chaı̂ne de caractères. Ici, la sortie de echo est re-
dirigée avec le symbole > vers un fichier. Cela a pour effet d’écrire la chaı̂ne de caractères 3 dans le
fichier export. Après cette écriture, de nouveaux fichiers (virtuels) sont créés dans le répertoire /sys/
devices/virtual/gpio afin de contrôler la broche 3. La commande ls est utilisée pour montrer le
contenu du répertoire /sys/devices/virtual/gpio/gpio3 ainsi créé. Ensuite, la valeur out est
écrite dans le fichier direction afin de configurer la broche 3 en sortie. Finalement, en écrivant 1 ou 0
dans le fichier value, l’état de la broche 3 est changé.
pi@rpi:˜$ echo "3" > /sys/class/gpio/export
pi@rpi:˜$ ls /sys/devices/virtual/gpio/gpio3
active_low direction edge power subsystem uevent value
pi@rpi:˜$ echo "out" > /sys/devices/virtual/gpio/gpio3/direction
pi@rpi:˜$ echo "1" > /sys/devices/virtual/gpio/gpio3/value → LED ON
pi@rpi:˜$ echo "0" > /sys/devices/virtual/gpio/gpio3/value → LED OFF
pi@rpi:˜$ echo "3" > /sys/class/gpio/unexport

     Le exemple qui suit illustre la configuration de la broche 2 en entrée digitale. La différence avec
l’exemple précedent est que la valeur in est écrite dans le fichier direction. De plus, le fichier value
est lu afin d’obtenir l’état de la broche.
pi@rpi:˜$ echo "2" > /sys/class/gpio/export
pi@rpi:˜$ ls /sys/devices/virtual/gpio
gpio2 gpiochip0
pi@rpi:˜$ echo "in" > /sys/devices/virtual/gpio/gpio2/direction
pi@rpi:˜$ cat /sys/devices/virtual/gpio/gpio2/value
... returns 1 (button released) or 0 (button pressed) ...
pi@rpi:˜$ echo "2" > /sys/class/gpio/unexport

4.2    Contrôle via API Python
     Un autre moyen simple de contrôler les entrées/sorties est d’utiliser le langage de programmation Py-
thon. Des API spécifiques à la plateforme Raspberry Pi sont disponibles. Celle utilisée ici est nommée
RPi.GPIO. L’API est très simple et est résumée à la Table 4. La documentation complète peut être obtenue
à l’adresse http://sourceforge.net/p/raspberry-gpio-python/wiki.
     Utiliser l’API RPi.GPIO requiert une première phase de configuration avec la fonction setmode. Cette
fonction indique comment les broche d’entrées/sorties sont identifiées. La première possibilité est d’utiliser
les numéros de broches du SoC BCM2835 (argument = BCM). La seconde possibilité consiste à utiliser le
numéro de broche du connecteur (argument = BOARD). La première approche nous semble préferable et
est donc utilisée dans les exemples qui suivent. La fonction setup permet de configurer une broche en
entrée ou en sortie. La fonction cleanup permet de libérer l’utilisation d’une broche et de restaurer sa
configuration initiale. La fonction input permet de lire l’état d’une broche (configurée en entrée). La
fonction output permet de changer l’état d’une broche (configurée en sortie).
   NDLR : L’objectif de cette séance n’est pas d’apprendre le langage Python. Si vous ne connaissez pas
   Python, vous pouvez consulter un tutoriel tel que par exemple https://docs.python.org/2/
   tutorial/
    L’exemple de programme qui suit contrôle une sortie digitale et en change régulièrement l’état. Les
lignes 1 et 2 servent à importer les modules RPi.GPIO et time. Le module RPi.GPIO sera désigné par
le nom GPIO grâce à la directive as de la clause import. La ligne 4 requiert l’usage de la numérotation
des broches selon le SoC. La ligne 5 configure la broche 3 en sortie. La ligne 7 initialise une variable
booléenne à la valeur False. Cette variable contiendra le prochain état de la broche. Les lignes 9 à 12
sont une boucle de 10 itérations. La directive for indique que le bloc qui suit sera répété. Le nombre
de répétition est contrôlé par la variable i qui prendra les valeurs entières successives comprises dans
l’intervalle 0 à 19. Les valeurs de cet intervalle sont générées par la fonction range. L’intérieur de la
boucle est composé des lignes 10 à 12. En python, il n’y a pas de délimitation de bloc. Le contenu du bloc

                                                       5
Fonction                           Description
                   setmode(mode)                      Selectionne le mode d’identification des broches :
                                                      BCM = selon le SoC BCM2835
                                                      BOARD = selon le connecteur de la platine
                   setup(broche,fonction)             Configure broche comme entrée (IN) ou comme
                                                      sortie (OUT)
                   cleanup                            Libère les ressources utilisées par le module et res-
                                                      taure la configuration des broches.
                   input(broche)                      Lit l’état d’une broche.
                   output(pin,state)                  Définit l’état d’une broche.

                                      TABLE 4 – Résumé de l’API python RPi.GPIO.

     est indenté : chaque ligne est précédée d’une tabulation. La ligne 10 change l’état de la broche : le nouvel
     état est celui trouvé dans la variable state. La ligne 11 inverse l’état de la variable booléenne state.
     La ligne 12 attend durant 1 seconde. La ligne 14 libère les ressources utilisées par le module RPi.GPIO et
     restaure l’état initial de la broche 3.
 1   import RPi.GPIO as GPIO
 2   import time
 3
 4   GPIO.setmode(GPIO.BCM)
 5   GPIO.setup(3, GPIO.OUT)
 6
 7   state= False
 8
 9   for i in range(20):
10       GPIO.output(3, state)
11       state= not(state)
12       time.sleep(1)
13
14   GPIO.cleanup()

         Afin d’exécuter le programme ci-dessus, il faut le placer dans un fichier texte. Ici, nous supposons qu’il
     s’agit du fichier gpio-out.py. Le programme peut ensuite être exécuté comme suit
     pi@rpi:˜$ python gpio-out.py
     Traceback (most recent call last):
       File "gpio-out.py", line 5, in 
         GPIO.setup(3, GPIO.OUT)
     RuntimeError: No access to /dev/mem. Try running as root!
     pi@rpi:˜$ sudo python gpio-out.py
     (...broche 3 devrait changer...)
     pi@rpi:˜$

         L’exemple de programme qui suit contrôle une broche d’entrée. La ligne 5 configure la broche 2
     en entrée. Le programme est constitué d’une boucle sans fin qui s’étale sur les lignes 9 à 18. Il s’agit
     d’une boucle while qui s’exécute tant que la condition est vraie. Ici, la condition est True et par
     conséquent toujours vraie. La ligne 10 lit l’état de la broche 2 et le stocke dans la variable state. La
     ligne 11 attend 100ms 7 . Lignes 12 et 13, si l’état lu (variable state) est identique à l’ancien état (variable
     oldState), alors l’itération courante est terminée : grâce au mot clé continue, le programme retourne
     immédiatement au début de la boucle. L’ancien état devient le nouvel état à la ligne 14. Aux lignes 15 à
       7. Sans cette attente, le programme va en permanence exécuter la boucle et consommer 100% du CPU.

                                                                 6
18, une chaı̂ne de caractère est affichée avec print selon l’état de la broche. Ce programme ne se termine
     pas, en raison de la boucle sans fin. Pour l’arrêter, presser les touches Ctrl-C.
 1   import RPi.GPIO as GPIO
 2   import time
 3
 4   GPIO.setmode(GPIO.BCM)
 5   GPIO.setup(2, GPIO.IN)
 6
 7   oldState= None
 8
 9   while True:
10       state= GPIO.input(2)
11       time.sleep(0.1)
12       if state == oldState:
13           continue
14       oldState= state
15       if state:
16           print "button released"
17       else:
18           print "button pressed"
19
20   GPIO.cleanup()

         Dans le programme ci-dessus, l’appel à la fonction cleanup de la ligne 20 n’est jamais réalisé. La
     raison est que le programme est terminé abruptement par la pression des touches Ctrl-C. Ceci résulte en
     l’envoi au programme d’un signal d’interruption. Il est possible de capturer ce signal afin d’interrompre
     le programme tout en libérant correctement les ressources. L’exemple ci-dessous illustre comment cela
     peut être fait. Tout le code du programme initial n’est pas repris. La clé de la solution est l’usage de la
     structure try...except qui permet de capturer l’exception KeyboardInterrupt produite lorsqu’un
     signal d’interruption est reçu durant l’exécution de la boucle.
1    try:
2        while True:
3            state= GPIO.input(2)
4            time.sleep(0.1)
5            # ...
6    except KeyboardInterrupt:
7        pass
8
9    GPIO.cleanup()

          Il est possible d’activer les résistances pull-up ou pull-down via le module RPi.GPIO, avec la fonc-
     tion setup. Par exemple, si le programme est utilisé avec le circuit bouton poussoir de la Figure 1b, la
     résistance R1 de 10kΩ doit être utilisée 8 . Cependant, elle peut être omise si la résistance de pull-up interne
     de la broche est activée. L’extrait suivant illustre comment configurer la broche 22 comme entrée avec
     pull-up interne activée.
1    GPIO.setup(22, GPIO.IN, pull up down=GPIO.PUD UP)

     4.3     Contrôle via les registres
         Les API telles que SysFS et RPi.GPIO permettent le contrôle des entrées/sorties au travers de registres
     spéciaux du SoC BCM2835. Les performances de SysFS et RPi.GPIO sont limitées (voir Table 1). Pour
         8. Sauf dans le cas de GPIO 2 et 3 (modèles rev.2 ou plus), des résistances de pull-up externe de 1,8KΩ sont présentes sur la
     platine

                                                                      7
cette raison, cette section s’intéresse au contrôle des entrées/sorties par la programmation directe des re-
gistres spéciaux du SoC. Les registres spéciaux sont accédés au travers d’opération de lecture / écriture en
mémoire. On parle de memory-mapped registers. Les registres font 32 bits de large.
     La Table 5 présente une liste partielle des registres destinés au contrôle des broches d’entrées/sorties.
Pour chaque registre, les informations suivantes sont fournies : le nom du registre, une brève description,
le type d’accès (lecture/écriture) et l’adresse à laquelle le registre est accessible en mémoire.

             Register name                    Description                Access      Addresses
             GPFSEL0                          select function             R/W       0x7E200000
             GPFSEL1                          select function             R/W       0x7E200004
             ...                              ...
             GPFSEL5                          select function             R/W       0x7E200014
             GPSET0                           set output state             W        0x7E20001C
             GPSET1                           set output state             W        0x7E200020
             GPCLR0                           clear output state           W        0x7E200028
             GPCLR1                           clear output state           W        0x7E20002C
             GPLEV0                           read input state             R        0x7E200034
             GPLEV1                           read input state             R        0x7E200038
             several registers                configure pin events                  read datasheet
             GPPUD, GPPUDCLOK..1              configure pull-up/down                read datasheet

   TABLE 5 – Aperçu des registres spéciaux destinés au contrôle des entrées/sorties du SoC BCM2835.

     Afin de contrôler une broche de sortie digitale, il est nécessaire de contrôler 3 registres. Le premier
registre, nommé GPFSELn, permet de sélectionner la fonction associée à la broche : dans notre cas, sortie
digitale. Ensuite, les registres nommés GPSETp et GPCLRp permettent respectivement de mettre l’état de
la broche à un état haut ou bas.
     La Table 6 indique la signification des bits du registre GPFSEL0. Les autres registres GPFSELn sont
structurés de la même manière. Le registre GPFSEL0 permet de contrôler la fonction des broches GPIO
0 à 9. Trois bits sont réservés dans ce registre par broche. Par exemple, la fonction de la broche 0 est
sélectionnée avec les bits 0 à 2 de GPFSEL0 alors que la fonction de la broche 6 sera sélectionnée avec les
bits 18 à 20. A ce stade, les seules fonctions qui nous intéressent sont 000 (0) pour une entrée et 001 (1)
pour une sortie.

                            Bits      Field     Description
                           31-30       —        Reserved
                           29-27     FSEL9      Select function of GPIO 9
                           26-24     FSEL8      Select function of GPIO 8
                             ...       ...
                            5-3      FSEL1      Select function of GPIO 1
                            2-0      FSEL0      Select function of GPIO 0

                                 TABLE 6 – Structure du registre GPFSEL0.

    La Table 7 décrit le contenu du registre GPSET0. Ce registre contrôle l’état des broches 0 à 31. Le
registre GPSET1 a une structure similaire et contrôle les broches 32 à 54. Mettre à 1 le bit k dans le
registre GPSET0 revient à mettre la broche k à un niveau haut (pour 0 ≤ k ≤ 31).
    La Table 8 décrit le contenu du registre GPCLR0. Le comportement est similaire à celui du registre
GPSET0 à l’exception que mettre à 1 le bit k dans le registre GPCLR0 revient à mettre la broche k à un
niveau bas (pour 0 ≤ k ≤ 31). Le registre GPCLR1 a une structure similaire et contrôle les broches 32 à
53.

                                                        8
Bits     Field     Description
                             31      SET31      0 = no change ; 1 = set GPIO pin 31
                              ...      ...
                              1       SET1      0 = no change ; 1 = set GPIO pin 1
                              0       SET0      0 = no change ; 1 = set GPIO pin 0

                                       TABLE 7 – Structure du registre GPSET0.

                             Bits     Field     Description
                             31      CLR31      0 = no change ; 1 = clear GPIO pin 31
                              ...      ...
                              1       CLR1      0 = no change ; 1 = clear GPIO pin 1
                              0       CLR0      0 = no change ; 1 = clear GPIO pin 0

                                       TABLE 8 – Structure du registre GPCLR0.

         La Table 9 décrit le contenu du registre GPLEV0. Ce registre permet de connaı̂tre l’état des broches 0
    à 31. Le registre GPLEV1 a une structure similaire et permet de lire l’état des broches 32 à 54.

                             Bits     Field     Description
                             31      LEV31      0 = low ; 1 = high
                              ...      ...
                              1       LEV1      0 = low ; 1 = high
                              0       LEV0      0 = low ; 1 = high

                                       TABLE 9 – Structure du registre GPLEV0.

         L’extrait de programme en C suivant illustre comment la broche 22 peut être configurée en sortie. Pour
    cela, il est nécessaire de remplacer les bits 6 à 8 dans le registre GPFSEL2 par la valeur 001. Assurez-vous
    que vous comprenez pourquoi il s’agit de ce registre et de ces bits. Le programme suivant fait l’hypothèse
    que l’adresse du registre GPFSEL2 se trouve préalablement dans la variable GPFSEL2. La valeur du
    registre est lue et les bits 6 à 8 sont masqués en effectuant un ET bit à bit (opérateur &) avec la valeur
    exprimée en hexadécimal 0xFFFFFE3F. Cette valeur correspond à un nombre de 32 bits dans lequel tous
    les bits sont à 1 sauf les bits 6 à 8 qui valent 0. Le résultat est combiné (opérateur |, OU bit à bit) avec la
    valeur sélectionnant une sortie (1) décalée de 6 positions vers la gauche (opérateur
Les commandes suivantes illustrent comment il est possible au travers de SysFS de configurer les
     interruptions matérielles GPIO et la génération d’événements fichiers.
     pi@rpi:˜$ echo "3" > /sys/class/gpio/export
     pi@rpi:˜$ echo "in" > /sys/devices/virtual/gpio/gpio3/direction
     pi@rpi:˜$ echo "both" > /sys/devices/virtual/gpio/gpio3/edge
     pi@rpi:˜$

     5.1    Evenements GPIO en Python
        L’exemple suivant illustre comment les événements GPIO peuvent être capturés au travers de l’API
     RPi.GPIO. L’API s’occupe également de configurer
 1   import RPi.GPIO as GPIO
 2
 3   GPIO.setmode(GPIO.BCM)
 4   GPIO.setup(2, GPIO.IN)
 5
 6   while True:
 7       GPIO.wait_for_edge(2, GPIO.BOTH)
 8       if GPIO.input(2):
 9           print "button released"
10       else:
11           print "button pressed"

     6     Projet d’application
          Que faire en pratique avec les entrées/sorties digitales de la platine Raspberry Pi ? Les applications sont
     nombreuses et sont seulement limitées par votre imagination. Un exemple pourrait être de contrôler un
     éclairage (p.ex. une LED), un moteur, un relais, etc. ou contrôler l’état d’un capteur à distance au travers
     d’une page web.

                                                            10
A     Annexe
A.1    Structure d’une broche GPIO
     Il peut être utile de comprendre la structure des broches d’I/O afin de les utiliser correctement et de
ne pas endommager le SoC. Toutes les broches GPIO ont une structure similaire. Chaque broche peut être
utilisée comme une entrée ou une sortie digitale. Certaines broches ont des fonctionnalités plus complexes
telles que des interfaces de communication (UART, I2 C, SPI) ou PWM.
     La Figure 4 illustre la structure d’une broche GPIO unique. La partie extérieure à droite du rectangle,
illustre la broche, tandis que l’intérieur du rectangle illustre le contenu du SoC permettant de contrôler cette
broche. Un buffer de sortie permet de changer la tension de la broche en fonction d’un bit dans un registre
du SoC. Ce buffer de sortie n’est actif que si la broche est configurée comme sortie via un bit d’un registre
du SoC (voir l’étiquette direction). Un buffer d’entrée permet de lire l’état de la broche.

                                  F IGURE 4 – Structure d’une broche d’I/O.

     Finalement, pour chaque broche, des résistances de pull-up ou pull-down peuvent être optionnellement
activées individuellement. Les résistances de pull-up ou pull-down sont utilisées avec les broches confi-
gurées comme entrées digitales, en l’absence de connection avec un circuit externe ou lorsque celui-ci est
en état de haute impédance. Une résistance de pull-up tire le niveau de l’entrée vers le haut, tandis qu’une
résistance de pull-down tire le niveau vers le bas.

A.2    Exemple complet en C
     Cette section contient un exemple complet de programme en C permettant d’accéder directement aux
registres du SoC.
     Le programme nécessite d’accéder aux registres spéciaux du SoC. Cependant, ces registres se situent
dans une zone mémoire qui n’est pas directement accessible à un programme utilisateur. Il est cependant
possible d’accéder à l’entiereté de la mémoire au travers du pseudo fichier /dev/mem. L’accès à ce pseudo
fichier est restreint : il faut les privilèges d’administrateur. Accéder au fichier /dev/mem avec des appels
systèmes tels que read et write n’est pas très efficace. Pour cette raison, le programme demande au
kernel de créer une page mémoire alignée sur la partie du fichier (lire de la mémoire) qui correspond
aux registres spéciaux contrôlant les GPIO. Ainsi, lire/écrire dans cette page mémoire aura pour effet de
lire/écrire dans /dev/mem à la position des registres qui nous intéressent. Pour obtenir une telle page,
l’appel système mmap est employé. Il est notamment nécessaire d’indiquer à mmap l’adresse mémoire de
base à laquelle se situent les registres. Cette adresse vaut 0x20200000.
     Les étudiants attentifs auront peut être remarqué que cette adresse est fort différente des adresses ren-
seignées à la Table 5. La raison est simplement que /dev/mem expose la mémoire au travers des adresses
physiques alors que la Table 5 renseigne les adresses des registres GPIO sur un bus interne au SoC. La
Figure 5 illustre la correspondance entre les divers types d’adresses.
     Toute cette technique est regroupée dans la fonction get mmapped gpio dans le programme ci-
dessous. Le restant du programme consiste à accéder aux registres au travers de la page mémoire ainsi
créée. Le programme configure la broche 27 en sortie puis change sa valeur toutes les secondes.

                                                       11
F IGURE 5 – Correspondance entre adresses de bus, physiques et virtuelles.

 1   #include   
 2   #include   
 3   #include   
 4   #include   
 5   #include   
 6   #include   
 7   #include   
 8   #include   
 9
10   #define PHYS_GPIO_BASE 0x20200000
11
12   #define GPFSEL0_OFFSET 0x00000
13   #define GPSET0_OFFSET 0x0001C
14   #define GPCLR0_OFFSET 0x00028
15
16   #define GPF_INPUT 0
17   #define GPF_OUTPUT 1
18
19   void * get_mmapped_gpio()
20   {
21     int page_size= getpagesize();
22     int fd= open("/dev/mem", O_RDWR | O_SYNC);
23     if (fd < 0) {
24       perror("open");
25       return NULL;
26     }
27
28       void * addr= mmap(NULL, page_size, PROT_READ | PROT_WRITE,
29                         MAP_SHARED, fd, PHYS_GPIO_BASE);
30       if (addr == MAP_FAILED) {
31         perror("mmap");
32         return NULL;
33       }
34
35       close(fd);
36       return addr;
37   }
38

                                                     12
39   int main()
40   {
41     void * addr= get_mmapped_gpio();
42     if (addr == NULL)
43       exit(EXIT_FAILURE);
44
45       volatile uint32_t * GPFSEL0= (volatile uint32_t *) (addr + GPFSEL0_OFFSET);
46       volatile uint32_t * GPSET0= (volatile uint32_t *) (addr + GPSET0_OFFSET);
47       volatile uint32_t * GPCLR0= (volatile uint32_t *) (addr + GPCLR0_OFFSET);
48
49       unsigned char pin= 27;
50       volatile uint32_t * GPFSEL= GPFSEL0 + (pin/10);
51       int bit_pos= 3 * (pin%10);
52       *GPFSEL= (*GPFSEL & ˜(7
3,3V     1        2    5V
     I2C SDA    GPIO 2     3        4    5V
     I2C SCL    GPIO 3     5        6    GND
     GPCLK0     GPIO 4     7        8    GPIO 14   UART TxD
                 GND       9        10   GPIO 15   UART RxD
               GPIO 17    11        12   GPIO 18   PCM CLK
               GPIO 27    13        14   GND
               GPIO 22    15        16   GPIO 23
                  3,3V    17        18   GPIO 24
    SPI MOSI   GPIO 10    19        20   GND
    SPI MISO    GPIO 9    21        22   GPIO 25
    SPI SCLK   GPIO 11    23        24   GPIO 8    SPI CE0
                 GND      25        26   GPIO 7    SPI CE1
EEPROM ID SD              27        28             EEPROM ID SC
                GPIO 5    29        30   GND
                GPIO 6    31        32   GPIO 12
               GPIO 13    33        34   GND
               GPIO 19    35        36   GPIO 16
               GPIO 26    37        38   GPIO 20
                 GND      39        40   GPIO 21

        TABLE 10 – Connecteur J8, modèles A+ et B+

                  3,3V    1          2   5V
    I2C1 SDA    GPIO 2    3          4   5V
    I2C1 SCL    GPIO 3    5          6   GND
     GPCLK0     GPIO 4    7          8   GPIO 14   UART TxD
                 GND      9         10   GPIO 15   UART RxD
               GPIO 17    11        12   GPIO 18   PCM CLK
               GPIO 27    13        14   GND
               GPIO 22    15        16   GPIO 23
                  3,3V    17        18   GPIO 24
   SPI0 MOSI   GPIO 10    19        20   GND
   SPI0 MISO    GPIO 9    21        22   GPIO 25
   SPI0 SCLK   GPIO 11    23        24   GPIO 8    SPI0 CE0
                 GND      25        26   GPIO 7    SPI0 CE1

       TABLE 11 – Connecteur P1, modèles A et B rev.2

                               14
3,3V    1          2   5V
 I2C0 SDA    GPIO 0    3          4   5V
 I2C0 SCL    GPIO 1    5          6   GND
  GPCLK0     GPIO 4    7          8   GPIO 14   UART TxD
              GND      9         10   GPIO 15   UART RxD
            GPIO 17    11        12   GPIO 18   PCM CLK
            GPIO 21    13        14   GND
            GPIO 22    15        16   GPIO 23
               3,3V    17        18   GPIO 24
SPI0 MOSI   GPIO 10    19        20   GND
SPI0 MISO    GPIO 9    21        22   GPIO 25
SPI0 SCLK   GPIO 11    23        24   GPIO 8    SPI0 CE0
              GND      25        26   GPIO 7    SPI0 CE1

    TABLE 12 – Connecteur P1, modèles A et B rev.1

                            15
Vous pouvez aussi lire