Communication serie

Bonjour à tous, je veux créer un dispositif de comptage d’impulsions générées par 2 compteurs (1 impuls = 2Wh) à raison d’environ 1 impuls toutes les 10 secondes.
Dans mon programme, chaque impuls appelle une interruption qui totalise les kWh dans 2 registres “Jour” ou “Nuit” et envoie le contenu des 2 registres au moniteur série.
Or a chaque impuls ce sont 2 lignes qui sont envoyées au moniteur comme s’il y avait eu 2 interruptions.
Et la… je ne comprends plus!
Je joins le programme
Merci de votre aide
gipi69

Essai8.ino (2.08 KB)

postez directement le code dans le forum, il est court... (avec les balises de code, merci de lire les messages épinglés pour comment bien poster).

  • les variables utilisées dans les interruptions doivent être volatiles
  • les interruptions doivent être les plus courtes possibles, ne pas faire de print ou de calculs compliqués dedans. Augmentez simplement un compteur d'impulsions et faites les affichages dans la loop() tous les ∆t
  • avec des impulsions toutes les 10s, pas besoin d'interruptions la loop() doit suffire

Bonjour,
J-M-L a bien résumé ce qu'il ne faut pas faire dans une routine d'interruption.
Ce qui ne provoque un dérèglement dans votre code sont les appels à Serial.print() dans les interruptions.
Ce qui est bien pensé est l'utilisation de l'interruption RISING, mais il faut corriger l'usage.
Voici juste un petit bout de code pour exposer un bon usage de RISING:

int compteur = 0;

void interrupt() {
  compteur += 1;
}

void setup() {
  ...
  attachInterrupt(n, interrupt, RISING);
  ...
}

void loop() {
  ...
  int _compteur;

  interruptDisable();
  _compteur = compteur;
  compteur = 0;
  interruptEnable();

  if (_compteur != 0) {
    Serial.print(_compteur);
    Serial.println(F(" comptages en plus."));
  }
  ...
}

Il y a 2 points à observer dans cet exemple:

  • la variable compteur est incrémentée par la routine d'interruption. il faut donc protéger l'accès à cette variable lorsqu'on veut se référer au contenu dans le code de loop().
  • la lecture et la remise à zéoro de la variable compteur sont "empaquetés" entre 2 instructions disableInterrupt() et enableInterrupt(), pour exclure une interruption pendant l'accès à la variable dans le code de loop().

Bonjour et merci à JML et jmparatte.
Après la réponse de JML j'ai continué mes essais et ai obtenu ce programme qui semble fonctionner et que je vous soumets pour avis.

const int JourNuit=5;//Inter Jour Nuit est sur broche 5 
int etatJourNuit;//Image de l'état du commut Jour Nuit
const int PulsDroit=2;//Impulsion compteur 2 (droite) est sur broche 2
const int PulsGauche=3;//Impulsion compteur 3 (gauche) est sur broche 3
volatile float kWhJour;
volatile float kWhNuit;
volatile int memcompt2=LOW;
volatile int memcompt3=LOW;

//-----------------------------------------------------------------------------------------------------------------
void setup()
{
pinMode(PulsDroit,INPUT);
pinMode(PulsGauche,INPUT);
pinMode(JourNuit,INPUT);

float kWhJour=0;
float kWhNuit=0;

Serial.begin(9600);//Initialisation communication série

attachInterrupt(0,Comptage2,RISING);// Définition  interruptions 0 produite par rising de puls2 pour sp Comptage0
attachInterrupt(1,Comptage3,RISING);// Définition  interruptions 1 produite par rising de puls3 pour sp Comptage1
}

//-------------------------------------------------------------------------------------------------------------------
void loop()
{
  if(memcompt2==HIGH)
  {
Serial.print(kWhJour,3);
Serial.print("kWhJour Droit");
Serial.print("\t");

Serial.print(kWhNuit,3);
Serial.print("kWhNuit Droit");
Serial.println("\t");

memcompt2=LOW;
}

if(memcompt3==HIGH)
  {
Serial.print(kWhJour,3);
Serial.print("kWhJour Gauche");
Serial.print("\t");

Serial.print(kWhNuit,3);
Serial.print("kWhNuit Gauche");
Serial.println("\t");

memcompt3=LOW;
}
}

//------------------------------------------------------------------------------------------------------------------------
void Comptage2()
{
  etatJourNuit=digitalRead(JourNuit);
if(etatJourNuit==HIGH)//Si Jour
{
 kWhJour=(float) kWhJour+0.002;//+0,002kWh au compteur Jour
}
else//Sinon
{
  kWhNuit=(float) kWhNuit+0.002;//+0,002 au compteur Nuit
}

memcompt2=HIGH;
}

//----------------------------------------------------------------------------------------------------------------------------
void Comptage3()
{
etatJourNuit=digitalRead(JourNuit);
if(etatJourNuit==HIGH)//Si Jour
{
  kWhJour=(float) kWhJour+0.002;//+0,002kWh au compteur Jour
}
else//Sinon
{
  kWhNuit=(float) kWhNuit+0.002;//+0,002 au compteur Nuit
}

memcompt3=HIGH;
}

Cependant la réponse de jmparatte me pose problème. je pensais que justement les interruptions permettaient de ne pas louper des impulsions. Or que se passerait il avec votre programme si l'une d'elles arrivait entre détachInterrupt et attachInterrupt?
Encore Merci, vous êtes géniaux
gipi69

que je vous soumets pour avis

utilisez plutôt des booléens (bool) que des entiers (int) pour

volatile int memcompt2 = LOW;
volatile int memcompt3 = LOW;

==>

volatile bool memcompt2 = false;
volatile bool memcompt3 = false;

et vous les passez à vrai quand une mise à jour est effectuée.

Comme l'a expliqué jmparatte, vous ne pouvez pas imprimer kWhJour ou kWhNuit directement dans la loop parce que les octets peuvent changer pendant que print traite l'impression. il faut donc créer une petite zone critique ou vous dupliquez les données

Or que se passerait il avec votre programme si l'une d'elles arrivait entre détachInterrupt et attachInterrupt?

si vous n'en recevez qu'une seule, elle serait mise en attente et traitée dès la sortie de la zone critique.
(petit bug dans la réponse de jmparatte on désactive en utilisant noInterrupts() et on réactive en appelant interrupts())

Bonjour JML et jmparatte.
des précisions SVP
JML: pourquoi du booléen plutot que int?
jmparatte: dans votre exemple vous créez une variable"compteur"
le sous programme d'interruption l'incrémente
par contre dans le loop je trouve un _compteur=compteur que je ne comprends pas.
encore merci
gipi

JML: pourquoi du booléen plutot que int?

parce que vous l'utilisez comme valeur de vérité et que le bon type pour cela c'est booléen
de plus, un bool:

  • n'occupera qu'un seul octet au lieu de deux
  • les opérations (affectation, comparaison) sur vrai/faux sont bcp plus rapides

par contre dans le loop je trouve un _compteur=compteur

compteur est sur plusieurs octets, quand vous allez faire des maths avec compteur, une interruption peut survenir qui va modifier un des octets pendant que les calculs ont commencés et le résultat sera faux. Pour se prémunir de ce désagrément, on ne travaille dans la loop jamais sur la variable de l'ISR mais sur une copie que l'on aura effectuée en section critique, c'est pour cela qu'il crée la copie en désactivant d'abord les interruptions, puis en les réactivant ensuite. Le code exact serait celui là:

noInterrupts(); // début section critique
_compteur = compteur; // on fait une copie
compteur = 0; // on remet à zéro
interrupts(); // fin section critique

comme cela vous travaillez avec la bonne valeur et si la loop est interrompue, compteur a été remis à zéro et va capturer ce qu'il se passe correctement

jmparatte: dans votre exemple vous créez une variable"compteur"

J’ai donné un exemple général pour le traitement d’une interruption. En effet il peut se présenter plusieurs impulsions avant de les traiter dans loop(). Ce n’est certainement pas le cas dans votre application “lente”.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.