Différences entre les interruptions et l'échantillonnage pour le bouton matériel?

8

J'ai un bouton matériel connecté à une interruption, mais mon problème est qu'il rebondit un peu, ce qui rend la pression sur le bouton peu fiable. Je pense qu'une bonne partie de ces problèmes seraient résolus par l'échantillonnage dans la boucle principale, mais cela semble techniquement incorrect.

Les interruptions conviennent-elles mieux à la communication intra-circuit ou les interruptions conviennent-elles également aux commutateurs matériels? Si oui, quelles techniques anti-rebond puis-je utiliser?

J'ai essayé de conserver une variable de minuterie et de la comparer à l'heure actuelle, aux retards et à d'autres techniques. Il semble que les rebonds soient si rapides que cela n'a pas d'importance.

OneChillDude
la source
2
Lisez ceci: ganssle.com/debouncing.pdf
Smith
Il n'y a rien de mal à l'échantillonnage dans la boucle principale, si vous réagissez dans la boucle principale. Les interruptions sont plus appropriées si vous souhaitez réagir de manière asynchrone. Parfois vous le faites et parfois non.
Eugene Ryabtsev
La meilleure façon de rebondir est un simple filtre passe-bas.
lucas92

Réponses:

8

L'anti-rebond est une FAQ. Vous devriez pouvoir trouver ... un nombre presque illimité de pages Web sur le sujet. Smith a également commenté le PDF largement lu de Jack Ganssle sur le sujet. Et avec toutes ces réponses, vous avez à la fois des méthodes matérielles et logicielles.

J'ajouterai un peu à cette "littérature" en parlant principalement d'idées qui ne sont pas déjà bien couvertes. Mais avant de le faire, un ou deux points:

  1. Le rebond dans le matériel analogique peut produire des résultats que vous ne pouvez pas obtenir par un commutateur "observé" uniquement numériquement sur une base périodique par interrogation ou même par des événements de changement de broche matériel. Mais vous pouvez faire "assez bien" à toutes fins utiles, numériquement. Presque personne de nos jours n'utilise de solutions anti-rebond analogiques externes. Mais j'ai tout utilisé, de l'étirement du pouls à l'aide d'un seul coup (74121) aux techniques mentionnées par Jack Ganssle ici .
  2. Pour ceux qui ne font que de la programmation intégrée et qui ne sont pas du tout intéressés par l'apprentissage de l'électronique, les commutateurs anti-rebond sont probablement l'un des deux ensembles de compétences de base nécessaires. Les LED de fonctionnement sont probablement l'autre. Et par cela, je ne veux pas dire avoir une seule compétence dans ces domaines. Je veux dire pouvoir le faire de plusieurs façons. Donc , vous avez vraiment faire besoin de pleinement appréhender ce que Jack Ganssle écrit au sujet, et encore plus, en ce qui concerne les commutateurs.

Depuis que je l' ai mentionné en utilisant une impulsion d' étirement 74121 et puisque Jack Ganssle ne pas le mentionner, ni personne ici ne encore, je peux aussi bien fournir ce supplémentaire lien comme suggéré supplémentaire de lecture sur l' utilisation du 74121 ou 555 comme un one-shot minuterie pour anti-rebond des commutateurs.


Passons maintenant à l'observation avec un microcontrôleur.

J'utilise généralement une machine à états pour gérer le rebond. Ceci est presque toujours piloté par un temporisateur "rythme cardiaque" que j'ai réglé à environ , si possible. (Je n'utilise généralement PAS les événements d'interruption déclenchés par le front pour plusieurs raisons.)8ms

La machine d'état ressemble à ceci:

schématique

simuler ce circuit - Schéma créé à l'aide de CircuitLab

La valeur DEBOUNCED pour le commutateur peut prendre des valeurs "inactif", "actif" et "inconnu". De cette façon, vous pouvez vous assurer que votre logiciel attend que la valeur du commutateur se stabilise après l'initialisation. Mais d'habitude, je ne m'embête pas avec ça. Je remplace la valeur "inconnue" par une valeur par défaut et j'utilise simplement un système de valeurs binaires à la place.

La machine d'état est entrée en définissant d'abord la valeur anti-rebond à sa valeur par défaut, puis en entrant dans l'état "CHANGING" de la machine d'état. À chaque intervalle de temps (généralement si je peux m'en tirer), je vais lire la valeur actuelle du commutateur et effectuer une mise à jour de l'état actuel et éventuellement de la valeur anti-rebond. Ensuite, je viens de sortir. Le code de haut niveau accède alors uniquement à l'état rebondi.8ms

Si cela m'importe, je peux également conserver un état antérieur de la déclaration. Dans ces cas, lors de la mise à jour de l'état anti-rebond lui-même, je copierai d'abord cet état dans un «état anti-rebond antérieur». Je peux ensuite utiliser la paire de valeurs pour déterminer s'il y a eu une transition anti-rebond. Parfois, je me fiche des transitions. Parfois je fais. Cela dépend donc. Mais dans tous les cas, je veux seulement connaître les transitions qui ont été dénoncées. Je ne me soucie jamais des transitions runt . Le code de haut niveau n'utilise jamais l'état interne que la machine d'état utilise pour son propre travail.

L'une des bonnes choses à propos de cette méthode est que je peux faire rebondir un port entier de commutateurs à la fois. Et je peux le faire sans une seule branche dans le code d'interruption, aussi. Cela signifie un code anti-rebond très rapide et court jusqu'à la largeur de port du microcontrôleur (généralement 8 bits de large). Un exemple de l'Atmel AT90 montre comment cela est réalisé en utilisant un événement d'interruption Timer0:

.equ    SWPORTPINS  =   PINB
.def    SwRawCurr   =   r4
.def    SwRawPrev   =   r5
.def    SwState     =   r6
.def    SwDebCurr   =   r7
.def    SwDebPrev   =   r8

            ; Debounce the input switches.

                mov     SwRawPrev, SwRawCurr
                in      SwRawCurr, SWPORTPINS
                mov     Timer0Tmp1, SwRawCurr
                eor     Timer0Tmp1, SwRawPrev
                mov     Timer0Tmp0, Timer0Tmp1
                or      Timer0Tmp1, SwState
                mov     SwState, Timer0Tmp0
                mov     Timer0Tmp0, Timer0Tmp1
                com     Timer0Tmp0
                and     Timer0Tmp1, SwDebCurr
                and     Timer0Tmp0, SwRawCurr
                or      Timer0Tmp1, Timer0Tmp0
                mov     SwDebPrev, SwDebCurr
                mov     SwDebCurr, Timer0Tmp1

Cet exemple montre maintenant l'intégralité de l'accord, y compris les valeurs de commutateur anti-rebond précédentes et actuelles. Et il effectue également toutes les transitions d'état nécessaires. Je ne montre pas l'initialisation de ce code. Mais ce qui précède fait comprendre à quel point la machine d'état est facile à utiliser et le peu de code requis pour le faire. C'est assez rapide et simple et ne nécessite pas de branchement (ce qui implique parfois des cycles supplémentaires ainsi que de l'espace de code supplémentaire.)


Je préfère utiliser le timing car de longs, longs tests avec une variété de personnes différentes utilisant des équipements sur lesquels j'ai travaillé dans le passé m'ont conduit là-bas. J'ai essayé des périodes plus longues et quand je le fais, je commence à faire dire aux gens que la "réactivité" n'est pas assez "vive". (De nos jours, avec des enfants qui grandissent en jouant aux jeux de tir en temps réel, je pourrais même les raccourcir davantage. Ils se plaindront amèrement des retards, même légers, causés par les téléviseurs numériques modernes dans la configuration et l'affichage d'un cadre.)8ms

Certaines personnes auront des sentiments très clairs sur la netteté et la réactivité d'un système. Croustillant et réactif signifie échantillonner plus souvent, pas moins. Mais personnellement, je trouve périodes d'observation acceptables. ( Cependant, je ne trouve pas les périodes plus longues suffisantes, même pour moi.)20ms

Veuillez noter que la machine d'état que j'ai mentionnée doit d'abord entrer dans l'état SETTLED, puis y rester pendant une fois de plus avant que la valeur de DEBOUNCED ne soit mise à jour. Donc, appuyer sur un bouton et le maintenir, même dans les meilleures circonstances, nécessitera ces transistions:

  1. passer de SETTLED à CHANGING
  2. passer de CHANGING à SETTLED
  3. rester dans SETTLED, mise à jour DEBOUNCED

Un nouvel état anti-rebond nécessite donc un minimum de 3 périodes d'échantillonnage pour être atteint.

Un bouton-poussoir nécessitera au moins 6 temps d'échantillonnage pour passer d'inactif à actif, puis revenir à inactif.


J'ai mentionné les détails ci-dessus afin qu'il soit absolument clair qu'un temps d'échantillonnage de signifie qu'il se situe quelque part entre pour passer d'inactif à un résultat anti-rebond actif reconnu. Et il faudra encore avant que l'état ne redevienne inactif. C'est un minimum de pour passer par un cycle complet de bouton poussoir.8ms16ms<t24ms24ms40ms<t48ms

L'utilisation de durées d'échantillonnage plus longues aura des périodes correspondantes plus longues. Utiliser le j'ai déjà mentionné comme "acceptable" signifie alors quelque part autour de pour un cycle complet de bouton-poussoir . Et qui devient carrément vers le haut dans la zone où les gens n'ont tendance à remarquer. Je n'aime certainement pas la "sensation" si elle devient plus longue que cela.20ms100ms<t120ms

Si vous optez pour cette voie, ne soyez pas cavalier quant à l'utilisation de temps d'échantillonnage plus longs. Si vous devez, alors je pense que vous devez également faire beaucoup de tests avec les utilisateurs / consommateurs.

Et si vous développez du code pour un clavier de saisie, utilisez des temps plus courts. Le record pour une dactylo a été établi il y a des décennies à 217 mots par minute. Il en résulte environ une clé tous les . Des dactylographes comme ça frappent plusieurs touches dans un ordre contrôlé. Pour obtenir de bonnes performances pour les dactylographes très rapides qui utilisent un système de commutation de relais à anche humidifié au mercure, j'ai constaté que45ms2ms fonctionnait bien.

jonk
la source
les temps de rebond varient de 0 pour les interrupteurs au mercure à "quelques" ms typ pour les micro-interrupteurs tactiles à 30 ms pour les interrupteurs à bascule maladroits, donc 8 ms est un bon nombre compte tenu de l'augmentation du temps de rebond avec le vieillissement.
Tony Stewart Sunnyskyguy EE75
@ TonyStewart.EEsince'75 J'ai choisi de faire des tests approfondis avec des utilisateurs utilisant des équipements avec une variété de différents types de commutateurs et le chiffre de 8 ms provient d'une distillation de tout ce travail. (Je ne me suis pas tellement inquiété de la "théorie" car la pratique de la construction et de la fabrication de commutateurs, et leur grande variété, a rendu la collecte et l'analyse de ces données intimidantes.) J'utilise toujours 8 ms, lorsque cela est possible, car il semble être le bon endroit compte tenu de sa longue expérience dans l'écriture de logiciels qui fonctionnent et où les plaintes après-vente
atteignent
@ TonyStewart.EEsince'75 Soit dit en passant, ce test COMPREND l'utilisation de relais reed mouillés au mercure dans le cadre des interrupteurs à clé utilisés dans les claviers (qui, je pense, ne semblent plus être faits.) Dans ces cas, cependant, Je passe à un échantillonnage de 1 à 2 ms (dépend de l'unité.)
jonk
Cette lumière de jardin laser que j'ai mentionnée il y a une fois .. a des commutateurs de télécommande à membrane tactile avec un temps de rebond bas mais le programmeur les a fait basculer à une fréquence de 10 Hz, donc il faut les libérer en <100 ms sinon l'alimentation s'éteint. ?. sur une autre note .. Le clavier de piano Yamaha est extrêmement rapide et prend en charge le roulement de 10 touches tandis que seul le clavier IBM PC original prend en charge le véritable roulement de bord d'attaque. Depuis lors, tous les claviers sont les premiers coups, le bord avant et ensuite le roulement du bord arrière, ce qui est un PITA pour les mauvaises compétences de frappe comme le mien
Tony Stewart Sunnyskyguy EE75
@ TonyStewart.EEsince'75 Cette zone d'échantillonnage de commutateur est un point sensible. L'arrivée de micros bon marché avec zéro rebouncing externe et un commutateur qui sait quoi appliqué, ajoutée à l'ignorance du programmeur intégré, a fait que je trouve en fait des problèmes avec presque CHAQUE SIMPLE instrument intégré avec un clavier ou un bouton poussoir. Ils fonctionnent TOUS terriblement, à mon gré. Et je pense que c'est principalement parce que les programmeurs ont peu ou pas d'expérience, il suffit de "googler et d'appliquer" sans réfléchir. Parfois, saler leur code avec des points de vote aléatoires, même. Ce sont des ordures. Bouleversant. C'est facile de bien faire.
2016
5

Le rebond peut être fait dans le logiciel en masquant les IRQ pour le temps de rebond ou dans le matériel en ajoutant un condensateur de maintien avec votre RC = T> temps de rebond allant de 1 à 15 ms selon la taille du commutateur.

  • par exemple 100 k pullup et 0,1 μF sur le commutateur = 10 ms à 63% ou ~ 8 ms à 50% Vdd ou si vous utilisez la porte de déclenchement Schmitt @ 1,33 V = Vil de 5 V ou ~ 73% V + ~ 12 ms
Tony Stewart Sunnyskyguy EE75
la source
4

Pour effectuer un rebond SW, enregistrez l'horodatage de l'événement en cours et vérifiez le retard par rapport au dernier événement valide:

#define DELAY_DEBOUNCE       150

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    __ts_lastpress = now;
    // do the job here
}

UPD: avec peu de modifications, vous pouvez enregistrer des doubles-clics:

#define DELAY_DEBOUNCE       150
#define DELAY_DOUBLE_CLICK   600

uint32_t    __ts_lastpress = 0;

ISR(some_vector)
{
    uint32_t    now = millis(); // some timer tick counter

    if ( now - __ts_lastpress < DELAY_DEBOUNCE )
        return; // ignore it

    // do the job here
    if ( now - __ts_lastpress < DELAY_DOUBLE_CLICK )
    {
        // it is double click
    }
    else
    {
        // it is single click
    }

    __ts_lastpress = now;
}
Flanker
la source
2

Les interruptions sont également parfaites pour les commutateurs matériels. En utilisant des interruptions, vous évitez un grand gaspillage de ressources et éventuellement d'énergie, surtout si vous avez affaire à des appareils alimentés par batterie.

De plus, à mesure que votre code devient de plus en plus gros, vous verrez qu'il est encore plus facile d'implémenter les interruptions pour les boutons que de les interroger dans votre boucle principale.

Quant à votre anti-rebond, c'est probablement un problème de codage. J'utilise généralement une minuterie de ~ 10 ms pour anti-rebond, tout en vérifiant la libération du bouton. Veillez également à désactiver temporairement l'interruption du bouton pendant que vous la rebondissez, afin que la routine d'interruption ne soit pas exécutée plusieurs fois.

Si vous rencontrez toujours des problèmes, publiez le code ici, afin que nous puissions vous aider.

Pedro Mantovani
la source
1

Ceci est assez similaire à la réponse de Tony Stewart, mais je pense qu'elle pourrait être développée en partie.

Le schéma supérieur correspond à une interruption sur un front bas ou sur un front descendant. Le schéma du bas correspond à une interruption sur un front haut ou montant.

schématique

simuler ce circuit - Schéma créé à l'aide de CircuitLab

personnellement, étant donné le coût d'un condensateur, cela vaut la peine de simplement l'utiliser, plutôt que de m'inquiéter si mon logiciel rebounce est buggé.

Notez que, comme l'a dit Tony Stewart, la constante de temps dans ce circuit est de 10 ms (RC ou 10kΩ1μF). Cela va prendre entre trois et cinq constantes de temps (selon la sensibilité de votre microcontrôleur pour que le bouton se réinitialise, donc si votre microcontrôleur a des problèmes avec la répétition de la fonction d'interruption, cela peut être la cause, et vous devrez peut-être ajustez le capuchon / la résistance pour que l'interruption ne se produise pas plusieurs fois (c'est-à-dire uniquement si votre interruption est configurée pour fonctionner sur un signal haut ou bas, et non sur le front montant ou descendant.

Lié au rebond matériel

ambitiose_sed_ineptum
la source
1
Les deux versions fonctionnent pour les deux côtés + ve ou -ve, surtout si la broche d'interruption a une caractéristique d'entrée de style schmitt (beaucoup le font). Les deux SW1 et SW2 subissent une surtension lors de la fermeture. Certains boutons-poussoirs à bouton carbone peuvent donner des résultats différents de ceux des boutons-poussoirs à dôme métallique.
glen_geek
1

Les humains sont lents, nous n'avons pas besoin de l'attention immédiate d'un micro qui est de l'ordre des microsecondes.

Ce n'est bien sûr pas la seule ni la bonne façon de toujours le faire, mais je trouve généralement plus judicieux de configurer une minuterie (de nombreux micros ont des sys ticks) pour déclencher une interruption à intervalles fixes et déplacer l'état de la broche dans un variable pour le code à examiner plus tard. Vous vous retrouvez avec un var plein de cendres lors du rebond

10010110 frêne

mais à certains moments, vous obtiendrez ces 4 valeurs:
01111111 front montant vient de rebondir
11111111 bouton en régime permanent vers le haut
10000000 front descendant vient de rebondir
00000000 bouton en régime permanent vers le bas

La plupart du temps, cependant, j'utilise simplement un compteur qui se réinitialise pendant le rebond. C'est rapide, testé et facile à faire.
Si cela échoue, j'essaye quelque chose de plus intelligent du document Ganssle que d'autres ont suggéré!

zakkos
la source