// Lecture PPM6ch & pilotage LED RVB
// C'est le timer1 qui est utilisé la lecture du PPM
#define ledRouge 3 // Constante pour la broche 3
#define ledVert 5 // Constante pour la broche 5
#define ledBleu 6 // Constante pour la broche 6
#define ledPin 13 // Pin de la LED "locale" de l'Arduino
// Données de capture du Rx
boolean endTrame=0,failsafe=0,firstEdge=1;
volatile unsigned int vchUp[8];
volatile unsigned int vchPos[6];
unsigned int cha[6];
byte curEdge;
// Valeurs RVB
int R,V,B;
// Variables permettant de faire clignoter la led de l'Arduino
int ledState = LOW;
long previousMillis = 0;
long interval = 500;
void setup()
{
// Pin setup, parmetrage des PIN en sortie :
pinMode (ledVert,OUTPUT); // Broche ledVert configurée en sortie
pinMode (ledRouge,OUTPUT); // Broche ledRouge configurée en sortie
pinMode (ledBleu,OUTPUT); // Broche ledBleu configurée en sortie
pinMode(ledPin,OUTPUT); // Arduino LED
digitalWrite(ledPin,HIGH); // Allumage de la LED de l'Arduino
// Sortie DEBUG
Serial.begin(57600);
Serial.println("");
Serial.println("PPM read & Led RVB");
// Timer1 setup
TCCR1A=B00000000; // OCR2A/OCR2B : disconnected, Timer: normal mode
TCCR1B=B00000010; // Falling edge CAPTUREPIN detection, Timer:normal mode, Prescaler=clk/8
TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt
delay(1000); // pour etre certain 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
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
}
void loop()
{
if (endTrame==1) {
cli();
for (int i=0;i<6;i++) {
cha[i]=vchPos[i]; // Recopie les valeurs des canaux
}
sei();
endTrame=0;
LedBlink(500);
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é"
for (int i=0; i < 6; i++){ // Imprimer les valeurs des canaux sur le port série
Serial.print(cha[i]);
Serial.print(";");
}
// Mapper trois canaux vers les couleurs RVB
R=map(cha[1],1960,3960,0,255);
V=map(cha[2],1960,3960,0,255);
B=map(cha[3],1960,3960,0,255);
ledRVBpwm(R,V,B); // génère impulsion largeur voulue pour la couleur
// Imprimer les valauers de la LED
Serial.print(R);
Serial.print(";");
Serial.print(V);
Serial.print(";");
Serial.print(B);
Serial.println(";");
}
// La 2eme partie "intelligente" du code se trouve ICI
// Elle est jouée à chaque tour de boucle
if (failsafe){
if (R>255) R=0; // En mode FAILSAFE, on fait varier les 3 couleurs progressivement & simultanément
else R++;
if (V>255) V=0;
else V++;
if (B>255) B=0;
else B++;
ledRVBpwm(R,V,B); // génère impulsion largeur voulue pour la couleur
Serial.print("FAILSAFE");
LedBlink(50);
Serial.print(R);
Serial.print(";");
Serial.print(V);
Serial.print(";");
Serial.print(B);
Serial.println(";");
}
}
void ledRVBpwm(int pwmRouge, int pwmVert, int pwmBleu) { // reçoit valeur 0-255 par couleur
//--- attention - avec une LED RGB anode commune : la LED s'allume sur niveau BAS !
analogWrite(ledRouge, 255-pwmRouge); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut
analogWrite(ledVert, 255-pwmVert); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut
analogWrite(ledBleu, 255-pwmBleu); // impulsion largeur voulue sur la broche 0 = 0% et 255 = 100% haut
}
void LedBlink(long interval) { // reçoit délais de clignotement
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}
EDIT : Le mode FAILSAFE indique que le signal PPM en entrée n'est plus fourni...
Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.
ou alors un filtre récursif
valeur = nouvelle_valeurp + valeur(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.
Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.
ou alors un filtre récursif
valeur = nouvelle_valeurp + valeur(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.
bonjour fdufnews
pas mieux et le recursif simple ici est surement la meilleure méthode sur arduino.
je n'arrive pas a remettre la main sur la vieille étude qui concluait statistiquement que P=0.85 (0.15) était le meilleur compromis
C'est ce que j'utilise "tout venant"
Artouste:
je n'arrive pas a remettre la main sur la vieille étude qui concluait statistiquement que P=0.85 (0.15) était le meilleur compromis
C'est ce que j'utilise "tout venant"
Filtrage sur une fenêtre glissante
on fait un tableau avec les n dernières valeurs et on calcule la moyenne sur ces valeurs.
ou alors un filtre récursif
valeur = nouvelle_valeurp + valeur(1-p)
0 < p < 1
on joue sur p pour fixer le temps de réaction du filtre. Plus p est petit plus lente sera la réaction à une variation sur la nouvelle valeur.
Dans cet exemple, doit-on faire le calcul à chaque fois avec l'ancienne valeur CALCULEE et l'ancienne valeur LUE ?
Après différent essais avec Excel, j'ai finalement opté pour un solution plus "radicale".
Je ne prend en compte la nouvelle valeur mesurée que si elle dépasse un écart minimum.
int const ecart=3; // C'est la valeur de l'hystérésis...
for (int i=0;i<6;i++) {
if (vchPos[i] > (cha[i]+ecart) || vchPos[i]< (cha[i]-ecart)) { // Si la nouvelle valeur dépasse l'hystérésis
cha[i]=vchPos[i]; // Recopie les valeurs des canaux
}
Ici c'est 3, ça représente 1,5 µs, ça ne bouge donc qu'à partir de 2µs d'écart, pour un servo ça laisse 500 pas, ça me parait correct.
Ca me donne une idée, cette fameuse lecture PPM, je l'utilise finalement assez souvent, et ça me fait faire pas mal de copier/coller de la reprendre à chaque fois.
// Lecture PPM6ch par INPUT CAPTURE UNIT
// C'est le timer1 qui est utilisé pour la lecture du PPM
// La lecture du PPM ne peut se faire QUE sur la PIN 8 (ICP1)
boolean endTrame=0,failsafe=0,firstEdge=1;
volatile unsigned int vchUp[8];
volatile unsigned int vchPos[6];
unsigned int cha[6];
byte curEdge;
int const ecart=3; // C'est la valeur de l'hystérésis...
void setup()
{
// Timer1 setup
TCCR1A=B00000000; // OCR2A/OCR2B : disconnected, Timer: normal mode
TCCR1B=B00000010; // Falling edge CAPTUREPIN detection, Timer:normal mode, Prescaler=clk/8
TIMSK1=B00100001; // Activate CAPTUREPIN interrupt & OVF interrupt
}
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
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
}
On y retrouve dans l'ordre :
La déclaration des variable (dois-je les garder en volatile ?)
Le paramétrage du timer1
L'interruption sur ICP1
L'interruption sur overflow du timer1
C'est principalement ces parties que je souhaite "librariser"...