Gradateur, roues codeuses et gestion UART avec un arduino pro mini

Bonjour,

L'idée est de faire un gradateur à partir d'une carte pro mini et un ensemble optocoupleur / triac.
La finalité de ce gradateur est de remplacer la carte "PIC" du radio réveil NafNaf Lys qui ne marche plus...

1/ la détection du 0v
Un pont de diodes placé derrière le transfo déjà existant du radio réveil, et une chute de tension (avec deux résistances de qq 100k) pour ne pas dépasser les 5v. Ce signal est connecté à la broche 2 de la carte Arduino Pro Mini.

#define LEDrouge  5 // PB5 PORTB ou pin 13
#define zeroCross 2 // PD2 PORTD ou pin 2
 
void setup() {
  DDRB |= 1<< LEDrouge;    // pinMode(13, OUTPUT);
  DDRD &= ~(1<<zeroCross);    // pinMode( 2,  INPUT);
 
  attachInterrupt(digitalPinToInterrupt(zeroCross), detectionZeroVolt, RISING);
}
void detectionZeroVolt() {
  // on bascule la LED rouge
  PORTB ^= 1<< LEDrouge;
}
void loop() {
}

Ce qui donne un signal sur la LED interne rouge de 50Hz

Le test du zeroCross fonctionne, il me faut maintenant une impulsion de 0,5ms, suffisant pour déclencher mon triac.

  • Utilisation du timer2 en mode normal.
  • Division de la fréquence d'origine (16MHz) par le prescaler à 64µs -> soit 250kHz (4µs)
  • multiplication de ces 4µs par 125, ce qui donne 0,5ms
#define LEDrouge 5 // PORTB ou pin 13
#define LEDverte 4 // PORTB ou pin 12
#define zeroCross 2 // PORTD
 
byte cptLEDverte;
bool flag;
 
void setup() {
  // Témoin LED rouge interne
  DDRB |= 1<< LEDrouge;    // pinMode(13, OUTPUT);
  DDRD &= ~(1<<zeroCross); // pinMode( 2,  INPUT);
  attachInterrupt(digitalPinToInterrupt(zeroCross), detectionZeroVolt, RISING);
 
  // Témoin LED verte pour un signal à 20ms
  DDRB |= 1<< LEDverte;    // pinMode(12, OUTPUT);
  // on veut une période de 500µs
  // prescaler 64 -> 250kHz soit 4µs ; 4µs x 125 = 500 µs
  TCCR2A = 0;
  TCCR2B = B00000100; // prescaler 64 CS20:2 = 100, le reste à 0 -> mode normal
  TCNT2 = 256 - 125;  // compteur pour la détection TOV2 dans le registre ITFR2
}
void detectionZeroVolt() {
  // on bascule la LED rouge
  PORTB ^= 1<< LEDrouge;
}
 
void loop() {
  // Test du flag OVF (bit TOV2 du registre TIFR2)
  if (TIFR2 & 1<< TOV2)  {
       TCNT2 = 256 - 125;  // Rechargement du timer à 125
       bitSet(TIFR2, TOV2); // on reactive
       // on bascule la LED verte
       PORTB ^= 1<< LEDverte;
  }
}

Vérification (avec une carte CY7C68013A mini board)

J'ai le zeroCross et mon signal de 0,5ms. Le zeroCross va synchroniser/déclencher le signal du timer2.

Au lieu de détecter dans le loop le débordement du compteur

  • zerocross éteint une LED verte (sur la broche 12) en attendant de mettre l'optocoupleur...
  • activation de l'interruption de débordement (registre TIMSK2, bit TOIE2) dans la fonction zeroCross
  • déclaration de la fonction ISR(TIMER2_OVF_vect) qui allume une LED verte.
#define LEDrouge 5 // PORTB ou pin 13
#define LEDverte 4 // PORTB ou pin 12
#define zeroCross 2 // PORTD
 
void setup() {
  // Témoin LED rouge interne
  DDRB |= 1<< LEDrouge;    // pinMode(13, OUTPUT);
  DDRD &= ~(1<<zeroCross); // pinMode( 2,  INPUT);
 
  // Témoin LED verte pour un signal à 20ms
  DDRB |= 1<< LEDverte;    // pinMode(12, OUTPUT);
 
  // on veut une période de 500µs, prescaler 64 -> 250kHz soit 4µs ; 4µs x 125 = 500 µs
  TCCR2A = 0;
  TCCR2B = B00000100; // prescaler 64 CS20:2 = 100, le reste à 0 -> mode normal
  TCNT2 = 256 - 125;  // compteur pour la détection TOV2 dans le registre ITFR2
 
  // activation interruption sur detect zero cross
  attachInterrupt(digitalPinToInterrupt(zeroCross), detectionZeroVolt, RISING);
}
void detectionZeroVolt() {
  // on bascule la LED rouge
  PORTB ^= 1<< LEDrouge;
  PORTB &= ~(1<< LEDverte);  // on éteint
  TCNT2 = 256 - 125;         // initialise 125 au compteur
  TIMSK2 |= 1<<TOIE2;        // on active l'interruption sur OVF
}
ISR(TIMER2_OVF_vect) {
  PORTB |= 1<< LEDverte;     // on allume
}
void loop() {
}

Vérification

Le signal est bien synchro...

Il faut maintenant pouvoir décaler ce signal pour avoir l'effet gradateur.

J'ai un signal de 0,5µs qui peut donc se balader dans la période de 10ms de mon signal secteur. Cela donne 20 positions possible.

  • zeroCross éteint toujours le LED verte (qui remplace l'optocoupleur)

  • zeroCross déclenche d'OVF du Timer2

  • ISR sur OVF compte une variable jusqu'à une valeur butée maxTempoLEDverte qui peut avoir des valeurs entre 0 et 20.

  • loop() incrémente la valeur butée toutes les 20ms pour avoir un joli effet sur la LED verte.

...
byte maxTempoLEDverte;
 
void setup() {
  ...
  // tempo de retardement de la LED verte
  maxTempoLEDverte = 0;
}
void detectionZeroVolt() {
  // on bascule la LED rouge
  PORTB ^= 1<< LEDrouge;
 
  PORTB &= ~(1<< LEDverte);  // on éteint
  TCNT2 = 256 - 125;         // initialise 125 au compteur
  TIMSK2 |= 1<<TOIE2;        // on active l'interruption sur OVF
  cptLEDverte = 0;
}
 
ISR(TIMER2_OVF_vect) {
  // compteur de retardement de l'allumage
  // 10ms de période et 0,5ms de fréquence du timer2, le compteur va de 0 à 19
  if (cptLEDverte++ >= maxTempoLEDverte) {
    PORTB |= 1<< LEDverte;   // on allume
    cptLEDverte = 0;         // IL FAUT remettre à 0 le compteur
  }
  TCNT2 = 256 - 125;         // initialise 125 au compteur
}
 
void loop() {
  delay(20);
  if (maxTempoLEDverte++ >= 20) {
    maxTempoLEDverte = 0;
  }
}

Vérification

A suivre...

Ce sujet, sauf erreur de ma part, serait plus à sa place dans le sub-board tutoriels.
Sujet déplacé.
Un petit schéma serait le bienvenu :slight_smile:

Quelle logiciel (sous linux) est le mieux adapté pour des schémas avec des cartes Arduino ?
Fritzing ?

Sinon, Kicad, si tu as le temps.

Voici le schéma

avec une petite remarque, c'est LED verte ou MOC mais pas les deux en // évidemment...
une deuxième remarque, l'alimentation (+5V) de la carte passe par un autre pont de diodes sinon on aurait une tension continue... et donc pas de détection zéro cross

Bel effort. Bravo !

Maintenant que j'ai mon signal, je peux le tester sur ma carte de test composée d'un MOC3020 et du triac TBA8-600B.
ATTENTION - présence du 220V -> danger on ne touche pas la carte montage rapide...
20211106_175645

Résultat : la lampe est toujours allumée... Cela signifie que quand le signal secteur passe par 0v, la gachette du triac est toujours activée.

Vue d'ensemble

Solutions

  • décaler le signal d'impulsion
  • réduire la largeur d'impulsion
    ...

Réduire la largeur d'impulsion
Le plus simple, ce sont des impulsions de 500µs, correspondant à la fréquence du timer 2. Le principe :

  • dans le programme principal (loop), on définit la valeur max du compteur OVF timer 2.
  • à la détection du 0v qui a lieu tous les 10ms, déclenchement du timer 2.
  • à chaque interruption OVF du timer 2, on incrémente le compteur OVF jusqu'à la valeur max.
  • si max est atteint, on allume la LED verte (flag à 1)
  • à l'interruption OVF suivante, on éteint la LED verte, on arrête l'interruption OVF.
// Témoin LED rouge interne
  DDRB |= 1<< LEDrouge;    // pinMode(13, OUTPUT);
  DDRD &= ~(1<<zeroCross); // pinMode( 2,  INPUT);
  
  // Témoin LED verte pour un signal à 20ms
  DDRB |= 1<< LEDverte;    // pinMode(12, OUTPUT);
  
  // on veut une période de 500µs
  // prescaler 64 -> 250kHz soit 4µs ; 4µs x 125 = 500 µs
  TCCR2A = 0;
  TCCR2B = B00000100; // prescaler 64 CS20:2 = 100, le reste à 0 -> mode normal
  TCNT2 = 256 - 125;  // compteur pour la détection TOV2 dans le registre ITFR2

  // activation interruption sur detect zero cross
  attachInterrupt(digitalPinToInterrupt(zeroCross), detectionZeroVolt, RISING);

  // l'impulsion sera de 500µs, l'intervalle de déclenchement entre 0 et 10ms-largeur d'impulsion soit 9,5ms
  // le compteur de largeur d'impulsion ira de 0 à 19 (la valeur maxTempoLEDverte)
  // Initialisation du retardement de la LED verte, 
  maxTempoLEDverte = 0;
  // flag signalant qu'on se trouve entre le zero cross et le LED éteinte
  flagLEDverte = LOW;
  // comptage ou decomptage du maximum d'éclairage
}

void detectionZeroVolt() {
  // on bascule la LED rouge
  PORTB ^= 1<< LEDrouge;
  
  PORTB &= ~(1<< LEDverte);  // on éteint
  TCNT2 = 256 - 125;         // initialise 125 au compteur
  TIMSK2 |= 1<<TOIE2;        // on active l'interruption sur OVF
  cptLEDverte = 0;           // Le compteur qui va de 0 à maxTempoLEDverte
  flagLEDverte = LOW;        // eriode entre zeroCross et LED verte éteinte
  
}
ISR(TIMER2_OVF_vect) {
  if (flagLEDverte == LOW)  {
    // compteur de retardement de l'allumage
    if (cptLEDverte++ >= maxTempoLEDverte) {
      PORTB |= 1<< LEDverte;   // on allume
      cptLEDverte = 0;         // IL FAUT remettre à 0 le compteur
      flagLEDverte = HIGH;
    }
    TCNT2 = 256 - 125;         // initialise 125 au compteur (500µs)
  } else {
    // LED verte allumée, il faut l'éteindre après 500µs
    TIMSK2 &= ~(1<<TOIE2);     // on désactive l'interruption sur OVF
    PORTB &= ~(1<< LEDverte);  // on éteint
    cptLEDverte = 0;           // IL FAUT remettre à 0 le compteur
  }
}
 
void loop() {
  delay(10);
  maxTempoLEDverte++;
  if (maxTempoLEDverte > 19)  {
    maxTempoLEDverte = 0;
  }
}

Au niveau signal, ça donne ça :

On observe bien une impulsion dont le retard varie en fonction de la valeur maxTempoLEDverte.

Je rebranche ma carte de montage rapide.
-> j'obtiens bien un dégradé de l'éclairage mais certaines valeurs maxTempoLEDverte "débordent" sur le 0v "réel" secteur suivant.

Signaux zero cross 300
Le Tmax dans le programme vaut le Tmax du schéma - la largeur d'impulsion (500µs)

Mon gradateur fonctionne bien pour des valeurs max qui vont de 0 (pleine lumière) à 14 (lumière éteinte), ce qui me satisfait pleinement.

Plusieurs raisons pour ce décalage du 0v secteur, d'après mes souvenirs lointains d'électronique

  • le transfo 220v/7v crée peut-être une première dérive du signal AC
  • plus la tension de sortie du transfo est faible, plus la tension du pont de diode (1,6v je crois) a aussi son importance quand à la chute de tension autour de 0
  • la détection du passage de 1 à 0 (RISING) se fait à une tension de 2,3v environ (comparée à mes 10vac crête, c'est énorme)

Si on veut être plus précis, il faut une grande tension (idéalement le secteur 220v mais attention danger) et l'optocoupleur H11AA1 mais je n'en ai pas :worried:

Voilà pour ma première partie. Notez qu'il y a peut-être des erreurs, des imprécisions, des coquilles, alors n'hésitez pas.

La première partie étant terminée, je vais m'attaquer aux roues codeuses.
Ce montage va permettre de réparer mes 6 radios réveil Nxxxxx qui sont tous tombés en panne après soit des coupures de secteur, soit des orages...

Ce qu'il y a de ragent, j'aimais bien ces radios réveil, très simple d'utilisation et étonnament bien conçus à l'exception de la carte qui possède un microcontrolleur P89LPC920 "pratiquement" directement sur le secteur...
tout ça pour simplifier la détection du 0 volt pour le gradateur (c'est mon opinion...).
Donc voici mon schéma de principe :

Pour les roues codeuses, je me suis inspiré de nombreux exemples sur la toile. Pour vérifier leurs bons fonctionnements, j'utilise le moniteur série qui m'affiche une valeur numérique pour chaque action sur ces 3 roues (avec 2 sens par roue), soit 6 valeurs.

Je ne présente que la partie loop() qui est la plus intéressante :

// Sens horaire
// A : 0 -> 1 -> 1 -> 0 -> 0 -> 1
// B : 0 -> 0 -> 1 -> 1 -> 0 -> 0
// Sens anti-horaire
// A : 0 -> 0 -> 1 -> 1 -> 0 -> 0
// B : 0 -> 1 -> 1 -> 0 -> 0 -> 1
// on in(de)cremente un compteur par bouton rotatif
// malgré les rebonds, la valeur du compteur positif ou négatif donnera le sens de rotation
void compare(int &cpt, int etatA, int etatB, int sens, int lastA, int lastB)
{
  if (etatA != lastA) {
    if (lastA == 0) {
      if (lastB == 0) { cpt = cpt + sens; }
      else { cpt = cpt - sens; }
    } else {
      if (lastB == 0) { cpt = cpt - sens; }
      else { cpt = cpt + sens; }
    }
  }
}

void traitement(int &cpt, bool &lastA, bool &lastB, int A, int B)
{
  etatA = digitalRead(A);
  etatB = digitalRead(B);

  compare(cpt, etatA, etatB,  PLUS, lastA, lastB);
  compare(cpt, etatB, etatA, MOINS, lastB, lastA);

  lastA = etatA;
  lastB = etatB;
}
void loop() {
  // traitement de VOLA et VOLB, TUNA et TUNB, DIMA et DIMB
  traitement(cptVOL, lastVOLA, lastVOLB, VOLA, VOLB);
  traitement(cptTUN, lastTUNA, lastTUNB, TUNA, TUNB);
  traitement(cptDIM, lastDIMA, lastDIMB, DIMA, DIMB);

  // pause en ms
  delay(10);
}

Dans ce petit bout de programme, j'utilise des pointeurs de variable qui permet de réduire considérablement le source.

Radio réveil NafNaf Roues codeuses Moniteur

C'est presque ça ! une des roues codeuses qui me retourne des 2 et des 6 ne fonctionne pas. Je pense que c'est la nappe de fil qui en est la cause. En tout cas, le programme fonctione, 2 roues réagissent comme il faut !
A suivre...

ça y est, j'ai réparé ma nappe, ça donne ça :

Radio réveil NafNaf Roues codeuses Moniteur-2
Les chiffres 2 et 6 sont maintenant corrects, ils correspondaient à la molette "Tuning" que je tourne dans un sens et dans l'autre.

Je peux maintenant "fusionner" les deux programmes

Bonne nouvelle, la fusion a fonctionné !
La partie intéressante se trouve dans la prise en compte des compteurs qui se fait tous les 0,2", avec une variable niveauLampe qui s'in(dé)crémente pour retarder plus ou moins la commande d'impulsion du triac...

void intAfficheValeur()
{
  if (cptVOL > 0) { Serial.print(VOLP);}
  if (cptVOL < 0) { Serial.print(VOLM);}
  if (cptTUN > 0) { Serial.print(TUNP);}
  if (cptTUN < 0) { Serial.print(TUNM);}
  if (cptDIM > 0) {
    // on augmente l intensité de la lampe (diminue le délai de déclenchement)
    if ( niveauLampe > 0 )  { niveauLampe--; }
  }
  if (cptDIM < 0) {
    if ( niveauLampe < 13 ) { niveauLampe++; }
  }
  cptVOL = 0;
  cptTUN = 0;
  cptDIM = 0;
}

Voici le radio réveil éventré et la carte arduino, le gradateur...

A suivre...

Maintenant , il ne reste plus qu'a trouvé le "protocole" de communication entre l'ancienne carte HS (qui gère lampe et boutons) et la carte Radio-Réveil.

Au passage, voici cette carte ou le pic P89LPC920 a rendu l'âme.

J'ai retourné la carte côté soudure... On voit le composant cms micro contrôleur en bas. C'est le coupable à 99,9% qui était alimenté par le secteur à travers capa et résistance et une diode pour redresser l'alternatif.
En haut, on trouve un connecteur "JST PH 2mm" à 4 broches qui est relié au radio réveil ici
Radio Réveil NafNaf conecteur RxTx

"Rx Tx", ça me plait bien, en espérant que le protocole est facile à décoder... Je branche mon "super oscillo", la petite carte à CY7C68013A et un logiciel pulseview qui simplifie son utilisation.
Un des mini-boutons , "Lamp On/Off" envoie son signal vers la carte Radio Réveil. Donc celui-ci doit faire suivre un ordre à la carte qui comande le gradateur. Je relève le signal sur un changement de niveau (signal D3) avec un échantillonage par défaut 20kHz.


Plusieurs appuis sur le mini bouton

2 signaux différents apparaissent, l'un pour Lampe On l'autre pour Lampe Off.
Ca ressemble à un simple signal de type UART à 7 ou 8 bits qu'il faut trouver... Pulseview sait décoder les UART... J'ai sélectionné le paramétrage vitesse 9600, 8 bits sans parité, le reste a peu d'importance.


Clairement, ce n'est pas ça, la vitesse est plus lente sinon le protocole serait sur de nombreux octets et impossible vu qu'il manque les bits de départ et de stop. Je met une valeur courante 1200 bauds

Frame erreur donc on s'en approche mais ce n'est pas ça. Je me suis dit que c'était peut être pas une valeur courante (modulo 75) et vue le signal, il faut encore ralentir la vitesse à 1000 bauds

ça me parait pas mal, j'ai vérifié le datasheet du microcontroleur, il accepte bien les 1000 bauds. Je tombre un peu par hasard sur l'octet 0x00, mais pour l'autre valeur on a ça.

Voilà, à 90% on peut penser qu'un signal UART à 1000 bauds est correct, seul la parité est incertaine vue que je n'ai que 2 valeur paire, 0x00 et 0x04. <-- FAUX
0x04 n'est pas une valeur paire mais impaire puisqu'il n'y a qu'un seul bit à 1 !!! Le paramétrage est bon, il n'y a pas de parité.

J'espère être à peu près clair :rofl: :rofl:
A suivre...

Bonsoir
Beau travail d'investigation!
Et la démarche est clairement exposée.....