Problème de classe et d'interruption : comment mixer les 2

Bonsoir à tous,

Je me faisais une joie d’utiliser les classes afin de programmer proprement, mais je tombe dans une impasse.
Sachant que :
Le code contenu dans une classe ne voit que les propriétés et méthodes de cette classe.
Il n’est pas possible d’attacher une interruption dans une méthode d’une classe (message = multiple definition of ‘PCintPort::PCint()’).
La seule possibilité de transmettre des données aux méthodes d’une classe est via les paramètres de ces méthodes.

Comment faire lorsqu’on doit récupérer plusieurs fois (boucle jusqu’à ce que cela soit bon) des données qui ne peuvent être lues que par interruption

Cas concret :
Un récepteur (modèle réduit) donne les positions de 4 voies.
Pour que le programme connaisse la fonction de chaque voie, une méthode demande à l’utilisateur de poisitionner les manches de la radio dans des configurations particulières (tout au neutre, puis pas au max, puis pas au mini, puis…)
Le programme détermine que l’utilisateur a positionner les manches dans la position demandée lorsque les données seront dites ‘stables’

J’ai donc développer une méthode ‘calibrer’ qui me renvoit dans l’intance ‘récepteur’ les valeurs demandées.

class recepteur {
public:
  unsigned long voie1;
  unsigned long voie2;
  unsigned long voie3;
  unsigned long voie4;

  recepteur();
  int calibrer(char);
};


int recepteur::calibrer(char type) {
  // Attendre que le Rx lu soit stable
  // Test : OK
  // --------------------------------------------------
  recepteur currentRx[NBRE_IND_TAB];  // Données issues du récepteur
  int nbreIter = 0; // Nbre d'itération pour avoir une valeur stable
  int indTab = 0;
  boolean stable = false;
  boolean minData = false;  // Il faut avoir rempli le tableau
  do{  // Attendre que les données soient stable
    currentRx[indTab++].lire(type);  // Lecture Rx...
    nbreIter++;
    if (indTab >= NBRE_IND_TAB) {
      indTab = 0;  // 
      minData = true;  // Le tableau est complet
    }
    if (minData) {  // Le tableau est complet alors calcul de l'écart
      //recepteur minRx = recepteur(-1);
      recepteur minRx(-1);
      recepteur maxRx = recepteur(-1);
      // Détermine le min et le max pour chaque voie
      for (int i = 0; i < NBRE_IND_TAB; i++){
        minRx.minimum(currentRx[i]);
        maxRx.maximum(currentRx[i]);
      }
      stable = maxRx.stable(minRx);  // Ecart sur chaque voie < ERR
//      minRx.debug();
//      maxRx.debug();
    }
  }
  while (!stable || !minData);
  
  recepteur stableRx = recepteur(0);
  for (int i = 0; i < NBRE_IND_TAB; i++) stableRx += currentRx[i];
  stableRx /= NBRE_IND_TAB;
  
  voie1 = stableRx.voie1;
  voie2 = stableRx.voie2;
  voie3 = stableRx.voie3;
  voie4 = stableRx.voie4;
  
  return nbreIter;
}  

void recepteur::lire(char type){
  // <TODO> A remplacer par la lecture du récepteur...
//  voie1 = random(900, 2100);
//  voie2 = random(900, 2100);
//  voie3 = random(900, 2100);
//  voie4 = random(900, 2100);

  voie1 = Donnée issue du traitement d'une interruption pin n (temps écoulé entre le front montant et le front descendant)
  voie2 = Donnée issue du traitement d'une it...
  voie3 = Donnée issue du traitement d'une it...
  voie4 = Donnée issue du traitement d'une it...
  
}

La lecture des voies RC se fait par du code du style :

#include <PinChangeInt.h>    // http://playground.arduino.cc/Main/PinChangeInt
#include <PinChangeIntConfig.h>
#include <TimerOne.h>        // http://playground.arduino.cc/Code/Timer1

#define NO_PORTB_PINCHANGES //PinChangeInt setup
#define NO_PORTC_PINCHANGES    //only port D pinchanges (see: http://playground.arduino.cc/Learning/Pins)
#define PIN_COUNT 3    //number of channels attached to the reciver
#define MAX_PIN_CHANGE_PINS PIN_COUNT

#define RC_TURN 3    //arduino pins attached to the reciver
#define RC_FWD 2
#define RC_FIRE 4
byte pin[] = {RC_FWD, RC_TURN, RC_FIRE};    //for maximum efficency thise pins should be attached
unsigned int time[] = {0,0,0};                // to the reciver's channels in the order listed here

byte state=0;
byte burp=0;    // a counter to see how many times the int has executed
byte cmd=0;     // a place to put our serial data
byte i=0;       // global counter for tracking what pin we are on

void setup() {
    Serial.begin(115200);
    Serial.print("PinChangeInt ReciverReading test");
    Serial.println();            //warm up the serial port

    Timer1.initialize(2200);    //longest pulse in PPM is usally 2.1 milliseconds,
                                //pick a period that gives you a little headroom.
    Timer1.stop();                //stop the counter
    Timer1.restart();            //set the clock to zero

    for (byte i=0; i<3; i++)
    {
        pinMode(pin[i], INPUT);     //set the pin to input
        digitalWrite(pin[i], HIGH); //use the internal pullup resistor
    }
    PCintPort::attachInterrupt(pin[i], rise,RISING); // attach a PinChange Interrupt to our first pin
}

void loop() {
    cmd=Serial.read();        //while you got some time gimme a systems report
    if (cmd=='p')
    {
        Serial.print("time:\t");
        for (byte i=0; i<PIN_COUNT;i++)
        {
            Serial.print(i,DEC);
            Serial.print(":");
            Serial.print(time[i],DEC);
            Serial.print("\t");
        }
        Serial.print(burp, DEC);
        Serial.println();
    }
    cmd=0;
    switch (state)
    {
        case RISING: //we have just seen a rising edge
            PCintPort::detachInterrupt(pin[i]);
            PCintPort::attachInterrupt(pin[i], fall, FALLING); //attach the falling end
            state=255;
            break;
        case FALLING: //we just saw a falling edge
            PCintPort::detachInterrupt(pin[i]);
            i++;                //move to the next pin
            i = i % PIN_COUNT;  //i ranges from 0 to PIN_COUNT
            PCintPort::attachInterrupt(pin[i], rise,RISING);
            state=255;
            break;
        /*default:
            //do nothing
            break;*/
    }
}

void rise()        //on the rising edge of the currently intresting pin
{
    Timer1.restart();        //set our stopwatch to 0
    Timer1.start();            //and start it up
    state=RISING;
//  Serial.print('r');
    burp++;
}

void fall()        //on the falling edge of the signal
{
    state=FALLING;
    time[i]=readTimer1();    // read the time since timer1 was restarted
//  time[i]=Timer1.read();    // The function below has been ported into the
                            // the latest TimerOne class, if you have the
                            // new Timer1 lib you can use this line instead
    Timer1.stop();
//  Serial.print('f');
}

unsigned long readTimer1()        //returns the value of the timer in microseconds
{                                    //rember! phase and freq correct mode counts 
                                    //up to ICR1 then down again
    unsigned int tmp=TCNT1;
    char scale=0;
    switch (Timer1.clockSelectBits)
    {
    case 1:// no prescalse
        scale=0;
        break;
    case 2:// x8 prescale
        scale=3;
        break;
    case 3:// x64
        scale=6;
        break;
    case 4:// x256
        scale=8;
        break;
    case 5:// x1024
        scale=10;
        break;
    }
    while (TCNT1==tmp) //if the timer has not ticked yet
    {
        //do nothing -- max delay here is ~1023 cycles
    }
    tmp = (  (TCNT1>tmp) ? (tmp) : (ICR1-TCNT1)+ICR1  );//if we are counting down add the top value
                                                        //to how far we have counted down
    return ((tmp*1000L)/(F_CPU /1000L))<<scale;
}

En bref, comment mixer les 2 programmes…

Merci pour vos lumières

Tu devrais regarder comment fonctionne la librairie softwareSerial. C'est une classe et elle fonctionne sous interruption.

Bonsoir,

J’ai toujours un peu de mal à “déchiffrer” des librairies mais je vais regarder avec attention.

J’ai néanmoins trouvé une idée cette nuit que je vais essayé de mettre en oeuvre. L’idée est d’ajouter une propriété cette classe qui correspond à l’adresse des variables globales dont on a besoin dans la classe.

Affaire à suivre…

Il fallait s'en douter, je tombe sur un problème.

J'ai une classe :

class recepteur {
public:
  unsigned long voie1;
  unsigned long voie2;
  unsigned long voie3;
  unsigned long voie4;

  int* ptrDataRC;

  recepteur(int*);
};

Dont voici le constructeur

recepteur::recepteur(int* wDataRC){
  voie1 = 1500;
  voie2 = 1500;
  voie3 = 1500;
  voie4 = 1500;
  ptrDataRC = wDataRC;
}

Ma question : comment déclarer un tableau de récepteur.
Tout les essais ne compilent pas :

  recepteur tabRx[NBRE_IND_TAB];  //  error: no matching function for call to 'recepteur::recepteur()'
  recepteur tabRx[NBRE_IND_TAB](NULL);  //  bad array initializer - invalid conversion from 'int' to 'int*'
  recepteur tabRx[NBRE_IND_TAB]((int*) NULL);  //  bad array initializer - invalid conversion from 'int' to 'int*'
  // Même en faisant référence 
  int* wPtr = NULL;   // ou autre valeur
  recepteur tabRx[NBRE_IND_TAB](wPtr);  // bad array initializer - invalid conversion from 'int' to 'int*'

Si quelqu'un a la solution....

Pour ceux que cela intéresse, voilà une partie de la solution trouvée dans un cours sur le net (ce n’est certainement pas le seul) :
http://www.bruno-garcia.net/www/Cours/cyclevie.html

//  recepteur currentRx[NBRE_IND_TAB](wptr);  // Données issues du récepteur
  
  recepteur  *currentRx[NBRE_IND_TAB];
  for (int i=0; i<NBRE_IND_TAB;i++) currentRx[i]=new recepteur(ptrDataRC);

Il faut modifier les accès aux données du tableau :

    (*currentRx[indTab++]).lire(type);  // Lecture Rx...

Je n’ai pas eu le temps de tester, d’autres traitements sont à mettre en oeuvre avant les premiers tests…

A+
Olivier