J'essaie d'utiliser des interruptions de changement de broche pour détecter les boutons enfoncés. Jusqu'à présent, je n'ai jamais travaillé avec ce type d'interruptions et il y a quelques problèmes, donc je veux m'assurer que c'est la bonne utilisation.
Si j'ai bien compris la fiche technique, les choses suivantes doivent être faites pour utiliser une interruption de changement de broche:
- Définissez les codes PIN que vous souhaitez contrôler dans le registre PCMSK
- Activer le registre PIN pour le contrôle d'interruption de changement de broche (PCICR)
- Activer les interruptions
- Utilisez le vecteur d'interruption correspondant
Projet: Simple Moodlamp, couleurs contrôlées via 4 boutons.
Installer:
- Atmega168A-PU
- 4 mini interrupteurs à bouton-poussoir
- MOSFETS pour contrôler ma LED RGB 3 Watt
Voici le code que j'utilise qui ne fonctionne pas comme prévu:
#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BUTTON1 (1<<PC5)
#define BUTTON2 (1<<PC4)
#define BUTTON3 (1<<PC3)
#define BUTTON4 (1<<PC2)
#define GREEN (1<<PB1)
#define BLUE (1<<PB2)
#define RED (1<<PB3)
void init() {
// enable LED
DDRB |= GREEN;
DDRB |= BLUE;
DDRB |= RED;
// button pullups
PORTC |= BUTTON1;
PORTC |= BUTTON2;
PORTC |= BUTTON3;
PORTC |= BUTTON4;
// pin change interrupts for buttons
PCMSK1 |= PCINT13;
PCMSK1 |= PCINT12;
PCMSK1 |= PCINT11;
PCMSK1 |= PCINT10;
// enable pin change for buttons
PCICR |= PCIE2;
sei();
}
ISR(PCINT2_vect) {
PORTB = BLUE;
}
void ledTest() {
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
_delay_ms(250);
PORTB ^= RED;
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
_delay_ms(250);
PORTB ^= BLUE;
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
_delay_ms(250);
PORTB ^= GREEN;
}
int main() {
init();
ledTest();
_delay_ms(500);
PORTB |= GREEN;
while(1) {
_delay_ms(100);
}
}
Remarque: les boutons doivent être anti-rebond. Étant donné que j'essaie de procéder pas à pas et que cela ne devrait pas être important pour allumer la LED, je l'ai ignoré ici.
Question: La manière dont j'essaie d'utiliser les interruptions est-elle correcte?
Problèmes avec ma configuration:
- Les boutons 1-3 sont totalement ignorés.
- Button4 déclenche une réinitialisation de l'atmega
Choses que j'ai vérifiées:
- Les boutons ne sont en aucun cas connectés au PIN de réinitialisation
- Les boutons sont correctement connectés à GND s'ils sont enfoncés
- Les boutons ne sont pas connectés à GND s'ils ne sont pas enfoncés
Les boutons fonctionnent bien si je les utilise sans interruption, par exemple:
si (! (PINC & BUTTON4)) {PORTB ^ = BLUE; }
- Cristal externe 16MHZ / cristal interne
- Toute erreur dans le routage
- J'utilise un condensateur 100nF entre PWR et GND sur l'atmega
- VCC (7), GND (8), GND (22), AVCC (20) sont connectés (car je n'ai pas besoin d'AREF, ce n'est pas connecté)
la source
Réponses:
Les interruptions de changement de broche ne sont généralement pas un bon moyen de détecter les actions des boutons. En effet, les boutons mécaniques rebondissent et vous obtiendrez de nombreuses interruptions dénuées de sens, et vous devrez quand même faire un anti-rebond de toute façon.
Une meilleure façon est d'avoir une interruption périodique, comme toutes les 1 ms (fréquence de 1 kHz). Cela prend beaucoup de temps sur la plupart des processeurs, donc la fraction de temps passée dans l'interruption sera petite. Échantillonnez simplement l'état du bouton à chaque interruption. Déclarez un nouvel état de bouton si vous avez vu le nouvel état 50 ms d'affilée. 50 ms est plus long que la plupart des boutons rebondissent, mais il est encore assez court pour que les humains ne remarquent pas le décalage ou ne s'en soucient pas.
Notez que de cette façon, vous pouvez également gérer plusieurs boutons dans la même interruption périodique de 1 ms. Tout ce dont vous avez besoin est d'un compteur pour chaque bouton.
Plus d'informations sur le temps de rebond:
Parfois, comme dans ce cas, quelqu'un dit que 50 ms est un temps de rebond trop long. Ce n'est pas vrai pour les boutons ordinaires appuyés par les humains. Cela pourrait être un problème dans des applications très critiques comme le chronomètre, mais jusqu'à présent, je n'en ai pas rencontré un. J'ai fait des tests là-dessus au début des années 80, et beaucoup d'autres personnes l'ont fait aussi.
Il est vrai que le temps de rebond typique des boutons-poussoirs est d'environ 10 ms, avec à peu près tous une stabilisation de 25 ms. Le facteur limitant du temps de rebond est la perception humaine. 50 ms est un peu plus court que lorsque les gens commencent à remarquer un retard lorsqu'ils ne le recherchent pas. Même alors, cela prend beaucoup plus de temps pour être ennuyeux. Il peut être possible dans certains cas pour un humain de détecter une différence entre 50 ms et 0 ms de retard s'il le recherche spécifiquement , mais c'est très différent de pousser un bouton et de voir quelque chose se produire et de ne pas penser au retard.
50 ms est donc un bon temps de rebond car le retard est inférieur à la limite de perception dans les applications ordinaires, bien inférieur à la limite de gêne, et bien supérieur au temps de rebond de la plupart des commutateurs. J'ai trouvé des commutateurs qui ont rebondi pendant presque aussi longtemps, alors vous pourriez aussi bien pousser à la limite de perception car il n'y a rien à perdre.
J'ai fait de nombreux produits avec des boutons anti-firmware avec un temps de anti-rebond de 50 ms. Pas une seule fois un client n'a mentionné avoir remarqué un retard. Ils ont tous accepté les boutons comme fonctionnant bien sans problème.
la source
Les interruptions de changement de broche sont un meilleur moyen de rebondir que d'interroger. L'interruption passe généralement par une logique telle qu'un D-Flip Flop ou D-Latch. Bien que cela soit vrai, il est plus difficile d'implémenter cette routine anti-rebond avec des compilateurs de niveau supérieur. Une fois que l'interruption se produit, le drapeau d'interruption n'est pas effacé et la validation d'interruption est effacée jusqu'à ce qu'un retard se soit produit. Une fois que le retard s'est produit, l'état de la broche est vérifié et s'il est toujours dans l'état donné qui a déclenché l'interruption, l'état du bouton est modifié et le drapeau d'interruption est effacé et la validation d'interruption est définie. S'il n'est pas dans l'état qui a provoqué l'initiation, la validation d'interruption est définie et l'état reste le même. Cela libère le processeur pour d'autres tâches. Des interruptions périodiques perdent du temps dans le programme.
la source
Faux. PC INT est la meilleure option. Si vous utilisez l'interrogation pour vérifier l'état d'un bouton, rien ne sera effectué la plupart du temps. Vous perdez beaucoup de temps CPU précieux. PC INT permet d'exécuter des actions uniquement sur demande.
Corriger le rebond. Pourtant, vous ne devez JAMAIS faire rebondir un bouton / commutateur à l'intérieur d'une routine d'interruption (même raison: perte de temps CPU). Les ISR sont censés être vraiment courts et efficaces, au niveau du code. Utilisez simplement le rebouncing matériel. Gardez votre logiciel propre!
Le rebouncing matériel est plus pratique, voir ici / Debouncing RC + déclencheur Schmitt pour référence. Je l'ai utilisé d'innombrables fois avec PC INT, il n'a jamais échoué.
Alors oui, vous pouvez (et devriez) utiliser PC INT pour obtenir un état de bouton. Mais vous devez également utiliser un anti-rebond matériel approprié.
la source