Contrôle moteur AC type "universel" : problème de parasites?

Bonjour à tous,
Dans le cadre de mon projet de programmateur de lave linge (http://arduino.cc/forum/index.php/topic,62096.0.html), je travaille sur la partie délicate du contrôle de la vitesse du moteur.
Sur un moteur universel, il s'agit d'utiliser un triac pour n'alimenter le moteur que pendant la fin de chaque demi-période du secteur à 50Hz.
Le principe est donc :

  • détecter le passage à zéro du secteur (100 fois par seconde)
  • temporiser (court si on veut augmenter la vitesse, plus long si on veut ralentir). La tempo est inférieure à la durée d'une demi-période (10ms). 0->moteur à 100%, 10ms->moteur à 0%
  • activer le triac jusqu'au prochain passage à zéro.
  • recommencer 100 fois par seconde.

C'est un fonctionnement assez classique et on trouve pas mal d'exemples sur le net. Mon schéma exact est attaché au message.

Mon problème est le suivant :
J'utilise un H11AA1 pour la détection du passage à zéro du secteur, relié à la pin 2 et attaché à l'interruption 0.
Quand l'interruption est levée, il faut lancer la tempo pendant laquelle le triac ne conduira pas. Cela doit être assez précis.
En théorie, on appelle la routine d'interruption 100 fois par seconde. Cela est vrai quand je n'active pas le triac. Par contre, quand celui-ci est activé juste après la tempo, on rentre beaucoup plus souvent dans l'interruption (peut-être 200 fois). Je ne sais pas exactement à quel instant. J'ai essayé de filtrer sans trop de succès. Pour l'instant le plus efficace est la capa directement entre la pin de l'arduino et le 5v mais ça n'est pas terrible. On arrive toujours à 115,120 interuptions par seconde.
J'ai du mal à comprendre la raison du problème et comment le résoudre.
Si vous avez des explications et des solutions, je suis preneur!

Merci à vous,
Eti

Il est fort possible que l'activation du triac génère un glitch sur le secteur qui redéclenche l'optocoupleur.
Le moteur aussi va probablement générer du bruit sur le secteur qui peut redéclencher l'optocoupleur.

Il est courant quand on cherche a se synchroniser sur un évènement périodique d'utiliser une "fenêtre" de (dé)validation qui permet de filtrer les glitches.
L'idée est de désactiver l'interruption entre le moment où elle est détectée et une tempo d'environ 9 ms par exemple. Ainsi tu ne prendras pas de parasite jusqu'au moment où tu ré-autorises l'interruption.
Plus tu peux gérer précisément la durée de la fenêtre, plus tu pourra la caler proche de 10ms.

Au pire, si tu peux peux pas désactiver l'interruption, dans la routine d'interruption elle même tu regarde le temps qui s'est écoulé depuis la dernière interruption "utile". Tant que ce temps est inférieur à 9..9,5ms, tu ignores l'interruption.

Ok donc plutôt pas de solution hard pour filtrer?

Comment reconnaitre une interruption utile d'une interruption parasite?

Une interruption utile se produit 10ms après une précédente interruption utile.

Tu prend la 1ere interruption comme utile puis tu ignore les suivantes pendant 9ms.
Si c'était bien une interruption utile, une nouvelle interruption utile va arriver tout de suite.
Sinon elle arrivera plus tard et tu recommence à ignorer pendant 9ms etc ...
Tu devrait très rapidement te synchroniser.

As tu un oscilloscope ?
Ou une autre arduino que tu peux utiliser en analyseur logique ?

Avec çà tu pourrait regarder les signaux coté Arduino, pas secteur

Non pas d'oscillo ni d'autre arduino.
C'est pour ça que je galère à comprendre

Tu peux aussi améliorer le filtrage hard mais pas comme cela. Il faut mettre la capa entre l'interruption et les masse. Là où elle est, elle ne sert pas a grand chose.
Le problème est que la durée du passage par zéro est faible (environ 0,05 ms) donc le filtrage doit très faible. Donc capa de 4n7, pas plus.
Je suis pas sur que ça aide beaucoup.

Bonjour, j'ai essayé de faire ce que tu m'as conseillé. La détection du passage à zéro déclenche le timer et interdit une nouvelle interruption.
Au bout du timer, on active le triac. On le relâche au bout de 500us avant la fin de la demi-période et on ré-autorise l'interruption.
Malgré cela je trouve encore beaucoup plus de 100 interruptions par seconde! Il doit y avoir une erreur. Voici mon code si qqun a une idée.

#include <TimerOne.h>
#include <MsTimer2.h>

//Const values
#define TRIAC_MOTOR_PIN 12
#define CYCLE_DURATION 1000


int count = 0; //count the number of zero cross


int delay_triac=10000;
int tachy_counter=0;



void setup() {

  
  pinMode(13,OUTPUT);
  pinMode(12,OUTPUT);
  digitalWrite(12,LOW);
  Serial.begin(19200);
  //setup interrupt
  pinMode(2, INPUT);
  pinMode(3, INPUT);

  Timer1.initialize(); // Initialize TimerOne library for the freq we need
  MsTimer2::set(CYCLE_DURATION, cycle); // 
  MsTimer2::start();
  attachInterrupt(0, zeroCrossAC, RISING); //pin2, function to call or RISING/FALLING
  attachInterrupt(1, topTachy, RISING);     //pin3, function to call or RISING/FALLING
}


void loop() {

  delay_triac = (int) (10000*(1-(analogRead(A0)/1024.0)));
  delay(1);
  
}

void zeroCrossAC()
{
  if (delay_triac < 500)
  {
    digitalWrite(12,HIGH);
  }
  else if (delay_triac > 9000)
  {
    digitalWrite(12,LOW);
  }
  
  else
  {
    Timer1.attachInterrupt(timeToFireTriac, delay_triac);
    detachInterrupt(0);
  }
   
  //uncomment the followig to verify that there are not too many interrupts  
  count ++;
;


  
   
}
void timeToFireTriac()
{
  //detachInterrupt(0);
  digitalWrite(12,HIGH);
  Timer1.stop();
  Timer1.setPeriod(10000-delay_triac-500);
  Timer1.restart();
  Timer1.attachInterrupt(releaseGate,10000-delay_triac-500);
}
void releaseGate()
{
  digitalWrite(12,LOW);
  Timer1.stop();
  Timer1.detachInterrupt();
  attachInterrupt(0, zeroCrossAC, RISING);
}



void cycle ()
{
  /*
  Serial.println("--CYCLE--");
  Serial.print("DC=");
  Serial.println(delay_triac, DEC);
  Serial.print("NZ=");
*/
  Serial.println(count, DEC);

  count = 0;
  tachy_counter=0;
}


void topTachy()
{
  //occurs when the tachy sensor value reaches 0
  //twice in a period
  tachy_counter++;
}

En théorie, 2 interruptions successives ne peuvent pas être rapprochées de plus de 500us, soit un maximum de 105 par seconde. Où est l'erreur?

Déjà quelques commentaires sur le code :

  delay_triac = (int) (10000*(1-(analogRead(A0)/1024.0)));

Pas terrble parce que tu forces ton Arduino a faire des calculs en flottants alors que ce n'est pas un processeur flottant.
Ici çà ne dérange peut être pas trop mais c'est une opération qui coûte très cher en temps CPU.
De toute façon tu n'as que 1024 pas possible sur ta lecture analogique.
Donc soit tu utilises map()

delay_triac = map( analogRead(A0), 0, 1023, 0, 10000 );

ou alors une fonction approchée telle que

int ana = analogRead(A0);
delay_triac = 10000 - (9 * ana + (ana >> 1));

Que de l'arithmétique entière.

TU dis qu'avec ce code, tu continue de te prendre plus que 100 interruptions par secondes.
Mais n'est pas uniquement dans le cas où delay_triac est <500 ou >9000 ?
Si delay_triac est à 5000 (quite à le patcher en dur dans le code pour essayer) est-ce que tu te prend toujours plus de 100 ITs par seconde ?
Si oui c'est qu'il y a quelque chose qui ne vas pas dans le reste du code.

Mais je pense que tu n'es pas obligé de détacher ta fonction. Tu peux faire en sorte d'ignorer l'interruption si elle arrive trop tôt:

unsigned long last_zerocross = 0;

void zeroCrossAC()
{
  // s'il s'est écoulé moins de 9 ms depuis la dernière interruption, on ignore
  if ( (millis() - last_zerocross) < 9 )
    return;

  // la c'est une bonne interruption 0X  (zero-crossing :))
  last_zerocross = millis();

  if (delay_triac < 500)
  {
    digitalWrite(12,HIGH);
  }
  else if (delay_triac > 9000)
  {
    digitalWrite(12,LOW);
  }
  else
  {
    Timer1.attachInterrupt(timeToFireTriac, delay_triac);
    // plus besoin de détacher
  }
   
  //uncomment the followig to verify that there are not too many interrupts  
  count ++;
}

Éventuellement, utiliser micros() au lieu de millis() et remplacer 9 par 9500 dans le test.

Je ne comprend as trop ton code Timer1 dans timeToFireTriac()
J'ai l'impression que tu en fait trop.
Mais je n'ai pas d'expérience perso sur Timer1.

Sinon je crois que le triac se désarme tout seul au moment du passage par zéro, donc je pense que tu n'as pas besoin de la fonction releaseGate. Tu devrais pouvoir te contenter de remettre la pin 12 à LOW dans l'interruption de zero-crossing.

Si j'ai un peu de temps, demain j'essaye ta manip. Au moins l'IT de zero crossing sauf que je ne n'ai pas ce type d'optocoupleur à double-sens. Je verrais ce que ca donne avec un opto standard.
J'ai pas d'optotriac non plus donc je n'aurais pas les parasites...

  • OK pour le calcul flottant mais cette partie du code n'était pas faite pour durer
  • bizarrement avec ce code, je continue à prendre + de 100 ITs par seconde. Si je fais un test soft vérification que le temps entre 2 ITs est > 9500, alors ça fonctionne. Je trouve ça moins propre.
  • La détection du passage à zéro ne semble pas très précise et il faut relâcher la gâchette avant la prochaine IT sinon on risque de réenclencher pour la demi période suivante. C'est comme ça que j'explique certains problèmes que j'ai eus.
  • Cool si tu peux essayer!

eti:

  • bizarrement avec ce code, je continue à prendre + de 100 ITs par seconde.

Avec ton dernier code ou avec ma proposition ?

Si je fais un test soft vérification que le temps entre 2 ITs est > 9500, alors ça fonctionne. Je trouve ça moins propre.

C'est à dire avec ma proposition ?
C'est parfaitement propre. C'est du filtrage temporel. C'est ce que j'exprimer depuis le début par la notion de "fenètre de validité".
Même principe que le debouncing sur un bouton poussoir.

J'ai galéré quelques semaines sur les variateurs, à l'époque je m'étais basé sur le schéma que tu as posté et celui la : Electronique - Realisations - Gradateur lumiere 011

Ce qui m'a également posé problème c'était la détection du passage à zero. Au final j'ai pris cette partie de ton schéma, auquel j'ai ajouté un AOP en comparateur pour avoir un pic bien net. Ensuite je trouve ton code beaucoup trop complexe pour le moment, simplifie le, sans interruption, et rajoute les fonctions ensuite en vérifiant que tout marche et que tu n'as pas de problème de performance.

Bonjour,
Après quelques semaines d'inactivité, je reprends mon projet.
Je tiens à vous dire que j'ai réglé le problème d'interruptions intempestives en choisissant en triac dit "snubberless" BTB16 700BW. Avec lui, toujours 100 interruptions "zero cross" par seconde :slight_smile: J'ai toutefois gardé les contrôles dont on a parlé mais je conseille donc l'utilisation de ce type de triac pour la commande d'un moteur.

Je m'attaque maintenant à la partie régulation, asservissement en vitesse. Pour cela je dois récupérer la vitesse réelle du moteur par le tachymètre intégré. Il délivre une tension alternative dont la fréquence est proportionnelle à la vitesse. La tension varie également de quelque volts à une trentaine de volts (à mesurer pour être sûr).
Que me conseillez-vous comme adaptation pour attaquer une entrée arduino ? Je vais me brancher sur la pin 3 et compter les fronts par interruption sur un temps précis pour avoir la vitesse.
J'ai essayé avec le même opto H11AA1 que pour le "zero cross" mais au delà de 1000 tops par seconde on ne détecte plus rien. J'imagine que les temps de montée/descente sont trop longs...

Si vous avez des idées je suis preneur :slight_smile:

Sans information supplémentaire, je suppose que tu mesure la fréquence par lecture directe de la broche de retour (digitalRead()).
Dans ce cas, en fonction de ce que fait ton ATmega par ailleurs, il n'est pas étonnant que tu trouve une limite.

par ailleurs la datasheet du H11AA1 n'est pas très claire sur les délais docn est-il une limite en lui même ?
Pas sur mais ce qui est clair c'est que pour cette mesure tu n'a pas forcément besoin d'un opto-bi-alternance. Donc un 6N136 qui est très rapide peut être une alternative.

Mais quelques questions pour cerner le contexte :

  • Qu'elle est la gamme de fréquence concernée ?
  • Quelle est la vitesse min et la vitesse max que tu veux mesurer ?

Pour mesurer une fréquence, il existe principalement 2 méthodes opposée :

  • mesurer le nombre d'impulsion dans un temps donné : ceci donne une meilleure précision à haute fréquence
  • mesurer la période : ceci donne une meilleure précision à basse fréquence

On peut faire l'un ou l'autre en utilisant un timer de l'ATmega :

1a) Mesurer le nombre d'impulsion dans un temps donné par interruption
Cette méthode ne nécessite pas de dédier un timer à l'opération.
Il suffit d'envoyer le signal à mesurer sur une des broches d'interruption, de créer une routine d'interruption qui va incrémenter un compteur puis de manière régulière, venir lire le compteur. On en déduit la fréquence par nombre d'impulsions entre 2 lectures / durée de comptage

1b) Mesurer nombre d'impulsions en utilisant un timer
Au lieu de compter par une interruption, on peut utiliser un timer en mode compteur.
Il faut le configurer pour une utiliser une broche externe comme horloge au lieu d'utiliser l'horloge interne.
Le compteur va s'incrémenter automatiquement à chaque impulsion externe.
En lisant le registre du compteur à intervalle régulier comme en 1a) on mesure le nombre d'impulsions pour une période donnée

2a) Mesurer la période par interruption
Ici on va utiliser l'impulsion externe pour déclencher une interruption dans laquelle on va lire la valeur du temps (micros())
On en déduit la valeur de la période donc de la fréquence

2b) Mesurer la période par un timer en mode capture
La méthode ci dessus peut être améliorer en précision en utilisant le mode capture d'un timer
Le timer va tourner sur une horloge interne définie par le prescaler
Une entrée externe va déclencher la capture et une interruption de capture
C'est à dire que sur l'impulsion, la valeur du compteur va être recopiée en interne dans le registre du capteur (copie effectuée en hard) et une interruption va être déclenchée
Dans la routine d'interruption, on va alors lire la valeur du registre de capture

Cela est plus précis que la méthode 2a) car la lecture du temps s'effectue en hard sans être perturbée par le temps de monter en interruption.

On peut aussi par exemple utiliser 1a) et 2a) simultanément ce qui permet de choisir le résultat le plus précis suivant la gamme de fréquence où l'on se trouve.

eti:
Je m'attaque maintenant à la partie régulation, asservissement en vitesse. Pour cela je dois récupérer la vitesse réelle du moteur par le tachymètre intégré. Il délivre une tension alternative dont la fréquence est proportionnelle à la vitesse. La tension varie également de quelque volts à une trentaine de volts (à mesurer pour être sûr).
Que me conseillez-vous comme adaptation pour attaquer une entrée arduino ? Je vais me brancher sur la pin 3 et compter les fronts par interruption sur un temps précis pour avoir la vitesse.
J'ai essayé avec le même opto H11AA1 que pour le "zero cross" mais au delà de 1000 tops par seconde on ne détecte plus rien. J'imagine que les temps de montée/descente sont trop longs...

Si vous avez des idées je suis preneur :slight_smile:

bonjour
en dehors de la reponse de barbudor
il y a peut etre une option plus simple et facilement testable pour vérifier la faisabilité
redresser ce qui sort du generateur tachymetrique, conditionner la tension redréssée 0--->xV (x à mesurer) en 0/5V et attaquer une entrée ana de l'arduino ?

Salut,
Merci pour tes explications.

De manière générale, j'essaie d'avoir un cycle principal (100ms) pour mon programme dans lequel je vais traiter toutes les tâches.
Donc je comptais utiliser la méthode 1a). L'interruption sur l'entrée incrémente un compteur. Avec ce compteur, je peux obtenir facilement la vitesse moyenne du moteur sur le cycle. Avec cette méthode, quelle fréquence peut-on espérer mesurer avec l'arduino? Est-ce l'arduino ou l'opto qui limite? J'avais un H11AA1 sous la main mais je peux bien sûr prendre un simple alternance.
J'ai pensé également prendre un AOP 5V en comparateur...

Pour mon application de lave linge, le tambour va tourner autour de 1000 tr/min soit 17 tr/sec. Avec la démultiplication, ça doit faire autour de 270 tr/sec. Je ne connais pas exactement les caractéristiques du tachymètre mais j'ai l'impression que c'est du 1 pour 1 : fréquence de rotation = fréquence du signal. Vous connaissez ce type de moteur?

A moins de 1000Hz, je ne pense pas que l'opto pose un problème.
Par contre avec un opto double alternance tu double la mesure : 100Hz = 200 impulsions !
C'est une avantage à basse fréquence.
Si ca ne monte pas plus que 300Hz, tu est peinard mais par contre il ne faut pas espérer une grande précision de mesure

Pourquoi un AOP ?
Pilote directement la pin de l'Arduino avec une résistances de pull-up et la sortie de l'opto en collecteur-ouvert

L'AOP serait pour remplacer l'opto si c'est lui qui limite la fréquence.
Je ne sais pas si mes calculs sont mauvais mais avec l'opto double alternance (le seul que j'ai sous la main), je sature à 1000 pulse/sec environ. Cela fait donc 500tr/sec.
Je fais des tests moteur à vide mais il semble très loin de sa vitesse maxi!

Je vais essayer en changeant la valeur de la resistance en entrée, et éventuellement avec un autre type d'opto...

De toute façon, il vaut mieux isoler ce signal de l'Arduino, donc un opto me semble indispensable.

Si tu utilise un opto standard mono alternance, il faut penser à mettre une diode (1N400x) en inverse sur la LED de l'opto.
En effet une LED bien qu'étant une diode supporte moins une tension inverse que ses frangines type diodes de redressement.