Dans les architectures embarquées, la lecture des entrées numériques exige une gestion précise des rebonds mécaniques pour éviter les déclenchements multiples indésirables, notamment lors de la navigation dans des menus ou l'actualisation d'afficheurs sept segments. Une méthode robuste consiste à implémenter un automate fini (FSM) qui isole chaque transition physique et garantit qu'un événement est traité exactement une fois par cycle d'action.
Architecture à trois états avec tampon temporaire
Cette approche divise le cycle de vie d'une touche en trois phases distinctes : repos, détection de pression et attente de relâchement. La valeur lue est stockée dans une varible locale jusqu'à la confirmation du relâchement, moment où elle est conssommée par la boucle principale.
enum etat_clavier { REPOS, PRESSION, RELACHEMENT };
unsigned char scan_etat = REPOS;
unsigned char touche_brute = 0;
void analyser_touche(void) {
switch (scan_etat) {
case REPOS:
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) != 0x0F) {
scan_etat = PRESSION;
}
break;
case PRESSION:
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) != 0x0F) {
if (!P30) touche_brute = 7;
else if (!P31) touche_brute = 6;
else if (!P32) touche_brute = 5;
else if (!P33) touche_brute = 4;
P3 = 0xF0; P42 = 1; P44 = 1;
if (!P34) touche_brute += 12;
else if (!P35) touche_brute += 8;
else if (!P42) touche_brute += 4;
scan_etat = RELACHEMENT;
} else {
scan_etat = REPOS;
}
break;
case RELACHEMENT:
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) == 0x0F) {
scan_etat = REPOS;
}
break;
}
}
L'intégration dans la routine principale repose sur un compteur de temps pour respecter le délai anti-rebond (typiquement 20 ms). Le traitement s'active uniquement lorsque l'automate atteint l'état de relâchement, en comparant la nouvelle valeur avec la précédente pour filtrer les répétitions parasites.
if (compteur_tempo >= 20) {
compteur_tempo = 0;
analyser_touche();
if (scan_etat == RELACHEMENT && touche_brute != touche_historique) {
switch (touche_brute) {
case 4: /* Action spécifique 4 */ break;
case 8: /* Action spécifique 8 */ break;
case 9: /* Action spécifique 9 */ break;
case 5: /* Action spécifique 5 */ break;
}
touche_historique = touche_brute;
} else {
touche_historique = 0;
}
}
Optimisation par registre de validation global
Il est possible de simplifier l'interface logicielle en déportant la persistance des données directement au sein de la routine de scan. Lorsqu'une pression est validée, la valeur est immédiatement copiée dans un registre accessible globalement. Ce registre est automatiquement effacé lors de la transition vers l'état de repos.
unsigned char touche_validee = 0;
void analyser_touche_optimise(void) {
switch (scan_etat) {
case REPOS:
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) != 0x0F) scan_etat = PRESSION;
break;
case PRESSION:
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) != 0x0F) {
if (!P30) touche_brute = 7;
else if (!P31) touche_brute = 6;
else if (!P32) touche_brute = 5;
else if (!P33) touche_brute = 4;
P3 = 0xF0; P42 = 1; P44 = 1;
if (!P34) touche_brute += 12;
else if (!P35) touche_brute += 8;
else if (!P42) touche_brute += 4;
touche_validee = touche_brute;
scan_etat = RELACHEMENT;
} else {
scan_etat = REPOS;
}
break;
case RELACHEMENT:
touche_validee = 0;
P3 = 0x0F; P42 = 0; P44 = 0;
if ((P3 & 0x0F) == 0x0F) scan_etat = REPOS;
break;
}
}
La boucle principale devient alors purement réactive. Il suffit d'interroger périodiquement le registre de validation sans gérer d'historique ou de comparaisons supplémentaires.
if (compteur_tempo >= 20) {
compteur_tempo = 0;
analyser_touche_optimise();
switch (touche_validee) {
case 4: /* Traitement 4 */ break;
case 5: /* Traitement 5 */ break;
case 6: /* Traitement 6 */ break;
case 7: /* Traitement 7 */ break;
default: break;
}
}
Gestion des ressources partagées et masquage binaire
Cette logique s'applique aussi bien aux claviers matriciels qu'aux interrupteurs indépendants. Une attention particulière doit être portée lorsque plusieurs périphériques partagent les mêmes registres d'E/S. Par exemple, si le port P3.4 est simultanément utilisé pour capter un signal de fréquence issu d'un oscillateur NE555, la lecture standard du clavier ne doit pas interpréter cet état comme une pression de touche. L'utilisation d'une opération de masquage (P3 & 0x0F) == 0x0F au lieu d'une comparaison directe (P3 == 0x0F) isole correctement les quatre bits inférieurs et préserve l'intégrité du signal d'horloge externe.