Adafruit 16-Channel 12-bit PWM/Servo Driver

Voici l'état actuel de mon code :

// Lecture PPM6ch & écriture PWM V003
// C'est le timer1 qui est utilisé pour les 2 fonctions

#define PWM1 9  // Pin Sortie PWM 1
#define PWM2 10 // Pin Sortie PWM 2
#define SWI1 11 // Pin Switch servo 1
#define SWI2 12 // Pin Switch servo 2
#define LEDP 13 // Pin de la LED "locale" de l'Arduino

boolean endTrame=0,failsafe=0,firstEdge=1;

//Structure Channel
struct channel {
  unsigned int val;
  boolean autop;
};

// Données de capture du Rx
volatile unsigned int vchUp[8];
volatile unsigned int vchPos[6];
volatile unsigned int ocr1a=3000,ocr1b=3000;
struct channel cha[6];
byte curEdge;

void setup()
{
  // Initialisation des valeur auto-pilotes à 0
  for (int i=0; i < 6; i++) { 
    cha[i].autop=0;
  }


  // Pin setup, parmetrage des PIN en sortie :
  pinMode(PWM1,OUTPUT); // Timer1 PWM1 (OCR1A)
  pinMode(PWM2,OUTPUT); // Timer1 PWM2 (OCR1B)
  pinMode(SWI1,OUTPUT); // Selecteur d'entrée servo 1
  pinMode(SWI2,OUTPUT); // Selecteur d'entrée servo 2
  pinMode(LEDP,OUTPUT); // Arduino LED
  digitalWrite(LEDP,HIGH); // Allumage de la LED de l'Arduino

  // Sortie DEBUG
  Serial.begin(57600);
  Serial.println("PPM read & PWM write");
  // Timer1 setup
  TCCR1A=B10100000; // OCR2A/OCR2B : Falling mode, Timer: normal mode
  TCCR1B=B00000010; // Falling edge CAPTUREPIN detection, Timer:normal mode, Prescaler=clk/8 
  TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt
  OCR1A=3000;
  OCR1B=3000;

  delay(1000); // pour etre sur que les interruptions aient tourné avant la main loop

}

ISR(TIMER1_CAPT_vect)
{
  vchUp[curEdge]=ICR1; // Capture des timestamp 1 à 6
  curEdge++;
  if (curEdge>7) {     // A partie du 7ème...   
    TCNT1=0;           // RESET du counter pour éviter le FailSafe
    TCCR1A=B11110000;  // OCR2A/OCR2B : En mode front montant
    TCCR1C=B11000000;  // OCR2A/OCR2B : Déclenchement "manuel" pour monter les 2 signaux servo
    TCCR1A=B10100000;  // OCR2A/OCR2B : En mode front descendant
    OCR1A=ocr1a;       // Mise à jour de la valeur du servo 1
    OCR1B=ocr1b;       // Mise à jour de la valeur du servo 2
    if ((vchUp[7]-vchUp[1]) > 30000) {  //Si la trame totale dépasse 15ms - Trame KO ou mauvaise synchro
      curEdge=0;            //Remise à 0 du compteur de fronts                      
    }
    else {                  //Sinon, une bonne trame est capturée, on calcule donc la valeur des canaux
      curEdge=1;
      for (int i=0;i<6;i++) {
        vchPos[i]=(vchUp[i+2]-vchUp[i+1]); //Mesure des canaux (diviser par 2 pour obtenir des µs)
      }
      endTrame=1;           // Pour dire à la MainLoop que de nouvelles valeurs sont disponibles
    }
  }
}

ISR(TIMER1_OVF_vect)
{
  failsafe=1;  // Le compteur a attends sa limite (32ms), on déclenche donc le FailSafe
  TCCR1A=B11110000; // OCR2A/OCR2B : En mode front montant
  TCCR1C=B11000000; // OCR2A/OCR2B : Déclenchement "manuel" pour monter les 2 signaux servo
  TCCR1A=B10100000; // OCR2A/OCR2B : En mode front descendant
  OCR1A=ocr1a;      // Mise à jour de la valeur du servo 1
  OCR1B=ocr1b;      // Mise à jour de la valeur du servo 2
}

void loop()
{
  if (endTrame==1) {
    cli();
    for (int i=0;i<6;i++) {
      cha[i].val=vchPos[i]; // Recopie les valeurs des canaux
    }
    sei();
    endTrame=0;
    if (failsafe){ 
      failsafe=0;
      Serial.println("End of FailSafe");
    }

    // La 1ere partie "intelligente" du code se trouve ICI
    // Elle n'est jouée que si les canaux ont "bougé"

    if (cha[4].val > 2500) { // Si le canal 5 n'est pas au min...
      cha[1].autop=1; // Mettre le canal 2 en auto-pilote
      digitalWrite(SWI1,HIGH);
      if (cha[4].val > 3500) { // Si le canal 5 est au max...
        cha[2].autop=1; // Mettre le canal 3 en auto-pilote
        digitalWrite(SWI2,HIGH);
      }
    }
    else {
      cha[1].autop=0; // Mettre le canal 2 en pilotage manuel
      digitalWrite(SWI1,LOW);
      cha[2].autop=0; // Mettre le canal 3 en pilotage manuel
      digitalWrite(SWI2,LOW);
    }

    for (int i=0; i < 6; i++){ // Imprimer les valeurs des canaux sur le port série
      Serial.print(cha[i].val);
      Serial.print(";");
    } 
    Serial.print(ocr1a);
    Serial.print(";");
    Serial.print(ocr1b);
    Serial.println(";");
  }

  // La 2eme partie "intelligente" du code se trouve ICI
  // Elle est jouée à chaque tour de boucle

  if (cha[1].autop==0) ocr1a=(cha[5].val); // Si le canal 2 est en pilotage manuel Le PWM1 est celle du canal 5
  else{                                    // Si non, le canal 2 est auto-piloté...
    if (ocr1a>2000) ocr1a--;               // Une simple boucle qui fait bouger lentement le servo d'un bout à l'autre puis revenir
    else ocr1a=4000;
    delay(1);
  }
  if (cha[2].autop==0) ocr1b=(cha[5].val); // Si le canal 3 est en pilotage manuel Le PWM2 est celle du canal 6
  else{                                    // Si non, le canal 3 est auto piloté...
    if (ocr1b>2000) ocr1b--;               // Une simple boucle qui fait bouger lentement le servo d'un bout à l'autre puis revenir
    else ocr1b=4000;
    delay(1);
  }

  if (failsafe){
    Serial.print("FAILSAFE");
    cha[1].autop=1;
    cha[2].autop=1;
    Serial.print(ocr1a);
    Serial.print(";");
    Serial.print(ocr1b);
    Serial.println(";");

  }
}

On peut dire qu'il est en 2 parties :

  • Un partie synchro qui ne se situe QUE dans les interruptions dont le rôle est de lire le PPM et de générer les 2 signaux servo
  • Une partie "multi-tâches" (dans la main loop), qui gère la partie "intelligente" du code et les éventuels calculs

L'avantage du shield en question, c'est qu'il me décharge de la génération des signaux servo.

Alors comme tu le dis, je pourrais réactiver les interruptions dans mes interruptions "ICP1" pour permettre à l'I2C de travailler même pendant mes inter.

Mais je préfère une solution où tout est orchestré par l'Arduino... lire le PPM dure 12 ms et j'ai un "silence" de minimum 10ms après ça... il faut que je cale mon envoi I2C pendant ce silence.... donc qu'il soit lancé par une interruption, cela parait-il faisable ?