Bonjour,
Voici le lien qui pointe vers la librairie en question :
Voici les explications concernant mon approche :
Objectif : Comprendre la librairie de @bricoleau et ne porter un regard que sur la gestion d'un simple clic sur des boutons-poussoirs et leurs rebonds. Le montage des boutons est en INPUT_PULLUP et la gestion se fait à l’aide d’un octet :
- le bit 7 ne sert à rien (c'est une modification de ma part);
- le bit 6 permet d’indiquer l’état du dernier digitalRead() ;
- les bits 0 à 5 permettent de mémoriser les numéros de pin (jusqu’à 64, c’est pas mal !).
Dans le header on trouve la classe simpleBouton de bricoleau sous une forme minimaliste :
- un constructeur qui prend en paramètre le numéro de pin et le délai anti-rebond (implémenté dans le CPP bien sûr) ;
simpleBouton(uint8_t pin, uint8_t delai_debounce_ms = 20);
- Les méthodes actualiser() et estEnfonce() toutes deux implémentées dans le CPP ;
bool actualiser(); //retourne true si l'état du bouton a changé
//Informations de statut
bool estEnfonce() const;
- la signature d'une méthode permettant de récupérer le numéro de la pin de l'instance en cours
uint8_t pin() const;
- Une méthode qui renvoie un booléen et en particulier TRUE si les méthodes actualiser() et estEnfonce() sont vraies ! (seule méthode implémentée directement dans le .h)
operator bool() {return actualiser() && estEnfonce();}
- Les attributs _statut (c’est l’octet utilisé avec les bits comme décrits dans l’objectif plus haut), _delai_debounce_ms (délai anti-rebond) et _millis_etat_actuel (participe au calcul du délai anti-rebond).
uint8_t _statut, _delai_debounce_ms;
uint32_t _millis_etat_actuel;
Voici le header dans sa totalité :
//Librairie Arduino pour la gestion de boutons poussoirs
//Version 4.2
//
//Cablage : pin---BP---GND
//
//Bricoleau 2020
#ifndef simpleBouton_h
#define simpleBouton_h
#include <Arduino.h>
class simpleBouton
{
public :
//Constructeur
simpleBouton(uint8_t pin, uint8_t delai_debounce_ms = 20); // delai anti-rebond par défaut à 20 millisecondes
//Lecture hardware et mise à jour des variables internes
//Idéalement, doit être appelée à chaque début de loop()
bool actualiser(); //retourne true si l'état du bouton a changé
//Informations de statut
bool estEnfonce() const;
uint8_t pin() const;
//Et pour un usage minimaliste :
operator bool() {return actualiser() && estEnfonce();} // si les deux conditions sont remplies alors le bouton est enfoncé avec le délai par défaut ou defini par l'utilisateur
private :
uint8_t _statut, _delai_debounce_ms;
uint32_t _millis_etat_actuel;
};
#endif
Le CPP implémente bien sûr le constructeur et les méthodes du header :
- Dans le constructeur tout d’abord, bricoleau construit l’octet utilisé pour une instance de la classe et l’affecte à l’attribut _statut. Dans un premier temps le numéro de la pin (bits 0 à 5) est inclu puis on ajoute son état à HIGH (bouton relâché aux fins d’initialisation (bit 6)). Ensuite l’attribut _delai_debounce_ms prend la valeur par défaut du constructeur ou celle fixée par l’utilisateur. Puis l’attribut _millis_etat_actuel est initialisé avec millis(). Enfin il initialise la pin choisie par l'utilisateur en INPUT_PULLUP.
//Constructeur
simpleBouton::simpleBouton(uint8_t pin, uint8_t delai_debounce_ms)
{
//Initialisation des variables internes
pin &= masque_pin_simpleBouton; // permet de fixer le nmr de pin en binaire ex : pin 7 = 0b00000111
this->_statut = masque_etat_simpleBouton | pin; // rajoute à la variable le bit d'etat = 0b01000111 : HIGH = bouton relâché
this->_delai_debounce_ms = delai_debounce_ms;
this->_millis_etat_actuel = millis();
//Initialisation hardware
pinMode(pin, INPUT_PULLUP);
}
- la méthode de type booléenne : estEnfonce() permet de retourner vrai ou faux suivant que le bouton de l’instance en cours est enfoncé ou non ;
bool simpleBouton::estEnfonce() const {return (this->_statut & masque_etat_simpleBouton) == 0;}
- la méthode de type uint8_t : pin() permet de retourner le numéro de la pin de l’instance en cours qui est notamment utilisé pour déterminer la variable locale etat_courant avec un digitalRead() (dans la méthode booléenne : actualiser()) ;
uint8_t simpleBouton::pin() const {return this->_statut & masque_pin_simpleBouton;}
Voici maintenant la méthode la plus importante, bool simpleBouton::actualiser() :
Explications :
1/ Elle effectue un test du changement d’état de l’instance en cours d’un bouton (enfoncé LOW- relâché HIGH) :
- la variable etat_precedent récupère le statut précédent de l’instance en cours du bouton ;
uint8_t etat_precedent = this->_statut & masque_etat_simpleBouton; // 0b010000000 (HIGH) ou 0b00000000 (LOW)
- puis la variable etat_courant récupère son statut actuel.
uint8_t etat_courant = (digitalRead(this->pin()) == HIGH) ? masque_etat_simpleBouton : 0; // si appui : 0b00000000 - si relaché : 0b01000000
2/ - premier test : si etat_courant est différent de etat_precedent et que le délai de temporisation fixé par défaut à 20 millisecondes ou par l’utilisateur n’est pas atteint alors etat_courant reste égal à etat_precedent donc rien à changé ;
uint32_t maintenant = millis();
if (etat_courant != etat_precedent) // en cas de changement d'etat bouton enfoncé ou pas : LOW ou HIGH
{
uint32_t delai = maintenant - this->_millis_etat_actuel;
if (delai < this->_delai_debounce_ms)
{
etat_courant = etat_precedent; // si delai est inférieur au temps défini pour filtrer les rebonds (par défaut 20 millisecondes)
}
}
- deuxième test : par contre si le bouton a été enfoncé ou relâché et que le délai de temporisation est atteint ou dépassé alors il y a changement et on met à jour le bit d’état (6) ainsi que l’argument _millis_etat_actuel avec millis().
bool changement = (etat_courant != etat_precedent);
if (changement) // si delai est supérieur au temps défini pour filtrer les rebonds (par défaut 20 millisecondes)
{
this->_statut ^= masque_etat_simpleBouton; //inversion du bit d'état
this->_millis_etat_actuel = maintenant;
}
//Serial.println(this->_statut, BIN);
3/ enfin la méthode actualiser() retourne la variable changement : 1 ou 0.
return changement;
Conclusion : cette méthode booléenne permet à l’aide de l’argument _statut et du 6ème bit de l’octet qui le constitue de comparer l’état du dernier digitalRead() avec le digitalRead() actuel. Si les deux bits sont différents et que le délai de temporisation fixé par l’utilisateur ou par défaut est atteint ou dépassé alors l’argument _statut est mis à jour en conséquence ainsi que l’argument _millis_etat_actuel qui permet l’évaluation du temps passé dans un état. La variable booléenne « changement » (d’état) est retournée par la méthode.
Voici le CPP :
//Librairie Arduino pour la gestion de boutons poussoirs simples
#include "simpleBouton.h"
//Cuisine interne - décomposition du statut pour tout gérer sur un octet :
//bit 6 = état du dernier digitalRead : 0 <=> LOW (bouton enfoncé) et 1 <=> HIGH (bouton relâché)
//bits 0 à 5 = numéro de pin, de 0 à 63
const uint8_t masque_etat_simpleBouton = 0b01000000;
const uint8_t masque_pin_simpleBouton = 0b00111111;
//Constructeur
simpleBouton::simpleBouton(uint8_t pin, uint8_t delai_debounce_ms)
{
//Initialisation des variables internes
pin &= masque_pin_simpleBouton; // permet de fixer le nmr de pin en binaire ex : pin 7 = 0b00000111
this->_statut = masque_etat_simpleBouton | pin; // rajoute à la variable le bit d'etat = 0b01000111 : HIGH = bouton relâché
this->_delai_debounce_ms = delai_debounce_ms;
this->_millis_etat_actuel = millis();
//Initialisation hardware
pinMode(pin, INPUT_PULLUP);
}
//Méthodes constantes non développées dans le .h (pour masquer la cuisine interne)
bool simpleBouton::estEnfonce() const {return (this->_statut & masque_etat_simpleBouton) == 0;}
uint8_t simpleBouton::pin() const {return this->_statut & masque_pin_simpleBouton;}
//Méthode principale
bool simpleBouton::actualiser()
{
uint8_t etat_precedent = this->_statut & masque_etat_simpleBouton; // 0b010000000 (HIGH) ou 0b00000000 (LOW)
//Lecture physique
uint8_t etat_courant = (digitalRead(this->pin()) == HIGH) ? masque_etat_simpleBouton : 0; // si appui : 0b00000000 - si relaché : 0b01000000
//Filtrage temporel
uint32_t maintenant = millis();
if (etat_courant != etat_precedent) // en cas de changement d'etat bouton enfoncé ou pas : LOW ou HIGH
{
uint32_t delai = maintenant - this->_millis_etat_actuel;
if (delai < this->_delai_debounce_ms)
{
etat_courant = etat_precedent; // si delai est inférieur au temps défini pour filtrer les rebonds (par défaut 20 millisecondes)
}
}
//Mise à jour des variables internes
bool changement = (etat_courant != etat_precedent);
if (changement) // si delai est supérieur au temps défini pour filtrer les rebonds (par défaut 20 millisecondes)
{
this->_statut ^= masque_etat_simpleBouton; //inversion du bit d'état
this->_millis_etat_actuel = maintenant;
}
//Serial.println(this->_statut, BIN);
return changement;
}
et enfin le fichier INO :
#include "simpleBouton.h"
simpleBouton boutonTest1(7);
simpleBouton boutonTest2(9);
simpleBouton boutonTest3(10);
void setup() {
Serial.begin(115200);
}
void loop() {
if (boutonTest1) Serial.println("Detection appui bouton 1");
if (boutonTest2) Serial.println("Detection appui bouton 2");
if (boutonTest3) Serial.println("Detection appui bouton 3");
}
Enfin, pour comprendre cette librairie il faut maîtriser l’utilisation des masques et des opérateurs binaires &, | et ^. Il s’agit respectivement des opérateurs ET, OU inclusif et OU exclusif. Voici les masques binaires définis au tout début du CPP :
const uint8_t masque_etat_simpleBouton = 0b01000000;
const uint8_t masque_pin_simpleBouton = 0b00111111;
Voici les tables de vérité concernant les 3 opérateurs binaires :
Je prends un exemple de code qui se trouve dans le constructeur :
pin &= masque_pin_simpleBouton;
this->_statut = masque_etat_simpleBouton | pin;
On commence par : pin &= masque_pin_simpleBouton;
==> Je décompose : pin = pin & masque_pin_simpleBouton;
Supposons que j’instancie par exemple la PIN 7 :
En binaire cela donne 0b00000111, masque_pin_simpleBouton = 0b00111111
Donc votre calcul se fait comme ceci :
00000111
& 00111111
= 00000111
Pour this->_statut = masque_etat_simpleBouton | pin;
01000000
| 00000111
= 01000111
On retrouve dans l’attribut _statut à la fois les bits qui constituent le numéro de la pin mais également le bit d’état.
Bonne journée à tous.
PS : Je ne suis pas du tout pédagogue, j'espère être suffisamment clair et surtout ne pas m'être trompé dans mon interprétation ?
Un grand merci à bricoleau pour son excellente librairie...
L'explication porte uniquement sur une partie de la classe simpleBouton (la classe boutonAction n’en fait pas parti) .