Introduction `a Raspberry Pi

Transcription

Introduction `a Raspberry Pi
Introduction à Raspberry Pi
Partie 2 : entrées/sorties
Résumé
B. Q UOITIN
D. H AUWEELE
G. H UYSMANS
Faculté des Sciences
Université de Mons
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 (https://www.sparkfun.com/products/
retired/12060).
Table des matières
1
Entrées/sorties GPIO
1
1.1
1.2
1.3
Structure d’une broche GPIO
Connecteur GPIO
Précautions et limites d’utilisation
1
1
1
2
Application : contrôle d’une LED et d’un bouton poussoir
2
2.1
2.2
2.3
Commande d’une LED
Capture d’un bouton-poussoir
Réalisation sur plaquette d’expérimentation
2
2
2
3
Contrôle logiciel
3
3.1
3.2
3.3
3.4
Contrôle en Python (RPi.GPIO)
Contrôle via sysFS
Contrôle via les registres
Interruptions
3
5
6
6
4
Extension du nombre de sorties
7
4.1
4.2
4.3
4.4
4.5
Principe de fonctionnement
Registre à décalage 74HC595
Réalisation du circuit
Logiciel de commande
Application : VU-mètre
7
7
7
8
8
TABLE DES MATIÈRES
TABLE DES MATIÈRES
5
Modulation en Largeur d’Impulsion (PWM)
10
5.1
5.2
5.3
5.4
Application : contrôle d’une LED
Synthèse PWM logicielle
Application : contrôle d’un servo-moteur
Synthèse PWM avec DMA
10
10
11
11
6
Produire et lire des valeurs analogiques
12
6.1
6.2
6.3
DAC à réseau R-2R
DAC à PWM
Lire la valeur d’une LDR
12
12
13
7
8
Afficheur LCD alphanumérique
Bus SPI, I2 C et 1-wire
15
17
8.1
8.2
8.3
1-wire
Inter-Integrated Circuit (I2 C)
Serial Peripheral Interface (SPI)
17
18
20
A
Annexe - Connecteurs d’entrées/sorties
22
B
Annexe - Contrôle des GPIOs via les registres
23
B.1
Exemple complet en C
24
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
2 / 25
1
1
Entrées/sorties GPIO
I2C1 SDA
I2C1 SCL
GPCLK0
Le processeur ou plus exactement System-on-Chip (SoC) qui
équipe la platine Raspberry Pi porte le doux nom de BCM2835.
Il possède un grand nombre de portes d’entrées/sorties. Cellesci 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 Input/Output. Le comportement et l’état de ces ports d’entrées/sorties est contrôlable par
programme.
1.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 1 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.
SPI0 MOSI
SPI0 MISO
SPI0 SCLK
1.2 Connecteur GPIO
Le BCM2835 dispose de 54 lignes GPIO. Certaines sont accessibles au travers de l’un des connecteurs de la platine. Les broches
GPIO accessibles dépendent de la version de la platine Raspberry
Pi. Par exemple, sur les modèles A et B (rev. 1 et 2) 17 GPIO
sont accessibles au travers du connecteur P1. Ce connecteur de 26
broches est illustré à la Table 1. Les connecteurs des autres modèles
de Raspberry Pi sont illustrés à la Section A en Annexe.
Toutes les broches du connecteur ne sont pas des GPIO du processeur. Les broches marquées 3,3V, 5V ou GND (0V) permettent
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
2
4
6
8
10
12
14
16
18
20
22
24
26
5V
5V
GND
GPIO 14
GPIO 15
GPIO 18
GND
GPIO 23
GPIO 24
GND
GPIO 25
GPIO 8
GPIO 7
UART TxD
UART RxD
PCM CLK
SPI0 CE0
SPI0 CE1
d’accéder à l’alimentation électrique de la platine Raspberry Pi.
Les autres broches sont reliées aux GPIO du processeur. Elles sont
étiquettées avec le numéro de broche GPIO et lorsque c’est possible par une fonction alternative.
Par exemple, on peut observer sur la Table 1 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.
Précautions et limites d’utilisation
+
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 configuré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. Intuitivement, ces résistances fixent l’état logique par défaut de la broche.
1
3
5
7
9
11
13
15
17
19
21
23
25
TABLE 1 – Connecteur P1, modèles A et B rev.2
1.3
F IGURE 1 – Structure d’une broche d’I/O.
3,3V
GPIO 2
GPIO 3
GPIO 4
GND
GPIO 17
GPIO 27
GPIO 22
3,3V
GPIO 10
GPIO 9
GPIO 11
GND
ENTRÉES/SORTIES GPIO
Attention ! L’utilisation des broches d’entrées/sorties
nécessite de respecter certaines limitations. Dans le cas
contraire, la broche d’entrée/sortie voir le SoC peuvent
être irrémédiablement endommagés ! Ni la platine
Raspberry Pi ni le SoC BCM2835 ne fournissent de
mécanisme de protection contre les surtensions ou les
courants trop forts.
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 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.
1 / 25
2
2
APPLICATION : CONTRÔLE D’UNE LED ET D’UN BOUTON POUSSOIR
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 2 et
3 présentent le schéma des circuits correspondants.
2.1 Commande d’une LED
Le circuit de commande de la LED (LD1) est articulé autour
de 2 résistances (R1 et R2) et d’un transistor bipolaire NPN (Q1).
Le transistor est commandé par la broche GPIO 2. L’usage du
transistor permet de limiter le courant tiré de cette broche 1 à environ 0,3mA, ce qui est bien en dessous des limites discutées en
Section 1.3. La résistance R1 de 330 ohms limite le courant qui
traverse la LED 2 à environ 4mA.
F IGURE 3 – Capture d’un bouton poussoir avec une entrée digitale.
la paquette d’expérimentation. A l’intérieur de la plaquette, les trous sont mis en contact selon un pattern
précis.
Les lignes de trous sur le haut et le bas de la plaquette
sont connectés ensemble suivant les lignes bleues
et rouges. Au centre de la plaquette, les trous sont
connectés par groupe de 5, en colonnes.
La première étape de la réalisation de ce circuit est l’identification des différents composants et de leurs broches.
— Résistances R1 et R2. Les résistances ont 2 broches. Leur
sens n’a pas d’importance. La valeur d’une résistance est
marquée à l’aide d’un code de couleurs. La résistance R1
F IGURE 2 – Commande d’une LED avec une sortie digitale.
de 330Ω a le code orange/orange/brun/or tandis que la
résistance R2 de 10kΩ a le code brun/noir/orange/or.
— LED LD1. La LED a 2 broches : une anode et une cathode.
2.2 Capture d’un bouton-poussoir
Celles-ci sont identifiées respectivement avec les lettres ”a”
Le circuit de lecture du bouton poussoir (PB1) est articulé auet ”c” sur le schéma. Sur le composant, la cathode est la
tour de 2 résistances (R1 et R2). En fonction de l’état du bouton
broche la plus courte.
poussoir, la tension présente sur la broche GPIO 3 sera proche de
— Transistor Q1. Le transistor a 3 broches : une base, un collec0V (état logique bas) ou proche de 3.3V (état logique haut). La
teur et un émetteur. Celles-ci sont identifiées respectivement
résistance R2 limite le courant à environ 10mA dans le cas où la
avec les lettres ”b”, ”c” et ”e” sur le schéma. La Figure 4
broche est configurée en sortie, ce qui reste dans les limites perdonne le brochage du transistor.
mises. La résistance R1 est une résistance dite de pull-up qui
détermine l’état par défaut de la broche (état logique haut). Cette
résistance est optionnelle 3
2.3 Réalisation sur plaquette d’expérimentation
La Figure 5 montre comment le circuit de contrôle de la LED
peut être implémenté sur une plaquette d’expérimentation (breadboard).
+
Utilisation d’une plaquette d’expérimentation. Les
composants et câbles sont enfichés dans les trous de
1. Le courant traversant la jonction base-émetteur du transistor est égal à Ibe =
(3, 3V − 0, 7V )/10kΩ ≈ 0, 26mA.
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, Vf orward ) est égale
à environ 2V pour une LED émettant dans le rouge. Le courant qui traverse la LED
peut être calculé simplement comme Ice = (3, 3V − Vf orward )/330Ω ≈ 4mA
3. 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.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
F IGURE 4 – Broches du transistor 2N2222.
La seconde étape de la réalisation est le placement des composants sur la plaquette d’expérimentation et leur câblage.
2 / 25
3
CONTRÔLE LOGICIEL
F IGURE 5 – Commande d’une LED avec une sortie digitale.
3
Contrôle logiciel
par seconde.
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 sera l’API RPi.GPIO au travers d’un programme écrit en
langage Python. Le second moyen sera l’API SysFS, qui permet
de manipuler les entrées/sorties comme des fichiers (virtuels).
Finalement, le troisième moyen sera l’accès direct aux registres
du SoC contrôlant les entrées/sorties, grâce à un programme
écrit en langage C. 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 2 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 4 . On peut constater que l’accès direct aux registres permet
de faire changer l’état d’une sortie digitale plusieurs dizaines de
millions de fois par seconde alors qu’avec l’API SysFS, il n’est
pas possible d’effectuer plus de quelques milliers de changements
4. http://codeandlife.com/2012/07/03/
benchmarking-raspberry-pi-gpio-speed
Language
Shell
Python
Python
C
C
C
Perl
Library
SysFS
RPi.GPIO
wiringPi
/dev/mem + mmap
BCM 2835
wiringPi
BCM 2835
3.1 Contrôle en Python (RPi.GPIO)
Un moyen simple de contrôler les entrées/sorties est d’utiliser le
langage de programmation Python au travers de l’API RPi.GPIO.
Il s’agit d’une API spécifique à la plateforme Raspberry Pi.
+
Pour en savoir plus sur le langage Python, il est
possible de se référer à un tutoriel en ligne tel
que par exemple https://docs.python.org/
2/tutorial/
L’API RPi.GPIO est très simple. Elle est résumée à
la Table 3. La documentation complète peut être obtenue
à
l’adresse
http://sourceforge.net/p/
raspberry-gpio-python/wiki.
La fonction setmode doit être appelée avant toute utilisation de l’API. Cette fonction spécifie comment les broches
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).
Version / tested
N.A. / July 3, 2012
0.3.0 / August 1, 2012
github / August 14, 2012
N.A. / July 3 and August 14, 2012
1.3 ? / July 3, 2012
not available / August 14, 2012
1.0 / July 3, 2012
Square wave
3.4 kHz
44 kHz
20 kHz
14-22 MHz
4.7 - 5.1 MHz
6.9 - 7.1 MHz
35 kHz
TABLE 2 – Comparaison des différentes API pour le contrôle d’une sortie digitale.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
3 / 25
3.1
Contrôle en Python (RPi.GPIO)
3
CONTRÔLE LOGICIEL
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 restaure la configuration des broches.
input(broche)
Lit l’état d’une broche.
output(pin,state)
Définit l’état d’une broche.
TABLE 3 – Résumé de l’API python RPi.GPIO.
La fonction output permet de changer l’état d’une broche (configurée en sortie).
La suite de cette section présente plusieurs exemples d’utilisation de l’API. Le premier exemple contrôle une sortie digitale et en
change régulièrement l’état. Le module RPi.GPIO est importé à
la ligne 1. A la ligne 4 la fonction setmode requiert l’usage de
la numérotation des broches selon le SoC. La ligne 5 configure la
broche GPIO 3 en sortie. La variable booléenne state déclarée
à la ligne 7 contient le prochain état de la sortie. Les lignes 9 à 12
contiennent une boucle effectuant 20 itérations. 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 GPIO 3.
+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Usage de l’indentation. En Python, il n’y a pas de
délimiteurs de bloc. C’est l’indentation qui permet de
déterminer à quel bloc une instruction appartient. Dans
l’exemple ci-dessous, les instructions des lignes 10 à
12 font partie de la boucle for.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(3, GPIO.OUT)
state= False
for i in range(20):
GPIO.output(3, state)
state= not(state)
time.sleep(1)
GPIO.cleanup()
pi@rpi:˜$ sudo python gpio-out.py
(...broche 3 devrait changer...)
pi@rpi:˜$
Le second exemple contrôle une broche d’entrée. La ligne
5 configure la broche GPIO 2 en entrée. Le programme est
constitué d’une boucle sans fin qui s’étale sur les lignes 9 à 18. La
ligne 10 lit l’état de la broche 2 et le stocke dans la variable state.
La ligne 11 attend 100ms 5 . 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. Sinon,
l’ancien état devient le nouvel état à la ligne 14. Aux lignes 15 à
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(2, GPIO.IN)
oldState= None
while True:
state= GPIO.input(2)
time.sleep(0.1)
if state == oldState:
continue
oldState= state
if state:
print "button released"
else:
print "button pressed"
GPIO.cleanup()
Afin d’exécuter le programme ci-dessus, il faut le placer dans
L’exemple ci-dessus a un inconvénient : l’appel à la fonction
un fichier texte (utiliser l’éditeur nano par exemple). Ici, nous cleanup de la ligne 20 n’est jamais réalisé. La raison est que le
supposons que le programme a été sauvegardé sous le nom programme est terminé abruptement par la pression des touches
gpio-out.py. Il peut ensuite être exécuté comme suit. L’usage Ctrl-C . Ceci résulte en l’envoi au programme d’un signal d’inde sudo est requis car le contrôle des GPIO nécessite des pri- terruption. Il est possible de capturer ce signal afin d’interrompre le
vilèges d’administrateur.
5. Sans cette attente, le programme va en permanence exécuter la boucle et
consommer 100% du CPU.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
4 / 25
3.2
Contrôle via sysFS
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
2
3
4
5
6
7
8
9
try:
while True:
state= GPIO.input(2)
time.sleep(0.1)
# ...
except KeyboardInterrupt:
pass
GPIO.cleanup()
Pour terminer cette section, notons que RPi.GPIO permet d’activer les résistances pull-up ou pull-down intégrées au SoC. Cette
fonction est réalisée par la fonction setup. Par exemple, si le programme est utilisé avec le circuit bouton poussoir de la Figure 3, la
résistance R1 de 10kΩ doit être utilisée 6 . 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
2
GPIO.setup(22, GPIO.IN, \
pull up down=GPIO.PUD UP)
3
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 4 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 4 – Fichiers SysFS sous /sys/class/gpio
Lorsqu’une broche d’entrée/sortie passe sous le contrôle de
SysFS, de nouveaux fichiers virtuels apparaissent dans le répertoire
/sys/class/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 sortie (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
3.2 Contrôle via sysFS
SysFS est un autre moyen de contrôler les GPIOs 7 . Celui-ci suit
la philosphie UNIX selon laquelle tout peut être contrôlé au travers de fichiers. SysFS est 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 6, 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.
CONTRÔLE LOGICIEL
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 5 – /sys/class/gpio/gpion
Pour illustrer le contrôle des GPIOs via SysFS, nous prenons
deux exemples. Le premier exemple contrôle la broche GPIO 3
en sortie. Cet exemple est illustré à la Figure 7. La première étape
consiste à mettre GPIO 3 sous le contrôle de SysFS en écrivant
la valeur 3 dans le fichier export. La commande echo est utilisée pour afficher une chaı̂ne de caractères. Ici, la sortie de echo
est redirigé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/class/gpio afin de contrôler la broche 3. La
commande ls est utilisée pour montrer le contenu du répertoire
/sys/class/gpio/gpio3 ainsi créé. Ensuite, la valeur out
F IGURE 6 – Illustration de l’interface SysFS.
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é.
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
Le second exemple contrôle la broche GPIO 2 en entrée.
Cet
exemple est illustré à la Figure 8. La différence avec
6. Sauf dans le cas de GPIO 2 et GPIO 3 (modèles rev.2 ou plus) pour lesl’exemple précedent est que la valeur in est écrite dans le fichier
quels des résistances de pull-up externe de 1,8KΩ sont présentes sur la platine
7. voir Ottawa Linux Symposium, 2005 - https://www.kernel.org/ direction. De plus, le fichier value est lu afin d’obtenir l’état
pub/linux/kernel/people/mochel/doc/papers/ols-2005/
de la broche.
mochel.pdf et https://www.kernel.org/doc/Documentation/
gpio/sysfs.txt
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
5 / 25
3.3
Contrôle via les registres
3
CONTRÔLE LOGICIEL
pi@rpi:˜$ echo "3" > /sys/class/gpio/export
pi@rpi:˜$ ls /sys/class/gpio/gpio3
active_low direction edge power subsystem uevent value
pi@rpi:˜$ echo "out" > /sys/class/gpio/gpio3/direction
pi@rpi:˜$ echo "1" > /sys/class/gpio/gpio3/value → LED ON
pi@rpi:˜$ echo "0" > /sys/class/gpio/gpio3/value → LED OFF
pi@rpi:˜$ echo "3" > /sys/class/gpio/unexport
F IGURE 7 – Contrôle de la broche GPIO 3 en sortie via SysFS.
pi@rpi:˜$ echo "2" > /sys/class/gpio/export
pi@rpi:˜$ ls /sys/class/gpio
gpio2 gpiochip0
pi@rpi:˜$ echo "in" > /sys/class/gpio/gpio2/direction
pi@rpi:˜$ cat /sys/class/gpio/gpio2/value
... returns 1 (button released) or 0 (button pressed) ...
pi@rpi:˜$ echo "2" > /sys/class/gpio/unexport
F IGURE 8 – Contrôle de la broche GPIO 2 en entrée via SysFS.
3.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 2). Ces limites de performances proviennent essentiellement
de la lourdeur des appels système. Derrière SysFS et RPi.GPIO se
trouvent des manipulations des registres spéciaux du SoC dédiés
au contrôle des GPIOs.
Il est possible d’écrire un programme qui manipule directement
ces registres. Cependant, ces manipulations sont relativement complexes pour quelqu’un qui n’est pas familier avec la programmation
système en langage C. Pour cette raison, la présentation de l’accès
direct aux registres est placée en Annexe, à la Section B.
3.4
Interruptions
Il n’est toujours pratique ou efficace de réagir à des événements
externes détectés au travers d’une entrée digitale avec les APIs
présentées dans les sections précédentes. Il est nécessaire que le
programme scrute sans cesse l’état de l’entrée digitale concernée.
Il existe pourtant un moyen matériel de détecter des changements
d’état d’une broche d’entrée digitale : une interruption matérielle.
Cependant, la gestion des interruptions ne peut être réalisée qu’au
sein du noyau linux.
Heureusement, SysFS fournit un moyen de reporter vers l’espace utilisateur les interruptions GPIO gérées par le kernel, sous
la forme d’événements fichiers (urgent data). Il est ainsi possible qu’un programme utilisateur soit bloqué (endormi) jusqu’à
ce qu’un tel événement soit reçu !
Les commandes de la Figure 9 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.
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
2
3
4
5
6
7
8
9
10
11
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(2, GPIO.IN)
while True:
GPIO.wait_for_edge(2, GPIO.BOTH)
if GPIO.input(2):
print "button released"
else:
print "button pressed"
pi@rpi:˜$ echo "3" > /sys/class/gpio/export
pi@rpi:˜$ echo "in" > /sys/class/gpio/gpio3/direction
pi@rpi:˜$ echo "both" > /sys/class/gpio/gpio3/edge
pi@rpi:˜$
F IGURE 9 – Activation des interruptions pour la broche GPIO 3.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
6 / 25
4
4
Extension du nombre de sorties
Le nombre de broches d’entreés/sorties disponibles sur la platine
Raspberry Pi est limité. Pour certains projets, davantage de sorties sont nécessaires. Un registre à décalage est un moyen simple
d’augmenter le nombre de sorties.
4.1 Principe de fonctionnement
Un registre à décalage est une petite mémoire (de par exemple 8
bits) qui a la particularité de pouvoir être chargée bit à bit. Une
seule sortie GPIO est nécessaire pour définir le prochain bit à
charger dans le registre. Il est également nécessaire de contrôler
quand un bit est chargé. Cela est réalisé par un signal d’horloge
qui rythme le chargement des bits dans le registre. Ce signal d’horloge est également généré par une sortie GPIO. Par conséquent,
l’interfaçage avec un registre à décalage peut ne nécessiter que 2
broches de sorties.
4.2 Registre à décalage 74HC595
Dans cette section le circuit intégré 74HC595 est utilisé. Il s’agit
d’un registre à décalage de 8 bits à entrée série et sortie parallèle.
Un tel registre permet de disposer de 8 sorties en utilisant seulement 2 (ou 3) sorties sur la platine Raspberry Pi. De plus, plusieurs
74HC595 peuvent être connectés en chaı̂ne, de façon à augmenter
plus encore le nombre de sorties.
La Figure 10 donne le schéma fonctionnel du circuit intégré
74HC595. Ce circuit contient un registre à décalage (8-stage shift
register), un registre de stockage (8-bit storage register) et un tampon de sortie (3-state outputs). Les entrées et sorties du 74HC595
sont décrites ci-dessous.
EXTENSION DU NOMBRE DE SORTIES
copie a lieu lors d’un flanc montant de l’entrée STCP. Le
registre de stockage permet que de nouvelles données soient
chargées dans le registre à décalage sans impact sur les sorties du circuit.
— L’état des 8 bits du registre de stockage sont disponibles sur
les sorties Q0 à Q7.
— L’entrée /OE (Output Enable) détermine si les sorties sont
connectées ou isolées 9 du registre de stockage par le tampon
de sortie. Lorsque la broche /OE est à l’état bas, les sorties
sont conenctées au registre de stockage.
4.3 Réalisation du circuit
La Figure 11 illustre l’utilisation d’un registre à décalage pour
le contrôle de jusqu’à 8 LEDs. Dans ce circuit, les sorties du registre à décalage 74HC595 sont utilisées pour piloter directement
les LEDs. Le circuit n’illustre que 4 LEDs connectées aux sorties
Q0 à Q3, mais l’extension à 8 LEDs est similaire. Pour chaque
LED, une résistance est placée en série de façon à limiter le courant la traversant 10 .
14 DS
11 SHCP
10 MR
8-STAGE SHIFT REGISTER
Q7S
12 STCP
9
F IGURE 11 – Commande de 4 LEDs avec un registre à décalage.
8-BIT STORAGE REGISTER
Les données sont envoyées à partir de la platine Raspberry Pi
en
utilisant les broches GPIO 23 à 25. La broche 23 est connectée
13 OE
3-STATE OUTPUTS
à l’entrée DS et fournira séquentiellement la valeur de chaque bit
Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7
à stocker dans le registre à décalage. La broche 24, connectée à
15 1
2 3 4 5 6 7
mna554
l’entrée SHCP, fournira après chaque bit une impulsion, de façon à
stocker ce bit dans le registre (en en décalant le contenu). La broche
F IGURE 10 – Schéma fonctionnel (source : 74HC595 datasheet, 25, connectée à l’entrée STCP, fournira après chaque 8 bits une impulsion de façon à stocker le contenu du registre à décalage dans le
NXP)
registre de sortie (storage register). Note : d’autres broches GPIO
peuvent être utilisées si nécessaire. Il suffira dans ce cas d’adapter
— L’entrée DS détermine la valeur d’un nouveau bit à stocker
le programme de pilotage.
dans le registre à décalage.
L’entrée /MR de remise à zéro du registre à décalage est
— L’entrée SHCP (SHift-register control input) décale tous les
connectée à 3,3V via la résistance de pull-up R1. L’entrée /OE
bits du registre à décalage d’un bit. Ainsi, le bit 7 prend la
d’activation des sorties est connectée à la masse (GND) via la
valeur du bit 7, le bit 6 prend la valeur du bit 5, etc. Le bit
résistance de pull-down R2.
1 prend la valeur donnée par l’entrée DS. Ce décalage a lieu
Ce circuit peut facilement être réalisé sur une plaquette
lorsque l’état de SHCP passe de bas à haut (flanc montant).
d’expérimentation. Il est nécessaire pour cela de disposer du
— L’entrée /MR (Master Reset) permet de remettre le contenu
schéma de brochage du registre à décalage 74HC595. Celui-ci est
du registre à décalage à zéro. Cette remise à zéro a lieu
exposé à la Figure 12.
lorsque l’entrée /MR est à l’état bas 8 .
— L’entrée STCP (STorage control input.) permet de copier les
9. On parle d’état haute impédance.
10. Le courant par broche Qi ne doit pas dépasser 35 mA et le courant total ne
bits du registre à décalage dans le registre de stockage. Cette
8. La barre qui précède le nom de la broche /MR indique qu’elle est active au
niveau bas.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
doit pas dépasser 70 mA. Une résistance de 330 Ωhm devrait convenir, limitant le
courant par broche à environ 4 mA et donc le courant total à 32 mA si toutes les
LEDs sont allumées simultanément
7 / 25
4.4
Logiciel de commande
4
74HC595
74HCT595
Q1
1
16 VCC
Q2
2
15 Q0
Q3
3
14 DS
Q4
4
13 OE
Q5
5
12 STCP
Q6
6
11 SHCP
Q7
7
10 MR
GND
8
9
Q7S
001aao241
F IGURE 12 – Schéma de brochage (source : 74HC595 datasheet,
NXP)
4.4
Logiciel de commande
Pour écrire dans le registre à décalage, il faut pouvoir commander les broches DS, SHCP et STCP à partir du Raspberry Pi.
L’écriture d’un octet dans le registre à décalage est effectué bit à
bit, en commençant par le bit de poids fort.“
Afin d’illustrer la procédure à suivre, la Figure 13 présente une
trace des signaux envoyés aux entrées DS, SHCP et STCP lors de
l’écriture d’un octet de valeur 0x35 dans le registre à décalage. La
ligne intitulée D1 correspond à l’entrée SHCP. On y observe 8 impulsions, une pour chaque bit de l’octet chargé. La ligne intitulée
D0 correspond à l’entrée DS. On y observe les 8 bits 00110101
de l’octet 0x35. La lidgen intitulée D2 correspond à l’entrée
STCP. On y observe une seule impulsion après le transfert des 8
bits.
Le programme suivant illustre comment écrire en octet en utilisant Python et l’API RPi.GPIO. La fonction shift init initialise les GPIOs : 3 sorties (23, 24 et 25) sont utilisées. Les sorties
correspondant aux d’horloge SHCP et STCP sont initialisée à 0.
La fonction shift pulse génère une impulsion sur une sortie
donnée. Cette fonction est utilisé pour générer un flanc montant
soit sur SHCP soit sur STCP. La fonction shift send byte
effectue l’envoi des 8 bits d’un octet vers le registre à décalage.
Les bits sont envoyés séquentiellement en commençant par le bit
de poids fort (son poids est égal à 0x80). Après chaque bit, une
EXTENSION DU NOMBRE DE SORTIES
impulsion est générée sur SHCP. Après l’envoi des 8 bits, une impulsion est générée sur STCP de façon à charger l’octet dans le
registre de stockage.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
4.5
import RPi.GPIO as GPIO
import sys, time
PIN_DS= 23
PIN_SHCP= 24
PIN_STCP= 25
def shift_init():
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_DS, GPIO.OUT)
GPIO.setup(PIN_SHCP, GPIO.OUT)
GPIO.setup(PIN_STCP, GPIO.OUT)
GPIO.output(PIN_SHCP, False)
GPIO.output(PIN_STCP, False)
def shift_pulse(pin):
time.sleep(0.001)
GPIO.output(pin, True)
time.sleep(0.001)
GPIO.output(pin, False)
def shift_send_byte(b):
for i in range(8):
GPIO.output(PIN_DS, b & 0x80)
shift_pulse(PIN_SHCP)
b= b << 1
GPIO.output(PIN_DS, False)
shift_pulse(PIN_STCP)
shift_init()
count= 0
try:
while True:
shift_send_byte(count);
time.sleep(1)
count= (count + 1) & 0xFF
except KeyboardInterrupt:
pass
GPIO.cleanup()
Application : VU-mètre
Une application possible de l’extension des sorties avec un registre à décalage est la réalisation d’un VU-mètre. Le nombre de
LEDs allumées doit refléter le niveau sonore d’une application.
Afin de réaliser une telle application, il est nécessaire de capturer
le signal sonore joué par une application.
Pour jouer un son, il est possible d’utiliser le programme aplay
comme illustré ci-dessous.
pi@rpi:˜$ aplay SIREN.WAV
F IGURE 13 – Signaux des entrées DS (D0), SHCP (D1) et STCP
(D2) lors de l’écriture d’un octet dans le registre à décalage.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
Il est possible de capturer le son d’une application en passant par
un périphérique audio virtuel. Le listing suivant montre le contenu
du fichier ˜/.asoundrc dans lequel un périphérique audio virtuel nommé capture est créé. Celui-ciduplique les échantillons
8 / 25
4.5
Application : VU-mètre
4
EXTENSION DU NOMBRE DE SORTIES
de son produit par l’application et les envoie vers un programme
python nommé snd.py.
pcm.capture {
type plug
slave.pcm {
type file
slave.pcm "hw:0,0"
file "|python -u snd.py"
format "raw"
}
}
Le programme python montré ci-dessous récupère des
échantillons à partir de l’entrée standard (stdin) en détermine
le maximum (variable maximum) et affiche une barre verticale
composée de caractères # qui reflète le volume sonore mesuré. Le
maximum est calculé sur chaque 32 échantillons. Le programme
fait l’hypothèse que les échantillons sonores sont encodés sur 16
bits en little-endian (octet de poids fort reçu en premier). Les
échantillons sont signés (complément à 2).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import os, sys
print "Starting python script"
while True:
data= sys.stdin.read(64)
if len(data) == 0:
break
samples= []
for i in range(len(data)/2):
int16= ord(data[2*i]) +
(ord(data[2*i+1]) << 8)
if int16 > 32767:
int16= -(65536-int16)
samples.append(int16)
maximum= max(samples)
value= maximum
print "\r%s%s" % \
("#"*((20*value)/32768), " "*20),
sys.stdout.flush()
print
print "done."
pi@rpi:˜$ aplay -D capture SIREN.WAV
Playing WAVE ’SIREN.WAV’ : Signed 16 bit
Little Endian, Rate 22050 Hz,
Mono
Starting python script
#########
done.
pi@rpi:˜$
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
9 / 25
5
5
Modulation en Largeur d’Impulsion (PWM)
Un moyen classique pour contrôler de façon numérique l’intensité d’une LED ou la vitesse d’un moteur est la modulation en
largeur d’impulsion ou encore PWM (Pulse Width Modulation).
Cette technique peut aussi être utilisée pour générer un signal audio (voix, musique) ou encore pour contrôler le positionnement de
servo-moteurs.
Le principe de la modulation en largeur d’impulsion est simple :
on génère à partir d’une sortie digitale un signal de fréquence
constante, mais de rapport cyclique variable. Le signal est tantôt
haut, tantôt bas et le rapport cyclique désigne le rapport entre la
durée du signal haut et la période du signal. Le rapport cyclique
peut varier entre 0 et 100%. Par exemple, un rapport cyclique de
25% correspond à un signal dans laquelle la partie haute dure 25%
de la période. La Figure 14 illustre ce concept avec trois signaux
de même fréquence mais 3 rapports cycliques différents.
MODULATION EN LARGEUR D’IMPULSION (PWM)
cas, les signaux PWM permettent de contrôler l’intensité de chaque
couleur séparément.
F IGURE 15 – Commande d’une LED par PWM.
5.2
Synthèse PWM logicielle
Cette section montre comment générer de façon logicielle un
signal PWM via l’API python RPi.GPIO 11 . L’API est résumée à
la Table 6. Pour générer un signal PWM à partir d’une broche,
il est nécessaire d’associer cette broche à un générateur qui se
charge d’en changer automatiquement l’état. Ce générateur est
créé avec l’appel 12 à PWM. Cet appel prend deux arguments :
le numéro de la broche et la fréquence du signal. Les méthodes
start et stop permettent respectivement de démarrer et arrêter
la génération du signal. Les méthodes ChangeDutyCycle et
ChangeFrequency permettent respectivement de changer le
F IGURE 14 – Signaux générés par PWM.
rapport cyclique et la fréquence du générateur.
(source : electronics.stackexchange.com)
Le programme donné en exemple ci-dessous peut être utilisé
pour contrôler l’intensité d’une LED connectée à la broche GPIO
Afin de générer un signal PWM, plusieurs approches sont pos- 3. Un signal PWM de fréquence égale à 1kHz est généré. Le prosibles. La plupart du temps, ce signal est généré sur un processeur gramme fait varier toutes les 100 ms le rapport cyclique de ce siou microcontrôleur qui dispose d’un périphérique PWM. Dans ce gnal.
cas, le signal PWM est généré par le matériel. C’est par exemple le
1 import RPi.GPIO as GPIO
cas avec le microcontrôleur ATmega328 au coeur de la plateforme
2 import time
Arduino Uno.
3
Le SoC du Raspberry Pi possède aussi un périphérique PWM,
4 GPIO.setmode(GPIO.BCM)
cependant il est déjà utilisé par la sortie audio. Pour cette raison,
5 GPIO.setup(3, GPIO.OUT)
il est préferable de générer le signal de façon logicielle tel qu’ex6 p= GPIO.PWM(3, 1000)
pliqué en Section 5.2. Si la résolution du signal PWM doit être plus
7 p.start(10)
grande, il est également possible de s’appuyer sur le contrôleur
8
DMA du SoC, tel qu’expliqué en Section 5.4.
9 dc= 10
10
delta_dc= 10
11
5.1 Application : contrôle d’une LED
12 while True:
Cette section montre comment contrôler l’intensité d’une LED.
13
dc+= delta_dc
Le signal PWM fait donc clignoter la LED à une fréquence suffi14
if (dc <= 0) or (dc >= 100):
samment élevée que pour ne pas être perçue par l’oeil (p.ex. à une
15
delta_dc= -delta_dc
fréquence de 1000 Hertz). Moins le rapport cyclique du signal est
16
time.sleep(0.1)
grand, moins la LED est éclairée, et inversément. Ceci peut être ex17
p.ChangeDutyCycle(dc)
pliqué aisément sur base de la Figure 14 où l’on constate que plus
le rapport cyclique est faible (durée de l’impulsion courte), moins
la tension moyenne (Vaverage ) est élevée.
11. Voir http://sourceforge.net/p/raspberry-gpio-python/
Le circuit montré à la Figure 15 permet de contrôler une LED via
wiki/PWM/
PWM. Il s’agit du même circuit qu’utilisé pour tester la commande
12. Note : l’appel GPIO.PWM crée une instance de la classe GPIO.PWM. Il
d’une sortie digitale. Une variante de ce circuit pourrait être uti- s’agit d’un exemple de programmation orienté-objet en python. L’instance de PWM
lisée pour contrôler une LED à plusieurs couleurs (RGB). Dans ce est associée à un thread qui se charge de changer l’état de la broche.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
10 / 25
5.3
Application : contrôle d’un servo-moteur
5
MODULATION EN LARGEUR D’IMPULSION (PWM)
Fonction
Description
PWM(broche, f )
p.start(rc)
p.stop()
p.ChangeDutyCycle(rc)
p.ChangeFrequency(f )
Crée un générateur de signal PWM de fréquence f associé à la broche.
Démarre le générateur PWM p avec un rapport cyclique rc.
Arrête le générateur PWM p.
Change le rapport cyclique du générateur PWM p à la valeur rc.
Change la fréquence du générateur PWM p à la valeur f .
TABLE 6 – API permettant de générer un signal PWM avec RPi.GPIO.
5.3 Application : contrôle d’un servo-moteur
Une autre application de la commande PWM est le contrôle
de position d’un servo-moteur. Un tel moteur contient un circuit électronique interne qui détermine l’angle de rotation du moteur à partir du signal PWM de commande. L’angle du moteur
est une fonction linéaire de la durée de l’impulsion haute du signal PWM. La plupart des servo-moteurs imposent que la période
du signal PWM soit d’environ 20ms et que la durée d’impulsion
soit comprise entre 1 et 2ms. Ainsi, si l’angle peut varier entre
αmin (p.ex. 0°) et αmax (180°), l’angle actuel est obtenu par
∗ (αmax − αmin ) où pulse est la durée de l’imαmin + pulse−1ms
1ms
pulsion haute du signal PWM.
Le schéma de la Figure 16 illustre comment interconnecter un
servo-moteur à la platine Raspberry Pi. Le moteur nécessitant un
courant important, il est fortement déconseillé de l’alimenter à partir du Raspberry Pi. Sur le schéma, une alimentation externe est
utilisée. Il peut s’agir de piles mises en série, d’une alimentation
de laboratoire, etc.
F IGURE 16 – Branchement d’un servo-moteur au Raspberry Pi et
à une alimentation externe.
signal PWM dans un buffer en mémoire. Le contenu de ce buffer
est alors transféré par DMA vers les registres qui contrôlent une
broche d’entrée/sortie. Cette approche permet de générer un signal
PWM beaucoup plus stable et précis que la technique logicielle de
la Section 5.2.
Le module RPIO n’est pas installé par défaut. Pour l’installer,
exécuter les commandes suivantes.
pi@rpi:˜$ sudo apt-get install
python-setuptools
pi@rpi:˜$ sudo easy install -U RPIO
L’exemple suivant montre comment l’API peut être utilisée pour
commander un servo-moteur. Les durées d’impulsion du tableau
sont envoyées à des intervalles de 2 secondes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from RPIO import PWM
import time
servo = PWM.Servo()
PIN_SERVO = 17
POS = [1200, 1500, 1800]
try:
i= 0
while True:
servo.set_servo(PIN_SERVO, POS[i])
time.sleep(2)
i= (i + 1) % len(POS)
finally:
servo.stop_servo(PIN_SERVO
5.4 Synthèse PWM avec DMA
L’API PWM de RPi.GPIO n’est pas très adaptée à la commande de servo-moteur. Le signal digital étant généré de façon
logicielle, il peut-être perturbé notamment par l’exécution simultanée d’autres programmes.
Il existe une seconde méthode pour générer un signal PWM sur
la plateforme Raspberry Pi. Cette méthode repose sur l’utilsiation
du DMA (Direct Memory Access). Le SoC BCM2835 contient un
contrôleur DMA (matériel) qui peut se charger d’effectuer des copies des données entre différentes zones mémoire sans que le processeur n’intervienne. Le module python RPIO propose une API 13
permettant d’effectuer la génération d’un signal PWM en utilisant
le contrôleur DMA. L’idée est de préparer les différents états du
13. https://pythonhosted.org/RPIO/pwm_py.html
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
11 / 25
6
6
Produire et lire des valeurs analogiques
La platine Raspberry Pi n’intègre ni convertisseur digital analogique (DAC) ni convertisseur analogique digital (ADC). Il est
cependant facile d’en ajouter. La première solution possible est
d’utiliser des convertisseurs tout faits. Les sous-sections suivantes
présentent plusieurs approches alternatives.
6.1 DAC à réseau R-2R
Le circuit de la Figure 17 est un DAC basé sur un réseau appelé
R-2R car composé de résistances de valeur R et 2 × R. Le circuit
présenté est un DAC à 4 bits permettant de produire une tension
allant de 0 à 3,1V en 15 pas de 200mV. Le choix de la tension de
sortie (Output) est déterminé par le Raspberry Pi via les 4 sorties
x
×
GPIO 22 à 25. La tension en sortie est donnée par Vout = 16
3, 3V où x est le nombre binaire correspondant aux bits GPIO 22
à 25.
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
PRODUIRE ET LIRE DES VALEURS ANALOGIQUES
GPIO.setup(PIN_B2, GPIO.OUT)
GPIO.setup(PIN_B3, GPIO.OUT)
x= 0
try:
while True:
GPIO.output(PIN_B0,
GPIO.output(PIN_B1,
GPIO.output(PIN_B2,
GPIO.output(PIN_B3,
x= (x + 1) % 16
time.sleep(2)
except KeyboardInterrupt:
pass
x
x
x
x
&
&
&
&
1)
2)
4)
8)
GPIO.cleanup()
La Figure 18 montre l’évolution de la tension en sortie du réseau
R-2R. Le signal est celui produit par le programme ci-dessus : la
valeur monte progressivement, par pas de 200 mV jusqu’à un maximum, puis elle recommence à 0 V. La Figure montre également les
valeurs des entrées du réseau R-2R, i.e. les sorties GPIOs 22 (D0)
à 25 (D3)
F IGURE 17 – Convertisseur DAC basé sur un réseau R-2R.
L’amplificateur opérationnel LM358 du circuit n’est pas strictement nécessaire. Il agit comme tampon entre la sortie et le réseau
R-2R (amplificateur à gain unitaire). La tension d’alimentation 14 F IGURE 18 – Signaux des bits d’entrées et de la sortie analogique
du réseau R-2R.
du LM358 est 5V.
Le programme ci-dessous pilote le réseau R-2R. Les GPIO 22 à
25 sont configurées en sortie. La valeur de sortie (variable x) varie
de 0 à 15 à raison d’un incrément de 1 par itération. Ce comportement est répété en boucle. Les valeurs des GPIO sont les valeurs 6.2 DAC à PWM
des bits représentant x.
La circuit de la Figure 19 illustre comment il est possible de
générer un signal analogique à partir d’un signal PWM (cf. Sec1 import RPi.GPIO as GPIO
tion 5). Le principe consiste à garder la valeur moyenne du signal
2 import time
PWM tel qu’illustré par Vaverage à la Figure 14). Le circuit arti3
culé autour de la résistance R1 et du condensateur C1 constitue
4 PIN_B0 = 22
un
filtre passe-bas, laissant passer les variations lentes du signal et
5 PIN_B1 = 23
atténuant
les variations rapides du signal PWM.
6 PIN_B2 = 24
Le
programme
ci-dessous permet de générer un signal sinusoı̈dal
7 PIN_B3 = 25
de fréquence 10 Hz et dont l’amplitude de crête est d’environ
8
9 GPIO.setmode(GPIO.BCM)
1,65V. Pour générer la sinusoı̂de, un signal PWM de fréquence
10
1000 Hz est utilisé. Le rapport cyclique de ce signal PWM varie de
11 GPIO.setup(PIN_B0, GPIO.OUT)
0 % à 100% en fonction de l’amplitude de la sinusoı̈de à générer.
12 GPIO.setup(PIN_B1, GPIO.OUT)
Pour chaque cycle de la sinusoı̈de, 30 échantillons sont utilisés.
14. Le LM358 est alimenté en 5V plutôt qu’en 3,3V car il n’est pas capable de
produire une tension de sortie suffisamment proche de sa tension d’alimentation.
S’il était alimenté en 3,3V, il pourrait produire une tension de sortie d’au plus 2V.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
1
2
import RPi.GPIO as GPIO
import time
12 / 25
6.3
Lire la valeur d’une LDR
6
PRODUIRE ET LIRE DES VALEURS ANALOGIQUES
F IGURE 19 – Convertisseur DAC basé sur un signal PWM et un
filtre passe-bas.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import math
F IGURE 20 – Signal en sortie du filtre RC.
PWM_FREQ = 1000
SIG_FREQ = 10
NUM_SAMPLES = 30
PIN_PWM = 23
GPIO.setmode(GPIO.BCM)
sateur est vide. Le condensateur se charge alors via LDR1. A un
certain moment, la tension présente aux bornes du condensateur va
dépasser le seuil de tension considéré comme un niveau logique
haut, ce qui pourra être détecté par programme.
GPIO.setup(PIN_PWM, GPIO.OUT)
p= GPIO.PWM(PIN_PWM, PWM_FREQ)
p.start(50)
t= 0.0
try:
while True:
x= 0.5 * math.sin(2 * math.pi \
* t * SIG_FREQ) + 0.5
p.ChangeDutyCycle(100.0 * x)
sample_duration= 1.0 / (SIG_FREQ \
* NUM_SAMPLES)
time.sleep(sample_duration)
t+= sample_duration
finally:
GPIO.cleanup()
F IGURE 21 – Lecture d’une photorésistance.
La Figure 22 montre une trace de la tension présente aux bornes
du condensateur C1. On observe que cette tension augmente progressivement de 0V jusqu’à un peu plus de 2,5 V. A ce moment, le
La Figure 20 montre l’évolution de la tension en sortie du filtre
programme détecte un niveau haut en entrée de la broche GPIO 4.
passe-bas. Les valeurs de R1 et C1 utilisées sont respectivement
Dans cette trace, le temps de chargement est environ 8 ms. Le pro1kΩ et 4, 7µF , ce qui donne une fréquence de coupure d’environ
gramme décharge ensuite le condensateur. Le temps de décharge
33 Hz. La sinusoı̈de est clairement reconnaissable. On observe ceest très court, mais le programme attend 10 ms. Après quoi, le propendant une légère variation de la tension en dents de scie. Ceci est
cessus de charge reprend... DS
dû au fonctionnement imparfait du filtre passe-bas utilisé.
6.3 Lire la valeur d’une LDR
Bien que la platine Raspberry Pi ne possède pas de convertisseur analogique / digital (ADC), cette section présente un circuit
très simple permettant de détecter des variations de luminosité au
travers d’un photoresistance (light-dependent resistor – LDR). La
Figure 21 présente le circuit utilisé. L’astuce consiste à mesurer
le temps nécessaire au chargement du condensateur C1 au travers de la photorésistance LDR1. La photorésistance utilisée a une
résistance variant entre < 1kΩ (éclairée) et > 1M Ω (dans l’obscurité).
Le principe du circuit est le suivant. La broche GPIO 4 va être
configurée d’abord en sortie et mise à un niveau bas, de façon
à vider le condensateur C1 au travers de la résistance R1 et la
résistance interne du port 4. Ensuite, la broche est re-configurée
en entrée. Le niveau logique est actuellement bas car le conden© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
F IGURE 22 – Evolution de la tension aux bornes du condensateur.
13 / 25
6.3
Lire la valeur d’une LDR
Le programme ci-dessous configure alternativement la broche 4
en entrée puis en sortie. Lorsque la broche est configurée en sortie
et mise à un niveau bas, le condensateur se vide en un temps assez
court. La broche est alors re-configurée en entrée le programme
attend en boucle qu’elle passe à un niveau haut. Le temps pour
passer du niveau bas au niveau haut dépend de la résistance de la
LDR et donc de l’éclairement à laquelle elle est soumise.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
6
PRODUIRE ET LIRE DES VALEURS ANALOGIQUES
détectant la direction de déplacement d’une main avec
une paire de LDR. En fonction de la direction du mouvement, diverses actions peuvent être prises telles que
l’augmentation/diminution du volume, le passage à la
plage suivante/précédente, etc.
import RPi.GPIO as GPIO
import time
PIN_LDR = 4
GPIO.setmode(GPIO.BCM)
try:
while True:
GPIO.setup(PIN_LDR, GPIO.OUT)
GPIO.output(PIN_LDR, 0)
time.sleep(0.01)
GPIO.setup(PIN_LDR, GPIO.IN)
start= time.time()
while not GPIO.input(PIN_LDR):
time.sleep(0.0001)
end= time.time()
print "%f" % (end-start)
finally:
GPIO.cleanup()
La Figure 23 montre un suite d’échantillons mesurés par le programme ci-dessus. L’axe des abscisses représente le numéro de
l’échantillon tandis que l’axe des ordonnées représente le temps
mesuré par le programme. Le temps est légèrement inférieur à 2
ms lorsque la LDR est éclairée puis passe à plus de 10 ms lorsqu’une main passe devant la LDR (échantillons 190 à 230).
F IGURE 23 – Temps de chargement du condensateur C1 à travers
la LDR.
+
A l’aide de ce circuit, il est possible de contrôler
le Raspberry Pi via une interface sans contact. Une
première application pourrait être l’équivalent d’un
Theremin, un instrument de musique dont le son
peut être modifié en en approchant ou éloignant la
main. Une autre application pourrait être une interface
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
14 / 25
7
7
AFFICHEUR LCD ALPHANUMÉRIQUE
Afficheur LCD alphanumérique
Les LEDs sont très pratiques pour afficher un état binaire. Cependant, leur capacité à transmettre une information complexe à un
utilisateur est limitée. Cette section explore l’utilisation d’un afficheur LCD alphanumérique et sa connexion à la platine Raspberry
Pi. L’afficheur utilisé est basé sur un contrôleur très répandu : le
HD44780 ou un modèle compatible. La communication avec ce
contrôleur passe par une interface parallèle qui nécessite 6 sorties
du Raspberry Pi.
La Table 7 reprend les différentes broches d’un afficheur LCD
alphanumérique. En fonction des modèles, ils possèdent 14 ou 16
broches. Les broches 1 et 2 servent à alimenter l’afficheur et son
contrôleur. La broche 3 permet de régler le contraste. La broche 4
indique si l’on transmet une commande ou une donnée vers l’afficheur. La broche 5 sert à indiquer si l’on écrit vers l’afficheur ou si
on lit à partir de l’afficheur. Dans notre cas, nous ne ferons que des
écritures. La broche 6 permet d’indiquer qu’une donnée doit être
prise en compte par l’afficheur. Les données sont transmises sur 4
bits via les broches 11 à 14 (bits 4 à 7) et sont prises en compte lors
d’un flanc descendant de la broche 6. Les broches 7 à 10 (bits 0 à
3) sont inutilisées. Les broches 15 et 16 sont présentes sur les afficheurs dotés d’un rétro-éclairage (backlight) et servent à alimenter
celui-ci.
Afficheur LCD
(broche et fonction)
Raspberry Pi
(broche)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GND
VCC
Contraste
Command (0) / Data (1)
Write (0) / Read (1)
Enable (flanc descendant)
Bit 0
Bit 1
Bit 2
Bit 3
Bit 4
Bit 5
Bit 6
Bit 7
GND
5V
(voir texte)
GPIO 17
GND
GPIO 27
—
—
—
—
GPIO 22
GPIO 23
GPIO 24
GPIO 25
15
16
Anode backlight
Cathode backlight
5V (option)
GND (option)
TABLE 7 – Brochage d’un afficheur LCD alphanumérique et
connection à la platine Raspberry Pi.
La Figure 24 présente le circuit interconnectant la platine
Raspberry Pi et un afficheur LCD. Les connexions sont établies
conformément à la Table 7. Une résistance variable configurée en
pont diviseur est connectée à la broche 3 de l’afficheur afin d’en
régler le constraste. Les broches 15 et 16, lorsqu’elles existent,
peuvent optionnellement être reliées à 5V et GND respectivement
afin d’alimenter le rétro-éclairage.
Le programme ci-dessous initialise les entrées-sorties du Raspberry Pi afin de permettre la communication avec le contrôleur de
l’afficheur. L’afficheur est ensuite initialisé (fonction lcd init
en lui envoyant une série de commandes bien déterminées. La
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
F IGURE 24 – Interconnexion de la platine Raspberry Pi et d’un
afficheur LCD.
description de ces commandes sort du cadre de ce document.
L’étudiant curieux pourra en trouver le détail dans la fiche technique du contrôleur HD44780. C’est lors de cette initialisation que
le type d’afficheur auquel le contrôleur est connectée doit être renseigné. Il faut spécifier si l’afficheur possède 1 ou 2 lignes ainsi
que la taille des caractères (5x8 ou 5x10 pixels). Pour des raisons
de facilité, les fonctions lcd cmd et lcd data afin d’envoyer
des commandes ou des données au contrôleur. Le programme affiche en boucle des chaı̂nes de caractères définies dans le tableau
messages.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import RPi.GPIO as GPIO
import time
import sys
PIN_DATA = 17
PIN_ENABLE = 27
PIN_D4 = 22
PIN_D5 = 23
PIN_D6 = 24
PIN_D7 = 25
NUM_ROWS = 4
NUM_COLS = 20
HD44780_CLEAR_DISPLAY
HD44780_RETURN_HOME
HD44780_ENTRY_MODE_SET
HD44780_DISPLAY_CTRL
HD44780_FUNCTION_SET
HD44780_SET_DDRAM_ADDR
=
=
=
=
=
=
0x01
0x02
0x04
0x08
0x20
0x80
HD44780_ENTRY_MODE_SET_INC = 0x02
HD44780_ENTRY_MODE_SET_SHIFT = 0x01
HD44780_DISPLAY_CTRL_DISP_ON = 0x04
HD44780_DISPLAY_CTRL_CURS_ON = 0x02
HD44780_DISPLAY_CTRL_BLINK_ON = 0x01
HD44780_FUNCTION_SET_ROWS_2
= 0x08
HD44780_FUNCTION_SET_FONT_5X10 = 0x04
HD44780_LINE_DDRAM_ADDR= \
[0, 0x40, 0x14, 0x54]
15 / 25
7
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIN_DATA, GPIO.OUT)
GPIO.setup(PIN_ENABLE, GPIO.OUT)
GPIO.setup(PIN_D4, GPIO.OUT)
GPIO.setup(PIN_D5, GPIO.OUT)
GPIO.setup(PIN_D6, GPIO.OUT)
GPIO.setup(PIN_D7, GPIO.OUT)
GPIO.output(PIN_ENABLE, 1)
def lcd_write4(n):
GPIO.output(PIN_D4, n &
GPIO.output(PIN_D5, n &
GPIO.output(PIN_D6, n &
GPIO.output(PIN_D7, n &
time.sleep(0.001)
GPIO.output(PIN_ENABLE,
time.sleep(0.001)
GPIO.output(PIN_ENABLE,
1)
2)
4)
8)
0)
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
AFFICHEUR LCD ALPHANUMÉRIQUE
HD44780_LINE_DDRAM_ADDR[y] + x)
lcd_init()
MESSAGES = ["Hello", "World", "of text"]
try:
i= 0
while True:
msg= MESSAGES[i % len(MESSAGES)]
x= (NUM_COLS - len(msg)) / 2
y= i % NUM_ROWS
lcd_clear()
lcd_goto(x, y)
lcd_str(msg)
time.sleep(2)
i+=1
finally:
GPIO.cleanup()
1)
def lcd_write(b):
lcd_write4(b >> 4)
lcd_write4(b & 0xFF)
def lcd_data(b):
GPIO.output(PIN_DATA, 1)
lcd_write(b)
def lcd_cmd(b):
GPIO.output(PIN_DATA, 0)
lcd_write(b)
def lcd_init():
GPIO.output(PIN_DATA, 0)
time.sleep(0.1)
lcd_write4(0x03)
time.sleep(0.01)
lcd_write4(0x03)
time.sleep(0.001)
lcd_write4(0x03)
lcd_write4(0x02)
if NUM_ROWS > 1:
lcd_write(HD44780_FUNCTION_SET
| HD44780_FUNCTION_SET_ROWS_2)
else:
lcd_write(HD44780_FUNCTION_SET)
lcd_write(HD44780_DISPLAY_CTRL
| HD44780_DISPLAY_CTRL_DISP_ON)
lcd_write(HD44780_CLEAR_DISPLAY)
lcd_write(HD44780_ENTRY_MODE_SET
| HD44780_ENTRY_MODE_SET_INC)
def lcd_str(s):
for c in s:
lcd_data(ord(c))
def lcd_clear():
lcd_cmd(HD44780_CLEAR_DISPLAY)
def lcd_goto(x, y):
x= x % NUM_COLS
y= y % NUM_ROWS
lcd_cmd(HD44780_SET_DDRAM_ADDR |
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
16 / 25
8
Bus SPI, I2 C et 1-wire
8.1
Le SoC BCM2835 permet la connexion de circuits intégrés
périphériques sur des bus série tels que SPI (Serial Peripheral Interface), I2 C (Inter Integrated Circuit) et 1-wire. Cette section explore l’utilisation de ces bus. La Table 8 fournit une première comparaison des types de bus.
Bus
Vitesse max.
Signaux
Adressage
SPI
I2 C
1-wire
≤ 125 Mbps
400 kbps
16.3kbps
(142kbps)
4
2
1
signal séparé (/CS)
dans le protocole
dans le protocole
TABLE 8 – Comparaison des bus SPI, I2 C et 1-wire.
SPI est destiné à des transferts à relativement haute vitesse entre
le SoC et un périphérique. Quatre signaux sont requis pour effectuer ces transferts : MOSI (Master Out Slave In) pour l’envoi
de données à partir du SoC, MOSI (Master In Slave Out) pour
la réception, CLK (CLocK) pour rythmer le transfert de chaque
bit et /CS (Chip Select) pour sélectionner le périphérique auquel le transfert s’adresse. Si plusieurs périphériques sont présents
sur un bus SPI, un signal /CS séparé est nécessaire pour chaque
périphérique.
I2 C est un bus plus complexe, permettant des débits plus faibles.
Il n’utilise que deux signaux : SDA (Serial DAta pour l’échange bidirectionnel de données et SCK (Serial ClocK) pour rythmer ces
échanges. Le protocole de communication utilisé inclut deux fonctionnalités importantes. La première est l’utilisation de conditions
d’acquittement (ACK) permettant de contrôler le bon échange de
données. La seconde est l’envoi de l’adresse du destinataire via le
bus. Cela permet d’ajouter facilement des périphériques additionnels sur le même bus, sans utiliser de signaux de contrôle propres
à chaque périphérique (comme dans le cas de SPI).
1-wire est un bus similaire à I2 C en terme de complexité et de
débit. La différence la plus importante est qu’il ne nécessite qu’un
signal DQ qui transport les données de manière bidirectionnelle et
ne nécessite pas la transmission d’un signal d’horloge séparé. Certains périphériques 1-wire peuvent aussi être alimentés via le signal
DQ. On parle dans ce cas d’ alimentation parasite .
Les trois bus sont supportés de façon matérielle par le SoC du
Raspberry Pi. Ils nécessitent également un support logiciel dans
le noyau linux. Ce support est placé dans des modules. Ceux-ci
peuvent être chargés au démarrage s’ils sont spécifiés dans le fichier /etc/modules. Ils peuvent également être chargés durant
le fonctionnement du noyau en utilisant l’utilitaire modprobe.
+
Les noyaux linux récents (> 3.18) utilisent un Device
Tree pour gérer certaines ressources matérielles, dont
les bus SPI, I2 C et 1-wire. Cela nécessite l’ajout de directives au démarrage du noyau, via l’intermédiaire du
fichier boot/config.txt. Les détails seront fournis dans les sections suivantes séparément pour chaque
bus.
1-wire
Le bus 1-wire est un bus composé d’un nombre de lignes très
réduit : un signal de données, la masse et une source d’alimentation. Cette dernière est optionnelle car certains périphériques
1-wire utilisent une alimentation ”parasite” tirée de la ligne de
données. Le bus 1-wire est majoritairement utilisé pour connecter
des capteurs de température tels que les DS18x20.
La Figure 25 précise comment les périphériques 1-wire sont
connectés au bus. La ligne de données DQ doit être connectée à
la broche GPIO 4 qui a la fonction alternative d’interface vers un
bus 1-wire. La broche GND doit être connectée à la masse (GND) du
Raspberry Pi. La broche VDD doit être connectée à l’alimentation
3,3V du Raspberry Pi. Une résistance de pull-up de 10 kΩhm 15
doit être placée entre DQ et VDD . La Figure 26 donne le schéma de
brochage du DS18B20.
F IGURE 25 – Schéma de branchement de capteurs de température
DS18x20.
MAXIM
18B20
1 2 3
1 2 3
(BOTTOM VIEW)
GND
DQ
VDD
8
BUS SPI, I2 C ET 1-WIRE
F IGURE 26 – Schéma de brochage du DS18B20.
(source : datasheet DS18B20, Maxim)
Le noyau linux contient déjà un support pour le bus 1-wire
et pour certains périphériques tels que les DS18x20. Pour utiliser le bus 1-wire, il est nécessaire d’insérer des modules noyau
supplémentaires avec la commande modprobe comme montré cidessous. Il est aussi possible de spécifier le chargement de ces modules au démarrage via le fichier etc/modules.
pi@rpi:˜$ modprobe w1-gpio
pi@rpi:˜$ modprobe w1-therm
+
Device tree (noyau > 3.18) : ajouter la ligne suivante
au fichier /boot/config.txt et redémarrer.
dtoverlay = w1-gpio
15. La datasheet recommande 4,7 kΩhm.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
17 / 25
8.2
Inter-Integrated Circuit (I2 C)
8
BUS SPI, I2 C ET 1-WIRE
pi@rpi:˜$cat /sys/bus/w1/devices/w1 bus master1/w1 master slaves
10-0008014bf476
28-000001a603ea
pi@rpi:˜$ cat /sys/bus/w1/devices/10-0008014bf476/w1_slave
27 00 4b 46 ff ff 08 10 52 : crc=52 YES
27 00 4b 46 ff ff 08 10 52 t=19250
pi@rpi:˜$ cat /sys/bus/w1/devices/28-000001a603ea/w1_slave
37 01 4b 46 7f ff 09 10 26 : crc=26 YES
37 01 4b 46 7f ff 09 10 26 t=19437
F IGURE 27 – Interaction avec des capteurs de température via 1-wire.
Lorsque les modules liés au bus 1-wire sont correctement installés, les périphériques présents sur le bus et supportés par le
noyau sont listés dans le système de fichiers (souvenez-vous
de SysFS) sous le chemin /sys/bus/w1/devices. Le fichier virtuel w1 bus master/w1 master slaves contient
les numéros de série des deux capteurs de température connectés
au bus à la Figure 25. Il est alors possible d’obtenir la température
reportée par les capteurs en interrogeant les fichier virtuels
<identifiant>/w1 slave. Un exemple d’une telle interaction est montré à la Figure 27.
8.2
Inter-Integrated Circuit (I2 C)
Dans cette section, un circuit intégré PCF8574 est connecté au
bus I2 C du Raspberry Pi. Ce circuit permet d’étendre le nombre
sorties, à la manière du registre à décalage utilisé en Section 4.
La Figure 28 illustre comment le PCF8574 peut être connecté au
Raspberry Pi et utilisé pour contrôler l’état de 4 LEDs.
F IGURE 29 – Brochage du PCF8574 (source : NXP).
+
Device tree (noyau > 3.18) : ajouter les lignes
suivantes au fichier /boot/config.txt et
redémarrer.
dtparam=i2c1=on
dtparam=i2c arm=on
Il est ensuite nécessaire de charger les modules i2c-bcm2708
et i2c-dev en utilisant la commande modprobe comme montré
ci-dessous. Il est aussi possible de spécifier le chargement de ces
modules au démarrage via le fichier etc/modules.
pi@rpi:˜$ modprobe i2c-bcm2708
pi@rpi:˜$ modprobe i2c-dev
F IGURE 28 – Schéma de branchement d’un PCF8574.
Le schéma de brochage du PCF8574 est fourni à la Figure 29.
Les broches SDA et SCL servent respectivement pour le transport bidirectionnel de données et comme signal d’horloge. Les
données sont transférées au rythme d’un bit par cycle d’horloge.
Les broches VDD et VSS sont connectées respectivement à 3.3V
et GND. Les entrées A0 à A2 permettent de donner une adresse
différente au PCF8574. L’adresse du PCF8574 est 0x20 + la valeur
binaire encodée avec A0 à A1. Dans le circuit de la Figure 28,
l’adresse configurée est donc 0x20. Les sorties P0 à P7 sont
contrôlables via le bus I2 C.
L’activation de I2 C sur la platine Raspberry Pi
nécessite quelques manipulations. Premièrement, le fichier
/boot/config.txt doit être modifié de façon à contenir les
lignes suivantes. La platine doit ensuite être redémarrée.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
Finalement, nous allons utiliser les commandes i2cdetect et
i2cset. Celles-ci font partie du package i2c-tools qu’il est
donc nécessaire d’installer comme montré ci-dessous.
pi@rpi:˜$ sudo apt-get install i2c-tools
L’utilitaire i2cdetect permet d’énumérer les adresses utilisées sur le bus. Pour cela, l’utilitaire envoie sur le bus un octet aux
adresses comprises entre 0x00 et 0xFF. Si un acquittement (ACK)
est reçu, c’est qu’un périphérique est présent. Le résultat de cette
commande est montrée à la Figure 30. On y observe que l’adresse
0x20 est utilisée. C’est à cette adresse que le PCF8574 est présent.
L’option -y de l’utilitaire permet de spécifier le numéro du bus I2 C
utilisé.
La Figure illustre également l’usage de l’utilitaire i2cset qui
permet de faire une écriture vers un périphérique I2 C, en spécifiant
18 / 25
8.2
Inter-Integrated Circuit (I2 C)
pi@rpi:˜$
0 1
00:
10: -- -20: 20 -30: -- -40: -- -50: -- -60: -- -70: -- -pi@rpi:˜$
pi@rpi:˜$
pi@rpi:˜$
8
i2cdetect -y 1
2 3 4 5 6 7 8 9
-- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -- -- --- -- -- -- -- -i2cset -y 1 0x20 0x0F
i2cset -y 1 0x20 0x05
i2cset -y 1 0x20 0x0A
a
--------
b
--------
c
--------
d
--------
e
--------
BUS SPI, I2 C ET 1-WIRE
f
--------
F IGURE 30 – Interaction avec le PCF8574 via I2 C.
son adresse. Dans l’exemple, les écritures 0x00, 0x05 et 0x0A dernier échantillon de température.
sont effectuées successivement. La première doit allumer toutes
les LEDs (un bit à 0 allume la LED correspondante). La seconde
Commande Description
allume une LED sur deux et la dernière allume les autres LEDs (à
0xEE
démarre une/les conversion(s)
nouveau une sur deux).
0x22
arrête les conversions
La Figure 31 montre une autre application du bus I2 C. Deux
0xAA
lit le dernier échantillon
capteurs de température DS1624 sont connectés sur le bus. Ils sont
configurés avec des adresses différentes. Le capteur de droite a
TABLE 9 – Commandes I2 C supportées par le DS1624.
les bits d’adresse à 0 tandis que celui de gauche a le bit A1 à 1.
Leurs adresses respectives sont 0x48 (adresse de base) et 0x4A
(adresse de base + 2). Ceci peut être vérifié en utilisant l’utilitaire
i2cdetect. Le schéma de brochage du DS1624 peut être obtenu
La valeur de la température lue via I2 C est encodée sur 16 bits.
à la Figure 32.
Seuls les 13 bits de poids fort sont utilisés, les autres valant toujours 0. Les 8 bits de poids fort constituent la partie entière de la
température en degrés Celsius tandis que les 8 bits de poids faible
1
sont des 256
èmes de degré. Les 16 bits sont transmis en 2 octets via
2
I C, l’octet de poids fort en premier. La Table 10 illustre la conversion entre valeurs lues et température. Par exemple, 0x12,0x78
correspond à une température de 18 °C (0x12) et 120
256 = 0, 46875
(0x78).
F IGURE 31 – Deux capteurs DS1624 connectés au bus I2 C.
Valeurs lues
Représentation binaire
T°C
0X12, 0x78
0X12, 0x18
0X12, 0x10
00010010 01111000
00010010 00011000
00010010 00010000
18,46875
18,09375
18,0625
TABLE 10 – Interpretation d’échantillons de température.
F IGURE 32 – Brochage du DS1624 (source : Maxim).
Le DS1624 est un circuit plus complexe que le PCF8574 utilisé dans le premier exemple de cette section. Il comporte notamment plusieurs commandes envoyées par I2 C pour le contrôler.
Un sous-ensemble de celles-ci est montré à la Table 9. Les deux
premières commandes (0xEE et 0x22) démarrent et arrêtent les
conversions de température. La troisième commande (0xAA) lit le
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
L’exemple ci-dessus illustre l’interaction en Python avec le bus
I2 C au travers de l’API smbus. Pour utiliser cet exemple, le package python-smbus doit être installé. Le programme démarre
la conversion, puis, à chaque seconde il lit un échantillon, le
convertit en température et l’affiche.
1
2
3
4
5
6
# -*- coding: utf-8 -*import smbus
import time
import sys
DS1624_DEV_ADDR= 0x4A
19 / 25
8.3
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Serial Peripheral Interface (SPI)
DS1624_READ
= 0xAA
DS1624_START_CONV = 0xEE
DS1624_STOP_CONV = 0x22
bus= smbus.SMBus(1)
bus.write_byte(DS1624_DEV_ADDR, \
DS1624_START_CONV)
while True:
word= bus.read_i2c_block_data( \
DS1624_DEV_ADDR, \
DS1624_READ, 2)
sample= (word[0] << 8) + word[1]
temperature= (1.0 * sample)/256
print "\r%.6f °C" % (temperature),
sys.stdout.flush()
time.sleep(1)
8
BUS SPI, I2 C ET 1-WIRE
périphérique est activée. Elle est désactivée lorsque le transfert est
terminé.
Une application possible du bus SPI est la commande d’un
afficheur LED 7-segments via un circuit intégré spécialisé, le
MAX7219. L’avantage de ce circuit est que seuls 3 signaux sont
utilisés pour commander jusqu’à 8 afficheurs. Il suffit d’écrire
les valeurs des chiffres de chaque afficheur dans des registres du
MAX7219. Celui-ci s’occupe alors de contrôler les LEDs qui composent les afficheurs. La Figure 33 présente un exemple de circuit à base de MAX7219. Seuls 3 afficheurs y sont connectés. Le
MAX7219 est connecté à la platine Raspberry Pi via le bus SPI.
Le schéma de brochage du MAX7219 est fourni à la Figure 34.
Note : le signal MISO n’est pas utilisé car seule des écritures vers
le MAX7219 sont effectuées.
8.3 Serial Peripheral Interface (SPI)
Le support du bus SPI nécessite le chargement du module
spi bcm2835. Lorsque ce périphérique est chargé, de nouveaux
devices apparaissent sous le nom /dev/spidev-x.y où x
désigne le numéro du bus et y désigne le numéro du périphérique.
Sur la plateforme Raspberry Pi, un seul bus SPI est présent (0) et
deux périphériques sont supportés (0 et 1). Les périphériques 0 et
1 correspondent respectivement aux broches Chip Select SPI CE0
et SPI CE1.
F IGURE 33
pi@rpi:˜$ modprobe spi\_bcm2835
pi@rpi:˜$ ls /dev/spidev*
/dev/spidev-0.0
/dev/spidev-0.1
+
Device tree (noyau > 3.18) : ajouter la ligne suivante
au fichier /boot/config.txt et redémarrer.
dtparam=spi=on
L’accès au bus SPI peut être effectué en Python au travers de
l’API spidev. Celle-ci n’est pas disponible sous forme de package.
Il est nécessaire de la télécharger, de la compiler et de l’installer.
La suite de commandes ci-dessous montre comment installer la
version 3.1.
pi@rpi:˜$ sudo apt-get
install python-dev
pi@rpi:˜$ wget https://pypi.python.org/\
packages/source/s/spidev/spidev-3.1.tar.gz
pi@rpi:˜$ cd spidev-3.1
pi@rpi:˜$ sudo python setup.py install
pi@rpi:˜$ cd ..
Une partie de l’API spidev est résumée à la Table 11. La fonction SPIDev crée un object permettant d’interagir avec le bus SPI.
La méthode open spécifie sur quel bus la communication SPI s’effectue. A cette fin, le numéro du bus et le numéro du périphérique
doivent être renseignés. La méthode xfer2 envoit une séquence
d’octets sur le bus (via MOSI) et lit simultanément les octets reçus
(via MISO). Avant le transfert, la broche Chip Select associée au
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
F IGURE 34 – Brochage du MAX7219 (source : Maxim).
Le programme ci-dessous contrôle les afficheurs 7-segments au
travers de SPI et du MAX7219. L’objectif du programme est d’afficher un compteur sur les afficheurs. Ce compteur est incrémenté
toutes les 100 ms. Le MAX7219 possède plusieurs registres qui
servent par exemple à configurer le nombre d’afficheurs auxquels
il est connecté (de 1 à 8) et la luminosité (grâce à un mécanisme
PWM intégré). Les chiffress à afficher par chaque afficheur sont
stockés dans 8 registres (cf. MAX7219 DIGITn ci-dessous).
1
2
3
4
5
6
import spidev
import time
MAX7219_DIGIT0 = 0x01
MAX7219_DECODE = 0x09
MAX7219_BRIGHTNESS = 0x0A
20 / 25
8.3
Serial Peripheral Interface (SPI)
8
BUS SPI, I2 C ET 1-WIRE
Fonction
Description
x =spidev.SPiDev()
x.open(bus, device)
x.close()
x.xfer2(bytes)
crée une instance x nécessaire à l’interaction avec un bus SPI.
initialise l’instance pour des communications sur le bus bus avec le périphérique device.
déconnecte l’instance du bus.
transfère la séquence d’octets bytes au sein d’une unique transaction.
TABLE 11 – Résumé de l’API spidev.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
MAX7219_SCANLIMIT = 0x0B
MAX7219_SHUTDOWN = 0x0C
spi = spidev.SpiDev()
spi.open(0,0)
spi.xfer2([MAX7219_DECODE, 0xFF])
spi.xfer2([MAX7219_BRIGHTNESS, 0x0F])
spi.xfer2([MAX7219_SCANLIMIT, 0x02])
spi.xfer2([MAX7219_SHUTDOWN, 0x01])
try:
c= 0
while True:
x= c
for d in range(3):
spi.xfer2([MAX7219_DIGIT0 + d, \
x % 10])
x= x/10
time.sleep(0.1)
c= c + 1
finally:
spi.close()
Un grand nombre de circuit intégrés peuvent être connectés à un
bus SPI. Par exemple, le TLC549 est un convertisseur analogique
digital dont les échantillons peuvent être lus via SPI. Les écrans
LCD graphiques des Nokia 5110 (circuit intégré PCD8544) sont
contrôlés via SPI. Le MCP23S17 est un circuit intégré qui permet
de contrôler via SPI jusqu’à 16 entrées/sorties. Certaines mémoires
Flash et EEPROM peuvent être lues et écrites via SPI.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
21 / 25
A
A
ANNEXE - CONNECTEURS D’ENTRÉES/SORTIES
Annexe - Connecteurs d’entrées/sorties
I2C SDA
I2C SCL
GPCLK0
SPI MOSI
SPI MISO
SPI SCLK
3,3V
GPIO 2
GPIO 3
GPIO 4
GND
GPIO 17
GPIO 27
GPIO 22
3,3V
GPIO 10
GPIO 9
GPIO 11
GND
EEPROM ID SD
GPIO 5
GPIO 6
GPIO 13
GPIO 19
GPIO 26
GND
1
3
5
7
9
11
13
15
17
19
21
23
25
27
29
31
33
35
37
39
2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
5V
5V
GND
GPIO 14
GPIO 15
GPIO 18
GND
GPIO 23
GPIO 24
GND
GPIO 25
GPIO 8
GPIO 7
UART TxD
UART RxD
PCM CLK
SPI CE0
SPI CE1
EEPROM ID SC
GND
GPIO 12
GND
GPIO 16
GPIO 20
GPIO 21
TABLE 12 – Connecteur J8, modèles A+ et B+
I2C0 SDA
I2C0 SCL
GPCLK0
SPI0 MOSI
SPI0 MISO
SPI0 SCLK
3,3V
GPIO 0
GPIO 1
GPIO 4
GND
GPIO 17
GPIO 21
GPIO 22
3,3V
GPIO 10
GPIO 9
GPIO 11
GND
1
3
5
7
9
11
13
15
17
19
21
23
25
2
4
6
8
10
12
14
16
18
20
22
24
26
5V
5V
GND
GPIO 14
GPIO 15
GPIO 18
GND
GPIO 23
GPIO 24
GND
GPIO 25
GPIO 8
GPIO 7
UART TxD
UART RxD
PCM CLK
SPI0 CE0
SPI0 CE1
TABLE 13 – Connecteur P1, modèles A et B rev.1
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
22 / 25
B
B
ANNEXE - CONTRÔLE DES GPIOS VIA LES REGISTRES
Annexe - Contrôle des GPIOs via les reLa Table 16 décrit le contenu du registre GPSET0. Ce registre
contrôle
l’état des broches 0 à 31. Le registre GPSET1 a une strucgistres
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.
La Table 14 présente une liste partielle des registres destinés au
contrôle des broches d’entrées/sorties. Chaque registre fait 32 bits
de large. 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.
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 15 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
31-30
29-27
26-24
...
5-3
2-0
—
FSEL9
FSEL8
...
FSEL1
FSEL0
ture 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).
Bits
Field
31
...
1
0
SET31
...
SET1
SET0
Description
0 = no change ; 1 = set GPIO pin 31
0 = no change ; 1 = set GPIO pin 1
0 = no change ; 1 = set GPIO pin 0
TABLE 16 – Structure du registre GPSET0.
La Table 17 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.
Bits
Field
31
...
1
0
CLR31
...
CLR1
CLR0
Description
Description
0 = no change ; 1 = clear GPIO pin 31
0 = no change ; 1 = clear GPIO pin 1
0 = no change ; 1 = clear GPIO pin 0
TABLE 17 – Structure du registre GPCLR0.
Reserved
Select function of GPIO 9
Select function of GPIO 8
La Table 18 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.
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
Select function of GPIO 1
Select function of GPIO 0
TABLE 15 – Structure du registre GPFSEL0.
Register name
Description
Access
Addresses
GPFSEL0
GPFSEL1
...
GPFSEL5
select function
select function
...
select function
R/W
R/W
0x7E200000
0x7E200004
R/W
0x7E200014
GPSET0
GPSET1
set output state
set output state
W
W
0x7E20001C
0x7E200020
GPCLR0
GPCLR1
clear output state
clear output state
W
W
0x7E200028
0x7E20002C
GPLEV0
GPLEV1
read input state
read input state
R
R
0x7E200034
0x7E200038
several registers
configure pin events
read datasheet
GPPUD, GPPUDCLOK..1
configure pull-up/down
read datasheet
TABLE 14 – Aperçu des registres spéciaux destinés au contrôle des entrées/sorties du SoC BCM2835.
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
23 / 25
B.1
Exemple complet en C
B
ANNEXE - CONTRÔLE DES GPIOS VIA LES REGISTRES
F IGURE 35 – Correspondance entre adresses de bus, physiques et virtuelles.
Bits
Field
31
...
1
0
LEV31
...
LEV1
LEV0
Description
0 = low ; 1 = high
0 = low ; 1 = high
0 = low ; 1 = high
TABLE 18 – Structure du registre GPLEV0.
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 <<). Le résultat final est écrit dans
le registre GPFSEL2.
1
*GPFSEL2= (*GPFSEL2 & 0xFFFFFE3F) | (1 << 6)
Un exemple de programme complet est fourni en annexe à la
Section B.1.
B.1
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’entièreté 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
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
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 renseignées à la Table 14.
La raison est simplement que /dev/mem expose la mémoire au
travers des adresses physiques alors que la Table 14 renseigne les
adresses des registres GPIO sur un bus interne au SoC. La Figure 35 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.
La suite de commandes ci-dessous montre comment compiler un programme en C et comment l’exécuter. Dans notre
cas, le programme est composé d’un seul fichier source nommé
test-gpio.c. La commande gcc désigne le compilateur C
(GNU C Compiler). Il est invoqué avec les options -Wall et
-Werror qui demandent respectivement de reporter tous les avertissements générés lors de la compilation et de considérer les avertissements comme des erreurs. L’option -o suivie d’un nom de
fichier spécifie le nom du fichier résultant de la compilation. Le
dernier argument est le fichier source (test-gpio.c).
pi@rpi:˜$ gcc -Wall -Werror -o test-gpio
test-gpio.c
pi@rpi:˜$ sudo ./test-gpio
pi@rpi:˜$
24 / 25
B.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Exemple complet en C
#include
#include
#include
#include
#include
#include
#include
#include
B
ANNEXE - CONTRÔLE DES GPIOS VIA LES REGISTRES
<fcntl.h>
<stdio.h>
<stdlib.h>
<stdint.h>
<sys/mman.h>
<sys/stat.h>
<sys/types.h>
<unistd.h>
#define PHYS_GPIO_BASE 0x20200000
#define GPFSEL0_OFFSET 0x00000
#define GPSET0_OFFSET 0x0001C
#define GPCLR0_OFFSET 0x00028
#define GPF_INPUT 0
#define GPF_OUTPUT 1
void * get_mmapped_gpio()
{
int page_size= getpagesize();
int fd= open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
perror("open");
return NULL;
}
void * addr= mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, PHYS_GPIO_BASE);
if (addr == MAP_FAILED) {
perror("mmap");
return NULL;
}
close(fd);
return addr;
}
int main()
{
void * addr= get_mmapped_gpio();
if (addr == NULL)
exit(EXIT_FAILURE);
volatile uint32_t * GPFSEL0= (volatile uint32_t *) (addr + GPFSEL0_OFFSET);
volatile uint32_t * GPSET0= (volatile uint32_t *) (addr + GPSET0_OFFSET);
volatile uint32_t * GPCLR0= (volatile uint32_t *) (addr + GPCLR0_OFFSET);
unsigned char pin= 27;
volatile uint32_t * GPFSEL= GPFSEL0 + (pin/10);
int bit_pos= 3 * (pin%10);
*GPFSEL= (*GPFSEL & ˜(7 << bit_pos)) | (GPF_OUTPUT << bit_pos);
while (1) {
*GPSET0= 1 << pin;
sleep(1);
*GPCLR0= 1 << pin;
sleep(1);
}
return 0;
}
© 2015, B. Q UOITIN, D. H AUWEELE et G. H UYSMANS
25 / 25

Documents pareils