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
// définition de la mémoire partagée
typedef struct {
float temp_TC72;
float temp_SHT20;
float hum_SHT20;
} T_SHM_DATA;
class CSharedMemory : public QSharedMemory
{
Q_OBJECT
public:
explicit CSharedMemory(QObject *parent = 0, int size = 10);
~CSharedMemory();
int attacherOuCreer();
int attacherSeulement();
int ecrire(int no, float mesure);
float lire(int no);
private:
int m_taille;
float *m_adrBase;
QObject *m_parent;
signals:
void sigErreur(QString mess);
public slots:
};
// CSHAREDMEMORY_H
CSharedMemory::CSharedMemory(QObject *parent, int size) :
QSharedMemory(parent)
{
m_parent = parent;
setKey(KEY);
m_taille = size;
m_adrBase = NULL;
qDebug() << "Objet CSharedMemory créé par " << m_parent->thread();
}
CSharedMemory::~CSharedMemory()
{
detach();
qDebug() << "Objet CSharedMemory détruit par " << m_parent->thread();
}
int CSharedMemory::attacherOuCreer()
{
int res;
attach(); // tentative de s'attacher
if (!isAttached()) { // si existe pas alors création
res = create(m_taille); // on réserve la place
if (!res) {
QString mess="CSharedMemory::attacherOuCreer Erreur de création de la mémoire partagée.";
emit sigErreur(mess);
return ERREUR;
} // if res
} // if isattached
attach();
m_adrBase = (float *)data();
return 0;
}
int CSharedMemory::attacherSeulement()
{
attach(); // tentative de s'attacher
if (!isAttached()) { // si existe pas
QString mess="CSharedMemory::attacherSeulement Erreur de création de la mémoire partagée.";
emit sigErreur(mess);
return ERREUR;
} // if isattached
m_adrBase = (float *)data();
return 0;
}
int CSharedMemory::ecrire(int no, float mesure)
{
if ( (no<0) && (no>2) ) {
QString mess="CSharedMemory::ecrire ERREUR, indice de la mesure incorrecte.";
emit sigErreur(mess);
return ERREUR;
} // if no
if (!isAttached()) { // si existe pas
QString mess="CSharedMemory::ecrire Erreur mémoire partagée non attachée.";
emit sigErreur(mess);
return ERREUR;
} // if isattached
m_adrBase[no] = mesure;
return 0;
}
float CSharedMemory::lire(int no)
{
if ( (no<0) && (no>2) ) {
QString mess="CSharedMemory::lire ERREUR, indice de la mesure incorrecte.";
emit sigErreur(mess);
return ERREUR;
} // if no
if (!isAttached()) { // si existe pas alors création
QString mess="CSharedMemory::lire Erreur mémoire partagée non attachée. ";
emit sigErreur(mess);
return ERREUR;
} // if isattached
return m_adrBase[no];
}
Exemple avec la classe CCapteur_I2c_SHT20
incorporant la classe CSharedMemory
// 0x40 + bit LSB à 0 pour write
class CCapteur_I2c_SHT20 : public QThread
{
Q_OBJECT
public:
explicit CCapteur_I2c_SHT20(QObject *parent = 0, int noMesBase = 1);
~CCapteur_I2c_SHT20();
bool m_fin;
private:
CSharedMemory *m_shm;
CI2c *m_i2c;
int m_noMesBase;
void run(); // méthode virtuelle à implémenter, contenu du thread
float lireMesureHum();
float lireMesureTemp();
signals:
void sigErreur(QString mess);
private slots:
void onErreur(QString mess);
};
// CCAPTEURTEMPHUMI2C_H
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.
CCapteur_I2c_SHT20::CCapteur_I2c_SHT20(QObject *parent, int noMesBase) :
QThread(parent)
{
m_shm = new CSharedMemory(this);
connect(m_shm, SIGNAL(sigErreur(QString)), this, SLOT(onErreur(QString)));
m_shm->attacherSeulement();
m_i2c = CI2c::getInstance(this, '1');
connect(m_i2c, SIGNAL(sigErreur(QString)), this, SLOT(onErreur(QString)));
m_fin=false;
m_noMesBase = noMesBase;
qDebug() << "Objet CCapteur_I2c_SHT20 créé !";
}
CCapteur_I2c_SHT20::~CCapteur_I2c_SHT20()
{
CI2c::freeInstance();
m_shm->detach();
delete m_shm;
qDebug() << "Objet CCapteur_I2c_SHT20 détruit !";
}
void CCapteur_I2c_SHT20::run()
{
float mesureHum, mesureTemp;
while(!m_fin) {
// écriture de la mesure dans le segment de mémoire partagé
mesureHum = lireMesureHum();
usleep(100000);
mesureTemp = lireMesureTemp();
m_shm->lock(); // on prend la mémoire partagée
m_shm->ecrire(m_noMesBase, mesureTemp); // écriture dans la mémoire partagée
m_shm->ecrire(m_noMesBase+1, mesureHum); // écriture dans la mémoire partagée
m_shm->unlock(); // on libère la mémmoire partagée
sleep(1); // lecture toutes les s
} // while
}
void CCapteur_I2c_SHT20::onErreur(QString mess)
{
emit sigErreur(mess);
}
float CCapteur_I2c_SHT20::lireMesureHum()
{
float hum;
unsigned char lecture[3];
unsigned char ecriture[1];
int res;
ecriture[0] = COM_MES_HUM;
m_i2c->ecrire(ADR, ecriture, 1);
usleep(100000);
res=m_i2c->lire(ADR, lecture, 2);
if (res != 2) {
QString mess="CCapteur_I2c_SHT20::lireMesureHum ERREUR Lecture";
emit sigErreur(mess);
return -1;
} // if res
unsigned char MSB = lecture[0];
unsigned char LSB = lecture[1]&0xF0;
hum=((MSB<<8)+LSB);
hum = -6+125*hum/65536;
return hum;
} // lireMesHum
float CCapteur_I2c_SHT20::lireMesureTemp()
{
float temp;
unsigned char lecture[2];
unsigned char ecriture[1];
int res;
ecriture[0] = COM_MES_TEMP;
m_i2c->ecrire(ADR, ecriture, 1);
usleep(100000);
res=m_i2c->lire(ADR, lecture, 2);
if (res != 2) {
QString mess="CCapteur_I2c_SHT20::lireMesureTemp ERREUR Lecture";
emit sigErreur(mess);
return -1;
} // if res
unsigned char MSB = lecture[0];
unsigned char LSB = lecture[1]&0xFC;
temp = ((MSB<<8)+LSB);
temp = -46.85+175.72*temp/65536;
return temp;
} // lire MesTemp
Je vous laisse le soin de développer l'interface graphique de cette l'application.
Complément : Quelques explications concernant CCapteur_I2c_SHT20
Cette classe hérite de
QThread
. La méthoderun()
est donc un thread autonome.L'instanciation de la classe
CSharedMemory
se fait dans le constructeur deCCapteur_I2c_SHT20
.Notez l'utilisation de la méthode
CSharedMemory::lock()
dans la méthodeCCapteur_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.
Remarque : CSharedMemory
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.