arduino uno - verification programme compte tour

Salut à tous,

je suis "encore" debutant, j'ai deja poste un premier programme qui compte 8 etats differents pour une rotation de 360°, ceci afin de mesurer des angles, lien ci-dessous :

en parallele je cherche à realiser un compte tour basé sur le meme principe mais je peche sur un truc.
j'ai donc un arbre avec 4 cames, sur un tour l'interrupteur s'actionne donc 4 fois.
pour faire un compte tour sur ce genre d'arbre il faut que je ne compte qu'un seul changement d'etat.
je dois faire comment ? ajouter une condition du type if (... % 4 ==0), oui mais où et comment ?
ou alors il me suffit de ne pas multiplier par 60 à la fin mais par 15 ?

merci d'avance !

volatile byte rpmcount;

unsigned int rpm;

const int BP=2;

unsigned long timeold;

void setup()
{
  Serial.begin(9600);
    pinMode(BP, OUTPUT);
  attachInterrupt(BP, rpm_fun, FALLING);

  rpmcount = 0;
  rpm = 0;
  timeold = 0;
}

void loop()
{
  if (rpmcount >= 50) { 
    //Update RPM every 20 counts, increase this for better RPM resolution,
    //decrease for faster update
    rpm = 30*1000/(millis() - timeold)*rpmcount/75;
    timeold = millis();
    rpmcount = 0;
    Serial.println(rpm,DEC);
  }
}

void rpm_fun()
{
  rpmcount++;
  //Each rotation, this interrupt function is run twice
}

Pas la peine d'ouvrir deux posts, vous avez déjà une discussion en cours sur le même sujet

Bonsoir et merci pour votre réponse

ce post ci traite d'un compte tour, l'autre post d'une mesure d'angle.

ce sont 2 programmes différents que je vais fusionner à terme mais que je traite séparément pour le moment.

si c'est un soucis n'hesitez pas

merci !

hello
je ne voie pas ce que calcule ta formule!!!

fais un double de ton prg et remplaces y la loop par celle ci jointe puis lance le prg.
visu sur le moniteur

 loop()
{
  timeold = millis() -1000;//pour test sans maquette
  rpmcount=24;             //pour test sans maquette
  if (rpmcount >= 0) { 
 
    rpm = 30*1000/(millis() - timeold)*rpmcount/75;
    Serial.print(rpm,DEC);Serial.println(" tr/mn");

  
    rpm = (rpmcount/4)*(60000/(millis() - timeold));
    Serial.print(rpm,DEC);Serial.println(" tr/mn");

    timeold = millis();
    rpmcount = 0;
  }
  while(1);//pour test sans maquette
}

punaise, me suis gouré de programme, c'est celui-ci

sincerment désolé :o

c'est celui-ci que je tente de modifier pour ne prendre en compte qu'une impulsion sur 4 ... et c'est là que je suis bloqué

/**
 * Tachymétre minimaliste avec une carte Arduino
 */

// Bug-fix pour Arduino 1.0.6
#define NOT_AN_INTERRUPT -1

/* constantes pour la broche de mesure */
const byte PIN_SIGNAL = 2;

/* Variables pour la mesure */
volatile unsigned long periode = 0;

/** Fonction d'interruption pour la mesure entre deux fronts */
void tick() {
  static unsigned long previousMicros = 0;
  unsigned long currentMicros = micros();
  
  /* Calcul le temps écoulé depuis le précédent front */
  periode = currentMicros - previousMicros;
  
  /* Met à jour la variable pour la prochaine interruption */
  previousMicros = currentMicros;
}

/** Fonction setup() */
void setup() {
  
  /* Initialise le port série */
  Serial.begin(115200);
  
  /* Met la broche en entrée */  
  pinMode(PIN_SIGNAL, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_SIGNAL), tick, FALLING);  
  /* Permet une première màj de "periode" */
  delay(500);
}

/** Fonction loop() */
void loop() {

  /* Affiche le résultat de la mesure en RPM */
  Serial.println(1000000 / periode * 60);
  delay(1000);
}

bon, du coup avec cette fonction d'interruption " tick", la periode sera toujours = à 0

void tick() {
  static unsigned long previousMicros = 0;
  unsigned long currentMicros = micros();
  
  /* Calcul le temps écoulé depuis le précédent front */
  periode = currentMicros - previousMicros;
  
  /* Met à jour la variable pour la prochaine interruption */
  previousMicros = currentMicros;
}

dfgh:
bon, du coup avec cette fonction d'interruption " tick", la periode sera toujours = à 0

Ben non.. vu que previousMicros est static

hello JML

explique moi stp

void tick() {
static unsigned long previousMicros = 0; //ici, previousMicro est bien déclaré et initialisé à zéro
unsigned long currentMicros = micros();
periode = currentMicros - previousMicros;//ici, on soustrait toujours 0 à CurrentMicros

donc période sera toujours égal à 0.xxx

[url=https://www.arduino.cc/en/Reference/Static]static[/url] veut dire que la variable est conservée entre 2 appels à la fonction (c'est comme une variable globale mais dont la visibilité est locale)

et quand on écrit static unsigned long previousMicros = 0;, l'initialisation n'est effectuée qu'une seule fois.

donc lors du premier appel

previousMicros = 0
currentMicros = le nombre de microSecondes depuis le début du programme
periode = currentMicros - previousMicros = le nombre de microSecondes depuis le début du programme

Donc la première valeur ne veut pas dire grand chose

mais ensuite il y a previousMicros = currentMicros donc on se souvient du nombre de microSecondes depuis le début du programme

la prochaine fois qu'on revient dans l'interruption, periode = currentMicros - previousMicros = le nombre de microSecondes depuis la dernière interruption

Ensuite il faut que periode soit déclarée volatile pour que ça fonctionne bien

merci pour les details !!!

je vais relire tout ça une paire de fois :wink:

une idée pour ne compter qu’une impulsion sur 4 ?

ok, merci JML pour ces précisions

zoilgust:
une idée pour ne compter qu'une impulsion sur 4 ?

en ignorer 3 de suite par exemple ou diviser par 4 le résultat ? :slight_smile: :smiling_imp:

allé, je m'y remet !

merci !!

j'ai modifié mon programme.

j'ai ajouté une condition au debut de la boucle tick appelée par l'interruption, cela semble fonctionner apres verification au chronometre.

Je lis le nombre de signal LOW, est j'incremente la variable compteur.
quand cette variable compteur est divisible par 4 à ce moment là les fonctions suivantes de "comptage de temps" s'accomplissent.

if(digitalRead(PIN_SIGNAL)== LOW) /*lit le type de signal*/
      {
         compteur++; /*incremente à chaque signal LOW*/
         if(compteur % 4 ==0){ /*si le résultat est divisible par 4 on passe à la suite*/

voici le code modifié, est-ce que cela vous semble conforme ?

/**
 * Tachymétre minimaliste avec une carte Arduino
 */
int compteur =0;

// Bug-fix pour Arduino 1.0.6
#define NOT_AN_INTERRUPT -1

/* constantes pour la broche de mesure */
const byte PIN_SIGNAL = 2; /* const met la variable en lecture seule / byte = 8 bytes 0 à 255 */

/* Variables pour la mesure */
volatile unsigned long periode = 0; /*volatile souvent associé à une interrupt  / unsigned = non negatif / long = 4 294 967 295 */

/** Fonction d'interruption pour la mesure entre deux fronts */
void tick() {

if(digitalRead(PIN_SIGNAL)== LOW) /*lit le type de signal*/
      {
         compteur++; /*incremente à chaque signal LOW*/
         if(compteur % 4 ==0){ /*si le resultat est divisible par 4 on passe à la suite*/

  static unsigned long previousMicros = 0; /*static  variable conservée entre 2 appels à la fonction / initialisation qu'une seule fois */
  unsigned long currentMicros = micros(); /*le nombre de microSecondes depuis le début du programme */
  
  /* Calcul le temps écoulé depuis le précédent front */
  periode = currentMicros - previousMicros; /*le nombre de microSecondes depuis la dernière interruption*/
  
  /* Met à jour la variable pour la prochaine interruption */
  previousMicros = currentMicros; /*on se souvient du nombre de microSecondes depuis le début du programme*/}}
}

/** Fonction setup() */
void setup() {
  
  /* Initialise le port série */
  Serial.begin(115200);
  
  /* Met la broche en entrée */  
  pinMode(PIN_SIGNAL, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_SIGNAL), tick, FALLING);  
  /* Permet une première màj de "periode" */
  delay(500);
}

/** Fonction loop() */
void loop() {  
  /* Affiche le résultat de la mesure en RPM */
  Serial.println(1000000 / periode * 60);
  delay(1000);
}

Franchement pourquoi te torturer à faire un modulo avec un test et une exécution conditionnelle alors qu'il suffit de diviser le résultat par 4 à la fin.

je ne suis pas certain de la valeur finale reele, je m'explique.

j'ai bien 4 cames dont je ne dois compter qu'une impulsion, mais ces 4 cames peuvent avoir de l'usure et ne pas donner toute le même temps d'interruption.

c'est la raison pour laquelle j'ai préféré cette solution, mais peut être est-elle trop lourde ou trop peu précise comparé à la division du résultat final ?
je suis trop debutant en prog pour savoir ce qui est mieux :confused:

sinon il me suffirait de faire periode / 4 ?
j'ai essayé est ça ne semble pas fonctionner

et autre question, pourquoi le resultat est "arrondi" ?

j'ai minium 60 trs min, puis 120, puis 180, mais rien entre les 2, j'aurais voulu avoir 42 trs/min par exemple, ou encore 1264 ...
je n'ai pas trouvé comment faire dans le programme

y'a des trucs louches...

  /* Met la broche [color=red]en entrée[/color] */  
  pinMode(PIN_SIGNAL, [color=red]OUTPUT[/color]);

votre interruption est déclenchée sur PIN_SIGNAL FALLING donc quand elle passe à LOW. pourquoi dans tick() tester si la pin est bien à LOW - le digitalRead va prendre bcp plus de temps que l'interruption a mis pour arriver là...

compteur devrait être une variable static de la fonction tick() pas la peine qu'elle soit déclarée globale et plutôt qu'elle grandisse bcp, déclarez un byte et remettez le à zéro quand il arrive à 4. c'est plus rapide de faire des addition sur 1 octet que sur 4 (si vous voulez aller encore plus vite commencez à 4 et diminuez la valeur au lieu de l'augmenter et quand vous arrivez à 0 (faire un test de comparaison à 0 est rapide en assembleur) repassez à 4.)

Vous avez une sorte de période "instantanée"... la dernière lue, c'est pas terrible. l'idéal serait de déclencher les interruptions, attendre x secondes, et regarder le nombre de ticks reçus sur cette durée. ça sera plus précis.

Quand vous calculez 1000000 / periode * 60 pour la fonction print vous devriez faire cela dans une section critique où les interruptions sont suspendues car période peut changer pendant le calcul.

unsigned long rpm;
noInterrupts();
rpm = 60000000ul / periode; // section critique, pour que les octets de périodes ne changent pas pendant le calcul
interrupts();
Serial.print(F("RPM=")); 
Serial.println(rmp);

merci pour votre reponse.

je commence à saisir le sens de ce que vous me dites, j’ai demarré les cours de programmation et les vacances sont passées entre temps ::slight_smile:

je vais approfondir un peu plus pour comprendre tout ça et l’integrer correctement

pour info, l’axe que je fais tourner (et qui comporte les 4 cames) va tourner de 400 trs/min environ à 4000 trs/min maximum je pense, ça va regulierement varier mais il y aura toujours une periode de stabilisation.

les interruptions seront donc de plus en plus courtes, à 4000 trs/min (valeur extreme) l’interruption serait de 0.003s

il faut donc effectivement que le programme ai le temps de compter :confused:

merci encore

3 millisecondes même pour un arduino "de base" c'est presque une éternité ! (À 16 MHz c'est 48000 tick d'horloges soit plusieurs dizaines de milliers d'instructions de base exécutées)

c'est deja une bonne nouvelle !

pour le reste je viens de decouvrir l'existence de ce genre de moteurs avec encodeur integré.
mon axe tournant doit etre entrainé par un moteur, c'est donc le moteur qui pourrait donner l'info des trs/min directement !

https://www.lextronic.fr/P5448-moteur-rducteur-avec-encodeur.html

du coup je dois quand même passer par une arduino je pense
et le programme "risque" d’être plus simple !

merci