La synthèse d’une période (inverse de la fréquence) par l’intermédiaire des temporisations matérielles (Timers) ou logicielles (delay()) n’est pas assez précises. En effet, les performances de l’oscillateur de la carte Arduino peuvent dériver dans le temps à cause de la température. Et par conséquent, la gestion efficace du temps. L’idée consiste à synchrones le code avec une source externe indépendante.
L’idée consiste à lancer le programme via le front montant (ou descendant) d’une horloge externe en utilisant une interruption externe. L’horloge sera branchée directement dans une entrée digitale de la carte dédiée au déclenchement de l’interruption. En résumé, le programme sera équivalent à une boucle loop() qui se lance chaque période de l’horloge externe. On distingue deux configurations.
La fréquence d’horloge externe doit être choisie en tenant en compte du temps d’exécution du programme a synchronisé. En effet, il faut que l’horloge soit assez lente par rapport à la durée d’exécution du programme comme il est indiqué dans la figure ci-dessous. Par exemple Tp=10ms, alors il faut choisir Tt>2*Tp=20ms dans le cas idéal.
Si l’horloge est trop rapide par rapport à la durée d’exécution du programme, alors la priorité sera donner au programme. Autrement dit, les fronts montants apparus durant l’exécution du programme seront ignorés. Les fronts ne seront pris en compte qu’à la fin d’exécution du programme (voir la figure ci-dessous).
/*
* Commande Symétrique: SetCMD1() & SetCMD2()
* Commande Décalée: SetCMD3() & SetCMD4()
* Commande PWM(MLI): Voir le programme
*/
#define Tr11 22 // Transistor 1/Bras 1
#define Tr12 23 // Transistor 2/Bras 1
#define Tr21 24 // Transistor 1/Bras 2
#define Tr22 25 // Transistor 2/Bras 2
#define Tr31 26 // Transistor 1/Bras 3
#define Tr32 27 // Transistor 2/Bras 3
#define T0_us 333 // Période de l'onduleur (Symétrique) : T=6*T0 (µs)
// Période de l'onduleur (Décalée) : T=12*T0 (µs)
// sPWM: Période d'incrémentation du Timer
// sPWM: Période globale T=2*N*T0_us => F=1/F: Fréquence de l'Onduleur
/* Params sPWM */
#define N 64 // Niveaux de l'onduleur: Nombre d’échantillons (foramt 2^n)
#define TimerStep 13 // Pas d'incrémentation du Timer rapide (>>1)
int Trans[6]; // Ou bien PORTA
unsigned long MedSine[N];
unsigned long TimerSPWM=0;
unsigned long i_sin[3]={0,0,0};
bool sPWM_l[3]={false,false,false};
bool sPWM_r[3]={true,true,true};
bool sinePolar[3]={false,false,false};
double sine_val=0.0, A=0.0;
int Nbit=0; // Amplitude du signal sinusoïdal 2^8
void setup()
{
// Pinout
Trans[0]=Tr11; Trans[1]=Tr12;
Trans[2]=Tr21; Trans[3]=Tr22;
Trans[4]=Tr31; Trans[5]=Tr32;
for (int i=0;i<6; i++)
{
pinMode(Trans[i], OUTPUT);
digitalWrite(Trans[i], LOW);
}
// Ou bien: Init du port A en sortie
//DDRA =0xff; PORTA=0x00;
// Cogade binaire de l'amplitude
Nbit=(int)round((log((float)N)/log(2.0)));
// Init Timers // Déphasage de 60°/Bras (60°=>T/6)
i_sin[0]=0; // Bras 1 - 0°
i_sin[1]=round((float)N/6.0); // Bras 2 - 60°
i_sin[2]=round(2.0*(float)N/6.0); // Bras 3 - 120°
// Génération du signal sinusoïdal: 1/2 Période (2*N échantillons/période)
A=(double)(pow(2.0,(double)Nbit)-1.0);
for (int i=0; i<N; i++)
{
sine_val=A*sin(PI*(double)i/(double)(N));
MedSine[i]=(int)(abs(floor(sine_val)));
}
// Afficahge
Serial.begin(9600);
pinMode(3,OUTPUT);
// Configuration de l'Interruption (Trigger Externe)
// Câblage du pin et configuration en entrée: INT0 => PD0 (pin 21 de la carte)
DDRD =0x00; // Port D en entrée
// Activation de l'interruption globale (registre SREG)
SREG|=0x80;
// Validation de l'interruption INT0 (Registre EIMSK)
EIMSK|=0x01; // INT0
// Choix du mode de détection: Front montant dans INT0
EICRA|=0x01;
}
void loop(){}
//ISR(_VECTOR(1)) // Ou _VECTOR(2)
ISR(INT0_vect)
{
//SetCMD1(Trans);
//SetCMD2(Trans);
//SetCMD3(Trans);
SetCMD2(Trans);
/*********** sPWM ***********/
// Incrémentation du Timer: Compteur rapide (signal triangulaire)
/*
TimerSPWM+=TimerStep;
if (TimerSPWM>N) TimerSPWM=0;
// Génération de la commande
for (int i=0; i<3; i++)
{
// 1. Incrémentation du Timer du signal: Compteur long (signal sinusoïdal)
i_sin[i]+=1;
if (i_sin[i]>N) i_sin[i]=0;
// 2. Inversion de la polarité de la période
if(!i_sin[i]) sinePolar[i]=!sinePolar[i];
// 3. Génération des signaux PWM
if(sinePolar[i]) // Période (+)
{
sPWM_l[i] =MedSine[i_sin[i]] > TimerSPWM;
sPWM_r[i] =false;
}
else // Période (-)
{
sPWM_l[i] = false;
sPWM_r[i] =MedSine[i_sin[i]] > TimerSPWM;
}
}
// Envoie des signaux au Port A
PORTA=(B00000001*sPWM_l[0])+ (B00000010*sPWM_r[0])+
(B00000100*sPWM_l[1])+ (B00001000*sPWM_r[1])+
(B00010000*sPWM_l[2])+ (B00100000*sPWM_r[2]);
*/
}
void SetCMD1( int *pins)
{
static int I=0;
const bool Cmd[6][6]={{0,1,1,0,0,1},
{1,0,1,0,0,1},
{1,0,0,1,0,1},
{1,0,0,1,1,0},
{0,1,0,1,1,0},
{0,1,1,0,1,0}};
for (int i=0;i<6; i++) digitalWrite(pins[i], Cmd[I][i]);
I++; I%=6;
return 0;
}
void SetCMD2( int *pins)
{
static unsigned char I=0;
const unsigned char Cmd[6]={B00011001,
B00101001,
B00100101,
B00100110,
B00010110,
B00011010};
PORTA=Cmd[I];
I++;
//I%=6; // Fréquence: +
//I=I*(I<6); // Fréquence: ++
if (I>=6)I=0; // Fréquence: +++
return 0;
}
void SetCMD3( int *pins)
{
static unsigned char I=0;
const bool Cmd[12][6]={
{0,1,1,0,0,0},
{0,0,1,0,0,1},
{0,0,1,0,0,1},
{1,0,0,0,0,1},
{1,0,0,0,0,1},
{1,0,0,1,0,0},
{1,0,0,1,0,0},
{0,0,0,1,1,0},
{0,0,0,1,1,0},
{0,1,0,0,1,0},
{0,1,0,0,1,0},
{0,1,1,0,0,0},
};
for (int i=0;i<6; i++) digitalWrite(pins[i], Cmd[I][i]);
I++;
if (I>=12)I=0;
return 0;
}
void SetCMD4( int *pins)
{
static unsigned char I=0;
const unsigned char Cmd[12]= {
B00011000,
B00001001,
B00001001,
B00100001,
B00100001,
B00100100,
B00100100,
B00000110,
B00000110,
B00010010,
B00010010,
B00011000
};
PORTA=Cmd[I];
I++;
if (I>=12)I=0;
return 0;
}
On verra dans le prochain tuto la commande générique d’un onduleur polyphasée. N’oublie pas me laisser un commentaire :