Utilisation d'un segment de mémoire partagé entre processus

Question

En vous servant du programme appGpio, appThread et appSpi, modifier les classes capteurs afin d'écrire les valeurs des capteurs dans un segment de mémoire partagé commun aux threads Ihm, capteurs.

L'objectif est de disposer d'une classe CIhmAppShm permettant l'affichage d'une ou plusieurs mesures à intervalle régulier.

Chaque capteur (I2C, SPI) est représenté par un objet thread lisant à intervalle régulier la valeur du capteur et la sauvant en mémoire partagée.

Indice

Il est avantageux de créer une classe de gestion du segment de mémoire partagé.

Solution

La classe CSharedMemory
1
#ifndef CSHAREDMEMORY_H
2
#define CSHAREDMEMORY_H
3
4
#include <QSharedMemory>
5
#include <QDebug>
6
#include "global.h"
7
8
// définition de la mémoire partagée
9
typedef struct {
10
    float temp_TC72;
11
    float temp_SHT20;
12
    float hum_SHT20;
13
} T_SHM_DATA;
14
15
class CSharedMemory : public QSharedMemory
16
{
17
    Q_OBJECT
18
19
public:
20
    explicit CSharedMemory(QObject *parent = 0, int size = 10);
21
    ~CSharedMemory();
22
    int attacherOuCreer();
23
    int attacherSeulement();
24
    int ecrire(int no, float mesure);
25
    float lire(int no);
26
27
private:
28
    int m_taille;
29
    float *m_adrBase;
30
    QObject *m_parent;
31
32
signals:
33
    void sigErreur(QString mess);
34
35
public slots:
36
37
};
38
39
#endif // CSHAREDMEMORY_H
1
#include "csharedmemory.h"
2
3
CSharedMemory::CSharedMemory(QObject *parent, int size) :
4
    QSharedMemory(parent)
5
{
6
    m_parent = parent;
7
    setKey(KEY);
8
    m_taille = size;
9
    m_adrBase = NULL;
10
    qDebug() << "Objet CSharedMemory créé par " << m_parent->thread();
11
}
12
13
CSharedMemory::~CSharedMemory()
14
{
15
    detach();
16
    qDebug() << "Objet CSharedMemory détruit par " << m_parent->thread();
17
}
18
19
int CSharedMemory::attacherOuCreer()
20
{
21
    int res;
22
23
    attach();   // tentative de s'attacher
24
    if (!isAttached()) {   // si existe pas alors création
25
         res = create(m_taille);   // on réserve la place
26
         if (!res) {
27
              QString mess="CSharedMemory::attacherOuCreer Erreur de création de la mémoire partagée.";
28
              emit sigErreur(mess);
29
              return ERREUR;
30
         } // if res
31
    } // if isattached
32
    attach();
33
    m_adrBase = (float *)data();
34
    return 0;
35
}
36
37
int CSharedMemory::attacherSeulement()
38
{
39
    attach();   // tentative de s'attacher
40
    if (!isAttached()) {   // si existe pas
41
        QString mess="CSharedMemory::attacherSeulement Erreur de création de la mémoire partagée.";
42
      emit sigErreur(mess);
43
      return ERREUR;
44
    } // if isattached
45
    m_adrBase = (float *)data();
46
    return 0;
47
}
48
49
int CSharedMemory::ecrire(int no, float mesure)
50
{
51
    if ( (no<0) && (no>2) ) {
52
        QString mess="CSharedMemory::ecrire ERREUR, indice de la mesure incorrecte.";
53
        emit sigErreur(mess);
54
        return ERREUR;
55
    } // if no
56
    if (!isAttached()) {   // si existe pas
57
        QString mess="CSharedMemory::ecrire Erreur mémoire partagée non attachée.";
58
        emit sigErreur(mess);
59
        return ERREUR;
60
    } // if isattached
61
    m_adrBase[no] = mesure;
62
    return 0;
63
}
64
65
float CSharedMemory::lire(int no)
66
{
67
    if ( (no<0) && (no>2) ) {
68
        QString mess="CSharedMemory::lire ERREUR, indice de la mesure incorrecte.";
69
        emit sigErreur(mess);
70
        return ERREUR;
71
    } // if no
72
    if (!isAttached()) {   // si existe pas alors création
73
        QString mess="CSharedMemory::lire Erreur mémoire partagée non attachée. ";
74
      emit sigErreur(mess);
75
      return ERREUR;
76
    } // if isattached
77
    return m_adrBase[no];
78
}
79

Exemple avec la classe CCapteur_I2c_SHT20 incorporant la classe CSharedMemory

1
#ifndef CCAPTEUR_I2C_SHT20_H
2
#define CCAPTEUR_I2C_SHT20_H
3
4
#include <QThread>
5
#include "global.h"
6
#include "csharedmemory.h"
7
#include "/home/pi/devQt/biblis/ci2c.h"
8
9
#define ADR 0x40 // 0x40 + bit LSB à 0 pour write
10
#define COM_MES_TEMP 0xf3
11
#define COM_MES_HUM 0xf5
12
#define COM_RESET 0xfe
13
#define COM_READ_REG 0xe7
14
#define COM_WRITE_REG 0xe6
15
16
class CCapteur_I2c_SHT20 : public QThread
17
{
18
    Q_OBJECT
19
20
public:
21
    explicit CCapteur_I2c_SHT20(QObject *parent = 0, int noMesBase = 1);
22
    ~CCapteur_I2c_SHT20();
23
    bool m_fin;
24
25
private:
26
    CSharedMemory *m_shm;
27
    CI2c *m_i2c;
28
    int m_noMesBase;
29
    void run();  // méthode virtuelle à implémenter, contenu du thread
30
    float lireMesureHum();
31
    float lireMesureTemp();
32
signals:
33
    void sigErreur(QString mess);
34
35
private slots:
36
    void onErreur(QString mess);
37
38
};
39
40
#endif // CCAPTEURTEMPHUMI2C_H
41

Notez l'attribut de classe m_shm. Il permettra l'instanciation de l'objet CSharedMemory qui rappelons le utilise le mécanisme système de gestion d'un segment de mémoire partagé. Donc, chaque objet CSharedMemory accédera au même segment mémoire pourvu que la clef soit la même.

1
#include "ccapteur_i2c_sht20.h"
2
3
CCapteur_I2c_SHT20::CCapteur_I2c_SHT20(QObject *parent, int noMesBase) :
4
    QThread(parent)
5
{
6
    m_shm = new CSharedMemory(this);
7
    connect(m_shm, SIGNAL(sigErreur(QString)), this, SLOT(onErreur(QString)));
8
    m_shm->attacherSeulement();
9
10
    m_i2c = CI2c::getInstance(this, '1');
11
    connect(m_i2c, SIGNAL(sigErreur(QString)), this, SLOT(onErreur(QString)));
12
    m_fin=false;
13
    m_noMesBase = noMesBase;
14
    qDebug() << "Objet CCapteur_I2c_SHT20 créé !";
15
}
16
17
CCapteur_I2c_SHT20::~CCapteur_I2c_SHT20()
18
{
19
    CI2c::freeInstance();
20
    m_shm->detach();
21
    delete m_shm;
22
    qDebug() << "Objet CCapteur_I2c_SHT20 détruit !";
23
24
}
25
26
void CCapteur_I2c_SHT20::run()
27
{
28
    float mesureHum, mesureTemp;
29
30
     while(!m_fin) {
31
         // écriture de la mesure dans le segment de mémoire partagé
32
         mesureHum = lireMesureHum();
33
         usleep(100000);
34
         mesureTemp = lireMesureTemp();
35
         m_shm->lock(); // on prend la mémoire partagée
36
         m_shm->ecrire(m_noMesBase, mesureTemp);  // écriture dans la mémoire partagée
37
         m_shm->ecrire(m_noMesBase+1, mesureHum);  // écriture dans la mémoire partagée
38
         m_shm->unlock(); // on libère la mémmoire partagée
39
         sleep(1); // lecture toutes les s
40
     } // while
41
}
42
43
void CCapteur_I2c_SHT20::onErreur(QString mess)
44
{
45
    emit sigErreur(mess);
46
}
47
48
float CCapteur_I2c_SHT20::lireMesureHum()
49
{
50
    float hum;
51
    unsigned char lecture[3];
52
    unsigned char ecriture[1];
53
    int res;
54
55
    ecriture[0] = COM_MES_HUM;
56
    m_i2c->ecrire(ADR, ecriture, 1);
57
    usleep(100000);
58
    res=m_i2c->lire(ADR, lecture, 2);
59
    if (res != 2) {
60
        QString mess="CCapteur_I2c_SHT20::lireMesureHum ERREUR Lecture";
61
        emit sigErreur(mess);
62
        return -1;
63
    } // if res
64
    unsigned char MSB = lecture[0];
65
    unsigned char LSB = lecture[1]&0xF0;
66
    hum=((MSB<<8)+LSB);
67
    hum = -6+125*hum/65536;
68
    return hum;
69
} // lireMesHum
70
71
float CCapteur_I2c_SHT20::lireMesureTemp()
72
{
73
    float temp;
74
    unsigned char lecture[2];
75
    unsigned char ecriture[1];
76
    int res;
77
78
    ecriture[0] = COM_MES_TEMP;
79
    m_i2c->ecrire(ADR, ecriture, 1);
80
    usleep(100000);
81
    res=m_i2c->lire(ADR, lecture, 2);
82
    if (res != 2) {
83
        QString mess="CCapteur_I2c_SHT20::lireMesureTemp ERREUR Lecture";
84
        emit sigErreur(mess);
85
        return -1;
86
     } // if res
87
    unsigned char MSB = lecture[0];
88
    unsigned char LSB = lecture[1]&0xFC;
89
    temp = ((MSB<<8)+LSB);
90
    temp = -46.85+175.72*temp/65536;
91
    return temp;
92
} // lire MesTemp
93

Je vous laisse le soin de développer l'interface graphique de cette l'application.

ComplémentQuelques explications concernant CCapteur_I2c_SHT20
  • Cette classe hérite de QThread. La méthode run() est donc un thread autonome.

  • L'instanciation de la classe CSharedMemory se fait dans le constructeur de CCapteur_I2c_SHT20.

  • Notez l'utilisation de la méthode CSharedMemory::lock() dans la méthode CCapteur_I2c_SHT20::run(). Elle permet de s'assurer que seul un objet aura au même moment le droit d'accéder à la mémoire partagé. CSharedMemory::unlock()libère le verrou d'accès à la mémoire pour les objets suivants ou en attente.

RemarqueCSharedMemory

Cette classe est spécialisée pour notre application. Pour l'utiliser dans un autre contexte, il sera nécessaire de la modifier pour l'adapter à la structure de données du nouveau contexte.