Entrées/sorties GPIO

Les GPIO de la Raspberry Pi du BCM2835 (900 MHz, Pi 1) au 2837 (1.2 GHz, Pi 3)

Les GPIO (General Purpose Input/Output) sont très utilisées en STS SN pour communiquer avec les capteurs/actionneurs.

Le composant électronique a évolué en fonction des versions : BCM2837 (Pi 3), BCM2836 (Pi 2) ou BCM2835 (Pi 1 et Zéro).

Il existe plusieurs moyens d'utiliser les GPIO en programmation.

Le plus simple pour les électroniciens est d'utiliser la bibliothèque wiringPi. Vous pouvez suivre un tutoriel d'Internet pour comprendre comment l'utiliser.

Ce n'est pas la solution que nous allons choisir.

Nous allons en fait utiliser les fichiers systèmes (sysfs) créés par Linux pour contrôler les GPIO. Ces fichiers spéciaux permettent d'utiliser le pilote système du media de communication.

GPIO pins diagram
GPIO pins diagram

Fichiers systèmes pour les GPIO

La gestion des GPIO se trouvent dans le dossier /sys/class/gpio.

Dans ce dossier, on trouve 2 fichiers : export et unexport.

export donne accès au paramétrage et à la communication avec la broche choisie du GPIO.

unexport permet de supprimer l'accès à la broche GPIO.

Quel numéro pour le GPIO ?

Il existe plusieurs manières de considérer une broche du GPIO. Le tableau ci-contre résume la situation.

Accéder aux pins du GPIO par le système de fichiers utilise la numérotation du composant électronique BCMXXXX. Il s'agit donc de la colonne BCM.

Si vous utilisez la bibliothèque wiringPi, vous devrez utiliser la colonne correspondante.

GPIO Pins
Gpio pins

ExempleExemple de programmation à partir du terminal de commande avec la GPIO4

La GPIO4 se trouve à la broche 7 du connecteur J8 de la Pi 3.

A partir du Shell (terminal de commande), il faudra réaliser les étapes suivantes en tant qu'administrateur (sudo suivi d'une commande permet son exécution en tant qu'administrateur).

Réalisez les actions suivantes :

1 - Choix de la broche GPIO4 :

1
sudo echo "4" > /sys/class/gpio/export
2
sudo chmod 777 /sys/class/gpio/gpio4

La ligne ci-dessus écrit la valeur 4 dans le fichier export qui sera utilisé par le driver système du BCM283X pour permettre l'accès à la broche.

Un nouveau dossier nommé gpio4 est créé. Il contient les fichiers de paramétrage et de communication avec la GPIO4.

ATTENTION, veillez à donner les droits d'accès à ce dossier à partir de votre application.

ATTENTION car la création de ce dossier ne peut se faire qu'une fois. Dans votre application, vous devrez en tenir compte.

2 - Il faut ensuite définir la direction : in ou out.

1
sudo echo "out" > /sys/class/gpio/gpio4/direction

En écrivant "out" dans le fichier direction, on définit la GPIO4 comme étant une sortie.

3 - Il est maintenant possible de fixer la valeur de la sortie à 1 ou 0 :

1
sudo echo "1" > /sys/class/gpio/gpio4/value

Le fichier value permet de lire/écrire dans la GPIO4.

Quand la sortie est à 1, la tension est de 3.3V).

4 - Une fois terminé, il faut libérer la GPIO de la manière suivante :

1
sudo echo "4" > /sys/class/gpio/unexport

Programmation en C++/Qt

La méthode est la même en programmation.

Créons une classe de gestion du GPIO dans un fichier cgpio.h :

1
#ifndef CGPIO_H
2
#define CGPIO_H
3
4
#include <QObject>
5
#include <QDebug>
6
#include <QProcess>
7
#include <QFile>
8
#include <unistd.h>
9
10
#define IN  0
11
#define OUT 1
12
13
class CGpio : public QObject
14
{
15
    Q_OBJECT
16
17
public :
18
    explicit CGpio(int addr, int dir);
19
    ~CGpio();
20
    int lire();
21
    int ecrire(int value);
22
23
private :
24
    char mFilename[50];
25
    QString mNomFic;
26
    int init();
27
    int gpioExport();
28
    int gpioUnexport();
29
    int gpioDirection(int dir);
30
    int gpioRead();
31
    int gpioWrite(int value);
32
    int mAddr;
33
34
signals:
35
    void erreur(QString msg);
36
};
37
38
#endif // CGPIO_H
39

La classe hérite de QObject afin de donner la capacité de communiquer par signal/slot.

La classe dispose de 2 méthodes publiques : lire() et ecrire() car finalement, c'est tout ce qu'on attend d'une E/S GPIO.

Le constructeur de la classe appelle une méthode privée init() permettant d'effectuer tout le processus d'accès à une GPIO, comme décrit ci-dessus.

Remarquez que le constructeur de la classe est paramétré. Le paramètre attendu est le numéro de l'E/S GPIO (colonne BCM du diagramme).

Voici maintenant l'implémentation des méthodes de la classe :

1
#include "cgpio.h"
2
3
CGpio::CGpio(int addr, int dir)
4
{
5
    mAddr = addr;
6
    gpioExport();
7
    init();
8
    gpioDirection(dir);
9
} // constructeur
10
11
CGpio::~CGpio()
12
{
13
  gpioUnexport();
14
} // destructeur
15
16
int CGpio::init()
17
{
18
    sprintf(mFilename,"/sys/class/gpio/gpio%d/",mAddr);
19
    QString command = "sudo chmod -R 777 "+QString(mFilename);
20
    QProcess *proc= new QProcess(this);
21
    proc->start(command);
22
    sleep(1); // laisse le temps du changement de droit
23
    return 1;
24
}
25
26
int CGpio::gpioUnexport()
27
{
28
    char buffer[3];
29
30
    QFile fUnexport("/sys/class/gpio/unexport");
31
    bool res = fUnexport.open(QIODevice::WriteOnly | QIODevice::Text);
32
    if (!res) {
33
        qDebug() << "CGpio::gpioUnexport: Erreur d'ouverture du fichier !";
34
        emit erreur("CGpio::gpioUnexport: Erreur d'ouverture du fichier !");
35
        return -1;
36
    } // if erreur open
37
    sprintf(buffer,"%d", mAddr);
38
    int nbw = fUnexport.write(buffer, strlen(buffer));
39
    if (nbw != int(strlen(buffer))) {
40
        qDebug() << "CGpio::gpioUnexport: Erreur écriture dans fichier !";
41
        emit erreur("CGPIO::gpioUnexport: Erreur écriture dans fichier !");
42
        return -1;
43
    } // if nbw
44
    fUnexport.close();
45
    return 0;
46
}
47
48
int CGpio::gpioDirection(int dir)
49
{
50
    char buffer[3];
51
    QString ficDirection;
52
53
    ficDirection = QString("/sys/class/gpio/gpio%1/direction").arg(mAddr,0,10);
54
    QFile fDirection(ficDirection);
55
    bool res = fDirection.open(QIODevice::WriteOnly | QIODevice::Text);
56
    if (!res) {
57
        qDebug() << "CGpio::gpioDirection: Erreur d'ouverture du fichier !";
58
        emit erreur("CGpio::gpioDirection: Erreur d'ouverture du fichier !");
59
        return -1;
60
    } // if erreur open
61
    if (dir==IN)
62
      strcpy(buffer,"in");
63
    else
64
      strcpy(buffer,"out");
65
    int nbw = fDirection.write(buffer, strlen(buffer));
66
    if (nbw != int(strlen(buffer))) {
67
        qDebug() << "CGpio::gpioDirection: Erreur écriture dans fichier !";
68
        emit erreur("CGPIO::gpioDirection: Erreur écriture dans fichier !");
69
        return -1;
70
    } // if nbw
71
    fDirection.close();
72
    return 0;
73
}
74
75
int CGpio::gpioExport()
76
{
77
    char buffer[3];
78
79
    QFile fExport("/sys/class/gpio/export");
80
    bool res = fExport.open(QIODevice::WriteOnly | QIODevice::Text);
81
    if (!res) {
82
        qDebug() << "CGpio::gpioExport: Erreur d'ouverture du fichier !";
83
        emit erreur("CGpio::gpioExport: Erreur d'ouverture du fichier !");
84
        return -1;
85
    } // if erreur open
86
    sprintf(buffer,"%d", mAddr);
87
    int nbw = fExport.write(buffer, strlen(buffer));
88
    if (nbw != int(strlen(buffer))) {
89
        qDebug() << "CGpio::gpioExport: Erreur écriture dans fichier !";
90
        emit erreur("CGPIO::gpioExport: Erreur écriture dans fichier !");
91
        return -1;
92
    } // if nbw
93
    fExport.close();
94
    return 0;
95
}
96
	
97
98
int CGpio::lire()
99
{
100
    char buffer[3];
101
    QString ficValue;
102
103
    ficValue = QString("/sys/class/gpio/gpio%1/value").arg(mAddr,0,10);
104
    QFile fValue(ficValue);
105
    bool res = fValue.open(QIODevice::ReadOnly | QIODevice::Text);
106
    if (!res) {
107
        qDebug() << "CGpio::gpioLire: Erreur d'ouverture du fichier !";
108
        emit erreur("CGpio::gpioLire: Erreur d'ouverture du fichier !");
109
        return -1;
110
    } // if erreur open
111
112
    int nbr = fValue.read(buffer, sizeof(buffer));
113
    if (nbr == -1) {
114
        qDebug() << "CGpio::gpioLire: Erreur lecture dans fichier !";
115
        emit erreur("CGPIO::gpioLire: Erreur lecture dans fichier !");
116
        return -1;
117
    } // if nbw
118
    buffer[nbr]=0;  // car NULL
119
    fValue.close();
120
    return atoi(buffer);
121
}
122
	
123
int CGpio::ecrire(int value)
124
{
125
    char buffer[3]={'0', '1'};
126
    QString ficValue;
127
128
    ficValue = QString("/sys/class/gpio/gpio%1/value").arg(mAddr,0,10);
129
    QFile fValue(ficValue);
130
    bool res = fValue.open(QIODevice::WriteOnly | QIODevice::Text);
131
    if (!res) {
132
        qDebug() << "CGpio::gpioEcrire: Erreur d'ouverture du fichier !";
133
        emit erreur("CGpio::gpioEcrire: Erreur d'ouverture du fichier !");
134
        return -1;
135
    } // if erreur open
136
137
    int nbw = fValue.write(&buffer[(value==0?0:1)], 1);
138
    if (nbw == -1) {
139
        qDebug() << "CGpio::gpioEcrire: Erreur écriture dans fichier !";
140
        emit erreur("CGPIO::gpioEcrire: Erreur écriture dans fichier !");
141
        return -1;
142
    } // if nbw
143
    fValue.close();
144
    return 0;
145
}	
146

RemarqueGPIO entrée ou sortie ? ou les deux ?

Cette classe ne permet l'utilisation d'une E/S que dans un seul sens de communication.

Pour les périphériques nécessitant les 2 sens de communication, il sera nécessaire de modifier cette classe.