Lecture d'un bouton poussoir

Les exemples qui suivent montrent comment lire le statut logique d'une entrée sur laquelle est raccordé un bouton poussoir. Les exemples portent sur 

- un poussoir raccordé entre une entrée du PIC et la masse, ou entre une entrée du PIC et le +Alim, 

- un poussoir raccordé entre une entrée du PIC et une sortie du même PIC, en mode multiplexé.

La lecture de l'entrée peut être réalisée à intervalles réguliers au sein d'une boucle, ou être déclenchée (détectée) à travers une interruption, ces deux cas seront analysés.

Lecture de base

Quand je parle de lecture de l'état d'un bouton poussoir, c'est pour aborder le côté pratique, manipulable. La lecture concerne une entrée logique et il n'y a pas que les boutons poussoir qui peuvent fournir un état logique changeant. La lecture de base objet du présent exemple consiste donc à regarder l'état logique présent sur une ligne configurée en entrée logique, sur un des ports d'un microcontrôleur PIC. Selon l'état logique lu au niveau logiciel, on pourra dire si le bouton poussoir est enfoncé ou s'il est relâché. Mais avant tout, formalisons la connexion du bouton poussoir, qui peut se trouver côté masse ou côté +Valim. Dans le schéma qui suit, deux boutons poussoir sont câblés : 

- un premier bouton poussoir SW1 qui amène sur la ligne RA0 du PIC, un état logique haut quand il est enfoncé;

- un second bouton poussoir SW2 qui amène sur la ligne RA1 du PIC, un état logique bas quand il est enfoncé.

pic_tuto_base_lecture_entree_001a

Au repos, quand aucun bouton poussoir n'est actionné, l'entrée RA0 se trouve à l'état logique bas et l'entrée RA1 se trouve à l'état logique haut. Ces états logiques, s'ils changent, indiqueront que les boutons poussoir ont été enfoncés. Sachant cela, on peut lire en boucle l'état logique des entrées RA0 et RA1, puis décider de ce qu'il faut faire si leur état subit une modification. Le code suivant montre une façon de faire, où la lecture des entrées RA0 et RA1 est répétée toutes les 100 ms, dans une boucle de type "while" elle-même située dans le corps de la procédure principale de l'application (main program).

program Test_16F628A_lecture_poussoir;

procedure Init;
begin
TRISA := %11111111; // toutes lignes du port A (RA0 à RA7) configurées en entrées logiques
TRISB := %00000000; // toutes lignes du port B (RB0 à RB7) configurées en sorties logiques
end;

procedure Read_Input_1;
begin
if TestBit(PORTA, 0) = 1 then // lecture ligne RA0
begin
// action si SW1 enfoncé
end;
end;

procedure Read_Input_2;
begin
if TestBit(PORTA, 1) = 0 then // lecture ligne RA1
begin
// action si SW2 enfoncé
end;
end;

// Main program
begin
Init;
while true do
begin
  Read_Input_1;
Read_Input_2;
delay_ms(100);
end;
end.

Ca fonctionne, mais...

Ce code fonctionne, mais présente une particularité qui peut devenir un inconvénient dans certains contextes d'utilisation. L'action dans les procédures Read_Input_1 et Read_Input_2 est exécutée toutes les 100 ms tant que le poussoir est enfoncé. Ce point n'est nullement gênant si le premier poussoir doit activer une sortie et que le deuxième poussoir doit désactiver cette même sortie, mais peut être gênant dans d'autres cas. De plus, aucune vérification de la durée de l'appui sur les poussoir n'est prévue, et un parasite bref sur une entrée pourrait parfaitement être pris en compte s'il arrive juste au moment de la lecture. Même problème pour les rebonds mécaniques des boutons poussoirs, inévitables sur les modèles courants, qui peuvent être vus comme plusieurs pressions successives et rapprochées. Cette façon de lire les entrées doit donc être réservée aux situations où ces petits inconvénients ne portent pas préjudice au bon fonctionnement du logiciel, tel qu'un télérupteur avec deux poussoirs ON et OFF séparés. 

Utilisation de la routine BUTTON

Dans beaucoup d'autres cas, on préfèrera utiliser une routine incluse dans le logiciel MikroPascal, qui se charge de tout, de façon quasi transparente pour l'utilisateur. Cette routine, qui s'appelle BUTTON, fonctionne de la façon suivante.

procedure Read_Input_1;
begin
if Button(PORTA, 0, 50, 1) then // lecture ligne RA0
begin
// action si SW1 enfoncé
end;
end;

procedure Read_Input_2;
begin
if Button(PORTA, 1, 50, 0) then // lecture ligne RA1
begin
// action si SW2 enfoncé
end;
end;

Cette routine Button attend de nous qu'on lui fournisse quatre paramètres, qui correspondent aux quatre valeurs situées entre parenthèses :

Pour la lecture de SW1

Button(PORTA, 0, 50, 1)

- PORTA correspond, comme on pourrait un peu s'en douter, au port sur lequel est branché le bouton poussoir.

- 0 correspond au numéro de l'entrée du port spécifié. Ici il s'agit de la ligne RA0, donc entrée numéro 0 (s'il s'agissait de RA5, on indiquerait la valeur 5).

- 50 correspond au temps (en ms) pendant lequel le bouton doit être enfoncé pour que l'action soit réellement prise en compte. Ce délai permet de s'affranchir des parasites et des rebonds mécaniques. Une valeur de 50 ms convient généralement, mais on peut prendre une valeur plus élevée, par exemple 100 ou 150 ms.

- 1 correspond à l'état logique que l'on doit avoir quand le poussoir est enfoncé.

Pour la lecture de SW2

Button(PORTA, 1, 50, 0)

- PORTA correspond au même port que précédemment, pas de surprise.

- 1 correspond au numéro de l'entrée du port spécifié. Ici il s'agit de la ligne RA1, donc entrée numéro 1.

- 50 correspond au temps (en ms) pendant lequel le bouton doit être enfoncé pour que l'action soit réellement prise en compte.

- 0 correspond à l'état logique que l'on doit avoir quand le poussoir est enfoncé.

Ca fonctionne aussi, mais...

Les éventuels problèmes liés aux parasites ou rebonds appartiennent au passé, mais l'exécution du code est toujours répétée à chaque appel de la procédure si le bouton reste enfoncé. Ce qui n'est pas toujours voulu. Pour remédier à ce problème, il faut ajouter une variable de type boolean (tout ou rien) qui stocke le changement d'état du bouton poussoir.

Amélioration de la routine Button

Le code suivant permet de n'exécuter qu'une seule fois le code inclus dans la procédure, au moment où le bouton poussoir est enfoncé, et ce même si le bouton est encore enfoncé lors des lectures suivantes de la ligne d'entrée du PIC. De plus, cette façon de faire n'est pas bloquante la procédure Button étant "sauté" si le bouton reste enfoncé (pas d'attente de relâchement).

procedure Read_Input_1;
var
bSW1: boolean;
begin
if Button(PORTA, 0, 50, 1) and (bSW1 = false) then // lecture ligne RA0
begin
// action si SW1 enfoncé
bSW1 := true;
end;
if PORTA.0 = 0 then bSW1 := false;
end;

procedure Read_Input_2;
var
bSW2: boolean;
begin
if Button(PORTA, 1, 50, 0) and (bSW2 = false) then // lecture ligne RA1
begin
// action si SW2 enfoncé
bSW2 := true;
  end;
 if PORTA.1 = 1 then bSW1 := false;
end;

Fonctionnement : le code à exécuter lors de l'appui sur les boutons poussoir est exécuté à la condition que la variable associée (bSW1 pour SW1 et bSW2 pour SW2) possède la valeur FALSE. Ce qui est le cas si l'entrée RA0 est à l'état bas (pour SW1) ou si l'entrée RA1 est à l'état haut (pour SW2). Quand un poussoir est actionné, le code de la procédure est exécuté, et les variables bSW1 et bSW2 prennent la valeur TRUE dans la foulée, ce qui interdit l'exécution de la procédure lors des lectures suivantes, tout du moins tant que les poussoirs n'ont pas été relâchés. En effet, une ligne a été ajouté à la suite de la procédure d'origine, qui permet de redonner aux variables bSW1 et bSW2 leur valeur FALSE dès que les lignes correspondantes (RA0 et RA1) sont repassées à leur état nominal (de repos, poussoir relâché).

Rebonds mécaniques : procédure Button indispensable ?

Non, bien sûr ! Certaines personnes utilisent un code bien plus simple pour ne pas tenir compte des rebonds mécaniques du bouton poussoir, comme le montre l'exemple suivant :

procedure Read_Input_1;
begin
if PORTA.0 = 1 then Delay_ms(50); // lecture ligne RA0
if PORTA.0 = 1 then 
begin
// action si SW1 enfoncé
 end;
end;

Si un changement d'état est détecté lors de la lecture de l'entrée concernée, on fait une petite pause de quelques dizaines de ms, puis on relit à nouveau l'état de l'entrée. S'il s'agissait d'un parasite, la seconde lecture a fort peu de chance de donner la même valeur que celle obtenue à la première lecture. Et si des rebonds surviennent, on "passe par dessus", la deuxième lecture ayant lieu assez longtemps après le dernier rebond potentiel.

Remarque : la durée exacte du délai à appliquer ici ou avec la routine Button, n'est pas très critique, elle peut en pratique être comprise entre 20 ms et 100 ms. Une durée trop courte risque de laisser passer des rebonds sur certains boutons poussoirs "lents", et une durée trop longue demande à ce que l'utilisateur laisse plus longtemps le doigt appuyé sur le poussoir. Notez en passant que vous disposez là d'une base pour détecter si l'appui à lieu pendant un certain temps, ce qui permet d'exécuter des actions différentes selon le temps d'appui...

Suppression des résistances de rappel externes

Une ligne d'un port quelconque configurée en entrée doit présenter un potentiel bien défini pour que sa lecture représente quelque chose de cohérent. Si on relie la sortie d'une porte logique (par exemple la sortie d'une porte NAND CD4011) sur l'entrée d'un PIC, aucun composant supplémentaire n'est requis, car la sortie de la porte logique présente un état logique parfaitement connu, qui est l'état bas ou l'état haut. En revanche, si l'on connecte un bouton poussoir sur une entrée du PIC, ce dernier provoque sur ladite entrée l'apparition d'un état logique bien défini, mais seulement quand il est enfoncé : état haut s'il est relié côté +Valim ou état bas s'il est relié côté masse. Pour que l'entrée du PIC soit à un potentiel maîtrisé quand le poussoir est relâché, il convient d'ajouter une résistance de rappel, qui fixe les règles (en tension) en absence d'action de la part de l'utilisateur. 

pic_tuto_base_lecture_entree_001b
Rappel : petit carré gris = état logique indéterminé et non accepté

Vous serez donc heureux d'apprendre que certains PICs intègrent la mécanique nécessaire pour éviter d'ajouter ces résistances externes, sous forme de "composant" appelé "pullup" (ou "pull-ups") toujours raccordé en interne du côté +Valim, et qui autorisent, moyennant une configuration correcte, d'élaborer le montage suivant :

pic_tuto_base_lecture_entree_001c

Le code nécessaire pour que ce type de raccordement des poussoirs soit autorisé, est le suivant :

program electronique_pic_tuto_base_lecture_poussoir_001c;

var
iValue: byte;

// initialisation générale
procedure Init;
begin
TRISA := %00000000; // port A configuré en sortie
TRISB := %11111111; // port B configuré en entrée
CMCON := $07; // désactivation comparateurs
OPTION_REG.NOT_RBPU := 0; // mise en service pullup
end;

Mais comme il est rare qu'une bonne nouvelle ne soit pas suivi d'une mauvaise... La fonction pullup n'est pas forcement disponible sur tous les ports. Par exemple, sur le PIC 16F628A, le port B en bénéficie mais pas le port A. Brancher des poussoirs sur le port A imposera donc l'ajout de résistances externes sur les entrées utilisées. D'un autre côté, les résistances externes peuvent être branchées côté masse ou côté +Valim, ce qui laisse toute latitude sur le câblage des poussoirs. Bien sûr, cela est également possible avec le port B, à condition de ne pas mettre en service le pullup. En résumé, vous pouvez utiliser le pullup interne s'il existe, mais cela impose un câblage du poussoir entre entrée du PIC et masse, et le changement d'état à détecter est alors un passage d'état logique haut vers un état logique bas.

 

 

 

Accuil








 

 

Recherche personnalisée