Filtre numerique RII, RIF….digital filter...FFT...atmega328, ESP32

Effectivement, le 4eme ordre passe haut ne fonctionne pas.
Matlab, fait les calculs avec 15 chiffres après la virgule. Mais pas l’Arduino.
Même si on utilise une fréquence de coupure de 100Hz, on a plutôt un passe bande

Car à la fréquence de 30Hz,
L’atténuation devrait être (fmesure/fc)^4=(30//100)^4=0.008 or on a seulement 0.66

A 100Hz, l’atténuation est de 1 à la place de 0.7

Mais à 250Hz, ou l’atténuation devrait être aussi de 1 mais celui-ci augmente

Déjà pour le troisième, c’était déjà le cas, il a fallu déterminer les coefficients par l’Arduino, pour que cela fonctionne. Cette méthode pourrait être encore essayer.

Tout est possible mathématiquement, mais cela demande du temps….car les équations sont à rallonges
Voici le coefficient a1, mais est que cela vaut le cout de passer du temps à calculer les autres ?

Par contre, il y a une bibliothèque qui calcul les complexes, ce serait intéressant de la tester


Mais comment la tester ? Voici 3 tests rapides
21=2
2i
1i=-2
Avec les valeurs de Z1 précèdent, Z1*conjuguées de Z1=réel^2+imangianire^2
Puis faire le calcul des coefficients du filtre

Cela pourrait donner les coefficients facilement de tous les filtres

Je ne sais pas combien de temps, la personne a passé pour faire cette Library pour calculer les complexes, mais, ce serait possible d’en realiser une car les complexes, c’est simples

Cette bibliothèque fonctionne parfaitement, il est possible d’afficher les valeurs sur l’afficheur LCD avec 3 chiffres après la virgule, mais impossible de fixer se nombre de chiffre alors que les nombres complexes sont en float.
De même, avec le serial.print.
https://github.com/RobTillaart/Complex/blob/master/Complex.h
Voici le résultat de la multiplication de 2 complexes, avec affichage du resulat et du module sur le port serie

Mais tous les calculs se font bien avec 7 chiffres après la virgule,
Voici le résultat de l’addition de 2 complexes

Voici le code qui a permis de vérifier le dernier résultat

#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include "Complex.h"


#define PWM3   3      //   timer2    
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0; 

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;

            float entreeMax;
            float entreeMin;
            float entreeMoy;
            float phase;
            float attenuation;
            float P;        //pour mesurer le dephasage
            float PMax;
            float PMin;
            float PMoy;
            
            float outMax;
            float Div;
            float factorPower;
//           float outMin;
//           float outMoy;
         
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
            
            float  out=0;
//filtre passe bas 4 ordre
/*
//            const float b0 =1;    
            const float b1 =4;            
      const float b2 =6; 
            const float b3 =4;
//            const float b4 =1;
             float a1=3.1806385;
            float a2=-3.8611943;
            float a3=2.1125535;
            float a4=-0.4382651;
            float Gain=2388;  
*/
//filtre passe haut
            const float b1 =-4; 
            const float b2 =6; 
            const float b3 =-4;  
                  
            float a1=2.369513;
            float a2=-2.313988;
            float a3=1.0546654;
            float a4=-0.1873794;  
                       
            float denominateur=0;
            float Gain=7.5;

            float  fe=1000;  //frequence d'echantilonnage   
            float  fc=100;  //frequence de coupure desiréé     
            

Complex Z1(0.1234567, 2);
Complex Z2(0, 1);
Complex Z3(10, 10);
float module;
float real;
float imag;                    

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
/*
lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage  
*/  
  
  Serial.begin(9600); 

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
temps++;

entree=analogRead(A0); //convertisseur 10 bits sous 5V      https://www.gammon.com.au/adc
entree=entree/4;  
      
// ---- exemple filtre  Butterwoth   pour fc=50hz,  fechantillon=1000Hz------    
entree4=entree3;      //entree(n-3)     
entree3=entree2;      //entree(n-3)
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie4=sortie3;      //sortie(n-3)
sortie3=sortie2;      //sortie(n-3)
sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)

sortie=(entree+entree1*b1+entree2*b2+entree3*b3+entree4+sortie1*a1+sortie2*a2+sortie3*a3+sortie4*a4)    ;
                                                                                 
out=((sortie/Gain)+127);
        
//rafraichissement mesure toutes les 0.2s
if (temps>=200)   {entreeMax=0;outMax=0;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur 
if (out> outMax) {outMax = out; }  // Enregistrer la valeur maximale du capteur 

if (out<0) {out=0;}
if (out>255) {out=255;}                                     
analogWrite(PWM3,out);
digitalWrite(LED13,LOW);  

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() { 
/*   
lcd.setCursor(0,0);
lcd.print("highpass haut 4eme");
lcd.setCursor(0,1);
lcd.print("fc=100Hz fe=1000Hz");
    
lcd.setCursor(0,2);
lcd.print("attenuation=");
lcd.print((outMax)/(entreeMax));
lcd.print("  ");
*/
lcd.setCursor(0,0);
lcd.print("Z1:");
real=Z1.real();
imag=Z1.imag();
lcd.print(real,5);
lcd.print("+i");
lcd.print(imag,5);
lcd.setCursor(0,1);
lcd.print("Z2:");
lcd.print(Z2);
Z3=Z1+Z2;
module = Z3.modulus();
lcd.setCursor(0,2);
lcd.print("MZ3=Z1+Z2= ");
lcd.print(module,1);
lcd.setCursor(0,3);
lcd.print("Z3:");
lcd.print(Z3);
Serial.print(Z3);Serial.print(";");Serial.println(module,7);

} // fin loop

Donc, il est possible de déterminer les coefficients des filtres en utilisant la library complex

le calcul des coefficients pour un filtre passe bas IIR du deuxime ordre en utilisant la library complexe,
fonctionne tres bien
N=2; //ordre du filtre
k=1; //cacul du pole P1
real=-sin(((2k-1)PI)/(2N)); // -sin(((2k-1)PI)/2N);
imag=cos(((2k-1)PI)/(2N));
P1.set (real, imag);
//P1.polar(module, phase);
k=2; //cacul du pole P2
real=-sin(((2
k-1)PI)/(2N));
imag=cos(((2k-1)PI)/(2N));
P2.set (real, imag);
Z1=(P1
fcPI/fe);
Z1+=1; //obligation avec un addition de reel int et un complexe
Zfinal=-(P1
fcPI/fe);
Zfinal+=1;
Z2=Z1/Zfinal; //Z2=(1+P1
fcPI/fe)/(1-P1fcPI/fe);
Z1= Z2.conjugate();
Zfinal=Z2+Z1;
a1=Zfinal.real();
Zfinal=-Z2
Z1;
a2=Zfinal.real();
Gain=((1+2+1)/(1-a1-a2));
une boucle for avec incremetation de k jusque N aurait pu etre fait

On retrouve les meme valeurs qu’avec mathcad qui peut faire 15 chiffres apres la virgule

Voici le code complet

//#include <Wire.h>      //I2C
#include <LiquidCrystal.h>
//#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include "Complex.h"


#define PWM3   3      //   timer2    
#define LED13    13 
#define     PI 3.1415926

      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0; 

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;

            float entreeMax;
            float entreeMin;
            float entreeMoy;
            float attenuation;

            
            float outMax;
         
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
            
            float  out=0;

            float  fe=1000;  //frequence d'echantilonnage   
            float  fc=50;  //frequence de coupure desiréé     

           
//pole
Complex P1(10, 10);  
Complex P2(0, 0);
//zero
Complex Z1(0, 0);  
Complex Z2(0, 0);
//un complexe permettant de faire des cacul intermerdiare
Complex Zfinal(0, 0); 
float module;
float real;
float imag; 
//filtre passe bas 2 ordre
float N=2;   //ordre
float k=2;
            float a1;   //coeficient filtre numerique
            float a2;
            float Gain;                      

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
/*
lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage  
*/  
  
  Serial.begin(9600); 

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

N=2;    //ordre du filtre
k=1;   //cacul du pole P1
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
P1.set (real, imag);
//P1.polar(module, phase);
k=2;   //cacul du pole P2
real=-sin(((2*k-1)*PI)/(2*N));    
imag=cos(((2*k-1)*PI)/(2*N));
P2.set (real, imag);
Z1=(P1*fc*PI/fe);
Z1+=1;             //obligation avec un addition de reel int et un complexe
Zfinal=-(P1*fc*PI/fe);
Zfinal+=1;
Z2=Z1/Zfinal;     //Z2=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
Z1= Z2.conjugate();
Zfinal=Z2+Z1;
a1=Zfinal.real();
Zfinal=-Z2*Z1;
a2=Zfinal.real();
Gain=((1+2+1)/(1-a1-a2));

}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
temps++;

entree=analogRead(A0); //convertisseur 10 bits sous 5V      https://www.gammon.com.au/adc
entree=entree/4;  
      
// ---- exemple filtre passe pas 2 ordre Butterwoth   pour fc=50hz,  fechantillon=1000Hz------    
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)


sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)

sortie=(entree+entree1*2+entree2+sortie1*a1+sortie2*a2)    ;
                                                                                 
out=((sortie/Gain));
        
//rafraichissement mesure toutes les 0.2s
if (temps>=200)   {entreeMax=0;outMax=0;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur 
if (out> outMax) {outMax = out; }  // Enregistrer la valeur maximale du capteur 

if (out<0) {out=0;}
if (out>255) {out=255;}                                     
analogWrite(PWM3,out);
digitalWrite(LED13,LOW);  

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main 
void loop() { 
   
lcd.setCursor(0,0);
lcd.print("fc=50Hz fe=1000Hz");
/*    
lcd.setCursor(0,2);
lcd.print("attenuation=");
lcd.print((outMax)/(entreeMax));
lcd.print("  ");
*/


lcd.setCursor(0,1);
lcd.print("a1:");
lcd.print(a1,7);
lcd.setCursor(0,2);
lcd.print("a2:");
lcd.print(a2,7);
lcd.setCursor(0,3);
lcd.print("Gain:");
lcd.print(Gain,7);
} // fin loop

le filtre passe-bas 3éme ordre avec les complexes
Cette méthode consiste à calculer les coefficients et le gain avec les nombres complexes.

Calcul des coefficients et du gain:

N=3;    //ordre du filtre
k=1;   //cacul du pole P1
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
P1.set (real, imag);
//P1.polar(module, phase);
k=2;   //cacul du pole P2
real=-sin(((2*k-1)*PI)/(2*N));    
imag=cos(((2*k-1)*PI)/(2*N));
P2.set (real, imag);
k=3;   //cacul du pole P3
real=-sin(((2*k-1)*PI)/(2*N));   
imag=cos(((2*k-1)*PI)/(2*N));
P3.set (real, imag);


Z1num=(P1*fc*PI/fe);
Z1num+=1;             //obligation avec un addition de reel int et un complexe
Z1den=-(P1*fc*PI/fe);
Z1den+=1;
Z1=Z1num/Z1den;     //Zfinal=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
Z2num=(P2*fc*PI/fe);
Z2num+=1;             
Z2den=-(P2*fc*PI/fe);
Z2den+=1;
Z2=Z2num/Z2den;     
Z3= Z1.conjugate();
Zfinal=Z1+Z2+Z3;
a1=Zfinal.real();
Zfinal=-(Z1*Z2+Z1*Z3+Z2*Z3);
a2=Zfinal.real();
Zfinal=Z1*Z2*Z3;
a3=Zfinal.real();
Gain=((1+3+3+1)/(1-a1-a2-a3));

Voici le code complet:

//#include <Wire.h>      //I2C
#include <LiquidCrystal.h>
//#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include "Complex.h"


#define PWM3   3      //   timer2    
#define LED13    13
#define     PI 3.1415926

      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0;

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;

            float entreeMax;
            float entreeMin;
            float entreeMoy;
            float attenuation;

            
            float outMax;
        
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
            
            float  out=0;

            float  fe=1000;  //frequence d'echantilonnage  
            float  fc=50;  //frequence de coupure desiréé    

          
//pole
Complex P1(10, 10);  
Complex P2(0, 0);
Complex P3(0, 0);
//zero
Complex Z1num(0, 0);  
Complex Z2num(0, 0);
Complex Z1den(0, 0);  
Complex Z2den(0, 0);
Complex Z1(0, 0);  
Complex Z2(0, 0);
Complex Z3(0, 0);
//un complexe permettant de faire des cacul intermerdiare
Complex Zfinal(0, 0);
float module;
float real;
float imag;
//filtre passe bas d'ordre 3
float N=3;   //ordre
float k=3;
            float a1;   //coeficient filtre numerique
            float a2;
            float a3;
            float Gain;                      

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
/*
lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage  
*/  
  
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

N=3;    //ordre du filtre
k=1;   //cacul du pole P1
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
P1.set (real, imag);
//P1.polar(module, phase);
k=2;   //cacul du pole P2
real=-sin(((2*k-1)*PI)/(2*N));    
imag=cos(((2*k-1)*PI)/(2*N));
P2.set (real, imag);
k=3;   //cacul du pole P3
real=-sin(((2*k-1)*PI)/(2*N));   
imag=cos(((2*k-1)*PI)/(2*N));
P3.set (real, imag);


Z1num=(P1*fc*PI/fe);
Z1num+=1;             //obligation avec un addition de reel int et un complexe
Z1den=-(P1*fc*PI/fe);
Z1den+=1;
Z1=Z1num/Z1den;     //Zfinal=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
Z2num=(P2*fc*PI/fe);
Z2num+=1;             
Z2den=-(P2*fc*PI/fe);
Z2den+=1;
Z2=Z2num/Z2den;     
Z3= Z1.conjugate();
Zfinal=Z1+Z2+Z3;
a1=Zfinal.real();
Zfinal=-(Z1*Z2+Z1*Z3+Z2*Z3);
a2=Zfinal.real();
Zfinal=Z1*Z2*Z3;
a3=Zfinal.real();
Gain=((1+3+3+1)/(1-a1-a2-a3));
}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
temps++;

entree=analogRead(A0); //convertisseur 10 bits sous 5V      https://www.gammon.com.au/adc
entree=entree/4;  
      
// ---- exemple filtre passe bas 3éme ordre Butterwoth   pour fc=50hz,  fechantillon=1000Hz------    
entree3=entree2;      //entree(n-3)
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie3=sortie2;      //sortie(n-3)
sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)

sortie=entree+3*entree1+3*entree2+entree3+a1*sortie1+a2*sortie2+a3*sortie3    ;
                                                                                
out=((sortie/Gain));
        
//rafraichissement mesure toutes les 0.2s
if (temps>=200)   {entreeMax=0;outMax=0;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }  // Enregistrer la valeur maximale du capteur

if (out<0) {out=0;}
if (out>255) {out=255;}                                    
analogWrite(PWM3,out);
digitalWrite(LED13,LOW);  

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

lcd.setCursor(0,0);
lcd.print("a1:");
lcd.print(a1,7);
lcd.setCursor(0,1);
lcd.print("a2:");
lcd.print(a2,7);
lcd.setCursor(0,2);
lcd.print("a3:");
lcd.print(a3,7);
lcd.setCursor(0,3);
lcd.print("Gain:");
lcd.print(Gain,7);
} // fin loop

Avec cette méthode on retrouve bien les valeurs des coefficients a1, a2 et a3 tout comme le gain.

Deuxième méthode filtre passe-bas 3éme ordre avec les complexes

Cette fois-ci on utilise une boucle “for” en incrémentant k de 1 jusqu’à N (N étant l’ordre du filtre). Cette méthode est plus rapide pour déterminer les expressions de Z. Par contre elle ne permet pas de calculer les coefficients de façon automatique.

Le nouveau code:

//#include <Wire.h>      //I2C
#include <LiquidCrystal.h>
//#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include "Complex.h"


#define PWM3   3      //   timer2    
#define LED13    13
#define     PI 3.1415926

      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0;

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;

            float entreeMax;
            float entreeMin;
            float entreeMoy;
            float attenuation;

            
            float outMax;
        
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
            
            float  out=0;

            float  fe=1000;  //frequence d'echantilonnage  
            float  fc=50;  //frequence de coupure desiréé    

          
//pole
//Complex P1(10, 10);  
//Complex P2(0, 0);
Complex Pk(0, 0);
//zero
Complex Znum(0, 0);  
//Complex Znum(0, 0);
//Complex Z1den(0, 0);  
Complex Zden(0, 0);
Complex Z1(0, 0);  
Complex Z2(0, 0);
Complex Z3(0, 0);
Complex Zfinal(0, 0);
//un complexe permettant de faire des cacul intermerdiare
Complex Zk(0, 0);
float module;
float real;
float imag;
//filtre passe bas d'ordre 3
float N=3;   //ordre
float k=3;
            float a1;   //coeficient filtre numerique
            float a2;
            float a3;
            float Gain;                      

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
/*
lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage  
*/  
  
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

N=3;    //ordre du filtre
for (k=1; k<= N; k++)
{
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
Pk.set (real, imag);
Znum=(Pk*fc*PI/fe);
Znum+=1;             //obligation avec un addition de reel int et un complexe
Zden=-(Pk*fc*PI/fe);
Zden+=1;
Zk=Znum/Zden;     //Zfinal=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
}


Zfinal=Z1+Z2+Z3;
a1=Zfinal.real();
Zfinal=-(Z1*Z2+Z1*Z3+Z2*Z3);
a2=Zfinal.real();
Zfinal=Z1*Z2*Z3;
a3=Zfinal.real();
Gain=((1+3+3+1)/(1-a1-a2-a3));
}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
temps++;

entree=analogRead(A0); //convertisseur 10 bits sous 5V      https://www.gammon.com.au/adc
entree=entree/4;  
      
// ---- exemple filtre passe bas 3éme ordre Butterwoth   pour fc=50hz,  fechantillon=1000Hz------    
entree3=entree2;      //entree(n-3)
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie3=sortie2;      //sortie(n-3)
sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)

sortie=entree+3*entree1+3*entree2+entree3+a1*sortie1+a2*sortie2+a3*sortie3    ;
                                                                                
out=((sortie/Gain));
        
//rafraichissement mesure toutes les 0.2s
if (temps>=200)   {entreeMax=0;outMax=0;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }  // Enregistrer la valeur maximale du capteur

if (out<0) {out=0;}
if (out>255) {out=255;}                                    
analogWrite(PWM3,out);
digitalWrite(LED13,LOW);  

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

lcd.setCursor(0,0);
lcd.print("a1:");
lcd.print(a1,7);
lcd.setCursor(0,1);
lcd.print("a2:");
lcd.print(a2,7);
lcd.setCursor(0,2);
lcd.print("a3:");
lcd.print(a3,7);
lcd.setCursor(0,3);
lcd.print("Gain:");
lcd.print(Gain,7);
} // fin loop

** Filtre passe-haut 4éme ordre avec les complexes**

Avec cette méthode on retrouve à peu prés les mêmes coefficients (il y’a une légère différence des chiffres après la virgule) comme vous pourrez l’apercevoir en simulation que nous avons effectuée.

Le code:

//#include <Wire.h>      //I2C
#include <LiquidCrystal.h>
//#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>
#include "Complex.h"


#define PWM3   3      //   timer2    
#define LED13    13
#define     PI 3.1415926

      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// Configuration des variables
//LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter 20x4ligne


unsigned    int    temps=0;

            float  entree=0;
            float  entree1=0;
            float  entree2=0;
            float  entree3=0;
            float  entree4=0;

            float entreeMax;
            float entreeMin;
            float entreeMoy;
            float attenuation;

            
            float outMax;
        
            float  sortie=0;
            float  sortie1=0;
            float  sortie2=0;
            float  sortie3=0;
            float  sortie4=0;
            
            float  out=0;

            float  fe=1000;  //frequence d'echantilonnage  
            float  fc=50;  //frequence de coupure desiréé    

          
//pole
Complex P1(10, 10);  
Complex P2(0, 0);
Complex P3(0, 0);
Complex P4(0, 0);
//zero
Complex Z1num(0, 0);  
Complex Z2num(0, 0);
Complex Z1den(0, 0);  
Complex Z2den(0, 0);
Complex Z1(0, 0);  
Complex Z2(0, 0);
Complex Z3(0, 0);
Complex Z4(0, 0);
//un complexe permettant de faire des cacul intermerdiare
Complex Zfinal(0, 0);
float module;
float real;
float imag;
//filtre passe haut d'ordre 4
float N=4;   //ordre
float k=4;
            float a1;   //coeficient filtre numerique
            float a2;
            float a3;
            float a4;            
            float Gain=1.5;                      

void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
/*
lcd.init();               //et pas lcd begin comme cetraine biblio
lcd.display();            // activer l'affichage
lcd.backlight();          // allumer retroeclairage  
*/  
  
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

N=4;    //ordre du filtre
k=1;   //cacul du pole P1
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
P1.set (real, imag);
//P1.polar(module, phase);
k=2;   //cacul du pole P2
real=-sin(((2*k-1)*PI)/(2*N));    
imag=cos(((2*k-1)*PI)/(2*N));
P2.set (real, imag);
k=3;   //cacul du pole P3
real=-sin(((2*k-1)*PI)/(2*N));   
imag=cos(((2*k-1)*PI)/(2*N));
P3.set (real, imag);
k=4;   //cacul du pole P4
real=-sin(((2*k-1)*PI)/(2*N));   
imag=cos(((2*k-1)*PI)/(2*N));
P4.set (real, imag);

Z1num=(P1*fc*PI/fe);
Z1num+=1;             //obligation avec un addition de reel int et un complexe
Z1den=-(P1*fc*PI/fe);
Z1den+=1;
Z1=Z1num/Z1den;     //Zfinal=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
Z2num=(P2*fc*PI/fe);
Z2num+=1;             
Z2den=-(P2*fc*PI/fe);
Z2den+=1;
Z2=Z2num/Z2den;     
Z3= Z2.conjugate();
Z4= Z1.conjugate();
Zfinal=-(Z1+Z2+Z3+Z4);
a1=Zfinal.real();
Zfinal=(Z1*Z2+Z1*Z3+Z1*Z4+Z2*Z3+Z2*Z4+Z3*Z4);
a2=Zfinal.real();
Zfinal=-(Z1*Z2*Z3+Z1*Z2*Z4+Z1*Z3*Z4+Z2*Z3*Z4);
a3=Zfinal.real();
Zfinal=-Z1*Z2*Z3*Z4;
a4=Zfinal.real();

}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
temps++;

entree=analogRead(A0); //convertisseur 10 bits sous 5V      https://www.gammon.com.au/adc
entree=entree/4;  
      
// ---- exemple filtre passe haut 4éme ordre Butterwoth   pour fc=50hz,  fechantillon=1000Hz------    
entree4=entree3;      //entree(n-4)
entree3=entree2;      //entree(n-3)
entree2=entree1;      //entree(n-2)
entree1=entree;       //entree(n-1)

sortie4=sortie3;      //sortie(n-4)
sortie3=sortie2;      //sortie(n-3)
sortie2=sortie1;      //sortie(n-2)
sortie1=sortie;       //sortie(n-1)

sortie=entree-4*entree1+6*entree2-4*entree3+entree4+a1*sortie1+a2*sortie2+a3*sortie3+a4*sortie4    ;
                                                                                
out=((sortie/Gain)+127);
        
//rafraichissement mesure toutes les 0.2s
if (temps>=200)   {entreeMax=0;outMax=0;temps=0;}
if (entree> entreeMax) {entreeMax = entree; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }  // Enregistrer la valeur maximale du capteur

if (out<0) {out=0;}
if (out>255) {out=255;}                                    
analogWrite(PWM3,out);
digitalWrite(LED13,LOW);  

}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

lcd.setCursor(0,0);
lcd.print("a1:");
lcd.print(a1,7);
lcd.setCursor(0,1);
lcd.print("a2:");
lcd.print(a2,7);
lcd.setCursor(0,2);
lcd.print("a3:");
lcd.print(a3,7);
lcd.setCursor(0,3);
lcd.print("a4:");
lcd.print(a4,7);
} // fin loop

Voici le résultat:

Comme pour le passe-bas 3éme ordre on peut utiliser une boucle “for” pour retrouver les les poles P(k) et les zeros Z(k). Puis, les coefficient an du filtre sont determinés .

Le code avec la boucle “for”:

for (k=1; k<= N; k++)
{
real=-sin(((2*k-1)*PI)/(2*N));    //  -sin(((2*k-1)*PI)/2*N);
imag=cos(((2*k-1)*PI)/(2*N));
Pk.set (real, imag);
Znum=(Pk*fc*PI/fe);
Znum+=1;             //obligation avec un addition de reel int et un complexe
Zden=-(Pk*fc*PI/fe);
Zden+=1;
Zk=Znum/Zden;     //Zfinal=(1+P1*fc*PI/fe)/(1-P1*fc*PI/fe);
}

Ce programme consiste à mersurer le facteur de puissance par la méthode de passage par zéro des 2 signaux detectés grace à 2 amplis operationnels (AOP)

J’ai utilisé deux interruptions pour gérer les deux signaux tout ou rien des AOP.
Time0 permet de mesurer la période du signal broche 2 (fonction func_k()).

la variable "temps " correspond à la mesure de l’écart du temps entre les deux passages à zeros des signaux.
Ce temps permet de calculer le “déphasage”.
Ce dephasage est obtenu à partir de la formule suivante: tempsfrequence2*pi

J’ai mis des diodes entres les deux AOP et l’oscilloscope pour avoir des signaux périodiques.

La fréquence est donnée suivant la formule T=1/Péride(Time0). Puis on multiplie par 1000000 pour avoir des Hz car Time0 est en micros seconde.

#include <Wire.h>
//#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

              float T1,T0;
              float pi;
              float temps;
              float Time0, Time1;
              float dephasage, fe, fr,fp;

void setup()
{
  pinMode(LED13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  
  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  lcd.begin(20, 4);                   //modifier pour un afficheur 16x2
  
  Serial.begin(9600);

  attachInterrupt(0, func_k, RISING);   // broche2

    T0 = millis();
    attachInterrupt(1, powerfactor, RISING);   // broche3
  
 // TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet 
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l'interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer la perriode du signal broche 2
T0= micros();
T1= T0;
}

void powerfactor() // la fonction appelée par l'interruption externe n°1
{
temps=Time0-micros()+T1;  //pour mesurer l'ecart du temps
}

void callback() 
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                     
digitalWrite(LED13,LOW);  
}//fin routine

 

void loop()
{     
  pi=3,14;  
dephasage =temps*fr*2*pi;
fp = cos(dephasage);// factor power
fp = abs (fp*100);
fe=1000000/temps;        //redressement double alternance time en microseconde

lcd.setCursor(0,0);
lcd.print("Dephasage=");
lcd.print(Time0,0);
lcd.print("   ");

lcd.setCursor(0,1);
lcd.print("F(Hz)=");     //
lcd.print(fe);
lcd.print("   ");

lcd.setCursor(0,2);
lcd.print("temps=");
lcd.print(temps,0);
lcd.print("      ");

lcd.setCursor(0,3);
lcd.print("FactorPower=");
lcd.print(fp,2);
lcd.print("   ");
} // fin loop

Voici la simulation sous isis

Pour une fréquence d’entrée de 50hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

Pour une fréquence d’entrée de 500Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

khadim_mbengue:
voici mon programme, j’ai utilisé deux interruptions. Mais le facteur de puissance que j’obtiens ne correspond à mes calcule.

#include <Wire.h>

//#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define PWM3   3      //   timer2    
#define LED13    13      
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

unsigned int T0;
             unsigned int T1;
           float temps;
             unsigned int Time0, Time1;
             int  tension;
             int   courent;
             float k, f;

void setup()
{
 pinMode(LED13, OUTPUT);
 pinMode(PWM3,OUTPUT);
 pinMode(A1, INPUT);
 pinMode(A2,INPUT);
 
 Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
 lcd.begin(16, 2);                   //modifier pour un afficheur 16x2
 
 Serial.begin(9600);

//attachInterrupt(0, pf_func, FALLING); //interruption qui permet d’appeler la fontion pf_func()
 //attachInterrupt(1, powerfactor, FALLING); // interruption qui permet d’appeler la fonction powerfactor() et d’arreter la fontion pf_func()
 attachInterrupt(0, func_k, FALLING);   // broche2
  T0 = micros(); // lecture d’une compteur en ms
 attachInterrupt(1, powerfactor, FALLING);   // broche3
  T1 = micros(); // lecture d’une compteur en ms
 
// TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l’interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer le temps
T0= micros();
}

void powerfactor() // la fonction appelée par l’interruption externe n°0
{
Time1=(micros()-T0);  //pour mesurer le temps
T0 = micros();
}

void callback()
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l’oscillo, le temps du calcul du filtre et le temps de la routine d’interruption
 
//tension=analogRead(A1);  //pour pouvoir la tenson
//courent=analogRead(A2);  //pour pouvoir le courent  
                                 
analogWrite(PWM3,HIGH);
digitalWrite(LED13,LOW);  
     
}//fin routine

void loop()
{

temps=T1-T0;
temps =  temps / 1000000; // pour convertir les ms en s
k =temps50360;
k = cos (k);
f = abs (k * 100);
lcd.setCursor(0,0);
lcd.print(“FactorPower=”);
lcd.setCursor(0,1);
lcd.print(f, 1);
lcd.print("   ");
} // fin loop

as tu regardé, les valeurs des temps Time1 et Time0 que donne ton programme ?
car pour les 2 à chaque routine, tu les reinitialise avec le meme variable To ? ? ? ?

le dephasage est l’ecart entre Time1-To, mais sans reinitailisé TO
d’aileurs T1 n’est meme pas utilisé dans ton programme ???

il faut mesurer la frequence et pas multiplié par 0.
le cos demande une valeur en radian et pas en degres.

Enfin cela avance, mais il manque un peu de finition. tu devrais utiliser un afficheur 4 lignes, 20 caracteres pour afficher plus de variables et mieux debugger ton programme.
on n’y est presque…
il faudrait mettre des explications pour que les gens puissent comprendre ce que tu veux faire et la methode que tu utilises et cela t’aidera à mettre tes idées au claires

à khadim
tu as modifié ton post precedent cela progresse encore

mais :

  • Tu calcules correctement le déphasage mais tu affiches la période ???????
    A l’oscilloscope, il y a une période de 20ms (50Hz) mais la fréquence affichée est de 397Hz ???????
    donc fr ou fe ???
    Mettre des commentaires dans ton programme pour chaque variable, cela te permettra d’être plus rigoureux.
  • pourquoi, il y a une diode avant l'AOP ?

Quelle est la précision de la mesure du déphasage avec cette méthode ? Est-ce que cette précision dépend de la fréquence ?

Donc remplir le tableau suivant pour vérifier le bon fonctionnement du programme et pour répondre aux questions précédentes

Nous allons faire des filtres RIF passe-bas avec une fréquence d’échantillonnage (fe) de 1000 Hz et une fréquence de coupure de 50 Hz.
Nous allons faire des exemples avec 5, 9 et 23 coefficients et tracer leur courbe d’atténuation avec Excel.
Voici la formule appliquée pour le calcul des coefficients:

Voici le code:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc=50;  //frequence de coupure desiréé 

byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients

           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet


// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*fc/fe;            //sinus cardinal(0)
//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));}
for (byte i = 0; i < M; i++) {Serial.print(b[i],3);Serial.print(";");} 
}



// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

 
lcd.setCursor(0,0);   
lcd.print("filtre RIF passe bas");
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde
lcd.print(" out_average");

 
  lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
  lcd.print(attenuation,2);   
  lcd.print("=Attenuat ");

} // fin loop

Voici les 3 lignes qui permettent de faire varier le nombre de coefficients:

byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients

Variation de l’atténuation pour chaque coefficient:

On constate que plus on augmente le nombre de coefficient ,plus la fréquence de coupure se rapproche de la fréquence de coupure désirée.

khadim_mbengue:
Pour une fréquence d’entrée de 50hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

Pour une fréquence d’entrée de 500Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes

kadhim, impose un dephasage et mesure un truc extravagant ? ? ? ? ? ? ? ? ? ? ? ?
sans conclusions ? ? ? ? ?

pour saliou
Comparer l’atténuation des filtres qui n’ont pas le même gain statique, n’est pas très pertinent.
alors qaue pour une frenquence tres inferieure à 50Hz nomalement, un filtre calissique devrait avoir un gain unitaire.
D'ailleurs, ce serait bien, qu’il y a une courbe théorie (deuxième ordre par butterwotrh exemple) pour faire des comparaisons
De plus, si on avait les 3 courbes sur le même graph, une comparaison serait plus facile à observer.

Pour les filtres FIR correspondront à l’équation suivante :

Le gain statique à 0Hz est pour z=1, donc pour la somme des coefficients b. d’ailleurs cette somme, donne un gain statique unitaire théoriquement. Mais lorsque le nombre de coefficient 2M+1 est faible, cela s provoque une erreur statique.
Donc, il faut le corriger sinon la comparaison entre les FIR de differents coefficients ne sont pas viables.

il faut faire les mesures jusque fe/2 pour montrer le phenomene de rebond de l'attenuation appelé gibbs

Je suis ce post régulièrement, car la vulgarisation des filtres numériques permet de les appliquer facilement et de connaitre les limites du processeur en audio. Et j’ai du mal aussi de passer de la théorie à la pratique.

Mais, il a un problème de neurones ce kadhim pour faire des mesures qui ne correspond pas à ce qu’il doit mesurer donc que son programme n’est pas correcte.
En plus, à force de corriger son post precedent, on ne comprend plus les commentaires qui lui sont fait.
Pas sur qu’il maitrise les tenants et les aboutissants de ce qu’il fait.

Pareil pour ce saliou, cela ne le gêne pas d’avoir des gains statiques différents.
Tous les 2, ils font des choses sans réfléchir à leurs résultats, sans synthèse.
mais, le programme de saliuo sur le filtre passe haut avec les complexes sont top.

pas sur qu’il ait le niveau d’écrire sur ce post, ce sont plutot des exécutants.

mais, il est vrai que le filtrage numérique demande une base mathématique importante….

filtres RIF passe-bas
Nous allons d’abord faire quelques exemples avec différents coefficients (M=5, M=9, M=23) sans la troncature Hamming. Ensuite nous ferons d’autres exemples avec les mêmes coefficients mais avec la troncature de Hamming qui nous permettra de résoudre le problème de rebonds (Phénomène de Gibbs).

Voici le code complet:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc=50;  //frequence de coupure desiréé 
float gain=0;
/*
byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients
*/
/*
byte M=9;               //nombre impaire de coefficients desirées 
float  entree[9];       //declaration de la table des entrées
float  b[9];   // table des coefficients
*/

byte M=23;               //nombre impaire de coefficients desirées 
float  entree[23];       //declaration de la table des entrées
float  b[23];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*fc/fe;            //sinus cardinal(0)
//Les coefficients et le gain sans la troncature hamming
//for (byte i = 0; i < M; i++) {b[i];gain=gain+b[i];}  

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));gain=gain+b[i];}
for (byte i = 0; i < M; i++) {Serial.print(b[i],3);Serial.print(";");} 
Serial.println("gain= ");
Serial.print(gain,3);Serial.print(";"); 
//Gain pour différents nombres de coefficients sans la troncature hamming
//M=5  gain=0.48
//M=9  gain=0.81
//M=23  gain=1.16

//Gain pour différents nombres de coefficients avec la troncature hamming
//M=5  gain=0.41
//M=9  gain=0.68
//M=23  gain=1.09

}


// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Les résultats des simulations effectuées sont représentés dans des tableaux excel pour mieux observer les rebonds et l’effet de la troncature de Hamming.

-sans la troncature Hamming

-avec la troncature Hamming

A 170 Hz pour M=23 nous avons ceci:

Sans la fenêtre de Hamming on voit qu’il y’a bien des rebonds entre 120 et 500 Hz: le phénomène Gibbs. Pour minimiser ces parasites, nous avons utilisé la fenêtre de Hamming qui améliore considérablement cette anomalie comme vous pouvez l’appercevoir sur la 2eme courbe.
J’ai oublié de rajouter cette ligne pour avoir un gain unitaire:

out=out/gain;

Bonjour,
Désolé je suis un débutant sur arduino et voici mon nouveau programme qui factionne.

#include <Wire.h>
///#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define LED13    13      

LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

              float T1,T0;  
              float pi;
              float temps;           //
              float Time0, Time1;   // peridiode des signaux
              float dephasage, dephasagerad,fp;
              float fe;                         //frequence d'entrée
  

void setup()
{
  pinMode(LED13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  
  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
  lcd.begin(20, 4);                   //modifier pour un afficheur 16x2
  
  Serial.begin(9600);

  attachInterrupt(0, func_k, RISING);   // broche2

  T0 = millis();
  attachInterrupt(1, powerfactor, RISING);   // broche3
  
 // TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet 
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l'interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer la perriode du signal broche 2
T0= micros();
T1= T0;
}

void powerfactor() // la fonction appelée par l'interruption externe n°1
{
temps=Time0-micros()+T1;  //pour mesurer l'ecart du temps
}

void callback()      
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                     
digitalWrite(LED13,LOW);  
}//fin routine

 

void loop()
{  
    
 fe=1000000/Time0;                        
dephasage=((360*temps)/Time0);

dephasagerad=((dephasage*PI)/180);
fp = cos(dephasagerad);// factor power

lcd.setCursor(0,0);
lcd.print("Frequence=");     //
lcd.print(fe);
lcd.print("   ");

lcd.setCursor(0,1);
lcd.print("Delta T=");
lcd.print(temps,0);
lcd.print("      ");

lcd.setCursor(0,2);
lcd.print("Dephasage=");
lcd.print(dephasage);
lcd.print("   ");



lcd.setCursor(0,3);
lcd.print("Factor Power=");
lcd.print(fp,3);
lcd.print("   ");

} // fin loop

La sumilation:
90° avec 50Hz

45° avec 500Hz

On a fait quelques tests pour vérifier le bon fonctionnement du programme.

Pour une fréquence d’entrée de 50Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes:

Pour une fréquence d’entrée de 500Hz, avec des phasages imposés entres les 2 signaux, le programme mesure les valeurs suivantes:

L’analyse de ces deux tableaux montre que la précision de mesure ne dépend pas de la fréquence .

khadim_mbengue:
L'analyse de ces deux tableaux montre que la précision de mesure ne dépend pas de la fréquence .

khadim
Comment fais tu l’analyse avec les tableaux et la précision ???????

Comment calculer l’erreur et la précision de la mesure de la fréquence ? puis l’erreur sur le dephasage
l'erreur depend du choix du compteur time qui est plus ou moins 1 microseconde avec les equations suivantes :
ErreurdeFe(Hz)=10^6/time0-10^6/(time0+1)= 10^6/ time0^2
Précision= ErreurdeFe/Fe=
Erreur Déphasage(°)=(360°/ time0).(temps- temps+1)= (360°/ time0)

Exemple pour 50Hz ou time0 en micro seconde donc 20 000 plus ou moins 1 microseconde.
Erreur de Fe= 10^6/ 20 000^2=0.0025Hz
Précision=0.0025Hz/50Hz*100=0.0005%
Erreur Déphasage(°)=360°/20000=0.018°
donc pas de probléme à ce niveau de frequence.

Exemple pour 500Hz ou time0 en micro seconde donc 2 000µs
Erreur de Fe= 10^6/ 2 000^2=0.25Hz
Précision=0.25Hz/500Hz*100=0.05%
Erreur Déphasage(°)=360°/2000=0.18°

Pour 5KHz, l’erreur de la fréquence passe à 25Hz, donc à 0.5% et l’erreur sur le déphasage à 1.8°.
cela commence à etre critique

Donc l’erreur sur le déphasage est proportionnelle à la fréquence et celle de l’erreur de la mesure de la frequence est inversement quadratique.
Mais si l’on désire, vérifier les erreurs, des calculs precedents , il faut mesurer les variations de Fe(Hz) mini et maxi, pareil sur le déphasage.
C’est très facile de le faire en programmation.

Mais une autre question plus cruciale :
Quelle est la fréquence maximale mesurable que peux faire ton programme ?
Mais tes méthodes , sur le forum…..cela devrait intéresser du monde….

A propos de mon dernier post, j’ai rajouté la dernière ligne que j’avais oublié pour ajuster le gain.

Voici le nouveau code complet:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc=50;  //frequence de coupure desiréé 
float gain=0;
/*
byte M=5;               //nombre impaire de coefficients desirées 
float  entree[5];       //declaration de la table des entrées
float  b[5];   // table des coefficients
*/
/*
byte M=9;               //nombre impaire de coefficients desirées 
float  entree[9];       //declaration de la table des entrées
float  b[9];   // table des coefficients
*/

byte M=23;               //nombre impaire de coefficients desirées 
float  entree[23];       //declaration de la table des entrées
float  b[23];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*fc/fe;            //sinus cardinal(0)
//Les coefficients et le gain sans la troncature hamming
//for (byte i = 0; i < M; i++) {b[i];gain=gain+b[i];}  

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));gain=gain+b[i];}
for (byte i = 0; i < M; i++) {Serial.print(b[i],3);Serial.print(";");} 
Serial.println("gain= ");
Serial.print(gain,3);Serial.print(";"); 
//Gain pour différents nombres de coefficients sans la troncature hamming
//M=5  gain=0.48
//M=9  gain=0.81
//M=23  gain=1.04

//Gain pour différents nombres de coefficients avec la troncature hamming
//M=5  gain=0.41
//M=9  gain=0.68
//M=23  gain=1.09

}


// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}
out=out/gain;   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {

 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Voici les nouvelles valeurs des atténuations

Avec la fenêtre de Hamming, nous avons les résultats suivants

En ajustant les gains, nous parvenons à mieux observer les différences entre les courbes. On voit aussi l’importance de la fenêtre de Hamming qui nous permet d’obtenir de nouveaux coefficients et donc d’améliorer la qualité des filtres.
Après le filtre passe-bas, Je vais faire le filtre passe-bande FIR et présenter le code et les résultats des simulations.

On peut aussi rajouter la courbe d’un filtre analogique du seconde ordre dans Excel pour faire des comparaissions.
Il faut juste rentrer l’équation comme on peut l’observer sur la figure suivante.

sur la courbe, precedente, on peut observer qu'il faut 9 coefficients FIR pour attenuer comme un second ordre.

Une petite aide : :smiling_imp: :roll_eyes:
Voici les calculs qui permettent de trouver les coefficients d’un passe bande d’un filtre FIR.

On peut observer que pour un nombre de coefficient égale à 17, L’atténuation basse fréquence est faible et que la bande passante n’est pas respectée par rapport à un filtre du seconde ordre analogique.


La valeur max du coefficient bm correspond à 2 fois la bande passante divisée par la fréquence d’échantillonnage.
Mais quel est le nombre de coefficients pour avoir une atténuation aux fréquences de coupures désirées ?
Si on augmente le nombre à 21 coefficients, l’atténuation n’est pas encore atteinte.

Si on augmente à 41 coefficients, alors l’atténuation correspond à ce que l’on désire.

On peut remarquer que plus la bande passante est faible et plus il faudra de coefficient pur avoir l’atténuation désirée.
De même, plus la fréquence fo et éloignée de la fréquence fe, et plus il faudra de coefficient.
En effet, les termes b du sinus cardinal deviennent de plus en plus négligeable avec
Sur la figure suivante les fréquences de coupure est passé à 30 HZ et 80Hz, il suffira de 10 coefficients pour avoir des coefficients faibles

Saliou, il te reste à vérifier la théorie…….
En calculant les coefficients par l’arduino
Attention le temps du calcul du filtre ne doit pas dépasser le temps de la période d’echantillonage.

D’ailleurs, il serait intéressant de savoir en combien de temps le filtre est calculé en fonction du nombre de coefficient :sunglasses:

1 Like

A la place d’utiliser une PWM pour avoir une sortie analogique, un DAC 12 bits MCP4725 I2C pourrait etre utilisé si on n’a pas besoin de periode d’echantillonage rapide.

http://www.bristolwatch.com/arduino2/ard4.htm

En effet, le temps d’ecriture dans le DAC avec l’I2C est d’environ 0.14ms ce qui est lent.

alors que l’ecriture, d’un PCF8574 8bits I2C est de 0.22ms donc realisé un reseau R2R n’est pas interressant à faire.

L’écriture du DAC interne de l’ESP32 dure 0.05ms, 2 fois plus rapide que l’ATMEGA328.

Mais on ne peut pas utiliser la liaison série dans la routine d’interruption timer1, ni l’écriture dans le DAC.
Donc, il faut tout faire dans la boucle loop

On peut observer sur la figure suivante, la sortie du DAC I2C qui recopie l’entrée A0 avec période d’échantillonnage de 1ms.

Voici le code avec les adresses des circuits I2C
I2C device found at address 0x27 LCD PCF8574
I2C device found at address 0x60 DAC MCP4725 A0=0
I2C device found at address 0x68 DS1307

#include <Wire.h>     // // Join i2c bus
#include <LiquidCrystal_I2C.h>
//#include "RTClib.h"      // Bibliothèque pour le module RTC
#include <Adafruit_MCP4725.h>
#include <TimerOne.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);     //A0, A1, A2 non shunter

#define LED13   13 
byte bufferDAC [3];
unsigned int counter;
float entree, out;
bool flag=0;


Adafruit_MCP4725 dac;

void setup()
{  // initialise l'afficheur LCD
pinMode(LED13, OUTPUT); 

lcd.init();  //et pas lcd begin comme cetraine biblio
  lcd.display();     // activer l'affichage
  lcd.backlight();   // allumer retroeclairage

  lcd.setCursor(0,0);
  lcd.print("IUT GENIE elect&info");
  lcd.setCursor(5,1);
  lcd.print("Soissons");
  //Serial.begin(57600);

 Wire.begin();

   
 // commande du DAC MCP4725 12 bits  4096
  bufferDAC [0] = 0b01000000; // control byte pointer
  bufferDAC[2]=0;
  bufferDAC[1]=64;     //1.25V
  Wire.beginTransmission(0x60);
  Wire.write(bufferDAC[0]);  
  Wire.write(bufferDAC[1]);  // 8 MSB   
  Wire.write(bufferDAC[2]);  // 4 LSB
  Wire.endTransmission();               //temps d'ecriture dans le DAC 0.4ms

  dac.begin(0x60);
 Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback); 


//  attachInterrupt(0, Rexterieur2, RISING);   // broche2
}


// Interruptions  tous les 1ms fait par le timer1***********************************
void callback()  {
flag=1;
//if (digitalRead(LED13)== 1) {digitalWrite(LED13,LOW);}  else {digitalWrite(LED13,HIGH);}
}//fin routine

void loop()
{
   
if (flag==1) {       //ne pas mettre dans la routine d'interruption timer1 ca rI2C ne foncitionne pas
  digitalWrite(LED13,HIGH);
  entree=analogRead(A0); //convertisseur 10 bits sous 5V
  out = entree*4;   //mise à l'echelle
  dac.setVoltage(out, false);            //12 bits

  flag=0;
  digitalWrite(LED13,LOW);                      //0.2ms
              }

}

Etant donné que les sorties de l’Arduino nano sont peu utilisées un DAC parallèle R2R serait intéressant à faire au niveau de la rapidité. Donc utiliser un DAC paralléle tel que DAC0800

mais, je n’ai pas trouvé de shield avec un DAC parallele ?

conclusions, la PWM avec ces 7micro secondes reste la meilleur solution

Pour kadhim
Plus de 60heures ouvrées pour trouver, la fréquence max mesurable par ton programme avec l’erreur le déphasage ??? .
C’est moi qui suis en train de faire ton sujet. :astonished:

Bref, avec un retard de 20µs imposé, qui peut être observé sur le signal à l’oscilloscope sur la figure suivante, mais le temps mesuré par l’Arduino est seulement de 12 µs, voir l’affichage LCD

En effet, le temps de lire micros() cela dure 6.5µs. mais, la lecture se fait sur les 2 routines d’interruptions.
Mais, il y a le temps d’empilement et dépilement due à la routine d’interruption.
De plus tout déclarer en float, ne permet pas de gagner du temps.
Voir le post suivant

Par contre, déclarer en entier non signé limite les fréquences mesurables supérieures à 15Hz.

Donc, avec un retard de 5µs± avec une erreur de 1 µs (20%), donne une fréquence maximale mesurable d’environ 25kHz comme on peut l’observer sur la figure suivante :

Voici le nouveau code

#include <Wire.h>
///#include <LiquidCrystal_I2C.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
//#include <TimerOne.h>
//#include <math.h>

#define A1 20   // transformateur de tension
#define A2 21   //transformateur de courent
#define LED13    13      

LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

 unsigned  int T1;  //temps routine d'interruption D3
 unsigned  int T0;  //temps routine d'interruption D2
              float pi;
 unsigned  int temps;           //
 unsigned  int Time0;    // peridiode des signaux
              float dephasage, dephasagerad;
              float fp;  //facteur power
              float fe;                         //frequence d'entrée
  

void setup()
{
  pinMode(LED13, OUTPUT);
  pinMode(A1, INPUT);
  pinMode(A2,INPUT);
  
//  Timer1.initialize(1000);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
//  Timer1.attachInterrupt(callback);  
  lcd.begin(20, 4);                   //modifier pour un afficheur 16x2
  
  Serial.begin(9600);

  attachInterrupt(0, func_k, RISING);   // broche2

  T0 = millis();
  attachInterrupt(1, powerfactor, RISING);   // broche3
  
 // TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
}

// faisons deux interruotion pour pouvoir calculer le temps

void func_k() // la fonction appelée par l'interruption externe n°0
{
Time0=(micros()-T0);  //pour mesurer la perriode du signal broche 2
T0= micros();
}

void powerfactor() // la fonction appelée par l'interruption externe n°1
{
 temps=micros()-T0;  //pour mesurer l'ecart du temps et compenser le temps de la routine 
}

void callback()      
{
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption                                    
digitalWrite(LED13,LOW);  
}//fin routine

 

void loop()
{  
 
 fe=1000000/Time0;                        
dephasage=((360*temps)/Time0);

dephasagerad=((dephasage*PI)/180);
fp = cos(dephasagerad);// factor power

lcd.setCursor(0,0);
lcd.print("Frequence=");     //
lcd.print(fe);
lcd.print("   ");

lcd.setCursor(0,1);
lcd.print("Delta T=");
lcd.print(temps);
lcd.print("      ");

lcd.setCursor(0,2);
lcd.print("Dephasage=");
lcd.print(dephasage);
lcd.print("   ");

lcd.setCursor(0,3);
lcd.print("Factor Power=");
lcd.print(fp,3);
lcd.print("   ");

} // fin loop

Ce serait bien qu’un signal sinusoïdal de la tension avec une amplitude de 220V soit mesuré, ainsi qu’un, courant d’amplitude de moins de 10A avec un ACS712 soit effectué ainsi que la mesure de puissance.
Donc un programme pour faire une pince wattmetrique……
Quelle sera les modifications du programme si l’on prend un HSTS016L par rapport à ACS712 ?

** Filtre passe-bande RIF (fo=50Hz, bande passante 60Hz, donc frequence de coupure de fc1=20Hz et fc2=80HZ)avec atmega 328**
Comme pour le filtre passe-bas, nous allons voir le code et le résultat des simulations avec et sans la fenêtre de Hamming.
pour 25 coefficients voici les valeurs des coefficients du filtre FIR qui sont calculés par le programme arduino.
on peut observer qu’ils sont identiques à ceux determinés par iutgeiisoissons precedement transféré par USB via le virtual terminal de la simulation suivante

Mais avec la fenetre de Hamming nous avons trouvé de nouveaux coefficients:


Le code:

#include <Wire.h>
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>
//#include <math.h>

#define PWM3   3      //   timer2   
#define LED13    13       
LiquidCrystal lcd(9, 8, 4, 5, 6, 7);   // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

#define PI 3.141592
unsigned    int    temps=0;
         
float  out=0;
float entreeMax;
float outMax;
float attenuation;

float outaverage;
float outaverage1;

float  fe=1000;  //frequence d'echantilonnage 
float  fc1=20;  //frequence de coupure basse 
float  fc2=80;  //frequence de coupure haute
float gain=0.35;
//float gain=0.1;

byte M=25;               //nombre impaire de coefficients desirées 
float  entree[25];       //declaration de la table des entrées
float  b[25];   // table des coefficients


           
void setup() {
  pinMode(LED13, OUTPUT);
  pinMode(PWM3,OUTPUT);

  Timer1.initialize(1000);           //   pour 0.001s  1000   0.002s 2000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 
  Serial.begin(9600);

  TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 3  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet

// calcul coefficient filtre FIR
for (byte i = 0; i < M; i++) {b[i]=(sin(2*fc2*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI))-(sin(2*fc1*PI*(i-((M-1)/2))/fe)/(((i-((M-1)/2)))*PI));}   
b[((M-1)/2)]=2*(fc2-fc1)/fe; //sinus cardinal(0)

//ajustement des coefficients avec la troncature hamming
for (byte i = 0; i < M; i++) {b[i]=b[i]*(0.54+0.46*cos(PI*(i-((M-1)/2))/M));}
for (byte i = 0; i < M; i++) {Serial.print("b");Serial.print(i);Serial.print("=");Serial.print(b[i],3);Serial.println(";");}
//for (byte i = 0; i < M; i++) {Serial.println(b[i],3);/*Serial.print(";");*/} 
}

// Interruptions  tous les 1ms fait par le timer1    fe=1000Hz***********************************
void callback()  {
//if ( digitalRead(13)== 1 ) {digitalWrite(13,LOW);}  else {digitalWrite(13,HIGH);}
digitalWrite(LED13,HIGH);  //permet de mesurer à l'oscillo, le temps du calcul du filtre et le temps de la routine d'interruption
entree[M-1]=analogRead(A0);         //convertisseur 10 bits sous 5V   https://www.gammon.com.au/adc
entree[M-1]=entree[M-1]/4;          //mise à l'echelle 10 bits en entrée et 8 bits en sortie+chiffre significatif   

out=0;     //remise à zero du calcul du filtre RIF
for (byte i = 0; i < M; i++) {out=(b[i]*entree[i]+out);/*Serial.print(entree[i]);Serial.print(";");*/}
out=out/gain;   
for (byte i = 0; i < M; i++) {entree[i]=entree[i+1];}       
//Serial.print(out,0);
//Serial.println(";");                                                             
if (out<0) {out=0;}
if (out>255) {out=255;}                 
analogWrite(PWM3,out);        //   

temps++;   
outaverage=outaverage+out;          //calcul de la valeur moyenne     
//rafraichissement toutes les 0.2s           
if (temps>=200)   {entreeMax=127;outMax=127;temps=0;outaverage1=outaverage/200;outaverage=0;}
if (entreeMax <entree[0]) {entreeMax = entree[0]; }  // Enregistrer la valeur maximale du capteur
if (out> outMax) {outMax = out; }
digitalWrite(LED13,LOW); 
}//fin routine



///////////////////////////////////////////// Boucle correspondant à la fonction main
void loop() {
 
lcd.setCursor(0,0);
lcd.print("Gain= ");
lcd.print(gain,2);   
lcd.setCursor(0,1);   
lcd.print("ordre");
lcd.print(M);
lcd.setCursor(0,2);
lcd.print("out_average");
lcd.print(outaverage1,0);   //valeur moyenne sur 0.2seconde

   lcd.setCursor(0,3);
  attenuation=((outMax-outaverage1)/(entreeMax-127));
    lcd.print("Attenuat= ");
  lcd.print(attenuation,2);   

} // fin loop

Le résultat des simulations sans la fenêtre de Hamming:

il a fallu mettre un gain 1/0.2 pour avoir un gain statique de 1 à 50Hz
On observe qu’il y’a des rebonds au niveau du filtre, ce qui est loin d’être idéal, raison pour laquelle nous allons essayer de résoudre ce problème.

Le résultat des simulations avec la fenêtre de Hamming avec un gain 1/0.35
la courbe bleu est la reponse numerique, la courbe orange est analogique


Avec la fenêtre, les rebonds n’apparaissent plus au niveau du filtre. Ce qui nous facilite aussi la comparaison des 2 filtres (numérique et analogique).
l’équation du filtre analogique est:

avec le coefficient Q=(fc2-fc1)/fo=1.2
pour le filtre analogique de second ordre pour les fréquence de coupure l’atténuation est de 0.707 alors que la bande passante pour le filtre numérique est bien plus grande.

avec une fréquence d’échantillonnage de 1KHz, il est possible d’augmenter le nombre de coefficients à 41 sans dépasser le temps de la routine d’interruption. cela devrait améliorer l’atténuation.
De plus, à la place de prendre une bande passante de 60Hz,
quelles seraient les valeurs des coefficients et l’atténuation si l’on prenait une bande passante de 20Hz?