Cours:TPheritageIO : Différence entre versions

De troyesGEII
Aller à : navigation, rechercher
(classe LampeI2C)
(Par pointeur)
Ligne 190 : Ligne 190 :
  
 
==Par pointeur==
 
==Par pointeur==
 +
 +
L'intérêt des pointeurs est de pouvoir créer des objets lorsque bon nous semble ... et aussi de faire n'importe quoi !
 +
 +
Voici une possibilité :
 +
{|
 +
|-
 +
|
 +
<source lang=cpp>
 +
class Controleur
 +
{
 +
  Controleur(Lampe * l);
 +
  Lampe * led = nullptr;
 +
}
 +
</source>
 +
||
 +
<source lang=cpp>
 +
class Utilisation
 +
{
 +
  // on ne peut pas créer le Controleur
 +
  // avant d'avoir créer une Lampe
 +
  // du coup on utilise également un pointeur
 +
  Controleur * c1 = nullptr;
 +
}
 +
</source>
 +
 +
|-
 +
|
 +
<source lang=cpp>
 +
 +
Controleur::Controleur(Lampe * l)
 +
{
 +
    // on pointe sur l'objet de type led dont on a passé l'adresse
 +
    led = l;
 +
}
 +
</source>
 +
||
 +
<source lang=cpp>
 +
Utilisation::Utilisation()
 +
{
 +
}
 +
 +
</source>
 +
|}
  
 
=Ressources=
 
=Ressources=

Version du 30 septembre 2022 à 09:43

Nous allons utiliser un gpio expander en i2c qui permet d'ajouter facilement un grand nombre d'entrées/sorties sur un système.

Le composant utilisé, un mcp23017, possède 16 gpio. Son @i2c est réglable à l'aide de 3 broches ce qui permet au maximum d'en utiliser 8 sur le même bus i2c.

On peut donc ajouter jusqu'à 128 entrées/sorties en utilisant ce composant.



Nous souhaitons écrire un programme qui permet de piloter des lampes/leds, certaines connectées directement sur les gpio de la carte programmable, d'autres branchées sur le gpio expander.


Dans tous les cas, il s'agit de Lampe, qu'elle soit une LampeI2c ou une LampeGPIO.

Chaque Lampe présente :

  • la propriété ou attribut suivant :
    • isAllumee
  • les fonctionnalités ou méthodes suivantes :
    • allumer
    • eteindre
    • changer

"Diagramme" de classe

classe abstraite Lampe

Question.jpg Faire une classe Lampe qui:

  • hérite de QObject
  • a un attribut protected :
    • isAllumee : bool
  • a des slots public :
    • allumer
    • eteindre
    • changer
  • a une méthode protected
    • virtual void setValue(bool etat)


Remarque :

  • setValue est la seule méthode qui sera redéfinie dans les classes filles
  • du coup il est nécessaire de la déclarer comme virtual
  • les méthodes(slots plutôt) allumer/eteindre/changer doivent utiliser la méthode setValue

classe LampeGPIO

Question.jpg Ecrivez maintenant la classe LampeGPIO :

  • elle hérite de Lampe
  • le constructeur doit permettre d'indiquer le numéro de broche
  • elle a un attribut protected
    • pin de type GPIO
  • elle doit redéfinir setValue
    • on ajoute le mot clé override dans la déclaration :
    • virtual void setValue(bool etat) override
    • virtual n'est pas indispensable, il est utile si on souhaite spécialiser la classe LampeGPIO
    • dans la définition de la méthode, on pourra appeler la méthode de la classe mère : Lampe::setValue(etat)


Remarque :

  • il convient de définir la broche en sortie ( méthode setDirection de la classe GPIO ) ... oui oui dans le constructeur !
  • pour modifier la sortie, on utilisera la méthode setValue de la classe GPIO


Todo.jpg Peut-être le moment de tester un peu tout ça ??

classe LampeI2C

Question.jpg Et c'est parti pour la dernière classe : LampeGPIO :

  • elle spécialise également la classe Lampe
  • on ajoute des attributs
    • pinMCP : int le numéro du GPIO, par ex 2 si on utilise PA2
    • port de type portMCP
      • enum portMCP {portA,portB};
      • du coup ce sera soit PORTA ou PORTB comme valeur
    • i2c de type QI2cDevice
      • ça va servir ... à utiliser le bus i2c c'est ça ;-)
      • chercher à la fin de l'énoncé, il doit y avoir les fichiers
      • sur les cartes raspberry-pi on utilise le bus i2c 1
  • il faut évidemment redéfinir setValue comme pour la classe LampeGPIO
    • dans la définition de la méthode, on pourra appeler la méthode de la classe mère : Lampe::setValue(etat)


Remarques :

  • les registres du mcp23017
enum regAd {IODIRA = 0    ,IODIRB = 1,
            IPOLA = 2     ,IPOLB = 3,
            GPINTENA = 4  ,GPINTENB = 5,
            DEFVALA = 6   ,DEFVALB = 7,
            INTCONA = 8   ,INTCONB = 9,
            IOCON = 10    ,IOCON_ = 11,
            GPPUA = 12    ,GPPUB = 13,
            INTFA = 14    ,INTFB = 15,
            INTCAPA = 16  ,INTCAPB = 17,
            GPIOA = 18    ,GPIOB = 19,
            OLATA = 20    ,OLATB = 21
            };
  • le numéro de port/pin ne changeant pas, on peut les déclarer comme constante à condition de :
    • déclaration : const int pinMCP;
    • définition :
      LampeMCP23017::LampeMCP23017( int _pinMCP, ...): Lampe{parent},pinMCP{_pinMCP} ....
  • pour déclarer la broche en sortie:
    regAd regDir;
    if (port==portA) regDir=IODIRA;
    else             regDir=IODIRB;
    unsigned char value=i2c.readRegister(IODIRA);
    value&=~(1<<pinMCP);
    i2c.writeRegister(regDir,value);
  • pour modifier la valeur de la sortie
    regAd regGPIO;
    if (port==portA) regGPIO=GPIOA;
    else             regGPIO=GPIOB;
    unsigned char value=i2c.readRegister(regGPIO);
    if (isAllumee==true) value|= (1<<pinMCP);
    else             value&=~(1<<pinMCP);
    i2c.writeRegister(regGPIO,value);


Todo.jpg Vérifier le fonctionnement de cette classe LampeI2c

Utilisation : polymorphisme

Nous allons créer une classe Controleur qui pourra agir sur n'importe quel type de Lampe.

Le comportement du Controleur est libre, par exemple arrêter/mettre en route le clignotement d'un voyant.


Comme le type de Lampe dépend du Controleur, nous allons devoir créer un objet de type Lampe(ou d'une classe fille !) à l'extérieur du Controleur

Le Controleur doit posséder un attribut pour ce voyant, il peut s'agir :

  • d'une référence
  • d'un pointeur


Par référence

L'utilisation de références permet d'éviter les erreurs de manipulation de pointeurs, mais peut être impossible dans certaines situations.

Le principe est le suivant :

class Controleur
{
   Controleur(Lampe &l);
   Lampe & led;
}
class Utilisation
{
   // attention l'ordre est important,
   // on doit créer l'objet de type Lampe avant le Controleur
   LampeGPIO l1;
   Controleur c1;
}
//on doit donner la référence
//avant le début du code du constructeur
Controleur::Controleur(Lampe &l)
           :led{l}
{
}
//exécution dans l'ordre des
//constructeurs des attributs l1 et c1
//avec passage de la référence sur l1
Utilisation::Utilisation(Lampe &l)
            :l1{10}, c1{l1}
{
}

Par pointeur

L'intérêt des pointeurs est de pouvoir créer des objets lorsque bon nous semble ... et aussi de faire n'importe quoi !

Voici une possibilité :

class Controleur
{
   Controleur(Lampe * l);
   Lampe * led = nullptr;
}
class Utilisation
{
   // on ne peut pas créer le Controleur
   // avant d'avoir créer une Lampe
   // du coup on utilise également un pointeur
   Controleur * c1 = nullptr;
}
Controleur::Controleur(Lampe * l)
{
    // on pointe sur l'objet de type led dont on a passé l'adresse
    led = l;
}
Utilisation::Utilisation()
{
}

Ressources

carte d'extension mcp23017

ShieldEcran.png

La correspondance entre la sérigraphie de la carte et le numéro de GPIO est le suivant :

  • broche numérotée 2 -> GPB0
  • broche numérotée 3 -> GPB1
  • ...
  • broche numérotée 9 -> GPB7
  • broche numérotée 10 -> GPA0
  • broche numérotée 11 -> GPA1
  • ...
  • broche numérotée 17 -> GPA7