Convertir la fréquence d'un signal PWM

Bonjour, je m'intéresse depuis peu au monde Arduino, et apprendre une nouvelle langue n'est pas une mince affaire à 58 ans...
Voici mon problème qui pour certains semblera un projet d'une simplicité déconcertante...J'ai récupéré une vieille radio commande professionnelle qui faisait fonctionner une grue mobile hydraulique. les vannes proportionnelles étaient pilotées en PWM pour ouvrir plus ou moins les tiroirs hydrauliques. la fréquence est réglable avec au minimum 200Hz. et la durée de l'impulsion varie entre 1mS et 2mS (j'ai pu faire ce réglage sur la radio commande car d'origine , la durée d'impulsion pouvait varier de 0 à un état haut permanent. Pour faire fonctionner un servomoteur de modélisme, il faudra donc convertir cette consigne de la façon suivante : La fréquence de redondance devra passer de 200Hz à 50Hz, (division par 4) et conserver la durée de l'impulsion d'entrée. J'ai jusqu'à présent réussi à programmer un Arduino pour faire un testeur de servomoteurs en faisant varier la durée de l'impulsion avec un potentiomètre , mais je ne sais pas comment faire ce que je viens de décrire ci dessus. Un grand merci d'avance pour votre précieuse aide.

Bonjour @x-3810 ,
Si j'ai bien compris ce que tu as écrit, tu dois faire un truc du genre:

  • Lire le signal PWM existant : mesurer la durée de l'impulsion entrante (avec pulseIn())
  • Rejouer cette impulsion toutes les 20 ms (= 50 Hz), en générant un nouveau signal.

Par contre, pulseIn() bloque le programme pendant la mesure. Si tu veux un truc plus propre (sans bloquer), il faudra utiliser des interruptions (attachInterrupt()).

Bonjour, axelmaux. oui, il faut calquer le signal rectangulaire entrant qui se répète à l'infini toutes les 5 millisecondes pour le répéter seulement une fois sur 4 (50 périodes au lieu de 200). La durée de l'impulsion en entrée de l'Arduino pourra varier de 1 à 2 millisecondes selon la consigne de mon émetteur radio. Entre deux impulsions à 200Hz, la durée de l'impulsion est censée pouvoir varier, mais comme on ne retiendra qu'une impulsion sur 4 on prendra comme référence de durée n'importe laquelle des impulsions par groupes de 4..On peut imaginer qu'en rafraichissant la consigne 4 fois moins souvent, le temps de réactivité sera moins rapide, mais s'agissant de milliseconde, je doute que l'on s'aperçoive de quelque chose.
Si besoin, je posterais un petit graphique représentant les impulsions en entrée et ce que je souhaite obtenir en sortie. N'oubliez pas que je suis un véritable débutant, je m'informe, j'append, je progresse, mais pour l'instant je vous remercierai de vous adresser à moi comme si j'étais en enfant en classe de maternelle ...Merci encore pour votre aide.

Bonsoir,

La période d'un signal de 200Hz est 5ms, celle d'un signal de 50Hz de 20ms.

Avec un ESP32, vous pouvez générer un PWM de la fréquence qui vous convient (50Hz par exemple) et paramétrer son Duty-Cycle sur un nombre de bits vous convenant (16bits par exemple), donc avoir un Duty-Cycle assez précis.

L'algorithme serait éventuellement, avec votre ESP32,
-Mesurer la largeur d'impulsion du signal entrant de fréquence 200Hz.

-Convertir cette mesure en largeur de Duty-Cycle sur un signal de fréquence 50Hz.

-piloter la sortie PWM 50Hz de l'ESP32 avec la bonne largeur d'impulsion

1ms sur du 50Hz = Duty-Cycle 5%
2ms sur du 50Hz = Duty-Cycle 10%.

Par flemme, j'ai fais générer le code par chatGPT pour la partie PWM

const int pwmPin = 18;  // Choisis un GPIO compatible avec PWM
const int freq = 50;    // 50 Hz
const int pwmChannel = 0;
const int resolution = 16; // Résolution 16 bits (0 - 65535)

void setup() {
  ledcSetup(pwmChannel, freq, resolution);
  ledcAttachPin(pwmPin, pwmChannel);

  // Calcul du duty cycle pour 2ms
  // 2 ms / 20 ms = 10% duty cycle
  // 10% de 65535 = ~6553
  uint32_t dutyCycle = 6553;
  ledcWrite(pwmChannel, dutyCycle);
}

void loop() {
  // Rien ici, signal PWM constant
}

Bonsoir. Je pense qu'une illustration vaudra bien plus que mes explications .
voici ce que je voudrais obtenir, mais que je ne sais pas encore comment m'y prendre ...

Tu utilise un arduino ou un esp32 ?

Utiliser une variable comme "compteur" qui s'incrémente à chaque impulsion et faire une condition qui se déclenche une fois sur 4, cela ne doit pas être trop compliqué...

Bonsoir, jef59 votre réponse est ultrarapide, et surtout ultra pécise !...
Sans vouloir être désagréable ni exiger quoi que ce soit de vous, je préfèrerais partir sur la base d'un simple Arduino UNO que je possède pour mes divers bricolages de débutant.
Il me semble qu'un ESP32 serait surfait pour réaliser ce programme simpliste je pense, mais que je ne suis pas en mesure d'écrire sans votre aide. De plus, je voudrais intégrer le montage sur un CI avec des borniers et pour cela j'ai acheté des ATMEGA328P en boitier DIL . Je viens tout juste de parvenir à charger le Bootloader après avoir mis un temps fou à comprendre pourquoi ça ne marchait pas... Bon week-end à tout le monde

Exemple de code pour ce que j'ai dit dans le post #6

int compteur = 0;

void loop() {
  if (detecterImpulsion()) { // ta fonction pour détecter l'impulsion
    compteur++;
    if (compteur >= 4) { 
      faireAction();
      compteur = 0; // remise à zéro après l'action
    }
  }
}

Ça n'a rien d'agréable ni de désagreable ni d'éxigeant.

C'est votre choix, la solution que je propose marche sur un ESP32, pas de veine.

Il n'y a pas mort d'homme.

Sinon, chatgpt propose un code simple, à tester...

const int inputPin = 2;    // Là où arrive ton signal 200Hz
const int outputPin = 9;   // Là où sortira ton signal 50Hz

unsigned long lastPulseTime = 0;
unsigned long pulseWidth = 1500;  // Valeur par défaut au cas où
int pulseCounter = 0;

void setup() {
  pinMode(inputPin, INPUT);
  pinMode(outputPin, OUTPUT);
}

void loop() {
  // Lire la largeur de l'impulsion
  unsigned long newPulseWidth = pulseIn(inputPin, HIGH, 6000); // timeout après 6ms

  if (newPulseWidth > 800 && newPulseWidth < 2200) { // filtrer les mesures bizarres
    pulseCounter++;

    if (pulseCounter >= 4) { // une fois qu'on a vu 4 impulsions
      pulseWidth = newPulseWidth; // on garde la dernière bonne mesure
      pulseCounter = 0;            // on recommence à compter
    }
  }

  // Envoyer notre propre impulsion toutes les 20 ms
  if (millis() - lastPulseTime >= 20) {
    lastPulseTime = millis();

    digitalWrite(outputPin, HIGH);
    delayMicroseconds(pulseWidth);
    digitalWrite(outputPin, LOW);
  }
}

1 Like

Vous allez bien plus vite que je ne l'imaginais et je ne peux que vous remercier...Après avoir ouvert ce post, je pensais avoir des réponses au mieux lundi prochain, donc j'ai soigneusement rangé tout mon bazar pour faire de la place sur la table ! !
Je vais tout ressortir, mais cela prendra un peu de temps avant que je puisse vous confirmer que ça marche (ou non) en effet, pour essayer le montage, je vais devoir rendre compatibles les signaux qui sortent du récepteur de ma radio commande (12volts) pour les adapter à l'amplitude d'entée de L'arduino (5 volts) ça ne devrait pas être trop compliqué en faisant un pont diviseur avec par exemple un potentiomètre ou des résistances. si le programme proposé par axelmaux est "prêt à l'emploi" je vais faire un simple copié collé dans l'IDE Arduino et le verser dans un ATMEGA. Je vous tiendrai au courant au plus vite .Merci encore.

Axel, il me semble que si tu connais la condition de départ.
-mesurer la largeur d'impulsion du signal 200Hz

Et la condition d'arrivée
-répeter cette largeur d'impulsion sur un signal 50Hz.

Alors tu n'a pas besoin de compter les périodes du signal d'entrée et de mesurer ses largeur d'impulsions.

Tu as juste besoin de mesurer les largeur d'impulsion.

Ça fait une chose +/- complexe en moins à faire

Tu ne crois pas ?

OUPS J'AI RÉPONDU TROP TARD!!

Oui tu as raison. Je cherchais quelque chose de simple et j'ai posté la première idée qui m'est venue.

L'idée me semble trés bonne.

Il faut ensuite trouver celle (aussi simple) qui fait un signal périodique de 50Hz.

Le code, ce n'est pas mon fort!

Si je ne me trompe pas, cette ligne dans le code le fait:

if (millis() - lastPulseTime >= 20) {
    lastPulseTime = millis();

    digitalWrite(outputPin, HIGH);
    delayMicroseconds(pulseWidth);
    digitalWrite(outputPin, LOW);
  }
}


Il y a surement une tolérance de précision "large" à propos de la fréquence 50Hz, l'important est je pense d'être précis sur la largeur d'impulsion, ça devrait le faire effectivement.

Je voyais un truc + complexe genre

-Tu génère en permanence un signal 50Hz avec tone() (signal carré).
-Tu rentre ce signal sur une broche "interruption" (qui génère des interruptions, broches 2 ou 3).
-Tu mesure en permanence la largeur d'impulsion du signal 200Hz.
-A chaque demande d’interruption tu génère un pulse de la longueur voulue avec "pulseln()"

Etc.. comme cela ton signal 50Hz reste de fréquence constante, mais est-ce important qu'elle le soit absolument?

au moins, on connaît déjà la cible, ça va simplifier les choses

c'est à ça que sert le réglage du TOP d'un TIMER

cette broche s'appelle ICPn, elle sert à capturer la valeur d'un TIMER au moment où se produit un événement

grâce à cette broche ICPn et la fonction CAPTURE y associée tu peux faire la différence entre le temps (la valeur du TIMER) des fronts montant et descendant (ou l'inverse)

c'est le but des modes PWM des TIMERs, en configurant la valeur de basculement des sorties dédiées à la PWM lors de l'évolution de la valeur dudit TIMER


je rappelle le postulat de base (je résume surtout pour moi):

ça veut dire qu'on ne la connaît pas réellement à priori

ça, on connaît et on sait reproduire

autrement dit, comme tout le monde l'a déjà compris : mesurer la largeur (durée) de l'impulsion (positive ? je suppose) du signal d'entrée, de fréquence (inconnue ?) > 200Hz et la reproduire sur un signal de sortie de fréquence différente fixe et connue = 50Hz


je réfléchis de mon côté sur l'écriture dans les registres et du code qui va avec avant de revenir ici (et vous laisse en faire autant, si vous le désirez bien sûr) ...

Apparemment la fréquence est connue, il utilisera la radio commande à 200Hz (voir #post 1)

Pour moi qui suis un parfait néophyte, certaines de vos explications me dépassent un peu.
Je vais tout de même tenter d'être le plus précis possible pour vous aiguiller vers une solution la plus simple possible.

Ceci n'est qu'un simple avis ou peut être une piste à exploiter : Je pense qu'il ne faut pas que le programme qui pourrait convenir génère lui même sa base de temps 50 Hertz pour créer des impulsions car il y aurait des problèmes de synchronisation entre les signaux d'entrée, et les signaux de sortie . Il serait difficile d'aller chercher la durée de l'impulsion d'entrée à un instant T et la reporter sur une impulsion dont la fréquence est 4 fois plus faible sans que ces impulsions ne se produisent nécessairement au même instant...Je présume que c'est faisable, et je ne doute aucunement de vos compétences, mais il me semble que c'est un peu tiré par les cheveux. Comme j'ai tenté de l'expliquer plus haut, la fréquence de référence existe déjà, elle est générée par la sortie PWM du récepteur de ma radio commande ,mais elle est 4 fois trop élevée pour convenir à la commande d'un servomoteur de modélisme. Alors pourquoi ne pas s'en servir comme base de tem

ps ? Je suis parvenu à abaisser cette fréquence à 200Hz, mais le logiciel de programmation de la radio ne permet pas d'abaisser cette fréquence en dessous de 200HZ. soit une impulsion toutes les 5 millisecondes. Je pense qu'il faut se servir de cette fréquence qui sera associée à une entrée de l'ATMEGA pour ne retranscrire qu'une impulsion sur 4 sur une sortie. Pour finir, la fréquence de sortie 50HZ n'est pas critique. un servomoteur s'accommode très bien d'une petite erreur ou variation de fréquence sans que cela ait de conséquences d'ailleurs il est tout à fait possible de réaliser un testeur de servos à l'aide d'un NE555 pour ceux qui connaissent ce composant, la fréquence se fixe par un réseau R/C pas du tout précis, nul besoin d'une base de temps à quartz...à l'aide de mon oscilloscope, j'ai comparé physiquement la sortie PWM de ma radio commande, puis la sortie PWM d'un petit contrôleur de servos acheté en chine sur la base d'un NE555 Vous noterez en haut à droite des images la fréquence de sortie de la radio commande puis du module de contrôle...La précision du NE555 n'est pas renversante, et pourtant, ça marche parfaitement.





Pour ce que tu veux faire, je pense aussi qu'on a pas besoin de se compliquer la vie avec des trucs compliqués genre timers et interruptions.