PulseIn et Millis

Bonjour à tous :slight_smile:

Je vous lit régulièrement et maintenant, je me permet de venir vers vous, car je cherche a prolonger l'état bas d'un signal d'entrée PWM qui a pour particularité, d'avoir une période variable.

Pour l'acquisition du signal, j'utilise la fonction pulseIn pour mesurer le temps a l'état haut et le temps à l'état bas, je ramène cela en ms (plus simple pour mon usage) et j'en déduis donc une période, une fréquence et un duty.

(Vous me dites si a un moment je me trompe :slight_smile: )

Pour générer le signal en sortie je pensais à, rallonger mon état haut, et déduire mon état bas en faisant (période - nouveau état haut).

A mes yeux, pour l'instant pas de souci.

Enfin pour l'instant, puisqu'a partir de maintenant il y a un souci ^^.

Je ne peux clairement pas utiliser la fonction delay sinon je ne comptabilise plus mes signaux d'entrée (max 1Khz)

Je comprends théoriquement la fonction (millis), dites moi si je me trompe, mais je compare ça au fait d’être par exemple dans un couloir, avec une montre en main, et d'avoir pour objectif d'allumer toutes les 10sec (exemple) la lumière pendant 3sec, le tout sans rester devant l'interrupteur montre en main. En gros je peux faire ce que je veux (compter les changements d'état sur l'entrée dans mon cas) entre l'allumage et l’extinction, je dois juste régulièrement regarder l'heure pour ne pas rater ce créneau.

Sauf que je n'arrive pas a le mettre en forme :frowning:

Je devrais

Relever le temps actuel,
Changer l'état,
Continuer a compter les entrées,
Regarder si j'ai atteins l'objectif temps, si ce n'est pas le cas, continuer a compter, si j'ai atteins l'ojectif temps, relever le temps actuel et changer d'état. etc.

Enfin je pense l'avoir compris, mais je n'arrive pas a le coder :frowning:

Si vous avez une piste pour moi je serai totalement reconnaissant, ca me rend un peu fou de bloquer la-dessus ^^

Merci.

Bonjour

Si vous avez une piste pour moi je serai totalement reconnaissant

Bien lire le Règle du Forum, découvrir où poser les questions et demander au modérateur (lien Report to Moderator) le déplacement du message au bon endroit.

Sujet déplacé dans la bonne section .....

Si tu veux compter des temps très précis, il existe la fonction micros() qui renvoie le nombre de microsecondes écoulées depuis le début du programme.

Tu veux générer un signal numérique dont la durée de l'état haut est constante et la durée de l'état bas est variable ?

Tu peux utiliser deux fois micros() : une fois pour l'état haut et une fois pour la durée totale (haut plus bas)

const unsigned long dureeHaut = 500; // microsecondes
unsigned long dureeTotale = 2350; // microsecondes aussi
unsigned long chrono ;

void setup() {
Serial.begin (115200);
pinMode (out, OUTPUT) ;
chrono = micros() ; 
} 

void loop () {
if (micros() - chrono < dureeHaut) digitalWrite (out, HIGH) ;
else digitalWrite (out, LOW) ;
if (micros() - chrono > dureeTotale) chrono = micros () ;
}

C'est juste un exemple, il faut ajouter ce qui modifie la durée de l'état bas

Edit : pas vérifié et fait sur mon smartphone donc bien vérifier et tester...

1 Like

Bonjour à tous,

Avant tout désolé, je ne trouvais plus mon sujet, je pensais qu'il avait été supprimé et j'ai enchainé avec des petits souci de santé, mais me revoilà, vraiment désolé.

al1fch promis je ferai attention la prochaine fois :slight_smile:

jfs merci :slight_smile:

Bonjour lesept,

Entre temps je me suis rendu compte (achat d'un oscilloscope) que la documentation que j'avais, été erroné sur la nature du signal que je voulais traiter :confused:

J'ai donc un signal (positif), qui se fait par une mise à la masse. Par exemple je suis à 0V et je passe a 12V par une la fermeture d'un "contact NO" sur la masse.

Ce signal a une période variable et un dutycycle variable.

Actuellement j'en suis au tout "début" (j'ai déjà largement dépassé les 100 heures dessus.... mais je suis coriace ^^) et j'ai du reprendre car le signal n'était pas celui annoncé...

Un exemple du signal de base que j'utilise actuellement, pour dans un premier temps réussir a recopier le signal d'entrée électroniquement, amplitude 0-12V, fréquence 16Hz, créneaux haut 3ms, créneaux bas 53ms.

Je capte ce signal d'entrée (capté par un pin configuré en INPUT_PULLUP) en utilisant la fonction pulseIn sur l'état HIGH. Cela me donne un résultat en µs ce qui est largement suffisant pour moi.

La difficulté étant de recopier ce signal sans créer ni interruptions, ni déphasage.

J'ai pensé a plusieurs solutions dont j'aimerai bien vos avis :slight_smile:

  1. Intercepter le signal puis générer un nouveau signal similaire (puis lui rajouter X% sur le créneau haut dans un 2e temps, le but du projet)

  2. Mesurer la durée du créneau haut du signal sans l'intercepter, puis au moment de la détection de la fin du créneau haut, maintenir ce créneau haut pendant X% du temps mesurer via l’Arduino.

J'ai essayé dans un premier temps avec pulseIn mais j'ai beau essayé, lire, recommencer je n'arrive pas a utiliser la fonction millis()

Du coup un ami m'a donné un coup de main et on a essayé avec la fonction count mais ça ne fonctionne pas correctement, le temps de réponse semble mauvais ce qui crée un déphasage.

Du coup j'en reviens à la fonction pulseIn et du coup pour le moment j'utilise ce simple code

void loop() {

  //noInterrupts();

int ETAT_HAUT = pulseIn(SIGNAL_1, HIGH);

 //interrupts();

//Converti en ms

int CRENEAU_HAUT = ETAT_HAUT/1000;

  if (ETAT_HAUT > 1);
  
  {      
  digitalWrite(13, HIGH);

Je mesure donc les créneaux haut, la valeur est bonne, pour être sur de ne pas créer de déphasage, dès la 1ere µs mesurée à l'état haut je passe ma sortie à l'état haut.

Mais malheureusement je me retouche bloqué sur ma capacité a appliquer une durée non bloquante sur l'état haut de la sortie..... (retardé le temps de passage à LOW équivalent a la variable CRENEAU_HAUT)

J'utilise énormément TINKERCAD comme base et après j'utilise plusieurs arduino pour simuler mon signal de base avant modification et pour traiter le signal, sur TINKERCAD cela semble fonctioner avec la fonction delay() mais dans la réalité non.

Toute aide est la bien venu, merci :slight_smile:

Bonjour Oz91

Relisez :
https://forum.arduino.cc/index.php?topic=394754.0

et postez TOUT le code !!!!!! Si possible compilable.

int ETAT_HAUT = pulseIn(SIGNAL_1, HIGH);

Attention !
pulseIn renvoie un unsigned long :
problème potentiel avec des créneaux trop longs

Cordialement,
bidouilleelec

J'ai toujours du mal à comprendre ce que tu veux faire. Je comprends qu'un signal rectangulaire aléatoire arrive et que tu veux le modifier, mais parfois tu veux modifier la durée du signal HAUT, parfois du signal BAS. Donc, peux-tu préciser ce point ?

Ensuite, attention : un signal à 12V sur un Arduino va le griller : il te faudra un pont diviseur pour le ramener à la tension supportée par ton Arduino (5V ou 3.3V selon le modèle) : quel modèle utilises-tu ?

Voici ce que j'ai compris de ton montage : un signal rectangulaire arrive sur l'Arduino. Il a une période donnée (inconnue mais pas grave) -- appelons la T -- et se décompose en une partie HAUTE de durée X et une partie BASSE de durée T-X. Cette durée X peut varier d'une période à une autre.

Tu voudrais créer un signal qui conserve la période T mais dont la durée HAUTE devient Y (par exemple X + 15%) et la durée basse devient T-Y. Il faut donc s'assurer que Y reste inférieur à T.

Un schéma : est-ce bien ce que tu cherches à faire ?

Je te propose un premier code:

const byte in = 10;
const byte out = 11;
const int pourcent = 15;

void setup() {
  Serial.begin(115200);
  pinMode(in, INPUT);
  pinMode(out, OUTPUT);
  digitalWrite (out, LOW);
  Serial.println("Attente signal haut");
}

void loop() {
  while (!digitalRead(in)) ; // Attente début signal HAUT
  digitalWrite (out, HIGH);
  unsigned long chrono = micros();
  while (digitalRead(in)) ;  // Attente fin signal HAUT
  unsigned long tempsHaut = micros() - chrono;
  unsigned long stop = tempsHaut + tempsHaut * pourcent / 100.0;
  while (micros() - chrono < stop) ;  // Attente fin durée suppl.
  digitalWrite (out, LOW);
  unsigned long dureeHaut = micros() - chrono;
  Serial.print("IN : temps haut =");
  Serial.print(tempsHaut);
  Serial.print("\tOUT : temps haut =");
  Serial.println(dureeHaut);
}

Il peut y avoir deux problèmes :
Les Serial.print vont ralentir le code, je ne sais pas de combien : ils pourraient faire louper des changements d'états
Le calcul du temps d'attente HAUT est à confirmer : ligne

  unsigned long stop = tempsHaut + tempsHaut * pourcent / 100.0;

Il faut connecter l'entrée sur la pin digital 10 et la sortie sur la 11 (ou modifier le code). On augmente la durée du signal haut de 15% (à modifier dans le code aussi).

A tester...

@ lesept J'ai compris la même chose.

@Oz91
Ce serait bien de nous expliquer le but de cette manip car cela nous permettrait de mieux aider et éventuellement proposer une autre solution mieux adaptés: la balle est dans ton camp.

En français on se comprend mieux en parlant de rapport cyclique (RC), le terme anglais n'est pas "duty" mais Duty Cycle (DC), le terme le plus important étant Cycle.

Le changement de rapport cyclique est à la portée de la fonction Wirig/arduino analogWrite(), elle ne fait même que cela.
Le changement de fréquence n'est pas possible avec les fonctions Wiring/arduino.
La PWM "à la mode arduino" est configurée dans une fonction appellée init() qui est ajoutée automatiquement par l'IDE en tête de programme.
Cette fonction init() est consultable dans les répertoires de l'IDE.
Par contre il existe des bibliothèques tierces qui font ce changement de fréquence.

As tu une exigence de rapidité et de propreté du signal PWM ?
Si tu n'en as pas la fonction analogWrite() conviendra. Il faut savoir que la fonction analogWrite() bégaye pendant une petite dizaine de périodes de PWM.
Vu que que nous ne savons pas ce que tu veux faire je préfère le signaler mais dans 99,999% des cas c'est sans conséquence.

Si oui tu as ces exigences il reste :
Les bibliothèques tierce partie que je n'ai pas testé, donc à voir.
Les registres du micro. J'ai expérimenté avec les registres (lire la datasheet du microcontrôleur) et cela fonctionne nickel et instantanément.
Mais pour que le jeu en vaille la chandelle il faut des vraies exigences bien argumentées et non pas "je veux ce qu'il se fait de mieux par principe sans savoir si c'est utile".

@68tjs : il me semble qu'il existe une bibliothèque qui fait les digitalRead & Write plus rapidement. En la cherchant, je suis tombé sur ça (que tu dois connaitre) : ça pourrait être utile.

J'ai utilisé micros() dans le code que je propose, afin d'être plus précis sur les durées qu'en utilisant millis(), mais si les digitalRead & Write prennent 50 périodes d'horloge (3 µs à 16 MHz ?) pour commuter et si ta fonction ne prend que 3 périodes (soit 0.2 µs ?), ça vaudrait le coup de l'utiliser.

Mais seulement quand on aura un cahier des charges précis lui aussi...

@ lesept
Sans plus d'indication sur les fréquences de PWM recherchées je rappelle que les fonction digitalWrite() ou Read() sont lentes (~ 60 cycles horloge) et surtout qu'elles sont étonnamment plus lentes sur les E/S PWM alors que quand on utilise les registres le temps est le même quelque soit l'E/S.

D'un autre coté si on n'utilise pas ces pins PWM_possibles les retards étant fixes leurs effets devraient se transformer en une simple translation dans le temps et devenir invisibles.

On peut essayer de se fixer les idées :
Si le micro est un avr à 16 MHz ce qui n'est pas dit :
60 cycles horloges à 16 MHz font 3,75 µs
La PWM a fréquence de récurrence maximale 16MHz/ 256 = 62,5kHz
Avec le plus petit bit "1" possible soit RC = 1/256 le temps de ce bit sera de 62,5 ns qui correspond à une période horloge à 16 MHz.
Donc gérer un temps de 62,5ns avec des temps de réponse de 3,75 µs va être assez délicat avec les E/S non PWM_capables.
Et strictement impossible avec les E/S PWM_capable puisque ce temps de 3,75 s fluctue.

Bien sur plus la fréquence de récurrence sera basse moins ces effets se feront sentir.

La bibliothèque digitalFast Write ne fait que la moitié du travail : elle supprime tous les contrôles anti-conneries ce qui fait gagner presque 30 cycles horloge mais elle conserve la numérotation Wiring/arduino des pins et c'est la moulinette qui fait la conversion dénomination Arduino vers dénomination Atmel qui prend le plus de temps. Conversion obligatoire car au final digitalWrite ou digitalFastWrite utilisent les registres.

En utilisant les registres directement c'est le programmeur qui fait le travail de conversion sur une feuille de papier annexe et c'est pour cela que c'est plus rapide, par contre c'est moins portable.
On n'y échappera jamais : le beurre ou l'argent du beurre.

La suite :
Pour passer "un IO" à une fonction quelques explication ici :
https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_port_pass.html

68tjs:
@ lesept J'ai compris la même chose.

@Oz91
Ce serait bien de nous expliquer le but de cette manip car cela nous permettrait de mieux aider et éventuellement proposer une autre solution mieux adaptés: la balle est dans ton camp.

Bonjour
+1
ça manque cruellement d'info pertinentes

J'avais fait avec "pulsin" de l'interception de signal "servo" en sortie de recepteur qui consistait soit à faire une simple recopie du signal servo entrant ou sur condition à mettre le servo en position de sécurité.

bidouilleelec:
Bonjour Oz91

postez TOUT le code !!!!!! Si possible compilable.

int ETAT_HAUT = pulseIn(SIGNAL_1, HIGH);

Attention !
pulseIn renvoie un unsigned long :
problème potentiel avec des créneaux trop longs

Cordialement,
bidouilleelec

Bonjour bidouilleelec

J'ai modifié mon post pour le code du coup, je n'ai pas mit le code complet parce-que j'en ai vraiment plusieurs versions différentes avec des méthodes différentes et j'ai peur que ça complique ma façon d'expliquer mon besoin :slight_smile:

Merci pour l'unsigned long je n'ai pas fait attention :slight_smile:

Tu es bien gentil mais tu répond très sélectivement aux questions et on ne sait toujours pas ce que tu veux faire.

J'avais écrit :

@Oz91
Ce serait bien de nous expliquer le but de cette manip car cela nous permettrait de mieux aider et éventuellement proposer une autre solution mieux adaptés: la balle est dans ton camp.

La balle tu ne veux pas la renvoyer, c'est ton droit comme ce sera le mien d'oublier ce sujet.

lesept:
parfois tu veux modifier la durée du signal HAUT, parfois du signal BAS. Donc, peux-tu préciser ce point ?

Ensuite, attention : un signal à 12V sur un Arduino va le griller : il te faudra un pont diviseur pour le ramener à la tension supportée par ton Arduino (5V ou 3.3V selon le modèle) : quel modèle utilises-tu ?

Voici ce que j'ai compris de ton montage : un signal rectangulaire arrive sur l'Arduino. Il a une période donnée (inconnue mais pas grave) -- appelons la T -- et se décompose en une partie HAUTE de durée X et une partie BASSE de durée T-X. Cette durée X peut varier d'une période à une autre.

Tu voudrais créer un signal qui conserve la période T mais dont la durée HAUTE devient Y (par exemple X + 15%) et la durée basse devient T-Y. Il faut donc s'assurer que Y reste inférieur à T.

Un schéma : est-ce bien ce que tu cherches à faire ?

Bonjour lesept :slight_smile:

Dans l'absolu je ne veux modifier que le signal haut, mais je dois aussi conserver la période (variable) ce qui "m'oblige" a raccourcir le signal bas. L'idée était de ne pas imposer de durée a la période basse.

Je pense faire comme "cela":

entrée a HIGH pendant 3ms = sortie a HIGH pendant 3ms avec un passage automatique a LOW a la fin de cette durée. La période est du coup normalement identique. Cela fonctionne sur tinkercad et en réel mais j'ai une latence/déphasage qui ne me permet pas de l'utiliser correctement.

Le signal est une mise a la masse, du coup je n'ai pas de souci avec la tension des 12V, j'utilise un nano ou un uno (j'en ai plusieurs)

Pour la période elle est variable, dans mon exemple elle est de 56 ms (3ms en HIGH + 53ms en LOW)

"Tu voudrais créer un signal qui conserve la période T mais dont la durée HAUTE devient Y (par exemple X + 15%) et la durée basse devient T-Y. Il faut donc s'assurer que Y reste inférieur à T."

C'est exactement cela, mais je suppose que je peux me passer du calcul de la valeur T-Y. j'avais tenté en mesurant les durée haute et basse pour calculer la période et faire comme tu viens de le dire, mais j'avais un décalage entre ce que le port série me remontait comme info et l'oscilloscope.

Oui ton schéma est bon :slight_smile:

68tjs:
Tu es bien gentil mais tu répond très sélectivement aux questions et on ne sait toujours pas ce que tu veux faire.

J'avais écrit :La balle tu ne veux pas la renvoyer, c'est ton droit comme ce sera le mien d'oublier ce sujet.

Je réponds au fur et a mesure pour ne pas me mélanger :wink:

Le temps de lire, de bien formuler mes réponses pour que cela soit clair :slight_smile:

Effectivement je suis pas l'homme le plus rapide du monde ^^

@ Oz91

Oz91:
J'ai modifié mon post pour le code du coup, ...........

1/ Modifié quoi , où ?
Je ne vois rien.

2/ Que signifie "intercepter un signal" ?

Cordialement,
bidouilleelec

Et as-tu testé le code que je t'ai proposé dans ma réponse #7 ?

lesept:
Je te propose un premier code:

A tester...

Merci :slight_smile:

Donc toi tu démarre un chrono au changement d'état et tu le stop au nouveau changement d'état. Tu peux m'expliquer si il y a une différence de résultat ou d'impact sur les performances de l'arduino par rapport a la fonction pulseIn?

Je n'avais pas l'impression qu'utiliser pulseIn était une mauvaise méthode, je bloque sur la façon de générer le signal en sortie sans impacter le comptage :slight_smile:

68tjs:
@ lesept J'ai compris la même chose.

@Oz91
Ce serait bien de nous expliquer le but de cette manip car cela nous permettrait de mieux aider et éventuellement proposer une autre solution mieux adaptés: la balle est dans ton camp.

En français on se comprend mieux en parlant de rapport cyclique (RC), le terme anglais n'est pas "duty" mais Duty Cycle (DC), le terme le plus important étant Cycle.

As tu une exigence de rapidité et de propreté du signal PWM ?
Si tu n'en as pas la fonction analogWrite() conviendra. Il faut savoir que la fonction analogWrite() bégaye pendant une petite dizaine de périodes de PWM.
Vu que que nous ne savons pas ce que tu veux faire je préfère le signaler mais dans 99,999% des cas c'est sans conséquence.

Si oui tu as ces exigences il reste :
Les bibliothèques tierce partie que je n'ai pas testé, donc à voir.
Les registres du micro. J'ai expérimenté avec les registres (lire la datasheet du microcontrôleur) et cela fonctionne nickel et instantanément.
Mais pour que le jeu en vaille la chandelle il faut des vraies exigences bien argumentées et non pas "je veux ce qu'il se fait de mieux par principe sans savoir si c'est utile".

Le but étant de faire un piggyback (boitier additionnel qui modifie les signaux envoyé par le calculateur moteur), les signaux sont carré et dépendent à la fois du temps d'actionnement que de la période défini par le régime moteur variable.

Désolé pour le rapport cyclique, petite fainéantise de ma part, encore désolé.

Oui j'ai besoin d’être aussi rapide et propre que ce que le calculo fait. Dans une 1er temps je voulais déjà intercepter le signal (l'ex 3ms + 53ms) et le reproduire de façon totalement transparente sur le fonction moteur, mais ce n'est pas le cas, je n'arrive pas a produire le signal, j'arrive a le mesurer, mais pas a le produire. Je n'arrive pas a sortir un signal non bloquant, aussi court soit-il avec ses 3ms.

Artouste:
Bonjour
+1
ça manque cruellement d'info pertinentes

J'avais fait avec "pulsin" de l'interception de signal "servo" en sortie de recepteur qui consistait soit à faire une simple recopie du signal servo entrant ou sur condition à mettre le servo en position de sécurité.

Bonjour Artouste :slight_smile:

C'est exactement ce que j'essaie de faire dans un 1er temps, mais je n'arrive pas a générer le signal de sortie sans ralenti la fonction pulseIn vu que je n'arrive pas a utiliser la fonction millis()....

Merci à tous pour votre aide :slight_smile:

lesept:
Et as-tu testé le code que je t'ai proposé dans ma réponse #7 ?

J'ai testé virtuellement et j'ai ca comme résultat qui reviens sur le serial. (correction 1% pour voir le décalage entre l'entrée et la sortie)

IN : temps haut =3132	OUT : temps haut =3220

IN : temps haut =3132	OUT : temps haut =3220

IN : temps haut =3148	OUT : temps haut =3236

IN : temps haut =3132	OUT : temps haut =3224

IN : temps haut =3148	OUT : temps haut =3240

IN : temps haut =3132	OUT : temps haut =3224

IN : temps haut =3148	OUT : temps haut =3240

IN : temps haut =3132	OUT : temps haut =3224

Quel impact sur les performances de mesurer le signal d'entrée et le signal de sortie? C'est insignifiant?

Merci :slight_smile: