Problème interruption compte-tours

Salut tout le monde,

je suis en train de fabriquer un compte-tours pour ma moto mais j'ai quelques soucis au niveau du code. A chaque fois que détecte une impulsion de la bougie, je crée une interruption. Le programme va mesurer le temps entre 2 impulsions ce qui va me donner après calcul le nombre de rotation par minute (1 impulsion = 2 tours moteur).
Je fais une moyenne sur 200 impulsions. Le problème est que entre chaque impulsion je voudrais faire autre chose, tel que le relevé de température, tension batterie ... Mais si j'essaie d'afficher quelque chose sur l'écran lcd dans mon do-while, je n'obtient pas la bonne valeur, comme si la commande lcd.print était plus prioritaire que l'interruption , ce qui me fait manquer des impulsion à cause de son temps d’exécution. Je ne sais pas si j'ai été très clair. N'hésiter pas à me demander plus de détails :slight_smile:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x3F, 20, 4);

const byte interruptPin = 2;
volatile byte Pulsation = 0;

float NewPulse = 0;
float OldPulse = 0;
float RPM = 0;

const int L1 = 53;
int PulsationCompt = 0;
const int NbPulseCompt = 200;

void setup()
{
  Serial.begin(9600);
  lcd.init(); // initialisation de l'afficheur
  lcd.backlight();
  pinMode(L1, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), pulse, FALLING);
  analogWrite(3, 150);
}

void pulse() {
  Pulsation = 1;
  
}

void loop() {
  do {
    
    //Serial.println("entrée dans le do");
    
//      digitalWrite(L1, HIGH);
//      lcd.setCursor(0,2);
//      lcd.print("Bonjour");
    
  }
  while (Pulsation == 0);
  
  
  if (Pulsation == 1) {


    PulsationCompt++;
    NewPulse = millis();

    if (PulsationCompt == NbPulseCompt) {

      detachInterrupt(interruptPin);

      //Serial.println(NewPulse);
      //Serial.println(OldPulse);

      RPM = (((NewPulse - OldPulse)/NbPulseCompt  ) * 60000) / 2;
      lcd.setCursor(0, 0);
      lcd.print("RPM = ");
      lcd.setCursor (7, 0);
      lcd.print(RPM);
//      Serial.print("RPM = ");
//      Serial.println(RPM);

      OldPulse = NewPulse;
      PulsationCompt = 0;

    }
    Pulsation = 0;
  }

}

Je pense qu'à la fin de ton if (PulsationCompt == NbPulseCompt) tu devrais rattacher la routine d'interruption. Ou carrément ôter la ligne detachInterrupt(interruptPin);

De plus, tu devrais initialiser OldPulse = millis(); à la fin du setup.

J'ai supprimé le detachInterrupt car je pense aussi qu'il ne sert à rien et ça me fait gagné du temps d'exec. Merci pour l'initialisation de OldPulse :wink:

Je viens de faire quelques test, et je trouve un temps d'exec d'un lcd.xxx d'environ 4-5ms ce qui est énorme pour moi.

Ce que je ne comprends pas non plus c'est pourquoi lorsque je fais la moyenne sur 2 impulsions et non pas 500, je trouve 200 000 tours par minute au lieu de 64 000 (j'utilise la pwm 480Hz pour générer mes pulsations)

hello
va voir ICI

Ah merci je vais regarder ça en détail :slight_smile:

Diabolux:
Je viens de faire quelques test, et je trouve un temps d'exec d'un lcd.xxx d'environ 4-5ms ce qui est énorme pour moi.

Et encore tu dois probablement afficher assez peu de caractères, car ce délai n'est pas énorme.
Un LCD à interface I2C ne convient pas du tout à ce montage.

Quand on soulève le capot pour analyser le détail de ce qui passe sur le bus I2C, on se rend compte que :

  1. la communication est ralentie par la faible fréquence du bus I2C (même en la poussant à son max) : le controleur HD44780 intégré au LCD pourrait accepter des signaux beaucoup plus brefs.
  2. pour un caractère à afficher sur le LCD, il y a 12 octets transmis sur le bus I2C...
    Ces deux facteurs cumulés donnent une efficacité déplorable, non perceptible pour la plupart des montages, mais très pénalisante ici.

Au final, avec un bus I2C standard à 100 kHz, l'envoi d'un caractère au LCD prend environ 1 ms, alors que le contrôleur du LCD est capable de réceptionner un caractère toutes les 500 ns (voir chronogrammes)

Ainsi, l'info arrive au LCD 2000 fois plus lentement que ce qu'il serait en mesure de traiter.
Et seulement 500 fois plus lentement si on a pris la précaution de pousser la fréquence du bus I2C à 400 kHz.

Conclusion :
Il faut utiliser un LCD standard (sans PCF8574 qui sert d'interface I2C), branché si possible en mode 8 bits si l'arduino dispose de suffisamment de sorties disponibles (sinon 4 bits mais c'est deux fois plus lent).
Et là tu peux sans problème viser un temps d'affichage divisé par 100.

Sur le comptage des impulsions via une interruption, ton approche est également perfectible.

Là tu as codé

void pulse() {
  Pulsation = 1;
 
}
...
void loop() {
...
    if (Pulsation == 1) {
...

Ton code suppose que la fonction loop() est exécutée plus souvent que la fonction pulse()
Ce qui induit une fragilité : si loop() dure trop longtemps (par exemple pour discuter avec un LCD...), tu vas rater des impulsions.

Normalement, l'objectif d'une interruption est justement de ne rien rater, même quand le programme se trouve par ailleurs en train d'exécuter une portion de code longue.

A mon sens, tu devrais plutôt coder la chose ainsi

void pulse() {
  Pulsation++;
 
}
...
void loop() {
...
    if (Pulsation != 0) {
...

NB : du coup, peut-être qu'un format byte ne suffit pas pour Pulsation, et qu'il faut passer en uint16_t

Je viens de tester la méthode de bricoleau mais je n'ai pas d'amélioration avec l'affichage mais je pense que c'est effectivement mieux de coder ainsi. Je vais maintenant modifier mon afficheur lcd pour pouvoir l'utiliser sans I2C

Je vois que DFGH t'as renvoyé sur mon post :stuck_out_tongue:

Personnellement j'ai un autre usage que la simple lecture, mais voila juste ce dont tu as besoin

//Definition des variables
volatile byte compteur_injections = 0;
byte memo_compteur_injections = 0;
unsigned long premiere_injection = 0;
int injecteur_Pin = 2; // signal de l'injecteur (pour la lecture des rpm)
int rpm = 0;


void setup()
{
  Serial.begin(115200);
  attachInterrupt(0, comptage_injections, RISING);
  premiere_injection = micros();
  pinMode(2, INPUT_PULLUP);
}


void loop()
{
  readRpm();
  Serial.println(rpm);
}


//Fonctions
void readRpm() {
  if (compteur_injections == 1) {
    premiere_injection = micros();
  }
  if (compteur_injections >= 3)
  {
    memo_compteur_injections = compteur_injections;
    compteur_injections = 0;
    rpm = ((60000000 / ((micros() - premiere_injection) / (memo_compteur_injections - 2)))) * 2;
  }
}

void comptage_injections()
{
  compteur_injections++;
}

Si tu veux acquérir un moteur 4 temps, tu peux laisser le code tel quel, et pour l’acquisition du signal en lui même je te conseil de laisser tomber la bougie, si tu es directement sur la bougie c'est une merde noire niveau précision. Si tu as un carbu, et une simple bobine à 2 fils (je parle bien de l'arrivé de la bobine), c'est une masse et un +12. Donc tu fais un petit pont diviseur et hop, la tu seras précis (je répète bien à l'arrivée de la bobine, pas coté anti-parasites :p.

Et à l'inverse, si tu as une injection, ne réfléchis même pas, et choppe direct un injecteur! :stuck_out_tongue: Si tu as plusieurs cylindre, choppe un des câbles de l'injecteur cible, et pas celui qui est commun à tous

Perso je joue avec une Gex 600 K8, et j'ai 8 injecteurs, 4 principaux et 4 de "suralimentation" qui se déclenchent à 8000Tr. Je me suis piqué sur l'injecteur 1 de la rampe principal, et je suis très précis (réellement satisfait grâce à dfgh encore une fois)

Et pour que tu ne sois pas surpris, j'ai environ 250tr d'écart entre ce que me dit le compte-tour d'origine et ce que l'arduino choppe. Ce qui est cohérent, sur mon ancienne SV650 (Suzuki sont les pro pour ça), le compteur de vitesse et le compte-tour sont toujours très optimises :stuck_out_tongue:

Bon courage et tient nous au jus

Ok, je vais voir pour me brancher directement sur le +12V. J'ai une ZR7 donc carbu. Par contre je ne comprends pas dans ton calcul rpm pourquoi tu soustrais 2 à ton memo_compteur_injections. Ca revient à diviser par 1

salut

perso je suis entrain de réaliser un petit alfano pour un karting 2t qui devrai afficher le régime du moteur, la température du moteur ainsi que le nombre de tour de circuit et le temps au tour

il ne me reste plus qu'a intégré le compte tour moteur et il sera fini

si cela t'intéresse : ordinateur de bord karting - Français - Arduino Forum

alexis

Diabolux:
Ok, je vais voir pour me brancher directement sur le +12V. J'ai une ZR7 donc carbu. Par contre je ne comprends pas dans ton calcul rpm pourquoi tu soustrais 2 à ton memo_compteur_injections. Ca revient à diviser par 1

J'avais fait un programme pas ouf et pas précis, dfgh m'a fourni ce que je t'ai passé :stuck_out_tongue:

J'ai juste du multiplier le résultat par 2 et c'est pas dégueux dégueux :hap: Je ne m'étais jamais servi des interruption avant, donc c'est pas ma tasse de thé

Bon petit brélon la ZR7! Choppe un injecteur, fait un pont diviseur un peu plus gros que la juste théorie (regarde mon topic avec la petite diode Zener en plus si tu as l'occasion)

Ok merci je vais regarder ça et le projet du compteur de kart :slight_smile:

du coup lesept a refait en partie mon programme il est donc fonctionnelle a 100%

je l'ai testé avec un GBF qui envoyait des impulsions a 330 Hz (soit 19800 T/Min) et sur le lcd l'arduino affichait exactement 19800 !
(0% d'erreur que demander de mieux)