Debian logo [embedded-µC.LINUX]

11. Gestion des entrées/sorties logiques

Le type de gestion d'entrées/sorties le plus simple à gérer pour un microcontrôleur consiste à utiliser directement des niveaux logiques '0' ou '1'. En effet, l'exploitation des niveaux logiques ne nécessite aucune transformation du signal.

11.1. Exemple d'entrées/sorties sur un bit

Dans cet exemple, on utilise la carte MSC1210 EValuation Module. On utilise un bouton poussoir et deux diodes électroluminescentes (LED) dont le câblage est fixé comme suit :

Tableau 10. Entrées/Sorties un bit MSC1210EVM

Type Désignation Broche microcontrôleur
Entrée Bouton poussoir SW2 P3.2
Sortie LED rouge P3.4
Sortie LED jaune P3.5

Exemple 15. Programme d'illustration des entrées/sorties logiques sur un bit

Voici un programme «minimaliste» d'illustration du fonctionnement des entrées/sorties sur un bit : sdcc_ttl_1bit.c. À chaque appui sur le bouton poussoir, on allume la LED rouge. Tous les quatre appuis sur le même bouton poussoir, on allume la LED jaune.

01: #include <stdio.h>
02: #include <msc1210.h>
03: #include "ser_msc1210.h"
04: 
05: __sbit __at (0xb2) IN_SW2;   // p3.2
06: __sbit __at (0xb4) OUT_RED;   // p3.4
07: __sbit __at (0xb5) OUT_YELLOW;   // p3.5
08: 
09: void putchar(char c) {
10:   ser_putc(c);
11: }
12: 
13: bit antirebond() {
14:    unsigned char i = 0;
15:    unsigned int j = 0;
16:    bit sw_now = 1, sw_previous = 1;
17: 
18:    while (i < 5) {
19:       sw_previous = IN_SW2;
20:       // Boucle de temporisation
21:       for(j = 0; j < 8000; j++)
22:          ;
23:       sw_now = IN_SW2;
24:       if (sw_now == sw_previous)
25:          i++;
26:    }
27: 
28:    return sw_now;
29: }
30: 
31: void main(void) {
32: 
33:    unsigned char c, i = 0;
34:    bit sw;
35: 
36:    /* Appuyer sur Entrée pour ajuster automatiquement
37:     * le débit sur la liaison série */
38:    autobaud();
39:    EA=1;
40: 
41:    puts("Lecture SW2/Ecriture LED");
42:    putcr();
43:    while (1) {
44:       if (sw = antirebond())
45:          OUT_RED = 1;
46:       else {
47:          OUT_RED = 0;
48:          if (++i % 4)
49:             OUT_YELLOW = 1;
50:          else
51:             OUT_YELLOW = 0;
52:       }
53:       c = sw + 0x30;
54:       putchar(c);
55:       putchar('\r');
56:    }
57: }

Adressage des entrées/sorties, lignes 5 à 7

En fonction du tableau donné ci-dessus on affecte les noms d'entrées/sorties à l'aide des directives du compilateur. Ici, la directive __sbit __at (0xb2) IN_SW2; affecte le nom IN_SW2 au bit accessible à l'adresse 0xb2. Cette adresse correspondant au troisième bit du port numéro 3 du microcontrôleur : P3.2.

Une fois ces affectations en place, on utilise les noms d'entrées/sorties de la même façon que des variables.

Comme avec n'importe quel type de variable, il faut respecter les espaces de représentation mémoire des entrées/sorties. Dans l'exemple, on a désigné des entrées/sorties de type bit. Il n'est donc pas question de placer le contenu d'une variable qui occupe plus d'un bit sur une sortie de type bit. À l'inverse, le transtypage automatique offert par le langage C permet très bien de stocker un bit dans une variable dont l'occupation mémoire est plus grande. À la ligne ligne 53, on place le bit sw dans c qui est une variable de type character dont l'espace de représentation est l'octet. Voir la Section 7.3, « Types de représentation des données ».

Fonction antirebond(), lignes 13 à 29

Comme l'exemple d'illustration propose de compter les appuis sur le bouton poussoir, il est nécessaire de se préoccuper des «rebonds». Lors d'un appui manuel sur le contact électrique du bouton poussoir, une suite d'impulsions transitoires est transmise. Ces impulsions parasites sont communément appelées rebonds.

La fonction présentée ici utilise une boucle de temporisation (lignes 20 à 22). L'emploi de ce type de boucle doit être évité au maximum mais le bouton poussoir de la carte MSC1210 EValuation Module a été câblé directement sur la broche P3.2 du microcontrôleur sans cellule RC de filtrage des rebonds.

Le compteur i sert à contrôler que l'on a lu 5 fois le même état entre chaque temporisation.

Programme principal, lignes 43 à 56

La condition d'appel au corps de la boucle tant que while (1) est toujours vraie. Il s'agit donc d'une boucle infinie. Seule la ré-initialisation du microcontrôleur peut interrompre le programme.

Sans appui sur le bouton poussoir, la fonction antirebond() renvoie un bit à 1. On éteint alors la LED rouge en envoyant un niveau 1 sur la broche P3.4 du microcontrôleur : OUT_RED = 1;.

Lors d'un appui sur le bouton poussoir, la fonction antirebond() renvoie un bit à 0. On allume alors la LED rouge en envoyant un niveau 0 : OUT_RED = 0;.

C'est aussi lors d'un appui sur le bouton poussoir que l'on incrémente le compteur i. On teste ensuite si le reste de la division entière de i par 4 est nul. Si la condition est vérifiée, on allume la LED jaune. Ce codage permet d'allumer la LED jaune tous les quatre appuis sur le bouton poussoir.

L'évaluation des conditions des lignes 44 et 48 utilise directement les résultats numériques des expressions codées avec l'instruction if-else. Si l'expression renvoie la valeur 0, c'est que le test est faux. Voir Une condition fausse vaut toujours 0.

Affichage de la valeur d'un bit, lignes 53 à 55

Pour afficher l'état de l'appui sur le bouton poussoir sur la console du système de développement via la liaison série, il est nécessaire de transformer le bit lu en un code ASCII. Sachant que le code ASCII du chiffre 0 est 0x30, il suffit d'ajouter la valeur du bit à ce code pour afficher les caractères '0' ou '1' sur la console.

Comme la scrutation de l'état du bouton poussoir est continue, on envoie le code ASCII de retour en début de ligne '\r' après chaque affichage de la valeur du bit d'état. De cette façon, on évite de remplir l'écran de la console et l'affichage est donc plus lisible.

11.2. Exemple d'entrées/sorties sur plusieurs bits

Dans cet exemple, on utilise la carte du département Génie Électrique de l'IUT 'A' Paul Sabatier. Relativement à la carte MSC1210 EValuation Module, cette carte offre une extension sur les entrées/sorties logiques.

L'utilisation du port P0 a été «étendue» en utilisant deux sorties du port P3 comme signaux de contrôles. Ces bits de contrôle servent à sélectionner un composant chacun : un pour les entrées et un pour les sorties. De plus, deux bits supplémentaires des ports P3 et P1 ont été alloués comme entrées et sorties. Le schéma ci-dessous montre que l'on dispose de 10 entrées et 10 sorties logiques.

Exemple 16. Programme d'illustration des entrées/sorties logiques sur plusieurs bits

Voici un programme d'illustration du fonctionnement des entrées/sorties sur plusieurs bits : sdcc_ttl_io_iut.c. Il propose un menu qui permet de lire ou écrire 10 bits simultanément ainsi que la lecture ou l'écriture d'un bit individuel parmi 10.

01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <msc1210.h>
04: #include "ser_msc1210.h"
05: #include "iut_msc1210.h"
06: 
07: #define EOF 0x04
08: #define MAXCHAR   4
09: 
10: char getchar(void) {
11:   return ser_getc();
12: }
13: 
14: void putchar(char c) {
15:   ser_putc(c);
16: }
17: 
18: unsigned char menu(void) {
19: 
20:    putcr();
21:    putcr();
22:    puts("------- Menu -------");
23:    putcr();
24:    puts("(a) : lecture des 10 entrees");
25:    putcr();
26:    puts("(b) : ecriture des 10 sorties");
27:    putcr();
28:    puts("(c) : saisie de la valeur a ecrire (< 1024)");
29:    putcr();
30:    puts("(d) : lecture bit individuel (0 <= b <= 9)");
31:    putcr();
32:    puts("(e) : ecriture bit individuel (0 <= b <= 9)");
33:    putcr();
34:    puts("(EOF) : taper CTRL D pour sortir");
35:    putcr();
36:    puts("--------------------");
37:    putcr();
38: 
39:    return getchar();
40: }
41: 
42: void main(void) {
43: 
44:    unsigned int val = 0;
45:    unsigned char c, chaine[MAXCHAR], num;
46: 
47:    /* Appuyer sur Entrée pour ajuster automatiquement
48:     * le débit sur la liaison série */
49:    autobaud();
50:    EA=1;
51: 
52:    while ((c = menu()) != EOF) {
53:       switch (c) {
54:          case 'a':
55:             val = lecture_10bits();
56:             break;
57:          case 'b':
58:             ecriture_10bits(val);
59:             break;
60:          case 'c':
61:             puts("Valeur :");
62:             putcr();
63:             ser_gets_echo(chaine, MAXCHAR);
64:             val = atoi(chaine);
65:                putcr();
66:             break;
67:          case 'd':
68:             do {
69:                puts("Numero du bit :");
70:                putcr();
71:                ser_gets_echo(chaine, MAXCHAR);
72:                num = atoi(chaine);
73:                   putcr();
74:             } while (num > 9);
75:             val = lecture_1bit(num);
76:             break;
77:          case 'e':
78:             do {
79:                puts("Numero du bit :");
80:                putcr();
81:                ser_gets_echo(chaine, MAXCHAR);
82:                num = atoi(chaine);
83:                   putcr();
84:             } while (num > 9);
85:             do {
86:                puts("Valeur du bit :");
87:                putcr();
88:                ser_gets_echo(chaine, MAXCHAR);
89:                val = atoi(chaine);
90:                   putcr();
91:             } while (val > 1);
92:             ecriture_1bit(num, val);
93:             break;
94:          default:
95:             puts("Option non reconnue");
96:             putcr();
97:       }
98:       printf ("/%03x/\n\r", val);
99:       putcr();
100:   }
101:   // Fin du programme
102:   puts("The End!");
103:   putcr();
104:
105:   while(1);
106:}