Encoder

drivers
drivers/Encoder.h
driver stable stable

Driver: Encoder Lecture d'un encodeur rotatif quadrature (2 voies A/B) par interruptions, avec suivi de position, de vitesse et de sens de rotation. Fonctionnalités : /

Méthodes publiques

Méthode Description Paramètres Retour
Encoder()
Encoder(uint8_t pinA, uint8_t pinB, bool usePullup = true);
Construit l'objet encodeur (ne configure pas encore les broches). / pinA: Broche de la voie A de l'encodeur
pinB: Broche de la voie B de l'encodeur
usePullup: Active les résistances de tirage internes (défaut: true) /
Encoder()
Encoder();
begin()
begin();
Configure les broches et attache les interruptions. d'instance (registre statique) sont déjà utilisés / true
true si l'initialisation réussit, false si les 2 emplacements d'instance (registre statique) sont déjà utilisés /
end()
end();
Détache les interruptions et libère l'emplacement d'instance. /
getCount()
getCount() const;
getTurns()
getTurns() const;
getAngle()
getAngle() const;
getRadians()
getRadians() const;
getNormalized()
getNormalized() const;
getDirection()
getDirection() const;
getSpeed()
getSpeed() const;
getRPM()
getRPM() const;
setCPR()
setCPR(uint16_t cpr) {
Définit le nombre de transitions par tour (Counts Per Revolution). / cpr: Résolution de l'encodeur (dépend du modèle physique) /
setDebounce()
setDebounce(bool enable, uint32_t debounceUs = 1000) {
Active/désactive l'anti-rebond logiciel sur les transitions. / enable: Active l'anti-rebond si true
debounceUs: Durée minimale entre 2 transitions valides, en µs (défaut: 1000) /
setReverse()
setReverse(bool reverse) {
Inverse le sens logique de comptage (utile si le câblage A/B est inversé). / reverse: true pour inverser le sens /
reset()
reset();
Remet le comptage de position à zéro. /
setPosition()
setPosition(int32_t newCount);
Force la position courante à une valeur donnée. / newCount: Nouvelle valeur de comptage /
hasMoved()
hasMoved() const;
Indique si l'encodeur a bougé depuis le dernier appel. / true
true si un mouvement a été détecté /
waitForMovement()
waitForMovement(uint32_t timeoutMs = 0);
Bloque jusqu'à détection d'un mouvement, ou expiration d'un délai. / timeoutMs: Délai d'attente maximal en ms (0 = attente infinie) true
true si un mouvement a été détecté avant expiration du délai /
printDebugInfo()
printDebugInfo(Stream& stream = Serial) const;
Affiche l'état complet de l'encodeur (position, vitesse, sens...) sur un flux série. / stream: Flux de sortie utilisé (défaut: Serial) /
_handleA()
_handleA();
_handleB()
_handleB();

Méthodes privées


            _process(uint8_t currState);;
_updateSpeed();;

                    

Variables membres


            uint8_t  _instanceCount;
bool     _usePullup;
bool     _reverse;
uint16_t _cpr;
int32_t   _count;
int32_t _lastCount;
Direction _direction;
float    _speed;
uint32_t _lastSpeedTime;
int32_t  _lastSpeedCount;
bool             _debounceEnabled;
uint32_t         _debounceUs;
uint32_t _lastIsrTime;
uint8_t  _lastState;
bool _initialized;
uint8_t _isrIndex;

                    

Code source


        #pragma once
#include <Arduino.h>

namespace crepp::drivers {

/*
 * =========================
 * Driver: Encoder
 * -------------------------
 * Lecture d'un encodeur rotatif quadrature (2 voies A/B) par interruptions,
 * avec suivi de position, de vitesse et de sens de rotation.
 *
 * @badge driver
 * @badge stable
 *
 * Fonctionnalités :
 * - Comptage incrémental de position (avec table de Gray pour un décodage fiable)
 * - Calcul de la vitesse et du régime (RPM) à partir du comptage
 * - Anti-rebond configurable, inversion de sens, remise à zéro
 * - Jusqu'à 2 instances simultanées (limite matérielle des ISR globales)
 * =========================
 */
class Encoder {
public:
    /**
     * @brief Sens de rotation détecté par l'encodeur.
     */
    enum class Direction {
        NONE              =  0, ///< Pas de mouvement détecté
        CLOCKWISE         =  1, ///< Rotation dans le sens horaire
        COUNTER_CLOCKWISE = -1  ///< Rotation dans le sens anti-horaire
    };

    /**
     * @brief Construit l'objet encodeur (ne configure pas encore les broches).
     * @param pinA Broche de la voie A de l'encodeur
     * @param pinB Broche de la voie B de l'encodeur
     * @param usePullup Active les résistances de tirage internes (défaut: true)
     */
    Encoder(uint8_t pinA, uint8_t pinB, bool usePullup = true);
    ~Encoder();

    /**
     * @brief Configure les broches et attache les interruptions.
     * @return true si l'initialisation réussit, false si les 2 emplacements
     *         d'instance (registre statique) sont déjà utilisés
     */
    bool begin();

    /**
     * @brief Détache les interruptions et libère l'emplacement d'instance.
     */
    void end();

    // Getters position
    /// Comptage brut de transitions depuis le dernier reset()
    int32_t getCount()      const;
    /// Nombre de tours complets effectués (déduit du CPR configuré)
    int32_t getTurns()      const;
    /// Angle courant en degrés (0-360), déduit du CPR configuré
    float   getAngle()      const;
    /// Angle courant en radians, déduit du CPR configuré
    float   getRadians()    const;
    /// Position normalisée entre 0.0 et 1.0 sur un tour
    float   getNormalized() const;

    // Getters mouvement
    /// Sens de rotation détecté au dernier changement d'état
    Direction getDirection() const;
    /// Vitesse angulaire courante (unité dépendante du CPR configuré)
    float     getSpeed()     const;
    /// Régime de rotation courant, en tours par minute
    float     getRPM()       const;

    // Configuration
    /**
     * @brief Définit le nombre de transitions par tour (Counts Per Revolution).
     * @param cpr Résolution de l'encodeur (dépend du modèle physique)
     */
    void setCPR(uint16_t cpr)                                    { _cpr = cpr; }

    /**
     * @brief Active/désactive l'anti-rebond logiciel sur les transitions.
     * @param enable Active l'anti-rebond si true
     * @param debounceUs Durée minimale entre 2 transitions valides, en µs (défaut: 1000)
     */
    void setDebounce(bool enable, uint32_t debounceUs = 1000)    { _debounceEnabled = enable; _debounceUs = debounceUs; }

    /**
     * @brief Inverse le sens logique de comptage (utile si le câblage A/B est inversé).
     * @param reverse true pour inverser le sens
     */
    void setReverse(bool reverse)                                 { _reverse = reverse; }

    // Contrôle
    /**
     * @brief Remet le comptage de position à zéro.
     */
    void reset();

    /**
     * @brief Force la position courante à une valeur donnée.
     * @param newCount Nouvelle valeur de comptage
     */
    void setPosition(int32_t newCount);

    // Utilitaires
    /**
     * @brief Indique si l'encodeur a bougé depuis le dernier appel.
     * @return true si un mouvement a été détecté
     */
    bool hasMoved()                           const;

    /**
     * @brief Bloque jusqu'à détection d'un mouvement, ou expiration d'un délai.
     * @param timeoutMs Délai d'attente maximal en ms (0 = attente infinie)
     * @return true si un mouvement a été détecté avant expiration du délai
     */
    bool waitForMovement(uint32_t timeoutMs = 0);

    /**
     * @brief Affiche l'état complet de l'encodeur (position, vitesse, sens...) sur un flux série.
     * @param stream Flux de sortie utilisé (défaut: Serial)
     */
    void printDebugInfo(Stream& stream = Serial) const;

    /// @internal Appelée par l'ISR globale de la voie A — ne pas appeler directement
    void _handleA();
    /// @internal Appelée par l'ISR globale de la voie B — ne pas appeler directement
    void _handleB();

    /// @internal Registre statique des instances actives (2 encodeurs max, limite ISR)
    static Encoder* _instances[2];
    /// @internal Nombre d'instances actuellement enregistrées
    static uint8_t  _instanceCount;

private:
    uint8_t  _pinA, _pinB;
    bool     _usePullup;
    bool     _reverse;
    uint16_t _cpr;

    volatile int32_t   _count;
    mutable volatile int32_t _lastCount;
    volatile Direction _direction;

    volatile float    _speed;
    volatile uint32_t _lastSpeedTime;
    volatile int32_t  _lastSpeedCount;

    bool             _debounceEnabled;
    uint32_t         _debounceUs;
    volatile uint32_t _lastIsrTime;
    volatile uint8_t  _lastState;

    volatile bool _initialized;
    uint8_t _isrIndex; ///< Slot ISR attribué à begin() (0 ou 1), 0xFF si non attribué

    /// Traite un changement d'état brut (2 bits AB) et met à jour comptage/sens
    void _process(uint8_t currState);
    /// Recalcule la vitesse à partir de l'évolution du comptage dans le temps
    void _updateSpeed();

    static const int8_t GRAY_TABLE[16]; ///< Table de décodage de Gray pour les 4 transitions valides
};

} // namespace crepp::drivers