Lire signal PPM avec mon arduino

Bonjour,

Je vous présente mon signal PPM :

Il reboucle à 22.5ms et chacun des 6 crénaux doit être mesuré (il font entre 1ms & 2ms), et c’est justement ces valeurs qui m’interressent

J’essaye de le lire avec l’Arduino, j’utilise le CAPTUREPIN (reglé sur front déscendant) du timer 1, voici mon programme :

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

// Rx capture data
volatile unsigned int vchUp[7];
unsigned int chPos[6];
byte curEdge;

void setup()
{
  // DEBUG serial OUTPUT
  pinMode(13,OUTPUT);
  Serial.begin(57600);

  // Parameters for timer1
  TCCR1A=B00000000; // Normal mode
  TCCR1B=B00000010; // 2MHz (arround 0.5µs precision), with falling edge CAPTUREPIN detection
  TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt
}



ISR(TIMER1_CAPT_vect)
{
  vchUp[curEdge]=TCNT1; //Take TimeStamp
  curEdge++;
  if (curEdge>6) { //Frome the 8th timestamp
    if ((vchUp[6]-vchUp[0]) < 30000) { //If total trame is lower than 15ms - MYSTERIOUS PART that make everythings allright
      curEdge=0;
      endTrame=1;
    }
    TCNT1=0;  //RESET COUNTER at end of trame, to avoid FailSafe detection
  }
}


ISR(TIMER1_OVF_vect)
{
  failsafe=1; //If the counter overflow (around 32ms) activate failsafe
}


void loop()
{
  if (endTrame==1) {
    for (int i=0;i<6;i++) {
      chPos[i]=(vchUp[i+1]-vchUp[i])/2; //Take channel values, divided by 2 to obtain µs
    }
    endTrame=0;
    if (failsafe){ 
      failsafe=0;
      Serial.println("End of FailSafe");
    }
    for (int i=0; i < 6; i++){ //Print the channels values on the serial port
      Serial.print(chPos[i]);
      Serial.print(";");
    }
    Serial.println(";");
  }
  if (failsafe) Serial.println("FAILSAFE");
}

Le problème de ce programme, c’est qu’il fonctionne bien… et je ne comprends pas pourquoi… (voir la partie MYSTERIOUS PART).

Ce que j’essaye de faire : le vchUp[0] doit être le timestamp du premier front déscendant de la trame et le vchUp[6] le dernier.

Pour ce faire, lorsque le curEdge (pointeur du tableau vchUp[]) dépasse le 7ème évenement, je mesure la durée total de la trame (vchUp[6]-vchUp[0]) et si c’est plus long que 15ms, alors je sais que je suis mal calé, c’est bien de le savoir mais je ne savais pas comment agir.

Ce que je fais pour l’instant si je detecte cette anomalie :

  • Je ne remet pas à zéro le curEdge, donc potentiellement, je vais sortir des dimensions prévue du tableau vchUp[], donc comportement imprévisible.
  • Je ne met pas le flag endTrame à 1 qui sert à informer la main loop que les valeurs de la trame peuvent être mesurées puisqu’on a une trame cohérente

Sev

Si tu sais que tu es mal calé, il faut que tu te recales ta séquence. Donc lorsque tu constates le décalage, tu positionnes un drapeau. Lorsque tu rentres dans ton ISR et que le drapeau est positionné tu effaces le drapeau et tu ne fais rien. Ainsi tu te décales d'un front. En te décalant d'un front à chaque fois que tu es mal calé, tu finiras par tomber correctement.

J'apprécie cette logique... mais le fait de laisser passer un wagon ne va pas me caler correctement le train :)

Bonjour,

Question bête, pourquoi tu te casse la tête avec une interruption sur overflow/capture du timer1 ? Une bête interruption en CHANGE + micros() ne serait pas plus simple ?

Avec cette technique j'arrive même à décoder des trames RF complète : http://arduino.cc/forum/index.php/topic,109892.msg830563.html#msg830563 (code -> sniffer)

UniseV: J'apprécie cette logique... mais le fait de laisser passer un wagon ne va pas me caler correctement le train :)

Bien sur que cela va te recaler. C'est le principe utilisé dans tous les liens de transmission à flux continu. Dans ton cas, au maximum au bout de 6 itérations tu seras calé. Et ensuite si ton logiciel n'a pas d'autre problème tu seras parfaitement calé et cela ne devrait plus bouger.

skywodd: Question bête, pourquoi tu te casse la tête avec une interruption sur overflow/capture du timer1 ? Une bête interruption en CHANGE + micros() ne serait pas plus simple ?

Parce qu'une interruption CHANGE avec ce signal va générer 14 interruptions par trame, alors qu'il me suffit de ne prendre que les FALLING (7). Parce que micros() a une résolution de 4µs et que je voulais du 1µs (j'ai du 0.5µs). Parce que le timestamp du capturepin est géré quasi hardware.

Le OverFlow c'est juste pour détecter le cas où je n'ai plus de signal.

J'ai déjà un dev qui fonctionne à peu près comme tu le décris, à l'aide de "PinChangeInt.h" : http://www.rcgroups.com/forums/showthread.php?t=1648040&page=2

C'est justement ça que je fait évoluer.

fdufnews: Bien sur que cela va te recaler. C'est le principe utilisé dans tous les liens de transmission à flux continu. Dans ton cas, au maximum au bout de 6 itérations tu seras calé. Et ensuite si ton logiciel n'a pas d'autre problème tu seras parfaitement calé et cela ne devrait plus bouger.

Ok, je détaille un peu, tout en sachant que j'essaye d'optimiser cette interruption au maximum, surtout dans les 2 premiers cas ou le calage est bon :

  • TimeStamp des vchUp[] de 1 à 6
  • TimeStamp du vchUp[7] avec un vchUp[7]-vchUp[1] cohérent, on repart au front 1 (et endTrame=1)
  • TimeStamp du vchUp[7] avec un vchUp[7]-vchUp[1] incohérent et donc on saute un wagon, en repartant du front 0
ISR(TIMER1_CAPT_vect)
{
  vchUp[curEdge]=TCNT1; //Take TimeStamp
  curEdge++;
  if (curEdge>7) {     //From the 8th timestamp
    TCNT1=0;           //RESET COUNTER at end of trame, to avoid FailSafe detection
    if ((vchUp[7]-vchUp[1]) > 30000) {        //If total trame is higher than 15ms - false trame, bad synchro
      curEdge=0;            //Begining from 0 !!                      
    }
    else                                          //If not, a good trame is kept, reset edge counter and "endTrame" to inform the main loop
    curEdge=1;
    endTrame=1;
  }
}

Ca marche IMPEC, merci à vous ;)

[u]EDIT :[/u] Message édité après avoir testé le truc qui fonctionne