GTC/GTB Default sending

Sur une architecture comme l’ESP32 vous avez 2 processeurs et une mémoire partagée, en un cycle vous pouvez toucher la même case mémoire.

Vous n’êtes pas garanti que l’écriture d’un bit dans une variable en C++ soit atomique. A ma connaissance la norme ne le dit pas. C’est sans doute toujours vrai en pratique avec une variable sur un octet dans une architecture 8 bits mais pas sûr avec une variable sur plusieurs octets (si architecture 8 bits)

La remise à zéro hors zone critique pourrait cependant détruire le flag d’une interruption concomitante qui aurait dû être traitée.

L’avantage de l’interruption c’est que vous enregistrez l’événement instantanément, mais l’inconvénient c’est aussi cela. Ce n’est faut qu’une seule fois, et si vous avez écrasé le flag vous ratez la demande.(qui pourrait être vue par polling de l’état si cet état dure assez longtemps par exemple)

à 1 cycle d'horloge près si j'écrase (comme tu dis) l'indicateur c'est qu'il a été modifié avant la fin de la période demandée par @alinus06640 donc je le considère comme valide.

et arrête de modifier tes messages pendant que je réponds, c'est pénible de revenir sans arrêt en arrière pour essayer de comprendre les réponses que tu modifies en permanence !

Il peut tomber pile au moment où vous faites la remise à zéro du flag, c’est à dire après avoir vérifié que les 3 secondes étaient écoulées et donc il aurait dû être pris en compte…

L’appel à millis() puis la comparaison en unsigned long puis la remise à zéro est loin d’être atomique

C’est bien sûr très théorique et dans l’absolu on peut se dire pas de bol c’est dans l’épaisseur du trait mais c’est possible.

Désolé je ne pensais pas que vous étiez en ligne,

merci de le dire (l'écrire, plutôt)

si, je ne parle pas d'effectuer un masquage mais simplement de lire la valeur, opération qui ne prend qu'un seul cycle : si cette valeur doit changer entre temps je n'y peux rien, elle aura évolué.
même en VHDL sur un FPGA (même le moins rapide) je ne peux pas empêcher une valeur d'être modifiée entre deux cycles d'horloge.

Je n’ai rien contre les interruptions, je dis juste qu’il faut écrire cela "correctement" pour la plateforme choisie et que c’est souvent plus compliqué que de faire du polling pour un débutant et qu’il faut réserver cela aux cas ou c’est nécessaire à mon avis. (Le contexte de cette discussion étant le premier post avec un délai d’une seconde dans l’ISR…)

La lecture du flag sur un octet est atomique mais généralement s’en suit un test sur le flag est des actions puis le raz du flag. Si vous ne protégez pas d’une manière ou d’une autre cette section vous avez théoriquement un risque

On voit souvent dans la loop (bien souvent en ayant aussi oublié le volatile)

if (flag) { // flag venant de l’ISR
  xxx
  xxx
  xxx
  flag = false;
}

Ce type de code vous met à risque de rater une interruption. Alors que Si vous faites

>>>Début de zone critique sur flag<<<
flagCopie = flag;
flag = false;
>>>Fin de zone critique sur flag<<<

if (flagCopie) {
  xxx
  xxx
  xxx
}

Vous n’aurez pas le problème (il peut y en avoir d’autres mais c’est pour une autre discussion)

Et Ce que j’essaye de dire c’est que suivant la plateforme., passer en zone critique n’est pas un concept maîtrisé par les débutants

je ne suis pas d'accord : si j'ai la valeur ''active'' de l'indicateur c'est qu'il a été ''validé'' dans la routine appelée pour le faire.

à partir du moment où il a été levé, je peux prendre en compte sa valeur ''active'' et effectuer les actions qui en dépendent.

quand je le baisse ensuite c'est parce que je l'ai moi-même décidé puisque j'ai exécuté les actions correspondant à la lecture de son ''état actif''.

si, entretemps (en 1 seul cycle ?), il a évolué, alors j'effectue un nouveau traitement ... c'est bien ce que demande @alinus06640 ?

On est d’accord sur la première partie

Là où je suis moins d’accord c’est sur ce point

Si une interruption arrive après les actions mais juste avant que vous le mettiez à inactif alors la loop efface le nouveau flag avant de reboucler et vous avez raté cette interruption.

Une bonne section critique empêche cela

pinailler pour un demi cycle d'horloge, j'appelle ça vouloir avoir le dernier mot : ciao

Rater une interruption ce n’est pas pinailler - mais spec c’est la spec :slight_smile: ici c’est pas important sans doute mais dans d’autres cas les timing est important

Mon point reste plus sur la gestion correcte des interruptions dans le cas général.

@5_cylindres,

Je préfère l’approche perfectionniste de @J-M-L que la vôtre :wink:

Bonne journée

J'ai l'impression que tu prends la "critique pour toi"
De mon point de vue la remarque de @J-M-L est général et ça remarque très juste, il faut de manière générale faire attention lorsque l'on manipule des variables dans des fonctions concurenciel.
Après si tu es sûr que tu ne peut pas tombé dans un cas où tu change la valeur d'une variable au milieu d'un traitement sur cette variable dans une autre partie de ton code, alors il n'y a pas de soucis et je te fais confiance sur le traitement de ton code.
Mais il en reste qu'un débutant ou programmeur occasionnelle, pourrait ce faire avoir.
Et dans ce cas, ce n'est pas toujours simple à debugger, sur tout sans debuggeur.

Après effectivement on peut se poser la question de choisir entre prévoyance et performance.
Étant un programmeur de PC ou gros système, je suis plus du côté de la prévoyance, mais je comprends que cela peut ne pas être ton cas ,:smiley:

J’ai passé l’âge de batailler pour avoir le dernier mot mais je veux défendre l’idée de bonne pratique car c’est comme cela qu’on apprend

Bonjour à tous,

Merci pour tous vos posts je ne pensais pas générer autant d’intérêt ...
J'ai trouvé une autre solution cette fois non bloquante avec la fonction timer.h qui fait le job.
Donc plus d’hérésie dans le code j’espère J-M-L...

/*
     _    _ _                       ____  _     _             _ _ _      
   / \  | (_)_ __  _   _ ___      | __ )(_) __| | ___  _   _(_) | | ___ 
  / _ \ | | | '_ \| | | / __|_____|  _ \| |/ _` |/ _ \| | | | | | |/ _ \
 / ___ \| | | | | | |_| \__ \_____| |_) | | (_| | (_) | |_| | | | |  __/
/_/   \_\_|_|_| |_|\__,_|___/     |____/|_|\__,_|\___/ \__,_|_|_|_|\___|
                                                                               
 Ce sketch génère une impulsion de 3s sur la sortie dès que le contact entre masse et et pin 02 est ouvert
 si le contact 02 se referme il n'y a pas de nouvelle génération d'impulsion.
 L’interruption 0 est configure l'entrée 02 de L'Arduino Méga.
 Utile pour signaler  une alarme une seule  fois  et ne rien faire si la situation redevient normale jusque à la prochaine alarme.
 Le clignotement est la pour prouver que nous ne sommes pas dans une fonction bloquante.
 29-05-2023
*/

#include "Timer.h"
Timer t;
int Relais = 12;
unsigned long timer;
boolean ledState = 0;

 void setup()
{
  pinMode(13, OUTPUT);
  pinMode(Relais, OUTPUT);
  attachInterrupt(0, blink, RISING); // attache l'interruption externe n°0 à la fonction blink
}

void loop()
{
  if (millis() - timer > 500) { //clignotement toutes les 1/2s
      ledState=!ledState; // changer l'état de la LED
      digitalWrite(13, ledState);
      timer = millis();}
  t.update();
}

void blink() // la fonction appelée par l'interruption externe n°0
{
   t.pulseImmediate(Relais,3000,HIGH); // Impulsion immédiate de 3 secondes après ouverture de l'entrée 2.
   }

:wink:

oui la classe Timer avec l'appel de t.update(); effectue la tâche de la petite machine à états qui gère ce que vous demandez de faire.

Il me semble cependant que si une nouvelle interruption arrive pendant la période des 3 secondes, blink est réactivé et vous relancez la periode de 3 secondes non ? donc votre durée d'activation peut être bcp plus longue en fonction de ce qu'il se passe au niveau des interruptions

vous pouvez essayer en appuyant / relâchant le bouton sur la pin 2 pour balancer des fronts

J'ai testé,

La durée d'activation ne change pas...
Quand au blink il est constamment actif car je voulais comparer avec le code incluant des delay().

Effectivement avec delay() il s’arrête lors de l'activation mais ce n'est pas le cas avec cette nouvelle version CQFD...... :slightly_smiling_face:

bien sûr que je la prends exclusivement pour moi : ce n'est pas la 1ère fois de sa part

je ne fais que répondre à la demande initiale, et je me retrouve face à un irréductible qui ne jure que par ses propres convictions (comme souvent)

j'avais décidé d'en rester là mais même si le demandeur s'est trouvé une solution qui lui convient (n'en déplaise à ...) voici la mienne :

EDIT : code corrigé

// je suppose par défaut une carte UNO, donc un ATMega328..
// la compil est effectuée sous Arduino 1.8.0

// IO12 sortie relais sur port B4
#define SortieRelaisON  PORTB|=0b00010000
#define SortieRelaisOFF PORTB&=0b11101111

#define int0_OFF        EICRA&=0b11111110
#define int0_ON         EICRA|=0b00000001
#define int0_RisingEdge EIMSK|=0b00000011

uint32_t debutTempoRelais;
#define duree_ms_TempoRelais 3000UL

void setup()
{
  DDRB  |= 0b00010000;        // = pinMode(sortieRelais=IO12, OUTPUT);
  int0_RisingEdge; int0_ON;   // pas besoin d'expliquer, voir les macros plus haut
}

void loop()
{
  if ((millis()-debutTempoRelais) > duree_ms_TempoRelais)
  {
    SortieRelaisOFF;
    int0_ON;
  }

  // tu mets ensuite ce que tu veux ici

}

ISR(INT0_vect)
{
  SortieRelaisON;
  int0_OFF;
  debutTempoRelais = millis();
}

Il n’y a aucune critique contre vous. Il y a une discussion et des points de vues sur des aspects techniques .
J’exprime le mien, je respecte le votre mais quand je ne suis pas d’accord je le dis,..

Par exemple je ne suis pas d’accord avec cela

Ca peut bugguer dans une cinquantaine de jours (finTempoRelais ferait un rollover alors que millis serait toujours super grand et donc le if de la loop serait déclenché immédiatement)… vous me direz que ce n’est pas grave de rater une tempo de temps en temps sans doute ?

Sinon désactiver les interruptions est un bon moyen de ne pas avoir à gérer la section critique oui.

exact, je n'ai pas géré le débordement ( trop l'habitude d'utiliser des timers et pas millis() )

correction effectuée

1 Like

Bonjour 5_cylindres

Je te remercie pour ton code, je ne connaissais pas cette façon de faire donc j'ai pris le temps de l'adapter pour le MEGA2560.
Ça fonctionne bien.

Pour le fun, je bosse aussi avec du Til Technologies et pour le même résultat il n'y à que ces 3 simples lignes de code :slightly_smiling_face:

;-------- Section Init --------E1==1
;---- Section Combinatoire ---
;--- Section évènementielle ---
EV(E1==0)
S2=PULSE(30)

Sinon merci pour vos messages j'ai beaucoup appris sur les Timers et la programmation en "bits" que je ne connaissais pas.
J'ai galéré aussi sur les ports du Méga mais avec le datasheet ça roule...
Voici donc le code

// IO12 sortie relais sur port B6
#define SortieRelaisON  PORTB|=0b01000000 // Mise à 1 de la sortie 12 du MEGA2560 
#define SortieRelaisOFF PORTB&=0b10111111 // Mise à 0 de la sortie 12 du MEGA2560

#define int0_OFF        EIMSK&=0b11111110
#define int0_ON         EIMSK|=0b00000001
#define int0_RisingEdge EICRA|=0b00000011 //Mode d'interruption en détection de front

/*
00 	Un niveau bas sur l'entrée INTx
01 	Un changement d'état sur l'entrée INTx
10 	Un front descendant sur l'entrée INTx
11 	Un front montant sur l'entrée INTx 
*/
uint32_t finTempoRelais;
#define duree_ms_TempoRelais 3000UL

void setup()
{
  DDRB  |= 0b01000000;        // = pinMode(sortieRelais=12 PORT B, OUTPUT);
  int0_RisingEdge; int0_ON;   // pas besoin d'expliquer, voir les macros plus haut
}

void loop()
{
  if (millis() > finTempoRelais)
  {
    SortieRelaisOFF;
    int0_ON;
  }

  // tu mets ensuite ce que tu veux ici

}

ISR(INT0_vect)
{
  SortieRelaisON;
  int0_OFF;
  finTempoRelais = millis() + duree_ms_TempoRelais;
}