Discussion programmation - IR, Pull-up, Pull-down, rebonds et interruptions

Bonjour
Juste une petite présicion que je n'ai pas trouvé claire sur internet :
Que signifie static et volatile ? Même sur Locoduino je n'ai pas trouvé...

Amitiés
Rémi

Il est surtout préconisé sur le web mais ça fonctionne sans, alors je ne sais plus trop :cry:

Rémi,

  • une variable qui est déclarée et utilisée dans une fonction est locale :
    la valeur et la variable disparaissent en fin d’exécution de la fonction. Si tu la déclares static, elle conserve sa valeur malgré la sortie de fonction ;
  • pour volatile, le compilateur va charger la variable depuis la ram et non depuis un registre (M. PERRONIN l'explique bien dans sa vidéo sur les interruptions). Les variables utilisées par les ISR doivent toutes être "volatiles" sinon le compilateur risque de ne pas les traiter correctement ...

Si ça sert à quelque chose.

Mais c'est moins efficace que s'il y avait 100 ohms en série sur l'alim.

Dans la théorie un fil de cuivre est un conducteur parfait.
Dans la réalité le cuivre est un bon conducteur mais il n'est pas parfait.
Si en plus le fil de câblage fait une boucle il y aura de l'inductance.

Même s'il ne fait pas de boucle, un fil rectiligne présente une inductance.

La théorie dit que :
Un fil parfaitement rectiligne, de longueur infinie, isolé dans le vide, présente une inductance de ~10 nH par cm (je passe sur les calculs)

Dans la vie de tous les jours la longueur n'est pas infinie, le fil n'est pas dans le vide, il n'est jamais rectiligne et il est toujours plus ou moins près d'un plan de masse.

10 nH par cm reste une bonne approche.

Il y a donc toujours des impédances dans le fil d'alimentation, elles font pont diviseur avec le condensateur.

La difficulté quand le produit que l'on alimente n'a pas une consommation constante en ampères c'est que si on met une résistance en série, la chute de tension dans cette résistance ne sera pas constante et le Vcc réel sera fluctuant.
D'un autre coté en numérique l'alimentation n'a pas besoin d'être aussi propre qu'en électronique linéaire.

De toute façon même si le filtrage est moins efficace, le court-circuit pour les "signaux qui bougent" est bien réalisé par le condensateur.

L'électronique, comme presque tous les domaines, est une affaire de compromis.
Rien ne pourra être parfait, on fait "au mieux", et le "mieux" dépend du projet.

Comment on sait ce qui sera le mieux ?
C'est la définition de l'expérience : si on analyse et si on comprend pourquoi on s'est planté on devient expérimenté.
Plus on se plante, plus on devient expérimenté (principe Shadock),

Pour les plus jeunes "Les Shadock était une série télévisée, pré 68, du temps où la télé était encore impertinente avant qu'elle ne devienne insipide.

PS : il existe des produit que je qualifierais de "magiques", domage qu'ils soient chers.
Je pense aux BLM (il existe plusieurs références) de MuRata

Ces "choses" présentent deux types d'impédances :

  • en continu elles ont toujours une impédance très faible
  • en signaux qui bougent elles présentent une impédance élevée. Elles passent soient par l'état résistif, mais uniquement pour l'alternatif pas pour le continu, à un état inductif.

Je n'en ai jamais mis sur des produits qui devaient être fabriqués en série (trop cher pour le gain apporté) , par contre les cartes de test destinées aux clients en étaient toujours équipées.

PS2 :
Et bien sûr le condensateur doit être placé le plus près possible de l'utilisation.
Evitez absolument toutes les longueurs synonymes d'inductance.

@68tjs
Merci pour cette excellente réponse.
Quand j'étais gamin Claude Piéplu était la voix des Shadoks par opposition au Gibis :wink:
J'ai bien rigolé avec ça :wink:

Du post #85:

Le sei(); ne sert à rien, les interruptions sont actives par défaut (l'horloge système fonctionne!)

Testé, les écritures ne passent pas par les interruptions. Du coup, c'est normal que cela fonctionne. Conséquence bien connue: quand on écrit à 9600 bauds, cela ralentit le programme

du post#117

Exactement!
sauf que
if ((temps - tempsprecedent) >= 50) { //Gestion des rebonds à 300 millis
c'est 50 ou 300?

Pareil, ici etat est un diviseur par deux: une interruption sur deux a un effet sur la led.
Si on initialise etat à LOW, le premier "change" le passe à HIGH et ne fait rien. Il faut attendre le relâchement. Si on veut la même chose à l'enfoncement, il suffit d'initialiser etat à HIGH.

Mais si, c'est moi que je décide! Une interruption va bloquer loop, mais c'est en principe ce que l'on veut. Mais on peut aussi interrompre une interruption soit par elle même (interruption réentrante), soit par une autre.

Pas tout à fait d'accord. Je dirais qu'une variable déclarée normalement est éphémère. Locale signifie pour moi que sa portée est la fonction. Globale est pour l'intérieur de la fonction et l'extérieur.
Les variables globales sont dans la ram et ont une place fixe et leur contenu se conserve donc. une variable locale classique est dans la pile et est recrée à chaque appel.
Une variable statique est en RAM comme une variable globale, mais n'est accessible que dans la fonction qui l'a déclarée. C'est strictement équivalent à une variable globale mais avec un accès uniquement pour la fonction. Cela permet surtout de ne pas chercher à savoir si le nom est déjà utilisé par ailleurs

Pas d'accord non plus, désolé. Si une variable est utilisée dans une interruption et dans loop, la déclarer volatile obligera loop à recharger vraiment la variable. Si on fait
boolean fin;
fin= false;
while (!fin);
il y a de fortes chances que le compilateur remplace ces lignes par
while(true);
si fin est déclarée volatile, le compilateur suppose que fin peut changer (par exemple par une interruption) et ira chercher la vraie valeur au cas ou elle ait changée.

Si le programme ne comporte qu'une seule variable, le compilateur peut parfaitement ranger cette valeur dans un registre. même si elle est volatile, car alors tout changement affectera ce registre.

Maintenant, si une variable est utilisée que dans une ISR, il n'est pas utile de la déclarer volatile. Cela prend plus d'octets et ralentit l'ISR. On ne doit déclarer volatile que les variables qui sont utilisées à la fois dans le programme normal et dans une interruption qui peut la modifier. Une variable qui n'est que lue dans une fonction d'interruption n'a pas besoin d'être volatile.

pas si le code principal peut la modifier (sinon vous n'êtes pas sûr d'avoir la dernière valeur connue dans l'interruption)

ba non, je ne suis pas d'accord, seul moi décide pour mes programmes :rofl:
et je ne veux pas forcément que l'ISR bloque trop longtemps les autre partis du programme :slight_smile:
Par exemple toi qui fait des librairies pour les autres, il me semble que tu ne veux pas forcément bloquer le programme avec un traitement long dans l'ISR ?

Du coup je rectifie :

Effectivement c'est tout co_ pourquoi n'y ai-je pas pensé :cry:

const int ledPin = 12; // LED 
const int interruptionPin = 3; // pin d'interruption
volatile boolean etat = HIGH;

void setup() {
  Serial.begin(9600);
  pinMode(interruptionPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  attachInterrupt(digitalPinToInterrupt(interruptionPin), fonctionBp, CHANGE); //front descendant car pullup

}

void loop() {}

void fonctionBp () {
  static unsigned long   tempsprecedent; //static pour ne pas perdre la valeur de la dernière transition
  unsigned long temps = millis(); // temps au moment d'entrer dans l'ISR
  if ((temps - tempsprecedent) >= 50 ) { //Gestion des rebonds à 50 millis
    //code qui s'execute lors de l'appuie sur le BP
    etat = !etat; // la led change une fois sur deux
    if (etat == LOW)  digitalWrite(ledPin, !digitalRead(ledPin));
    //tempsprecedent = temps;
  }
  tempsprecedent = temps;
}

ça fonctionne parfaitement et j'ai bien compris comment.

Je n'ai pas regarder, mais à par le CHANGE, ce n'est pas ce que tu avais fait la première fois ?

@vileroi, j'ai oublié de te dire merci
C'est fait maintenant.

Ben j'en ai fait tellement,
Il manque également l'initialisation à HIGH de la variable etat.
Pour CHANGE, @J-M-L m'avait mis sur la voie, j'aurai du comprendre de suite ...

Bonsoir @philippe86220, merci pour votre réponse !
Il y a un truc que je ne comprends pas :

  • Pourquoi ne pas la mettre directement en variable globale ?
  • Quel est l'utilité/application d'une telle variable ?

:warning: Je viens peut-être de dire une grosse bourde... :warning:
Merci pour vos réponses :wink:

Amitiés
Pandaroux007

pour qu'elle reste ''invisible'' par les autres fonctions, mais puisse voir sa valeur évoluer d'un appel à l'autre de la fonction dont elle dépend.

je suis d'accord, on peut dire que c'est une variable ''globale'' au sens que sa durée de vie est celle de la totalité du programme, mais elle reste ''locale'' car accessible uniquement par la fonction qui l'a créée, et donc ''invisible'' ailleurs même si ''permanente'' ... c'est peut-être comme ça qu'il faudrait qualifier les variables ''static'' : ça permettrait peut-être de faire la différence avec ''globale''.

mais en fait, ''static'' ou ''permanente'', c'est pareil, non ?

Rémi,
Je résume :

  • une variable globale c'est à dire déclarée hors bloc existe pendant toute l’exécution du programme à partir de sa déclaration (elle est déclarée avant le setup(). Elle est accessible dans tous les blocs, dans tout le programme ;

  • une variable locale a une durée de vie d'une variable locale à un bloc et à la durée de vie du bloc, elle dure le temps du bloc où elle est déclarée :

void maProcedure()
{ //ouverture du bloc
int a,b;
// instructions
} // fermeture du bloc

a et b apparaissent à l'entrée du bloc et disparaissent à sa sortie.

  • une variable static existe pendant toute la durée du programme dans le bloc où elle a été déclarée

  • Une variable globale peut-être déclarée statique dans ce cas et en simplifiant, elle aura une portée limitée au fichier où elle se trouve (dans le cas où ton programme comporte plusieurs fichiers) sinon on peut la déclarer globale à tous les fichiers avec le mot clé extern, mais ça oublies le pour l'instant.

Exemple d'utilisation d'une variable statique :

void setup() {
 Serial.begin(115200);
 for (int x = 0; x < 10 ; x++) statique();
 
}

void loop() {
  // put your main code here, to run repeatedly:

}

void statique()
{
  static int a;
  Serial.print("variable statique : ");
  Serial.println (a);
  a++;
  
}

en sortie :

variable statique : 0
variable statique : 1
variable statique : 2
variable statique : 3
variable statique : 4
variable statique : 5
variable statique : 6
variable statique : 7
variable statique : 8
variable statique : 9

Exemple d'utilisation d'une variable locale :

void setup() {
 Serial.begin(115200);
 for (int x = 0; x < 10 ; x++) nonStatique();
 
}

void loop() {
  // put your main code here, to run repeatedly:

}

void nonStatique()
{
  int a;
  Serial.print("variable locale  : ");
  Serial.println (a);
  a++;
  
}


en sortie :

variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0
variable locale  : 0

Maintenant concernant ta question :

Une variable statique locale n’est donc en réalité pas véritablement une variable locale. C’est une variable globale déguisée, qui ne disparaît pas à la fin de la fonction dans laquelle on la déclare. Le mot static la confine cependant à la portée de sa fonction, comme une variable locale.

Pour moi quand tu déclares une variable static dans une fonction, elle conserve sa valeur d'un appel à l'autre de la fonction, donc ce n'est pas du tout pareil qu'une variable globale qui la conserve dans tout le programme.
Je pense que la différence est là !
L’intérêt se situe peut-être au niveau de la gestion de la mémoire et de la lisibilité du programme.

PS : A lire également le post #126 de @vileroi et peut-être les précisions de @J-M-L ou d'autres membres du forum ...

Forcément si l'ISR ne la modifie pas, c'est que le code principal va la modifier. Sinon, on y mettrait une constante.

Mais je ne vois pas comment l'ISR pourrait avoir une ancienne copie. Le compilateur pourrait garder un registre qui ne serait pas utilisé par le code principal?


− cela permet de la définir directement dans la fonction qui l'utilise, cela fait un seul bloc autonome.
− cela permet à plusieurs fonctions d'avoir des variables RAM qui s'appellent comme on veut. On peut donc avoir plusieurs fonctions qui utilisent des variables "globales" de même nom, mais chacune des fonction n'utilisera que les siennes
− une variable globale n'est pas protégé, elle peut être modifiée par erreur ailleurs. Par exemple j'ai deux variables pinM (pin marche) et pinMot (pin moteur) et je me trompe de nom;

c'est donc une variable locale mais dont la valeur est sauvegardée d'une itération/appel de la fonction à l'autre.

il faut faire la différence entre variable globale qui est vue/lisible/modifiable de la totalité du programme, variable locale uniquement accessible (vue/lisible/etc...) dans la fonction qui l'a créée mais dont on perd la valeur à la sortie de la fonction, et variable locale static, qui n'est vue que par la fonction qui l'a créée mais dont la valeur est conservée d'un appel à l'autre de cette même fonction.

Entièrement d’accord avec toi @5_cylindres,
D’ailleurs je n’avais pas vu ta réponse juste avant la mienne sinon je n’aurai peut-être pas répondu à la question (quoiqu’il me l’avait posée à moi du coup je me suis senti obligé) :wink:
Bonne soirée

C’est au contraire une excellente question :wink:

en fait il ne faut pas s'attacher à ces noms qui viennent du C car c'est plus étendu maintenant avec la possibilité d'appeler un destructeur sur un objet par exemple ou les namespace, les objets temporaires, les try-blocks, les fonctions lambda et capture, les template etc

il y a deux concepts à maîtriser :

  • la portée d'un objet ➜ lire scope

  • la durée de vie d'un objet ➜ lire lifetime