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

De la nuance.

Ces résistances servent à fixer un potentiel pour éviter que les perturbations electromagnétiques perturbent le fonctionnement.

Un entrée de porte électronique ou de microordinateur a une très grande impédance, énorme plusieurs millions d'ohms et une petite capacité, oh 1 ou 2 picofarads mais ils sont bien là.

Si on ne fait rien, le moindre fil connecté se transformera en antenne et pourait faire basculer aléatoirement l'entrée du micro.

Ces résistances n'empêchent pas, elles protègent contre.

Comme tu as vus elles sont de valeurs suffisament élevées pour ne pas intervenir sur le fonctionnement.

Imagines que les contacts du boutons poussoir soient completement oxydés et font un équivalent de 500 ohms.
Quand on appuis sur le boutons poussoir il y aura un effet pont diviseur.
Si la pull up faisait 1 k jamais la tension descendrait assez bas pour que le micro voit un état 0 logique.
Alors qu'avec 40 k on a de la marge pour utiliser des boutons poussoirs qui trainent depuis 10 ans au fond d'un tiroir.

Une pull-up ou down ne participe pas au fonctionnement et ne doit pas modifier pas le fonctionnement.

Pour les mêmes raisons d'impédance élevée, il faut toujours placer une résistance de 10k entre la grille et la source d'un MosFet.
Sinon si le Mosfet a son fil de commande de grille en l'air il peut devenir conducteur et s'il est passant et que le fil de commande se coupe le MosFet mettra plusieurs heures ou jours pour se couper. C'est à cause de la capacité grille/source qui elle fait plusieurs centaines de pF.

Ma compréhension c'était que la résistance servait surtout à éviter un court circuit :

Dans l'absolu pour fixer le potentiel on n'aurait pas besoin de résistance, non ? on pourrait connecter directement à 5V et la pin lirait 5V sans soucis.

image

Mais le problème survient quand on ferme l'interrupteur
image

on a un chemin direct entre 5V et GND et si le bouton n'est pas trop pourri (contacts oxydés) ça va faire un joli court circuit

Avec la résistance on limite le courant et dissipe la puissance (U = RI & P=UI ➜ P = U2/R) dans la résistance sous forme de chaleur et on n'a pas de court circuit

mais bon je suis un softeux, donc pas sûr que ma compréhension soit la bonne.

Bonsoir,

J'ai repris mes recherches concernant les interruptions et par hasard je suis tombé sur un excellent tuto d'Eric PERONNIN qui concerne la gestion des rebonds lors de l'appui sur un bouton :

Du coup après l'avoir compris et pour m’entraîner, je me suis amusé à créer une librairie permettant de gérer les rebonds lors de l'appui sur un bouton pour allumer et éteindre une LED. Cette librairie ne présente guère d’intérêt car elle se limite à gérer les rebonds d'un bouton uniquement pour allumer et éteindre une LED (ou plusieurs boutons avec plusieurs Leds). Elle pourra évoluer par la suite mais pour l'instant j'ai tellement à apprendre sur les interruptions et autre que je la laisse de côté, tout au plus elle pourra servir de modèle à ceux qui voudront se lancer dans les librairies mais de part ses limitations, elle ne présente guère d’intérêt.

D'autre part, je rappelle que je suis débutant et ma librairie présente peut-être des imperfections mais la voici :

fichier Bouton.h :

//Selon Eric PERONNIN
#ifndef Bouton_h
#define Bouton_h

class Bouton {
  public:
    // Constructeur
    Bouton(int boutonPin, int ledPin);
    void allumeLed(void);
    void Setup(void);

    // Destructeur
    ~Bouton();

  private:
    int _boutonPin;
    int _ledPin;
    const unsigned long delaiSansRebond = 10000;
};

#endif

fichier Bouton.cpp :

#include "Arduino.h"
#include "Bouton.h"
// Initialisation classe bouton
Bouton::Bouton(int boutonPin, int ledPin)
  : _boutonPin(boutonPin),
  _ledPin(ledPin)
{}

// Destruction
Bouton::~Bouton() {}


void Bouton::Setup()
{
  pinMode(_ledPin, OUTPUT);
  pinMode(_boutonPin, INPUT_PULLUP);  
  
}

void Bouton::allumeLed() 
{
  static boolean dernierBP = HIGH;
  boolean BP = digitalRead(_boutonPin);
  static unsigned long derniereTransition = 0;
  unsigned long _micros;
  // présence d'un front
  if (dernierBP != BP) {
    _micros = micros();
    if ((_micros - derniereTransition) >= delaiSansRebond) {
      if (BP == LOW) { // front descendant
        digitalWrite(_ledPin, !digitalRead(_ledPin)); // changement d'état
      }
    }
    derniereTransition = _micros;
  }
  dernierBP = BP;
}

fichier :

#include "Bouton.h"

Bouton monBouton(2,12);


void setup() {
 monBouton.Setup();
}

void loop() {
 monBouton.allumeLed();
}

L'utilisation est simple on crée une instance de la classe Bouton où le premier paramètre concerne la pin du BP et le second celle de la led. La pin du bouton doit avoir une résistance interne de pullup car la librairie la gère comme telle dans sa fonction setup().
Dans la loop() avec la fonction allumeLed() le code de M. PERONNIN est appelé en permanence.

Bonne soirée.

Bonjour

C'est le cas limite où la résistance de pull-up vaut 0 Ohm avec les inconvénients d'une aussi faible valeur :wink:

hello
image
je n'avais jamais fais attention à cette possibilté...

j'ai donc voulu voir, et effectivement, cette condition d'interruption (ICS11 et ICS10) à LOW déclenche une interruption dès que le niveau de l'entrée concernée passe à LOW.
la routine d'interruption s'exécute en boucle tant que le niveau de l'entrée concernée est maintenu à LOW.
si la routine d'interruption est faite dans ce sens, on pourrait en faire une "loop" et choisir entre deux programmes selon la position d'un interrupteur raccordé sur l'entrée interruptible.
voici le petit programme avec lequel j'ai joué.
il faut le mettre dans une uno, ouvrir le moniteur à 115200 et lire ce qui s'affiche.
puis avec un fil ramener le GND sur D2

void setup() {
Serial.begin(115200);
DDRD   = 0b00000101;
PORTD  = 0b00000100;
EICRA  = 0b00000000;// inter si LOW sur D2
EIMSK  = 0b00000001;
sei();
}

ISR (INT0_vect)
{Serial.println("interruption car LOW sur D2");}
void loop() {
  {Serial.println("loop normale");}
}

Si on veut deux loop, il vaut mieux faire:

void loop1()
{
  // Premier programme
}

void loop2()
{
  // Deuxième programme
}


void loop()
{
  if (digitalRead(D2) == LOW) loop1();
 else loop2();
}

La méthode par INT1 fonctionne aussi, mais:
~ comme on est sous interruption, à chaque itération, il faut sauvegarder le contexte et le restituer. Ce qui fait une boucle plus longue.
~ on ne peut pas facilement utiliser l'horloge interne, ce qui exclu delay(), millis() et micros() (l'interruption du timer est moins prioritaire que INT1)

hello
bien sur, je ne peux qu'etre d'accord, c'était juste por essayer de trouver une raison ou utilité à cette option pour INT0 et INT1.

Bonsoir @dfgh, @vileroi
Les deux méthodes sont intéressantes à connaître.
Qu’est-ce qu’on pourrait faire avec plusieurs boucles ?
A première vue, je ne trouve pas d’applications ?

Un genre de machine à état avec des boucles différentes ?

Si condition1 on exécute tel programme sinon tel autre …

Ou

Pourquoi pas…

hello Philippe
pour l'instant, je ne vois pas de raison d'avoir cette 4eme option pour INT0 et INT 1
peut etre qu'à la conception, les ingénieurs ont voulu utiliser les 4 possibilités offertes par les différents états /combinaisons des 2 bits ISC11_ISC10 ou ISC1_ISC0......

attention : ta pin2 est en sortie ! (ainsi qu'RX)

Le programme du post#85 a-t-il été testé? J'ai un doute sur l'écriture qui doit utiliser une interruption. On ne devrait pas pouvoir écrire dans l'INT1, car l'interruption qui commande l'envoi du caractère suivant est moins prioritaire que l'INT1 et ne devrai pas être appelée tant que D est bas.

Cela me fait penser à une fonction d'arrêt d'urgence. Un état bas provoque un blocage du micro en restant coincé dans la fonction d'interruption. Même mal écrit on ne peut pas retourner au programme principal tant que l'arrêt d'urgencC'est peut être une raison qui fait que cette interruption est la plus prioritaire de toutes (hormis RESET qui n'est pas vraiment une interruption).

Une boucle fait clignoter une lampe, l'autre l'éteint. J'ai ça sur ma bagnole, c'est mon clignotant.

On peut avoir un cycle de production différent, domotique présent / absent...

Effectivement une machine à état si les états dépendent d'un bouton.

Testé tel quel, il fonctionne.

Bonne journée

:scream: :scream: :scream:

oui, et j'ai donné le prg et les conditions pour exécuter le test . d'ailleurs, je t'invite à réaliser ce test.

Bonjour @dfgh,
ça fait longtemps que j'ai pas touché aux registres mais pourquoi tu mets D2 en OUTPUT ?
Je l'aurai plus vu en INPUT :
DDRD = 0b00000001;
Ceci dit ça fonctionne et D2 est positionné à HIGH au départ :
PORTD = 0b00000100;

EDIT 1 fois
PS : pas la peine de tirer un câble de GND sur D2, il suffit d'inverser D2 dans PORTD à LOW - ce qui revient au même.

Oui et cela correspond bien a ma définition des résistances de tirage :

  • elles n’interviennent pas dans le fonctionnement, c’est a dire que si elle ne sont pas présentes et hors perturbation extérieure, le circuit fonctionnera parfaitement.
  • elles protègent des perturbations extérieures en fixant un potentiel qui autrement aurait été flotant.
  • elles ont une valeur adaptée pour ne pas perturber le fonctionnement.

Dans le cas d’une liaison directe au Vcc que tu cites, la resistance de tirage fait 0 ohm et perturbe le fonctionnement. Quoi qu’on utilise comme ”bouton” , même s’il n’y a pas de court-circuit, le potentiel restera bloqué sur Vcc.

Du temps de l’électronique numérique les entrées non utilisées des portes NAND, NOR, etc, étaient reliées directement a GND ou Vcc parce que les entrées étaient a haute impedance. Une liaison directe n’etait pas perturbante.

Une résistance de tirage c’est une ” rustine intelligente ”.
Quand on place une résistance de tirage, il n’y a pas de valeur universelle, même si ce qu’on voit ici le laisse a penser et que les marges sont énormes. Il faut quand même se poser la question : avec la valeur que je mets, je perturbe ou pas?

j'aime bien cette définition poétique :slight_smile:

hello
oui tu as raison, alors j'ai inversé
et ça fonctionne également . c'est à n'y rien comprendre.
il y a un mystère...
j'ai testé:

void setup() {
  
Serial.begin(115200);
pinMode(inter,INPUT_PULLUP);// put your setup code here, to run once:
//DDRD   = 0b00000101;
Serial.print("DDRD = ");Serial.println(DDRD); //0=entree,1=sortie
//PORTD  = 0b00000100;
Serial.print("PORTD = ");Serial.println(PORTD);//1=pullup
Serial.print("PIND = ");Serial.println(PIND);
while(1){;}

le moniteur m'affiche
image

avec un Serial.begin, j'attendais au moins TX en sortie et RX en entrée
soit 0b00000010

alors pourquoi DDRD m'affiche : 0

Oui, une très grande marge sur la valeur de R. Un peu moins grande (mais grande quand même) sur la valeur de RC quand il y a un bouton mécanique sur le pin.

l'UART est gérée avec les bits RXEN0 et TXEN0. Pour activer le Rx et Tx vous faites

    UCSR0B |= (1 << RXEN0) | (1 << TXEN0);  // on active aussi généralement RXCIE0

c'est géré séparément du pinMode

void printlnByte(const byte b, const char char4zero = '0', const char char4one = '1') {
  for (int i = 7; i >= 0; i--) Serial.write(bitRead(b, i) == 0 ? char4zero : char4one);
  Serial.println();
}
void setup() {
  volatile byte UCSR0B_avantBegin = UCSR0B;
  volatile byte DDRD_avantBegin = DDRD;

  Serial.begin(115200);
  volatile byte UCSR0B_apresBegin = UCSR0B;
  volatile byte DDRD_apresBegin = DDRD;

  Serial.print("REGISTRE USART AVANT  = "); printlnByte(UCSR0B_avantBegin);
  Serial.print("REGISTRE USART APRES  = "); printlnByte(UCSR0B_apresBegin);

  Serial.print("REGISTRE DDRD AVANT  = "); printlnByte(DDRD_avantBegin);
  Serial.print("REGISTRE DDRD APRES  = "); printlnByte(DDRD_apresBegin);
}

void loop() {}

REGISTRE USART AVANT  = 00000000
REGISTRE USART APRES  = 10011000
REGISTRE DDRD AVANT  = 00000000
REGISTRE DDRD APRES  = 00000000