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.
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.
Exemple : Exemple 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 :
sudo echo "4" > /sys/class/gpio/export
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.
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 :
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 :
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
:
class CGpio : public QObject
{
Q_OBJECT
public :
explicit CGpio(int addr, int dir);
~CGpio();
int lire();
int ecrire(int value);
private :
char mFilename[50];
QString mNomFic;
int init();
int gpioExport();
int gpioUnexport();
int gpioDirection(int dir);
int gpioRead();
int gpioWrite(int value);
int mAddr;
signals:
void erreur(QString msg);
};
// CGPIO_H
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 :
CGpio::CGpio(int addr, int dir)
{
mAddr = addr;
gpioExport();
init();
gpioDirection(dir);
} // constructeur
CGpio::~CGpio()
{
gpioUnexport();
} // destructeur
int CGpio::init()
{
sprintf(mFilename,"/sys/class/gpio/gpio%d/",mAddr);
QString command = "sudo chmod -R 777 "+QString(mFilename);
QProcess *proc= new QProcess(this);
proc->start(command);
sleep(1); // laisse le temps du changement de droit
return 1;
}
int CGpio::gpioUnexport()
{
char buffer[3];
QFile fUnexport("/sys/class/gpio/unexport");
bool res = fUnexport.open(QIODevice::WriteOnly | QIODevice::Text);
if (!res) {
qDebug() << "CGpio::gpioUnexport: Erreur d'ouverture du fichier !";
emit erreur("CGpio::gpioUnexport: Erreur d'ouverture du fichier !");
return -1;
} // if erreur open
sprintf(buffer,"%d", mAddr);
int nbw = fUnexport.write(buffer, strlen(buffer));
if (nbw != int(strlen(buffer))) {
qDebug() << "CGpio::gpioUnexport: Erreur écriture dans fichier !";
emit erreur("CGPIO::gpioUnexport: Erreur écriture dans fichier !");
return -1;
} // if nbw
fUnexport.close();
return 0;
}
int CGpio::gpioDirection(int dir)
{
char buffer[3];
QString ficDirection;
ficDirection = QString("/sys/class/gpio/gpio%1/direction").arg(mAddr,0,10);
QFile fDirection(ficDirection);
bool res = fDirection.open(QIODevice::WriteOnly | QIODevice::Text);
if (!res) {
qDebug() << "CGpio::gpioDirection: Erreur d'ouverture du fichier !";
emit erreur("CGpio::gpioDirection: Erreur d'ouverture du fichier !");
return -1;
} // if erreur open
if (dir==IN)
strcpy(buffer,"in");
else
strcpy(buffer,"out");
int nbw = fDirection.write(buffer, strlen(buffer));
if (nbw != int(strlen(buffer))) {
qDebug() << "CGpio::gpioDirection: Erreur écriture dans fichier !";
emit erreur("CGPIO::gpioDirection: Erreur écriture dans fichier !");
return -1;
} // if nbw
fDirection.close();
return 0;
}
int CGpio::gpioExport()
{
char buffer[3];
QFile fExport("/sys/class/gpio/export");
bool res = fExport.open(QIODevice::WriteOnly | QIODevice::Text);
if (!res) {
qDebug() << "CGpio::gpioExport: Erreur d'ouverture du fichier !";
emit erreur("CGpio::gpioExport: Erreur d'ouverture du fichier !");
return -1;
} // if erreur open
sprintf(buffer,"%d", mAddr);
int nbw = fExport.write(buffer, strlen(buffer));
if (nbw != int(strlen(buffer))) {
qDebug() << "CGpio::gpioExport: Erreur écriture dans fichier !";
emit erreur("CGPIO::gpioExport: Erreur écriture dans fichier !");
return -1;
} // if nbw
fExport.close();
return 0;
}
int CGpio::lire()
{
char buffer[3];
QString ficValue;
ficValue = QString("/sys/class/gpio/gpio%1/value").arg(mAddr,0,10);
QFile fValue(ficValue);
bool res = fValue.open(QIODevice::ReadOnly | QIODevice::Text);
if (!res) {
qDebug() << "CGpio::gpioLire: Erreur d'ouverture du fichier !";
emit erreur("CGpio::gpioLire: Erreur d'ouverture du fichier !");
return -1;
} // if erreur open
int nbr = fValue.read(buffer, sizeof(buffer));
if (nbr == -1) {
qDebug() << "CGpio::gpioLire: Erreur lecture dans fichier !";
emit erreur("CGPIO::gpioLire: Erreur lecture dans fichier !");
return -1;
} // if nbw
buffer[nbr]=0; // car NULL
fValue.close();
return atoi(buffer);
}
int CGpio::ecrire(int value)
{
char buffer[3]={'0', '1'};
QString ficValue;
ficValue = QString("/sys/class/gpio/gpio%1/value").arg(mAddr,0,10);
QFile fValue(ficValue);
bool res = fValue.open(QIODevice::WriteOnly | QIODevice::Text);
if (!res) {
qDebug() << "CGpio::gpioEcrire: Erreur d'ouverture du fichier !";
emit erreur("CGpio::gpioEcrire: Erreur d'ouverture du fichier !");
return -1;
} // if erreur open
int nbw = fValue.write(&buffer[(value==0?0:1)], 1);
if (nbw == -1) {
qDebug() << "CGpio::gpioEcrire: Erreur écriture dans fichier !";
emit erreur("CGPIO::gpioEcrire: Erreur écriture dans fichier !");
return -1;
} // if nbw
fValue.close();
return 0;
}
Remarque : GPIO 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.