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 ?