Cours:TPgpioQT : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
(Sorties)
(Classe Lampe)
 
(21 révisions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
 
  [[Cours:3105|{{Rouge|'''Retour à la liste des Tds'''}}]]
 
  [[Cours:3105|{{Rouge|'''Retour à la liste des Tds'''}}]]
 +
  
 
=GPIO=
 
=GPIO=
Ligne 35 : Ligne 36 :
 
*Créer un nouveau projet de type {{Rouge|"Widget application"}}
 
*Créer un nouveau projet de type {{Rouge|"Widget application"}}
 
*Choisir le kit {{Rouge|"Rpi"}}
 
*Choisir le kit {{Rouge|"Rpi"}}
*Télécharger/décompresser et placer les fichiers de l'archive suivant dans votre projet
+
*{{Rouge|'''ajouter à la fin du fichier .pro la ligne'''}} <code>LIBS += -lpigpio</code>
**[[Media:FichiersGPIO.zip|FichiersGPIO.zip]]
 
 
*Dans l'arborescence de votre projet,
 
*Dans l'arborescence de votre projet,
 
**Ajouter des fichiers existants
 
**Ajouter des fichiers existants
  
 +
=Sorties=
  
==Sorties==
+
==Classe Lampe==
 
 
===Classe Lampe===
 
 
On utilisera la classe Lampe pour piloter des sorties.
 
On utilisera la classe Lampe pour piloter des sorties.
  
 
{|style="vertical-align:middle; width:100%; text-align:left; "
 
{|style="vertical-align:middle; width:100%; text-align:left; "
 
|-
 
|-
| {{boîte déroulante/début|titre=[[Media:lampe.h|lampe.h]]}}
+
| {{boîte déroulante/début|titre=lampe.h}}
 
<source lang=cpp>
 
<source lang=cpp>
 
#ifndef LAMPE_H
 
#ifndef LAMPE_H
Ligne 54 : Ligne 53 :
  
 
#include <QObject>
 
#include <QObject>
#include "gpio.h"
 
using namespace exploringRPi;
 
  
 
class Lampe : public QObject
 
class Lampe : public QObject
Ligne 61 : Ligne 58 :
 
     Q_OBJECT
 
     Q_OBJECT
 
public:
 
public:
     Lampe(int nbBroche);
+
     Lampe(int _numGpio);
  
 
private :
 
private :
     bool etat;
+
     bool isAllumee;
     GPIO broche;
+
     int numGpio;
  
signals:
 
    void nouvelEtat(bool etat);
 
 
public slots:
 
public slots:
 
     void allumer();
 
     void allumer();
 
     void eteindre();
 
     void eteindre();
     void setValue(bool e);
+
private:
 +
     void setValue(bool nouvelEtat);
 
};
 
};
  
Ligne 81 : Ligne 77 :
 
<source lang=cpp>
 
<source lang=cpp>
 
#include "lampe.h"
 
#include "lampe.h"
 +
#include <pigpio.h>
  
Lampe::Lampe(int nbBroche) : QObject(), broche(nbBroche)
+
Lampe::Lampe(int _broche) : QObject(),
 +
                            numGpio(_numGpio)
 
{
 
{
     broche.setDirection(OUTPUT);
+
     gpioInitialise();
 
     setValue(false);
 
     setValue(false);
 +
    gpioSetMode(numGpio,PI_OUTPUT);
 
}
 
}
  
Ligne 98 : Ligne 97 :
 
}
 
}
  
void Lampe::setValue(bool e)
+
void Lampe::setValue(bool nouvelEtat)
 
{
 
{
     if (e==true) broche.setValue(HIGH);
+
     gpioWrite(numGpio,e);
            else broche.setValue(LOW);
+
     isAllumee = nouvelEtat;
     etat = e;
 
    emit nouvelEtat(etat);
 
 
}
 
}
 
</source>
 
</source>
Ligne 113 : Ligne 110 :
  
  
On va instancier la classe Lampe en précisant le numéro de GPIO utilise. Pour cela, le constructeur de la classe GPIO nécessite le passage d'un paramètre :
+
On va instancier la classe Lampe en précisant le numéro de GPIO utilise. Pour cela 2 approches sont possibles :
Lampe(int nbBroche);
 
 
 
2 approches sont possibles :
 
  
 
{|
 
{|
Ligne 137 : Ligne 131 :
 
//2ère possibilité on l'indique dans la définition du constructeur
 
//2ère possibilité on l'indique dans la définition du constructeur
 
// dans le mainwindow.h
 
// dans le mainwindow.h
#include "gpio.h"
+
#include "lampe.h"
 
...
 
...
 
class MainWindow : public QMainWindow
 
class MainWindow : public QMainWindow
Ligne 159 : Ligne 153 :
 
{{Question|Instancier la classe Lampe et l'utiliser pour allumer la led verte à l'aide de bouton sur l'interface graphique}}
 
{{Question|Instancier la classe Lampe et l'utiliser pour allumer la led verte à l'aide de bouton sur l'interface graphique}}
  
 
+
==Ajout de fonctionnalité à la classe Lampe==
===Ajout de fonctionnalité à la classe Lampe===
 
  
 
On souhaite pouvoir changer l'état d'une lampe (si elle est allumée elle doit s'éteindre et inversement).
 
On souhaite pouvoir changer l'état d'une lampe (si elle est allumée elle doit s'éteindre et inversement).
Ligne 169 : Ligne 162 :
  
  
=== Clignotement avec QTimer ===
+
== Clignotement avec QTimer ==
  
 
On peut facilement faire clignoter une lampe avec un <code>QTimer</code> :
 
On peut facilement faire clignoter une lampe avec un <code>QTimer</code> :
 
*on choisit une durée : <code>monTimer.setInterval(dure_Ms)</code>;
 
*on choisit une durée : <code>monTimer.setInterval(dure_Ms)</code>;
 
*le signal timeout est émis toutes les duree_Ms
 
*le signal timeout est émis toutes les duree_Ms
*il faut se connecter sur le signal : <code>connect(&monTimer,SIGNAL(timeout()),&maLampe,SLOT(toggle()))</code>;
+
*il faut se connecter sur le signal :
 +
<code>connect(&monTimer,&QTimer::timeout,&maLampe,&Lampe::changerEtat)</code>;
 
*on lance le timer : <code>monTimer.start()</code>;
 
*on lance le timer : <code>monTimer.start()</code>;
  
Ligne 181 : Ligne 175 :
 
{{Question|En utilisant un QTimer, faire clignoter la lampe rouge}}
 
{{Question|En utilisant un QTimer, faire clignoter la lampe rouge}}
  
==Entrées==
+
==Cahier des charges==
 +
 
 +
Ecrire un programme qui permet de :
 +
*allumer individuellement chaque led
 +
*éteindre individuellement chaque led
 +
*allumer les 2 leds avec 1 seul bouton
 +
*éteindre les 2 leds avec 1 seul bouton
 +
*mettre en route/arrêter individuellement le clignotement de chaque led (en utilisant un QTimer):
 +
**la led rouge à 1Hz
 +
**la led verte à 5Hz
 +
*quitter l'application
 +
 
 +
=Entrées=
 +
 
 +
 
 +
==Classe Bouton==
  
'''On pourrait''' procéder de façon analogue :
+
On donne les fichiers correspondants à la classe Bouton qui va permettre d'observer une entrée logique de type bouton poussoir :
 +
 
 +
{|style="vertical-align:middle; width:100%; text-align:left; "
 +
|-
 +
| {{boîte déroulante/début|titre=bouton.h}}
 
<source lang=cpp>
 
<source lang=cpp>
#include ....
+
#ifndef BOUTON_H
using namespace exploringRPi;
+
#define BOUTON_H
...
+
 
GPIO bp{16};
+
#include <QObject>
....
+
#include <QTimer>
bp.setDirection(INPUT);
+
 
int n = bp.getValue();
+
enum EdgeType{ NONE, RISING, FALLING, BOTH };
 +
 
 +
class Bouton : public QObject
 +
{
 +
    Q_OBJECT
 +
public:
 +
    explicit Bouton(int _numGpio,EdgeType front,QObject *parent = nullptr);
 +
 
 +
    int getValue();
 +
 
 +
private:
 +
    int numGpio;
 +
    int interruptLevel;
 +
    QTimer tictocAntiRebond;
 +
 
 +
    void getEvent();
 +
    void processAntiRebond();
 +
 
 +
    static void processInterrupt(int gpio, int level, uint32_t tick, void *userdata);
 +
 
 +
signals:
 +
    void change();
 +
    void startTimerAntiRebond();
 +
 
 +
};
 +
 
 +
#endif // BOUTON_H
 
</source>
 
</source>
 +
{{boîte déroulante/fin}}
 +
|| {{boîte déroulante/début|titre=bouton.cpp}}
 +
<source lang=cpp>
 +
#include "bouton.h"
 +
#include <QDebug>
 +
#include <pigpio.h>
  
{{Rouge|Attention, }} en procédant de la sorte, il convient de scruter régulièrement la valeur de l'entrée pour vérifier si celle-ci n'a pas changée d'état. On peut par exemple utiliser un {{Rouge|QTimer}} pour réaliser ce {{Rouge|polling}}.
+
Bouton::Bouton(int _numGpio, EdgeType front, QObject *parent) : QObject(parent)
 +
{
 +
    numGpio=_numGpio;
 +
    gpioInitialise();
 +
    gpioSetMode(numGpio,PI_INPUT);
 +
    tictocAntiRebond.setInterval(4);
 +
    tictocAntiRebond.setSingleShot(true);
 +
    connect(&tictocAntiRebond,&QTimer::timeout,
 +
            this,&Bouton::processAntiRebond);
 +
    connect(this,&Bouton::startTimerAntiRebond,
 +
            &tictocAntiRebond,QOverload<>::of(&QTimer::start));
  
 +
    interruptLevel=0;
 +
    switch (front)
 +
    {
 +
        case FALLING: interruptLevel=0;break;
 +
        case RISING: interruptLevel=1;break;
 +
        case BOTH: interruptLevel=2;break;
 +
        default : interruptLevel=-1;
 +
    }
 +
    if (interruptLevel!=-1)
 +
    {
 +
        gpioSetISRFuncEx(numGpio,interruptLevel, 0, Bouton::processInterrupt,this);
 +
    }
 +
}
 +
 +
int Bouton::getValue()
 +
{
 +
    return gpioRead(numGpio);
 +
}
  
Comme nous réalisons une programmation événementielle, cette méthode '''n'est pas la plus adaptée''', voyons comment faire :
+
void Bouton::getEvent()
*les GPIOs se trouvent dans l'arborescence linux dans le répertoire {{Rouge|/sys/class/gpio/}}
+
{
*si une broche est configurée, la 10 par ex, on aura un répertoire : {{Rouge|/sys/class/gpio/gpio10/}}
+
    if (interruptLevel!=2)    emit startTimerAntiRebond();
*dans ce répertoire, il y a un fichier {{Rouge|value}}
+
    else                      emit change();
*on va observer ce fichier {{Rouge|/sys/class/gpio/gpio10/value}} qui reflète l'état de la broche
+
}
*on demandera l'exécution d'un SLOT lors de la modification de ce fichier
 
*mise en oeuvre :
 
  
<source lang=cpp>
+
void Bouton::processAntiRebond()
// il nous faut un objet de type QFileSystemWatcher
+
{
#include <QFileSystemWatcher>
+
    if (gpioRead(numGpio)!=interruptLevel)  emit change();
 +
}
  
// On ajoute un attribut de type QFileSystemWatcher:
+
void Bouton::processInterrupt(int gpio, int level, uint32_t tick, void *userdata)
QFileSystemWatcher bpWatcher;
+
{
GPIO bp{10};
+
    ((Bouton *)userdata)->getEvent();
...
+
}
...
 
// On configure la broche en entrée:
 
bp.setDirection(INPUT);
 
// Quel type d'événement nous intéresse ? NONE, RISING, FALLING, BOTH
 
bp.setEdgeType(BOTH);
 
// On ajoute la liste des fichiers dont il faut surveiller la modification
 
bpWatcher.addPath("/sys/class/gpio/gpio10/value");
 
// Un signal "fileChanged" est généré à chaque événement surveillé (cf plus haut)
 
connect(&bpWatcher,SIGNAL(fileChanged(QString)), ... , SLOT( .... ));
 
 
</source>
 
</source>
 +
{{boîte déroulante/fin}}
 +
|}
 +
 +
 +
{{Question|Ajouter la classe Bouton à votre diagramme de la classe}}
 +
 +
==Instanciation de la classe Bouton==
 +
 +
{{Question|En observant le constructeur de la classe Bouton, ajouter à la classe MainWindow 2 attributs de type Bouton pour gérer les 2 boutons poussoirs}}
  
 +
{{Question|Créer des slots dans votre classe MainWindow et les connecter au signal émis par les objets de type Bouton}}
  
{{Question|Interfacer le switch pour commander la led verte}}
+
==Utilisation==
  
{{Question|Interfacer les boutons poussoirs rouge et vert pour augmenter/diminuer la période de clignotement de la led rouge}}
+
{{Question|Modifier l'application de gestion des lampes pour pouvoir utiliser soit les boutons de l'écran tactile soit les boutons physiques}}

Version actuelle datée du 23 septembre 2024 à 21:43

Retour à la liste des Tds


GPIO

Gpio rpi3.png

On dispose d'un connecteur avec un certain nombre de GPIO sur la carte rpi.

Ce sont des broches configurables qui peuvent servir de :

  • entrée logique
  • sortie logique
  • fonction particulière :
    • entrée analogique (pas sur les rpi actuelles)
    • i2c
    • spi
    • pwm
    • liaison série
    • ...

Cartographie GPIO <-> modules

Plusieurs modules sont connectés sur les ports GPIO de la Rpi :

n° GPIO Module n° GPIO Module n° GPIO Module
5 Encodeur 16 Bouton poussoir 1 (vert?) 18 Switch
6 Encodeur 17 Bouton poussoir 2 (rouge?)
22 led Rouge 24 led Verte

Préparation du projet

  • Créer un nouveau projet de type "Widget application"
  • Choisir le kit "Rpi"
  • ajouter à la fin du fichier .pro la ligne LIBS += -lpigpio
  • Dans l'arborescence de votre projet,
    • Ajouter des fichiers existants

Sorties

Classe Lampe

On utilisera la classe Lampe pour piloter des sorties.

lampe.h

#ifndef LAMPE_H
#define LAMPE_H

#include <QObject>

class Lampe : public QObject
{
    Q_OBJECT
public:
    Lampe(int _numGpio);

private :
    bool isAllumee;
    int numGpio;

public slots:
    void allumer();
    void eteindre();
private:
    void setValue(bool nouvelEtat);
};

#endif // LAMPE_H

lampe.cpp

#include "lampe.h"
#include <pigpio.h>

Lampe::Lampe(int _broche) : QObject(),
                            numGpio(_numGpio)
{
    gpioInitialise();
    setValue(false);
    gpioSetMode(numGpio,PI_OUTPUT);
}

void Lampe::allumer()
{
    setValue(true);
}

void Lampe::eteindre()
{
    setValue(false);
}

void Lampe::setValue(bool nouvelEtat)
{
    gpioWrite(numGpio,e);
    isAllumee = nouvelEtat;
}

Question.jpg Faire le diagramme de classe d'après le fichier lampe.h


On va instancier la classe Lampe en précisant le numéro de GPIO utilise. Pour cela 2 approches sont possibles :

//1ère possibilité on l'indique dans la déclaration (mainwindow.h)
// ajout d'un attribut dans le header
#include "lampe.h"
...
class MainWindow : public QMainWindow
{
   ....
   Lampe ledRouge{20};
   ....
}
//2ère possibilité on l'indique dans la définition du constructeur
// dans le mainwindow.h
#include "lampe.h"
...
class MainWindow : public QMainWindow
{
   ....
   Lampe ledRouge;
   ....
}
...
// dans le mainwindow.cpp
....
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::MainWindow),ledRouge(16)
{
   ....
}

Question.jpg Instancier la classe Lampe et l'utiliser pour allumer la led verte à l'aide de bouton sur l'interface graphique

Ajout de fonctionnalité à la classe Lampe

On souhaite pouvoir changer l'état d'une lampe (si elle est allumée elle doit s'éteindre et inversement).

Pour ajouter cette fonctionnalité nous devons ajouter une méthode.

Question.jpg Ajouter une méthode changerEtat à la classe Lampe et l'utiliser


Clignotement avec QTimer

On peut facilement faire clignoter une lampe avec un QTimer :

  • on choisit une durée : monTimer.setInterval(dure_Ms);
  • le signal timeout est émis toutes les duree_Ms
  • il faut se connecter sur le signal :
connect(&monTimer,&QTimer::timeout,&maLampe,&Lampe::changerEtat);
  • on lance le timer : monTimer.start();

Il faut bien entendu ajouter monTimer comme un attribut de la classe MainWindow (ajouter #include <QTimer>)

Question.jpg En utilisant un QTimer, faire clignoter la lampe rouge

Cahier des charges

Ecrire un programme qui permet de :

  • allumer individuellement chaque led
  • éteindre individuellement chaque led
  • allumer les 2 leds avec 1 seul bouton
  • éteindre les 2 leds avec 1 seul bouton
  • mettre en route/arrêter individuellement le clignotement de chaque led (en utilisant un QTimer):
    • la led rouge à 1Hz
    • la led verte à 5Hz
  • quitter l'application

Entrées

Classe Bouton

On donne les fichiers correspondants à la classe Bouton qui va permettre d'observer une entrée logique de type bouton poussoir :

bouton.h

#ifndef BOUTON_H
#define BOUTON_H

#include <QObject>
#include <QTimer>

enum EdgeType{ NONE, RISING, FALLING, BOTH };

class Bouton : public QObject
{
    Q_OBJECT
public:
    explicit Bouton(int _numGpio,EdgeType front,QObject *parent = nullptr);

    int getValue();

private:
    int numGpio;
    int interruptLevel;
    QTimer tictocAntiRebond;

    void getEvent();
    void processAntiRebond();

    static void processInterrupt(int gpio, int level, uint32_t tick, void *userdata);

signals:
    void change();
    void startTimerAntiRebond();

};

#endif // BOUTON_H

bouton.cpp

#include "bouton.h"
#include <QDebug>
#include <pigpio.h>

Bouton::Bouton(int _numGpio, EdgeType front, QObject *parent) : QObject(parent)
{
    numGpio=_numGpio;
    gpioInitialise();
    gpioSetMode(numGpio,PI_INPUT);
    tictocAntiRebond.setInterval(4);
    tictocAntiRebond.setSingleShot(true);
    connect(&tictocAntiRebond,&QTimer::timeout,
            this,&Bouton::processAntiRebond);
    connect(this,&Bouton::startTimerAntiRebond,
            &tictocAntiRebond,QOverload<>::of(&QTimer::start));

    interruptLevel=0;
    switch (front)
    {
        case FALLING: interruptLevel=0;break;
        case RISING: interruptLevel=1;break;
        case BOTH: interruptLevel=2;break;
        default : interruptLevel=-1;
    }
    if (interruptLevel!=-1)
    {
        gpioSetISRFuncEx(numGpio,interruptLevel, 0, Bouton::processInterrupt,this);
    }
}

int Bouton::getValue()
{
    return gpioRead(numGpio);
}

void Bouton::getEvent()
{
    if (interruptLevel!=2)    emit startTimerAntiRebond();
    else                      emit change();
}

void Bouton::processAntiRebond()
{
    if (gpioRead(numGpio)!=interruptLevel)  emit change();
}

void Bouton::processInterrupt(int gpio, int level, uint32_t tick, void *userdata)
{
    ((Bouton *)userdata)->getEvent();
}


Question.jpg Ajouter la classe Bouton à votre diagramme de la classe

Instanciation de la classe Bouton

Question.jpg En observant le constructeur de la classe Bouton, ajouter à la classe MainWindow 2 attributs de type Bouton pour gérer les 2 boutons poussoirs

Question.jpg Créer des slots dans votre classe MainWindow et les connecter au signal émis par les objets de type Bouton

Utilisation

Question.jpg Modifier l'application de gestion des lampes pour pouvoir utiliser soit les boutons de l'écran tactile soit les boutons physiques