Vous êtes sur la page 1sur 82

Notes de cours

Numéro du cours Pondération Unités


247-236 2-4-2 2.66

Titre du cours Département

H 21 Lier l'architecture et la
programmation d'un
microcontrôleur
TGE

Programme Enseignants ou enseignantes


Technologie de systèmes ordinés Francois Bouchard
INDEX

Contenu
1. Résumé de la première session. ................................................................................................................5
1.1. Algorithmie. ....................................................................................................................................5
1.2. Le C. ...............................................................................................................................................6
1.3. Matériel ...........................................................................................................................................8
1.4. Environnement de programmation .................................................................................................8
2. Les Fonctions ...........................................................................................................................................9
2.1. La Déclaration, l'Appel et la Définition ..........................................................................................9
2.1.1. Fonction et type VOID ..........................................................................................................10
2.1.2. Retour de valeur par une fonction .........................................................................................10
2.1.3. Quatre exemples de fonctions : .............................................................................................11
2.1.4. Fonction avec passage de paramètres par valeur: ..................................................................12
2.2. Entête de fonction .........................................................................................................................12
2.3. Analyse d'un problème (le dé). Algorithme et programme avec fonction. ...................................13
2.3.1. Problème: Allumer les DELs pour faire afficher les 6 états d'un Dé. ...................................13
2.3.2. Algorithme complet pour afficher un Dé. .............................................................................14
2.3.3. Et si on y allait avec des fonctions? .......................................................................................14
3. La boucle "for" .......................................................................................................................................15
3.1. Différence entre la boucle ‘for’ et la boucle ‘while’ ....................................................................16
4. Architecture du DS89C450 ....................................................................................................................17
4.1. Schéma Bloc .................................................................................................................................17
4.2. Caractéristiques.............................................................................................................................18
4.3. Registres spéciaux du DS89C450 (Adresses finissant par 0 et 8  Bit adressables) ..................19
4.4. Les Timers / Compteurs (0 et 1) ...................................................................................................20
4.4.1. La Gestion des Timers / Compteurs ......................................................................................20
4.4.2. Les quatre modes des Timers/Compteurs sont : ....................................................................21
4.5. Les registres des Timers décortiqués: ...........................................................................................22
4.5.1. TMOD (89h) : Ce registre permet de configurer les Timers 0 et 1. ......................................22
4.5.2. TCON 0x88 : .........................................................................................................................22
4.5.3. Calcul de la valeur à mettre dans THx et TLx .......................................................................23
4.6. Exercices sur les Timers ...............................................................................................................24
4.7. Résumé des Timers/Compteurs du DS89C450: ...........................................................................25
5. Lecture des interrupteurs: Détection Front et Anti-Rebond ...................................................................26
5.1. La lecture des interrupteurs...........................................................................................................26
5.2. Technique logicielle, déjà vue, pour détecter Front avec Anti-rebond:........................................26
5.3. Techniques matérielles: ................................................................................................................27
5.4. Technique de l'anti-rebond par compteur: ....................................................................................28
6. Les Tableaux ..........................................................................................................................................29
6.1. Généralités: ...................................................................................................................................29
6.2. Initialisation des tableaux .............................................................................................................29
6.3. Tableau à plusieurs dimensions ....................................................................................................30
6.4. Les chaines de caractères ..............................................................................................................31
6.5. Localisation de nos tableaux (variables).......................................................................................32
7. Les claviers .............................................................................................................................................34
7.1. Lecture du clavier de l'ordinateur (PC):........................................................................................34
7.2. Clavier à brancher: ........................................................................................................................35
7.3. Clavier à matrice: ..........................................................................................................................36
7.3.1. Décodé avec des circuits de bases. ........................................................................................36
7.3.2. Décodé avec un port du microcontrôleur. .............................................................................37
7.3.3. Décodé avec un 74C922. .......................................................................................................38
8. La structure conditionnelle "switch" ......................................................................................................41
9. La boucle "do while" ..............................................................................................................................44
10. Variable locale, globale et statique.........................................................................................................45
10.1. Les variables locales et globales ...................................................................................................45
10.2. Variables statiques ........................................................................................................................46
10.3. Variables globale static .................................................................................................................46
11. Les Bus, la mémoire. ..............................................................................................................................47
11.1. Organisation mémoire du Dallas ..................................................................................................47
11.2. Bus d'adresse, de donnée et de contrôle........................................................................................48
11.2.1. Le 74HCT573 (même fonction qu'un LS373): .....................................................................49
11.3. Lecture d'un programme dans une ROM externe: ........................................................................50
11.4. Accès à une donnée dans une RAM externe: ...............................................................................51
11.5. Décodeur d'adresse. ......................................................................................................................52
11.5.1. Décodeur d'adresse avec le 74LS138. ...................................................................................54
11.6. Carte mémoire complète du Kit Dallas. .......................................................................................55
11.6.1. Circuit Décodeur des I/O du Kit Dallas.................................................................................56
11.6.2. Schéma de la partie Décodeur / Mémoire du Kit Dallas. ......................................................57
11.7. Accès à la RAM externe. ..............................................................................................................58
12. Les pointeurs ..........................................................................................................................................59
12.1. Déclaration ....................................................................................................................................59
12.2. Initialisation ..................................................................................................................................59
12.3. Utilisation .....................................................................................................................................59
12.4. Opérations sur les pointeurs..........................................................................................................60
12.5. Les pointeurs et les tableaux .........................................................................................................60
12.6. Quand utiliser les pointeurs? (3 exemples). ..................................................................................61
12.6.1. Utiliser les pointeurs pour permettre à une fonction de modifier plus d'une variable de la
fonction appelante..................................................................................................................61
12.6.2. Utiliser les pointeurs pour passer de "gros" éléments à une fonction....................................61
12.6.3. Utiliser les pointeurs pour accéder à nos circuits externes. ...................................................62
12.6.4. Quelques informations sur l'écran LCD. ...............................................................................63
12.6.5. Exemple : Accès externe et tableau contenant diverses informations. ..................................64
12.6.6. Exercices sur les pointeurs ....................................................................................................65
12.7. La fonction scanf( ) :.....................................................................................................................66
12.7.1. Exemples de scanf (tiré du manuel du compilateur Keil page 303): ....................................67
13. Utilisation de plusieurs fichiers dans un projet. .....................................................................................68
14. Convertisseur. .........................................................................................................................................69
14.1. Le circuit ADC0804 .....................................................................................................................70
14.1.1. Fonctionnement du ADC0804 ...............................................................................................70
14.1.2. Timing du ADC0804 .............................................................................................................72
15. Organigramme. .......................................................................................................................................73
15.1. Organigramme Traditionnel ou Pseudocode?...............................................................................73
15.2. Organigramme Traditionnel .........................................................................................................74
15.2.1. Exemple: ................................................................................................................................75
15.2.2. Raison des organigrammes traditionnels. ..............................................................................77
15.3. Exemples de programmes: ............................................................................................................78
15.4. Organigramme traditionnel: If, then, else, for, while et switch case. ...........................................81

F.B. / D.C. / P.C. 3 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
F.B. / D.C. / P.C. 4 Cours 247-236 Programme de TSO
06-12-2019 NotesCours236_FB.docx
1. Résumé de la première session.

1.1. Algorithmie.

DEBUT SI (Condition) TANT QUE (Condition)


Instructions. Instructions si Vrai. Instructions si Vrai.
FIN. SINON FIN TANT QUE.
Instructions si Faux.
FIN SI.

LIRE iTemperature. // Lire une valeur au clavier.


LIRE ucInterrupteurs a P1. // Lire un port a 8 bits (P1).
LIRE ucBoutonStart a P1_3. // Lire le bit3 du port1.

ECRIRE iVitesse. // Ecrire une valeur a l'ecran.


ECRIRE ucBargraph a P1. // ECRIRE 8 bits sur un port (P1).
ECRIRE ucLedMarche a P1_3. // Ecrire sur le bit3 du port3.

Opérateurs Logiques (tests):


== Égalité: SI(ucHumidite == 80)
!= Différent de: SI(ucEtatMarche != ON)
ET ET Logique: SI((iCompte == 5) ET (ucEtat != ON))
OU OU Logique: SI((ucBouton == PESE) OU (ucFull == VRAI))
NON Inverse condition: TANT QUE( NON(ucTouche == PESE) )
< Plus petit que, <= Plus petit ou égal
> Plus grand que, >= Plus grand ou égal

Opérateurs:
Affectation ( = ): uiDelai = UNESECONDE.
Addition ( + ): iDistance = iDistance + 100.
Soustraction ( - ): cEcart = cEcart – 3.
Multiplication ( * ): uiTotal = uiMoyenne * uiQuantite.
Division ( / ): fMoyenne = (float)uiSomme / uiNombre. // Cast controlé.
ucLuminosite = fConvertisseur / 23.745. // Cast automatique ???
Modulo ( % ): uiUnite = uiTime % 10.
uiDizaine = (uiTime % 100) / 10.
uiCentaine = (uiTime % 1000) / 100.

F.B. / D.C. / P.C. 5 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
1.2. Le C.

Structures de programmation:

void main(void) if (Condition) while (Condition)


{ { {
Instructions si Vrai. Instructions si Vrai.
} } }
else
{
Instructions si Faux.
}

LIRE:
cTouche = getchar( ); // Lire un code Ascii au clavier.
ucInterrupteurs = P1; // Lire un port a 8 bits.
ucBoutonStart = P1_3; // Lire un bit du port.
ÉCRIRE:
printf("%d", iVitesse); // Ecrire une valeur entiere (16 bits) a l'ecran.
printf("%d", (int)ucMode); // Ecrire une valeur entiere (8 bits) a l'ecran.
printf("%bd", ucMode); // Ecrire une valeur entiere (8 bits) a l'ecran.
P1 = ucBargraph; // ECRIRE 8 bits sur un port.
P1_3 = ucLedMarche; // Ecrire un bit sur le port.

Opérateurs Logiques (tests):


== Égalité: if(ucHumidite == 80)
!= Différent de: if(ucEtatMarche != ON)
&& ET Logique: if((iCompte == 5) && (ucEtat != ON))
|| OU Logique: if((ucBouton == PESE) || (ucFull == VRAI))
! Inverse condition: while(!(ucTouche == PESE))
< Plus petit que, <= Plus petit ou égal
> Plus grand que, >= Plus grand ou égal

Opérateurs:
Affectation ( = ): ucLuminosite = fConvertisseur / 23;
Calculs: +, -, *, /, %
Binaires (Masques):
&: Pour mettre bit à 0. ucPort = ucPort & 0xF7;
&: Pour isoler des bits. ucPort = ucPort & 0x38;
| : Pour mettre des bits à 1. ucPort = ucPort | 0x02;
^ : Pour inverser l'état des bits. ucPort = ucPort ^ 0x01;
Décalage
>>: Droite: ucPort = ucPort >> 2;
<<: Gauche: ucPort = ucPort << 1;

Types de données:
Nombre (Décimal, Binaire, Hexadécimal)
Caractère ('B') (code ASCII)
Chaine de caractères ("Avion")

F.B. / D.C. / P.C. 6 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Format du printf: %d (%03d), // Entier signé (16 bits).
%bd, // Entier signé (8 bits) (Pas besoin cast).
%f (%5.2f), // Point flottant.
%c, // Caractère.
%x, // Hexadécimal (lettre minuscule).
%X, // Hexadécimal (lettre majuscule).
%s, // Chaine de caractères (terminée par 0).
%u, // Entier non signé (16 bits).
%bu, // Entier non signé (8 bits) (pas besoin de cast).
%lu, // Entier long non signé (32 bits).
%#x // Hexadécimal avec 0x au début.
Code contrôle du printf: \n (changement de ligne), \r (retour au début de la ligne)
Commentaires: /*....*/ et //
Entêtes de fichier
Types de variables:
int
char
float
long
unsigned
Cas particuliers:
sfr
sbit
Constantes:
Entières:
Décimales: 1, 234, 32665
Hexadécimales: 0x1B, 0x03B4, 0XA4
Flottantes:
Littérales: 12.6531
Scientifiques: 1.26531 e1
Caractères:
Entre apostrophes: 'a', 'B', '6'
Code ASCII: '\x61', '\x42', '\x36'
Code spéciaux:
'\a': (x07) Signal sonore (BEL)
'\b': (x08) Backspace (BS)
'\n': (x0A) Saut de ligne (LF)
'\r': (x0D) Retour au début de la ligne (CR)
'\\': (x5C) Barre oblique
'\'': (x27) Apostrophe
'\"': (x22) Guillemet
#define:
Pour donner à un mot (toujours en majuscule) une valeur quelconque.
Exemple: #define DELAI 10

F.B. / D.C. / P.C. 7 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Détection de changement sur un signal:

ucBoutonMarche = P3_4; // Lecture du bouton Marche.


if(ucAncienEtat != ucBoutonMarche) // Changement du bouton Marche?
{
ucAncienEtat = ucBoutonMarche; // Sauvegarde le nouvel etat.
if(ucBoutonMarche == 1) // Front de monte?
{
// Mettre ici le code a executer si le bouton a ete relache.
}
else // Front de descente.
{
// Mettre ici le code a executer si le bouton a ete pese.
}
}

Multiplexage:

16.6 ms pour cesser le clignotement (temps max pour tout activer tous les affichages).

200 ms temps minimum pour permettre au cerveau de fixer une image.

VCC

1.3. Matériel R1 R2 R3
2k2 2k2 2k2

Comment brancher un interrupteur sur un port (Pull-Up):


uProc

SW1
P1.4

SW2
P1.5

SW3
P1.6

VCC

Comment brancher une Del sur un port ( R ): R5


470

P2.4

LED

Caractéristique des ports après un reset: Ports à 0xFF (en entrés).

1.4. Environnement de programmation

Orbee
Keil 5.0

F.B. / D.C. / P.C. 8 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
2. Les Fonctions

2.1. La Déclaration, l'Appel et la Définition


Une fonction est un sous-programme qui effectue un travail bien précis, à laquelle on passe
(généralement) des données et qui retourne (le plus souvent) une valeur. Cette capacité nous permet
de créer des routines indépendantes qui pourront être appelées depuis d'autres fonctions.
Pour pouvoir être exécuté, un programme doit toujours contenir, au minimum, la fonction main( )
appelée fonction principale.
Une fonction est définie par 4 éléments:
 son type ( type de la valeur retournée );
 son nom (un identificateur);
 la liste de ses arguments (entre les parenthèses);
 instructions à exécuter lorsqu'elle est appelée.
Une fonction devra toujours être connue avant son emploi. Pour y arriver nous pouvons procéder de
deux façons.
1- Nous pourrons définir la fonction avant son utilisation.
2- Nous pourrons faire une prédéclaration de type.
Ou en d'autres termes; écrire le prototype de la fonction.
C'est préférable d'utiliser la deuxième façon.

Exemple:
Prédéclaration ou prototype de la fonction (avant le main( ) )
char cConvMaj( char cCarac );

Appel de la fonction (dans le main( ) ou dans une autre fonction)


char cLettre = 'a';
char cCaractere;
cCaractere = cConvMaj(cLettre); //Appel de la fonction.

Définition (après le main( ) )


char cConvMaj( char cCarac )
{
if((cCarac >= 'a') && (cCarac <= 'z')) // Minuscule?
cCarac = cCarac & 0xDF; // Met en Maj.
return (cCarac);
}

Notes: Par défaut, en C, on considère qu'une fonction retourne toujours une valeur (int).
Quand une fonction qui ne retourne rien, nous devrons le spécifier (void).
Une fonction ne peut pas être définie à l'intérieur d'une fonction.
Le nom des arguments donnés dans la prédéclaration (prototype de la fonction) n'est pas
utilisé par le compilateur. Il sert à rendre la lecture du programme plus clair pour le lecteur.
F.B. / D.C. / P.C. 9 Cours 247-236 Programme de TSO
06-12-2019 NotesCours236_FB.docx
2.1.1. Fonction et type VOID

Lorsqu'une fonction ne retourne aucune valeur, nous le spécifions avec le type void.
Ex.: void vCalcul(int iNombre);

Si une fonction n'a pas d'argument nous pouvons également utiliser le type void pour le spécifier.
Ex.: int iCalcul(void); // Ne reçoit rien et retourne un int.
void vTravail(void); // Ces fonctions ne retournent aucune valeur
void vAnalyse(void); // et ne reçoivent pas d'argument.

2.1.2. Retour de valeur par une fonction

Le type de la valeur retournée par une fonction est fixé dans le prototype de la fonction.
Il peut être un type de base (char, int ) ou un type abstrait (structure, union, etc (notions plus
avancées que vous verrez dans un autre cours)).

Pour retourner cette valeur nous utilisons le mot réservé "return" suivit du paramètre entre
parenthèse ou non. Normalement le return est placé à la fin de la fonction.
Ex.: return (expression_Retournee);

 La prédéclaration peut être répétée plusieurs fois dans le programme (idéalement une seule
fois avant le main( )), elle indique de quelle sorte de fonction il s'agit (type de retour, types
des paramètres…).
 La définition doit apparaitre une seule fois dans le programme (après le main( )), elle
indique ce que fait la fonction.
 L'appel peut se faire autant de fois que désiré.

 Le corps d'une fonction (sa définition) commence par {.


Il peut y avoir des déclarations de variables locales, suivi d'instructions et se termine par }.
 Les arguments reçus, sont des variables locales à la fonction.
 Les valeurs fournies à l'appel de la fonction y sont recopiés à l'entrée dans la fonction.
 Les instructions de la fonction s'exécutent du début du bloc délimité par {.
Elles s'exécutent jusqu'à return, ou la fin du bloc }.
 Si la fonction retourne une valeur, celle-ci doit figurer après le mot return et avant le ;
Ex.: return (valeur ou variable);
 Une fonction ne peut retourner qu'une seule valeur.

 Quand une fonction est appelée, l'exécution du programme est transférée à la première
instruction de cette fonction.
L'exécution de la fonction se termine après l'instruction return ou après la dernière instruction
du bloc de la fonction.
 Quand la fonction appelée est terminé, l'exécution dans la fonction appelante, reprend à
l'endroit de l'expression où l'appel avait été fait. La valeur retournée par la fonction peut être
alors utilisée comme opérande dans cette expression.

F.B. / D.C. / P.C. 10 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
2.1.3. Quatre exemples de fonctions :
1) Fonction ne recevant aucun argument et ne retournant rien. (void-void)

void vMessage(void)
{
printf("Bonjour le monde\n");
}

Exemple d'appel de cette fonction : vMessage();

2) Fonction recevant un (ou plusieurs) argument(s) et ne retournant rien. (void-paramètres)

void vDelai(unsigned int uiValDelai)


{
while (uiValDelai > 0)
{
uiValDelai = uiValDelai – 1;
}
}

Exemple d'appel de cette fonction : vDelai(10);

3) Fonction ne recevant aucun argument et retournant une valeur. (type-void)

unsigned char ucLireChiffre(void)


{
return (getkey() – 0x30);
}

Exemple d'appel de cette fonction : ucValeur = ucLireChiffre( );

4) Fonction recevant un (ou plusieurs) argument(s) et retournant une valeur. (type-paramètres)


float fFProduit(float fArgument1, float fArgument2)
{
float fResultat;
fResultat = fArgument1 * fArgument2;
return(fResultat);
}

Exemple d'appel de cette fonction : fX = fFProduit(2, 4.5); // fX type float.

ou encore : printf("Le double de %f est %f\n", fZ, fFProduit(fZ, 2));

F.B. / D.C. / P.C. 11 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
2.1.4. Fonction avec passage de paramètres par valeur:

Dans ce cours nous utiliserons les fonctions avec passage de paramètre pas valeur. La valeur qui
est transmise à la fonction est une copie de la valeur de base, ainsi il est impossible pour la
fonction qui reçoit le paramètre de modifier la valeur de base. Exemple :

void vNeModifiePas(UC ucX); // Prototype.

void main(void)
{
UC ucX = 1;
vNeModifiePas(ucX); // Appel de la fonction avec une image de ucX.
printf("%u", ucX); // Affiche : 1.
}

void vNeModifiePas(UC ucX)


{
ucX = 3;
printf("%u", ucX); // Affiche : 3.
}

Note: Ici on a 2 variables ucX, mais comme elles sont locales et même si elles ont le même nom,
elles sont vues comme deux variables différentes.

Pour le moment c'est la seul manière que nous allons utiliser les fonctions et pour qu'une fonction
appelée puisse transmettre une valeur à la fonction appelante, nous allons utiliser l'instruction
"return" ce qui permet de retourner une valeur, mais une seule. Ainsi les fonctions par passage de
paramètre peuvent prendre plusieurs paramètres mais ils ne peuvent modifier qu'une seule valeur
de la fonction qui l'a appelé.

Notes: Une fonction possède ses propres variables locales. Elle a accès aux variables globales
mais n'a pas accès aux variables locales des autres fonctions, pas même celles du "main"

Une fonction ne peut retourner qu'une seul valeur d’où l'utilité des pointeurs. (Vues plus
tard)

2.2. Entête de fonction

L'entête de fonction doit contenir au moins les items suivants:


 Nom de la fonction.
 Date de conception.
 Nom du programmeur.
 Description détaillée du problème.
 Le prototype.
 Les paramètres d'entrées et de sorties.
 Un historique des modifications.

F.B. / D.C. / P.C. 12 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
2.3. Analyse d'un problème (le dé). Algorithme et programme avec fonction.

2.3.1. Problème: Allumer les DELs pour faire afficher les 6 états d'un Dé.

Tout commence par le branchement des DELs.


Après analyse du problème le branchement suivant
est choisi. (DELs allumées sur un 1.)

On a donc 6 valeurs à sortir sur le port.


Grace aux branchements choisis, on peut trouver P1_0
une séquence entre deux valeurs à sortir. P1_1
P1_2
Bits du Port P1_3
3 2 1 0 Action pour prochaine valeur
0 0 0 1
0 0 1 0
P1_ 3 2 1 0 DÉ P1
0 0 1 1 0 0 0 1 1 0x01
0 1 1 0 0 0 1 0 2 0x02
0 0 1 1 3 0x03
0 1 1 1 0 1 1 0 4 0x06
1 1 1 0 0 1 1 1 5 0x07
1 1 1 0 6 0x0E
0 0 0 1

Nous pouvons donc faire ressortir l'algorithme pour résoudre ce problème:

DEBUT
De = 0x01
TANT QUE(VRAI)
SI(De == 0x0E) // Fin de sequence?
De = 0x01. // On recommence.
SINON // On decale ou Inc?
SI((De & 0x01) == 0x01) // Bit 0 = 1?
De = De << 1. // On decale.
SINON
De = De + 1. // On incremente.
FIN SI.
FIN SI.
FIN TANT QUE.
FIN

Évidemment, si on veut écrire le programme, il faudrait effectuer le changement d'état seulement


si le bouton est pesé et ne pas oublier de sortir la valeur de "De" sur le port 1.

Voici donc, à la page suivante l'algorithme au complet.

F.B. / D.C. / P.C. 13 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
2.3.2. Algorithme complet pour afficher un Dé.

DEBUT // Affichage du De.


De = 0x01
TANT QUE(VRAI)
P1 = De. // Allume le De.
SI(P3_2 == 0) // Bouton pese?
SI(De == 0x0E) // Fin de sequence?
De = 0x01. // On recommence.
SINON // On decale ou Inc?
SI((De & 0x01) == 0x01) // Bit 0 = 1?
De = De << 1. // On decale.
SINON
De = De + 1. // On incremente.
FIN SI.
FIN SI.
FIN SI.
FIN TANT QUE.
FIN

2.3.3. Et si on y allait avec des fonctions?

DEBUT // Affichage du De.


De = 0x01
TANT QUE(VRAI)
P1 = De. // Allume le De.
SI(P3_2 == 0) // Bouton pese?
De = CalculNext(De). // Trouve le suivant.
FIN SI.
FIN TANT QUE.
FIN // Du programme principal.

DEBUT CalculNext(De)

SI(De == 0x0E) // Fin de sequence?


De = 0x01. // On recommence.
SINON // On decale ou Inc?
SI((De & 0x01) == 0x01) // Bit 0 = 1?
De = De << 1. // On decale.
SINON
De = De + 1. // On incremente.
FIN SI.
FIN SI.

RETOURNE(De).

FIN CalculNext(De).

F.B. / D.C. / P.C. 14 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
3. La boucle "for"
Cette boucle nous est utile lorsque nous connaissons le nombre d'exécution de la boucle.
La syntaxe est de la forme:

Syntaxe : for( [Expression1] ; [Expression2] ; [Expression3] )


{
Instruction ou bloc d'instructions;
}

Description: Instruction est répétée tant que la valeur d'Expression2 est non nulle (VRAI).
Avant la première itération, Expression1 est exécutée. En général elle sert à initialiser
les variables de la boucle.
Après chaque itération de la boucle, Expression3 est exécutée. En général elle sert à
incrémenter le compteur de la boucle.
Exemple: for(i = 0; i < 23; i = i + 1)
{
Total = Total + 2;
}

Remarques: Toutes les expressions sont facultatives.


Si Expression1 n'existe pas il n'y aura pas d'initialisation.
Si Expression2 n'existe pas, elle sera supposée vraie (valeur 1) (boucle infinie).
Si Expression3 n'existe pas la variable testée devra être modifiée dans le corps de la
boucle.

En algorithme on pourrait écrire:


POUR (Init; Test; Incrément)
Bloc d'instruction à exécuter.
FIN POUR.

Exemple: POUR (i = 0; i < 23; i = i + 2) // Comme en C.


Total = Total + 2.
FIN POUR.

Autres exemples:
#1: int i, iValeur1, iValeur2 = 0, iValeur3 = 0;
for( iValeur1 = 0 ; iValeur1 < 4 ; iValeur1++)
{
iValeur2 = iValeur2 + 5;
iValeur3 = iValeur3 + 1;
}

#2: for(i=0, iSomme = 0; i < 10; i++)


{
iSomme = iSomme + iTab[i];
}

#3: for( i = 0 , j = 0 ; i < 10 ; i++ , j++ )


...

F.B. / D.C. / P.C. 15 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
3.1. Différence entre la boucle ‘for’ et la boucle ‘while’
Les boucles ‘while’ et ‘for’ peuvent être utilisé indépendamment dans tous les cas de
programmation. Cependant, pour des raisons de lisibilité et de compréhension rapide, les boucles
‘for’ seront utilisées seulement pour des boucles de comptages et les boucles ‘while’ pour des
boucles de comparaison ou de vérification.

Bonne utilisation Mauvaise utilisation


while(P3_2) for( ; P3_2 == 1 ; )
{ {
... ...
} }
while(1) // ou: while(TRUE) for(;;)
{ {
... ...
} }
for(i = 0; i < 10; i ++) i = 0;
{ while(i < 10)
... {
} ...
i++;
}
i = 0;
for(i = 0; i < 10; i ++); //Delai while(i < 10)
{
i++;
}

F.B. / D.C. / P.C. 16 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4. Architecture du DS89C450
4.1. Schéma Bloc

Core
Timers
Clock Internal Internal
Part MCU Speed Data 8-16bit VSUPPLY
Type Flash SRAM USART Features
Number Core (MHz) Proces (V)
(KB) (KB)
max
DS89C430 16 5V Tolerant I/O
256-Byte Internal
Scratchpad
4.5
General 8051 Data Pointer
33 8-bit 1 2 3 to
DS89C450 Purpose (CISC) 64 inc/decrement
5.5
MNL 33 Watchdog External
ENG 25 Interrupts
Reduced EMI Mode

F.B. / D.C. / P.C. 17 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.2. Caractéristiques

Key Features Applications/Uses


 High-Speed 8051 Architecture  Automated Test Equipment (ATE)
o One Clock-Per-Machine Cycle  Building Energy Control and Management
o DC to 33 MHz Operation (MNL)  Building Security and Door Access Control
o DC to 25 MHz Operation (ENG)  Consumer Electronics
o Single Cycle Instruction in 30ns  Data Logging
o Optional Variable Length MOVX  Gaming Equipment
to Access Fast/Slow Peripherals  HVAC
o Dual Data Pointers with Automatic  Industrial Control and Automation
Increment/Decrement and Toggle
 Magstripe Reader/Scanner
Select
 Motor Control
o Supports Four Paged Memory-
Access Modes  Programmable Logic Controllers
 Telephones
 On-Chip Memory  Uninterruptible Power Supplies
o 16kB/64kB Flash Memory  Vending
o In-Application Programmable  White Goods (Washers, Microwaves, Etc.)
o In-System Programmable
Through Serial Port
o 1kB SRAM for MOVX

 80C52 Compatible
o 8051 Pin and Instruction Set
Compatible
o Four Bidirectional, 8-Bit I/O Ports
o Three 16-Bit Timer Counters
o 256 Bytes Scratchpad RAM

 Power-Management Mode
o Programmable Clock Divider
o Automatic Hardware and
Software Exit

 ROMSIZE Feature
o Selects Internal Program Memory
Size from 0 to 64kB
o Allows Access to Entire External
Memory Map
o Dynamically Adjustable by
Software

 Peripheral Features
o Two Full-Duplex Serial Ports
o Programmable Watchdog Timer
o 13 Interrupt Sources (Six
External)
o Five Levels of Interrupt Priority
o Power-Fail Reset
o Early Warning Power-Fail
Interrupt
o Electromagnetic Interference
(EMI) Reduction

F.B. / D.C. / P.C. 18 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.3. Registres spéciaux du DS89C450 (Adresses finissant par 0 et 8  Bit adressables)

F.B. / D.C. / P.C. 19 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.4. Les Timers / Compteurs (0 et 1)
Le DS89C450 possède trois Timers/Compteurs 16 bits, notés Timer0, Timer1 et Timer2,
programmables en 4 modes différents. Le fonctionnement des deux premiers Timer/Compteurs
(TMR0 et TMR1) est déterminé par les registres de configuration TMOD et TCON. Ces deux
Timers ont un fonctionnement identique. Le registre TMOD détermine le mode de fonctionnement
des deux compteurs. Le registre TCON contient les bits de gestion du Timer0 et du Timer1.
La différence entre les termes Timer ou Compteur dépend du signal qui sera utilisé pour
incrémenter le Timer/Compteur. Les signaux horloges de ces circuits proviennent soit de l'horloge
interne du système (mode Timer) soit des entrées externes Timer0 (P3.4) et Timer1 (P3.5) (mode
Compteur). Il y a un indicateur de débordement (Flag overflow) de ces Timers lorsque le
compteur atteint sa valeur max + 1.
La valeur du compte du Timer0 est stockée sur deux octets (2 x 8 bits) TH0 et TL0 et celle du
Timer1 dans les deux octets TH1 et TL1 qui constituent leurs parties haute et basse.
On dit de ces compteurs qu’ils sont ‘Compteur-Up’ car les valeurs sont incrémentées.
Les registres utilisés avec les Timers/Compteurs 0 et 1 sont :
Registre (SFR) Description Adresse
TH0 Timer 0 High Byte Valeur Haute du Timer 0 0x8C
TL0 Timer 0 Low Byte Valeur Basse du Timer 0 0x8A
TH1 Timer 1 High Byte Valeur Haute du Timer 1 0x8D
TL1 Timer 1 Low Byte Valeur Basse du Timer 1 0x8B
TCON Timer Control État et activation Timer Pour signaux externes 0x88
TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
TMOD Timer Mode Timer1 Timer0 0x89
Gate C/T M1 M0 Gate C/T M1 M0
THx et TLx représente la valeur Haute et la valeur basse d’un compteur 16 bits

4.4.1. La Gestion des Timers / Compteurs


Pour bien travailler avec les Timer/Compteurs il y a deux éléments à respecter. L'initialisation et
l'utilisation. Ces deux étapes sont constituées de démarches essentielles. La première étape,
l'initialisation, comporte 4 éléments: le calcul du délai, le choix du Mode et de l'incrément,
l'ajustement du Flag et le contrôle. La deuxième étape, l'utilisation, compte 3 éléments : lecture du
Flag, ajustement du délai et le contrôle.
Initialisation: Calcul du délai
Mode et Incrément (Timer/Compteur)
Flag
Contrôle (Flag et Run)
Utilisation: Lecture du Flag
Ajustement du délai
Contrôle (Flag et Run)
Pour commencer voyons en détail chacun des éléments de l'initialisation et de l'utilisation des
Timers/Compteurs.

F.B. / D.C. / P.C. 20 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.4.2. Les quatre modes des Timers/Compteurs sont :
Mode 0 : 13 bits
Le Timer/Compteur 13 bits (8 bits avec un pré diviseur sur 5 bits). Ce mode sert surtout à garder la
compatibilité avec le 8048, le prédécesseur du 8051. Il est très peu utilisé dans les nouveaux
développements. Seuls les bits 0 à 4 de TLx sont utilisés.
Osc/12 C/T = 0
THx TLx TFx Interrupt
8 bits 5 bits
Tx pin C/T = 1 Contrôle
TRx
GATEx
INTx pin

Mode 1 : 16 bits
C'est le mode le plus utilisé. Avec 16 bits, on a une période de comptage de 65536 cycles. Ce mode
permet l’utilisation du Timer (mesure d’un temps). Ce mode permet la mesure du temps pendant
lequel un signal extérieur est au niveau haut. Permet de générer des évènements (OverFlow) à
intervalle régulier. En mode Compteur, permet de calculer le nombre de transitions (10) sur une
entrée (T0: P3_4 ou T1: P3_5).
Osc/12 C/T = 0
THx TLx Interrupt
8 bits 8 bits TFx
Tx pin C/T = 1 Contrôle
TRx

GATEx
INTx pin

Mode 2 : 8 bits auto-reload


Dans ce mode nous disposons d’un compteur à rechargement automatique c'est le compteur bas TLx
qui fonctionne en Timer/Compteur, Quand il déborde, au lieu d'être chargé par 0, il est chargé par la
valeur contenue dans le compteur haut THx. Ce mode est souvent utilisé pour définir la vitesse de
communication du port série.
Osc/12 C/T = 0
TLx TFx Interrupt
8 bits
Tx pin C/T = 1
Contrôle Reload
TRx

GATEx THx
8 bits
INTx pin

Mode 3 : Split-Timer
Dans ce mode les deux registres TL0 et TH0 du Timer 0 fonctionnent comme deux Timers 8 bits
indépendants. TL0 utilisent tous les bits de contrôle de Timer0 et active TF0 quand il déborde. Pour
sa part, TH0 utilisent les bits de contrôle de Timer1 (C/T1 et TR1) et active TF1 quand il déborde. En
conséquence, le Timer1 ne peut plus fonctionner en mode compteur, mais il peut fonctionner en mode
Timer pour les modes 0, 1 ou 2 sans action sur son flag ou sur l’interruption. Il peut aussi continuer à
fonctionner pour le baud rate du UART.

Dans ce cours nous utiliserons plus spécifiquement les modes 1 (16 bits) et 2 (8 bits auto-reload)

F.B. / D.C. / P.C. 21 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.5. Les registres des Timers décortiqués:

4.5.1. TMOD (89h) : Ce registre permet de configurer les Timers 0 et 1.

Timer 1 Timer 0
GATE C/T M1 M0 GATE C/T M1 M0

GATE == 0 : Timer fonctionne indépendamment de l'entrée INT1.


GATE == 1 : Timer fonctionne que si l'entrée INTx = 1 (Start/Stop sur P3_2 ou P3_3)
C/T == 0 : Timer est incrémenté par l'horloge système (mode Timer)
C/T == 1 : Timer fonctionne en compteur sur l'entrée Tx (P3_4 ou P3_5) (mode Compteur)
M1, M0 choix du mode de 0 à 3:
M1 M0 Mode Description
0 0 0 13-bit Timer.
0 1 1 16-bit Timer
1 0 2 8-bit auto-reload
1 1 3 Split timer mode

4.5.2. TCON 0x88 :

état et activation Timer indicateur pour signaux externes IT0 et IT1


TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0

TF1: Mis à 1 à chaque débordement du Timer 1.


C'est un flag (ou drapeau) qui peut être lue pour tester le débordement.

TR1: Permet de démarrer/arrêter (1 / 0) le Timer 1.


Doit être à 1 pour que le compteur fonctionne. (voir aussi le bit Gate (TMOD_7))

TF0: Mis à 1 à chaque débordement du Timer 0.


C'est un flag (ou drapeau) qui peut être lue pour tester le débordement.

TR0: Permet de démarrer/arrêter (1 / 0) le Timer 0.


Doit être à 1 pour que le compteur fonctionne. (Voir aussi le bit Gate (TMOD_3))

IE1: Flag qui indique qu'une interruption Externe 1 (/INT1) est active
IT1: Sélection du type de signal sur /INT1: Front descendant/Niveau 0
IE0: Flag qui indique qu'une interruption Externe 0 (/INT0) est active
IT0: Sélection du type de signal sur /INT0: Front descendant/Niveau 0

F.B. / D.C. / P.C. 22 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.5.3. Calcul de la valeur à mettre dans THx et TLx

Pour calculer la valeur de départ à mettre dans le Timer pour avoir un délai précis nous utilisons la
formule suivante:

Nombre de comptes = Délai (sec) * Fosc(Hz) / 12


Pour le Dallas, Fosc =11059200 Hz.

Avec un compteur 16 bits, on met dans THx:TLx = (65536 – Nombre de comptes) en Hexa.

Donc,
Si on cherche la valeur à mette dans le Timer :

Délai(sec) * Fosc( Hz)


Timer(THx : TLx)  65536 
12

Si on cherche le Délai :

(65536  Timer (THx : TLx )) *12


Délai(sec) 
Fosc( Hz )

Si le "Nombre de compte" est plus petit que 256 on peut utiliser un Timer 8 bits

Si on cherche la valeur à mette dans le Timer :

Délai(sec) * Fosc( Hz )
Timer(TLx)  256 
12

Si on cherche le Délai :

(256  Timer (TLx )) *12


Délai(sec) 
Fosc( Hz )

Note : Pour le Timer 8 bits, si on est en mode 2 (8 bits Autoreload) on met aussi la valeur
dans THx.

F.B. / D.C. / P.C. 23 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
4.6. Exercices sur les Timers

Exercice 1:
On désire faire un délai de 5 ms avec le Timer 0 et un cristal de 11.0592 MHz calculez les valeurs à
mettre dans TH0 et TL0 ?

Exercice 2:
Utilisation du Timer0 en mode 1. Complétez les commentaires et évaluez le délai du Timer0 ainsi
que la fréquence des pulse sur la pin P3_5 ? (délai = _____ ms, Fréquence = __________ Hz)

#include "DeclarationGeneral.h" //
//
void vInitT0(void); // Predeclaration de la fonction vInitT0
//
void main (void) //
{ //
vInitT0(); // Initialise le ________ en mode ___ ___ Bits
//
while (1) // Faire sans fin
{ //
if(TF0 == 1) // Debordement du __________ ( ms)?
{ //
TL0 = 0x00; //
TH0 = 0xA6; // Remet le d________ a 2_________
TF0 = 0; // Desactive le F_____ de d_________
P3_5 = P3_5 ^ 1; // Fait ____________________________
} //
} // Recommence
} //
void vInitT0(void) // Initialise le Timer___
{ //
TR0 = 0; // Arrete le T___________
TMOD = TMOD & 0xF0; // GATE = __  Timer actif, C/T = __  Mode Timer
TMOD = TMOD | 0x01; // Mode = __  Timer 16 bits.
TL0 = 0x00; // Valeur du Timer = 0xA600 --> Overflow:__________
TH0 = 0xA6; // (65536 - 0xA600) * 12 / 11059200 = _____________
TF0 = 0; // Desactive le F_____ de d_________
TR0 = 1; // Démarre le T__________
} //

Note: Il serait aussi possible de faire un délai plus petit et le compter plusieurs fois pour faire 25ms.

F.B. / D.C. / P.C. 24 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Exercice 3:
Que devez-vous modifier dans le programme précédant pour obtenir une fréquence, sur la pin P3_5,
de 800 Hz avec 50% de cycle de service ?

Exercice 4:
Que devez-vous modifier dans le programme précédant pour obtenir une fréquence, sur la pin P3_5,
de 1.6 kHz, avec 50% de cycle de service ?

Exercice 5:
Est-il possible de modifier le programme précédant pour obtenir une fréquence, sur la pin P3_5, de
800 Hz avec 25% de cycle de service ?

Exercice 6:
Que devez-vous modifier dans le programme précédant pour obtenir une fréquence, sur la pin P3_5,
d'environ 2 kHz avec 50% de cycle de service ?

Exercice 7: Trouvez la fréquence maximum pouvant être générée.

4.7. Résumé des Timers/Compteurs du DS89C450:


L'emploi des Timer/Compteur comporte 2 étapes: l'Initialisation et l'utilisation, dans lesquelles seront
abordées les 4 démarches suivantes:
1 - Calcul de la valeur a mettre dans THx et TLx selon le délai au compte désiré:
fosc
THx:TLx = (65536 ou 256) – ( Délai désiré (sec) * )
12
2 - Sélection du modes en fonction des besoins (M1 et M0):
Mode 0 = 13 bits, Mode-1 = 16 bits, Mode-2 = 8 bits auto-reload et Mode-3 = Split mode.
Dans le cours nous utiliserons seulement le mode 1 et le mode 2.
3 - Le comportement Timer ou Compteur:
Timer: avec fosc / 12 et Compteur: par une broche externe.
Valeur à mettre dans bit C/T de TMOD (0x89): 0  Timer et 1  Compteur.
T1 T0
TMOD (0x89) = Gate C/T M1 M0 Gate C/T M1 M0 Gate = 0: indépendant de INTx.
4 - Configurer et Lire Flag/Run:
TCON (0x88): TF1, TR1, TF0, TR0, IE1, IT1, IE0, IT0
(4 derniers, pas cette session-ci)

F.B. / D.C. / P.C. 25 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
5. Lecture des interrupteurs: Détection Front et Anti-Rebond
5.1. La lecture des interrupteurs.
Lors de la lecture d'interrupteurs, doit-on toujours utiliser un anti-rebond et une détection de front?
La réponse est évidemment non!

L'utilisation ou non, d'une ou des deux techniques dépend de l'application.


Exemple:
SI(le bouton est pesé) Allume la Led.
SINON Eteint la Led.

Ici, pas besoin d'aucune technique.

Par contre dans cet exemple:


SI(le bouton est pesé) Compte = Compte + 1.

Ici, il faudra détecter un front et même utiliser l'anti-rebond sous peine de voir augmenter
le Compte beaucoup trop vite.

Exemple avec augmentation du compte quand on détecte seulement un front de descente:

5Volts

0Volt
Bouton pesé Bouton relâché
Sans anti-rebond: Compte serait rendu à 3 serait rendu à 5

5.2. Technique logicielle, déjà vue, pour détecter Front avec Anti-rebond:
SI(ucAncienEtat != ucBoutonMarche) // Changement du bouton Marche?
ucAncienEtat = ucBoutonMarche. // Sauvegarde le nouvel etat.
Delai de 10ms. // Anti-rebond (99% du programme).
SI(ucBoutonMarche == 1) // Front de monte?
// Mettre ici le code a executer si le bouton a ete relache.
SINON // Front de descente.
// Mettre ici le code a executer si le bouton a ete pese.
FIN SI.
FIN SI.

Le gros désavantage de cette technique est que nous restons pris dans le délai de 10ms. Et ce délai
représente environ 99% du temps d'exécution de notre programme. En d'autres mots, on passe 99% de
notre temps à ne rien faire.

Évidemment si on veut faire du contrôle à grande vitesse, cette technique n'est plus valable.

F.B. / D.C. / P.C. 26 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
5.3. Techniques matérielles:
Parfois, nous pouvons régler le problème de l'anti-rebond de façon matériel. Voici quelques circuits:
OU OU

Base 3k3 Schmit Trigger Complet


T = RC 470R S

33uF

p737 Trussard R

Pour le choix de R et C: (E = 5Volts)

Charge (0 à 1):
V=E(1-e-temps/RC)
V(RC) = 0.63E (ici: 0.63*5 = 3.2V)
Temps = -RC*ln(1-V/E)

Décharge (1 à 0):
Temps1/2E = RC*ln2 (1/2E = 2.5V)
V(RC) = 0.37E (ici: 0.37*5 = 1.85V)
Donc on cherche RC ≈ 0.01 (10ms).

Schmit: 1.6V: L'entrée passe au niveau Haut


0.8V: L'entrée passe au niveau Bas

Technique: Lorsqu'il y a changement de l'interrupteur, il faut trouver une valeur de RC qui nous
permet de rester au même niveau logique durant au moins 10ms.

Passage de 0 à 1. Il faut que le voltage à l'entré reste inférieur à 0.8V durant au moins 10ms.
Avec l'équation: Temps = -RC*ln(1-V/E)
On trouve RC = -Temps / ln(1-V/E) = -0.01Sec / ln(1-0.8/5) = -0.01 / ln(1-0.16)
= -0.01 / ln(0.84) = -0.01 / -0.1743 = 0.057 = RC
Si C = 10uf  R = 0.057 / 0.00001 = 5735 ohms. (5600 valeur standard 10%)
Donc, R = 5.6K et C = 10uf.

Passage de 1 à 0. Il faut que le voltage à l'entré reste supérieur à 2V durant au moins 10ms.
Avec l'équation: Temps1/2E = RC*ln2 (Temps1/2E = 0.01sec pour avoir 2.5V)
On trouve RC = 0.01sec / ln2 = 0.01sec/0.693 = 0.0144 = RC
Si C = 10uf  R = 0.0144 / 0.00001 = 1442 ohms
Donc, R = 2K et C = 10uf feraient l'affaire.

Pour avoir les mêmes valeurs dans les deux cas, on prend C= 10uf et R = 5.6K.
Avec R = 5.6K le passage du niveau 1 au niveau 0 sera tout simplement plus long.

Pour le Schmit (avec les valeurs du schéma):


(0 à 1) On a 0.38V après 10ms (formule: V=E(1-e-temps/RC) , où: RC = 3770*33*10-6 = 0.12441)
(1 à 0) Après 10.7ms on est à 2.5V (formule: Temps1/2E = RC*ln2. où: RC = 470*33*10-6 = 0.01551)

F.B. / D.C. / P.C. 27 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
5.4. Technique de l'anti-rebond par compteur:
Malheureusement, nous n'avons souvent pas accès aux circuits électroniques. Il nous faudrait donc
une solution logicielle. Une manière simple d'attendre le 10ms sans bloquer dans un délai, est de
compter le nombre de fois qu'on passe dans la boucle principale du programme lorsque le bouton a été
pesé. Si ça fait 100 ou 200 fois qu'on passe dans la boucle, on peut penser que le rebondissement est
terminé.
Voici un algorithme qui décrit le fonctionnement de l'anti-rebond par compteur:
Rebond = 0.
...
TANT QUE(VRAI)
...
LIRE Boutons sur Port.
SI(Boutons pesés) // Un ou plusieurs boutons.
SI(Rebond < 250)
Rebond = Rebond + 1.
SI(Rebond == 200) // 200 Boucles? On espère ≈10ms.
ICI, Action à faire quand rebondissement terminé.
FIN SI.
FIN SI.
SINON // Bouton pas pese.
Rebond = 0. // On désactive anti-rebond.
FIN SI.
...
FIN TANT QUE.

Exemple: La boucle principale de mon programme prend 50us (0.05ms) à s'exécuter.


Si je pèse sur un bouton voici ce à quoi ça ressemble une fois agrandi.
La largeur d'une pulse dure 1ms (1ms / 0.05ms = 20 boucles).

200 * 0.05ms = 10ms

Rebond: 0,1….20,0…0,1….20,0.0,1,2,3….. ..…199,200,201,…

Action ici
L'avantage de cette technique est que nous ne restons pas pris dans une boucle de délai.
L'inconvénient de la technique c'est le délai qui est difficile à calculer. Ça dépend de la boucle totale
du programme. De plus, le temps d'exécution de la boucle du programme peut varier (changement aux
entrées, affichage de message, etc…).
Pour avoir un délai précis on pourrait utiliser un Timer. Mais les Timers sont très utilisés en contrôle,
il est préférable de les garder pour des applications spécifiques.
Le désavantage des anti-rebonds, c'est que s'ils sont mal ajustés, on peut perdre des informations
pertinentes (un signal qui revient vite pourrait ne pas être lu). Ou avoir des rebonds si trop court.

F.B. / D.C. / P.C. 28 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
6. Les Tableaux
6.1. Généralités:
Un tableau est un regroupement, dans une même variable, de plusieurs données, de même type.

La déclaration: Type Nom[NOMBRE_ELEMENTS];

Exemple: unsigned char ucTab[10];


Ceci réserve, en mémoire, un espace contigu pouvant contenir 10 éléments (ici 10 octets).
Le premier élément est ucTab[0] et le dernier est ucTab[9] (de 0 à 9 donne 10 éléments).
ATTENTION: Il n'y a aucune vérification du débordement de tableau.
En utilisant ucTab[10] ou plus, aucune erreur ne sera signalée et vous utiliserez une
partie de mémoire qui a certainement été réservée pour autre chose.

N’oubliez pas, avec les tableaux:


 L'indice du premier élément est 0
 Le dernier élément du tableau est [NOMBRE_ELEMENTS – 1]

L'identificateur ucTab est une variable spéciale, dont la valeur est l'adresse de ucTab[0].
Il ne sera donc pas possible de faire figurer ucTab dans le premier membre d'une affectation (Lvalue).
Exemple: ucTab = 10; // Donne l'erreur "C213: left side of asn-op not an lvalue".
// asn-op: assignement operator ( = ).
// lvalue: quelque chose qui peut être à gauche du signe =
// donc qui peut être modifié.

6.2. Initialisation des tableaux


Nous pouvons initialiser les tableaux au moment de leur déclaration.
Cette façon de faire nous assure que la variable ne sera jamais utilisée sans être initialisée.
Ex.: unsigned char ucTable[3] = {'1', 0x32, 51}; // 3 éléments, 3 octets.
unsigned int uiTableau[5] = {1000, 2000, 3000, 4000, 5000}; // 5 éléments, 10 octets.
char cTableauCaractere[10] = {"Bonjour !"}; // 9 caractères, 10 éléments, 10 octets.
Note: En dehors de la déclaration, il n'est pas possible d'affecter une chaîne de caractère à un tableau.
Il faudra faire une recopie de la chaîne que nous voulons écrire dans le tableau octet par octet.
Si on initialise un tableau, alors, il n'est pas nécessaire de spécifier la dimension.
UI uiChiffres[]={0,1,2,3,4,5,6,7,8,9}; //Réserve 10 espaces mémoire (20 octets)

Mais si la dimension est donnée, et qu'elle est supérieure au nombre de valeurs données, les suivantes
seront indéterminées.
UI uiTableau[20]={1,2,3}; // 3 espaces définis les 17 autres indéterminés.

Si la dimension du tableau est inférieure aux nombres de données, il va y avoir une erreur.
int iBad[2] = {1,2,3,4}; // error C242: 'array[]': too many initializers.

F.B. / D.C. / P.C. 29 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Si l’initialisation des valeurs d’un tableau n'est pas faite à la déclaration, il faudra alors procéder avec
une boucle, case par case. Chaque valeur d’un tableau peut être initialisée individuellement.

Les éléments du tableau sont rangés en mémoire de façon consécutive.

Exemple : Considérons le programme suivant :

void main()
{
UI i;
int iTb[10] = {1,2,3,4,5,6,7,8,9,10}; // Combien d'octets?

vInitPortSerie();
iTb[7] = iTb[4] * 3; // Met (5 * 3) à la place de 8.

printf("%X,%X,%X,%X,%X,%X.\n",iTb[9],iTb[8],iTb[2],iTb[7],iTb[1],iTb[0]);

for(i=0; i<10; i++) //


{ //
iTb[i] = 0; // Initialise le tableau à 0.
} //
}

Résultat : A, 9, 3, F, 2, 1.

6.3. Tableau à plusieurs dimensions

Un tableau peut avoir plusieurs dimensions, exemple : 3 x 2 = 3 lignes par 2 colonnes.

UC ucTab[3][2] = {{1,2}, {3,4}, {5,6} }; // Initialise le Tableau.


printf("\n%u ", (int) ucTab[1][1]); // Affiche : 4
printf("\n%u ", (int) ucTab[2][1]); // Affiche : 6
printf("\n%u ", (int) ucTab[1][2]); // Affiche : 5 // Incorrecte
printf("\n%u ", (int) ucTab[3][1]); // Affiche : ? // hors zone.

Et même plus, 2 x 3 x 4 (et même 500 x 7000 x 23 x 1000 etc…)


UC ucTab[2][3][4] = { { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9,10,11,12} },
{ {13,14,15,16}, {17,18,19,20}, {21,22,23,24} } };

Les accolades permettent au programmeur de mieux visualiser les données, ils ne sont pas
obligatoires, mais ils peuvent êtres mal placés. On ne peut pas faire ce qui suit :
unsigned char ucTab[3][2] = {{1,2},{3},{4,5,6}};
unsigned char ucTab[2][3] = {{1,2},{3,4},{5,6}};

Vous obtiendrez le message: error C242: 'array[]': too many initializers

Par contre, ceci est correct:


unsigned char ucTab[3][2] = {1,2,3,4,5,6};

F.B. / D.C. / P.C. 30 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
6.4. Les chaines de caractères
Les chaines de caractères sont un type de tableau qui contient des variables de type caractères pour
former un mot ou une phrase. Pour afficher une chaine de caractères avec "printf" on utilise %s :

Exemple 1:
UC ucChaine1[] = "Salut";
UC ucChaine2[] = "Bonjour";

printf("\n%s",ucChaine1); // Affiche : Salut.


printf("\n%s",ucChaine2); // Affiche : Bonjour.

Note: Un tableau formé d'une chaine de caractères, contient le nombre de caractères de la chaine + 1
car le compilateur ajoute toujours un zéro ('\0') à la fin pour être capable de retrouver la fin
de la chaine lors de la manipulation de cette dernière. Ainsi dans l'exemple précédant
ucChaine1 contient 6 octets et ucChaine2 en contient 8. Si vous voulez travailler sur les chaines
de caractères avec les fonctions du compilateurs, vous devez inclure la librairie <string,h>.

Il est possible de mettre des messages dans des tableaux à plusieurs dimensions pour pouvoir
sélectionner le message avec un indice, mais pour cela vous devez avoir des messages avec le même
nombre de caractère. Dans ce cas vous devez absolument indiquer les dimensions du tableau et
n’oubliez pas d’ajouter un espace pour le '\0', sinon le printf ne saura pas ou se termine la chaine et il
affichera n’importe quoi.

Vous devez soit, placer des espaces à la fin des chaines pour les compléter.
Exemple 2:
UC ucMessage[3][10] = {"Salut ", //
"Bonjour ", //
"Bye Bye !"}; // 9 caracteres + 1 pour le zero = 10.

for(i=0; i<3; i++)


{ // Affiche : Salut____
printf("\n%s",ucMessage[i]); // Bonjour__
} // Bye Bye !

Ou, vous devez inclure vous-même le caractère de fin de chaine ('\0').


Exemple 3:
UC ucMessage[3][10] = {"Salut\0", //
"Bonjour\0", //
"Bye Bye !"}; // 9 caracteres + 1 pour le zero = 10.

for(i=0; i<3; i++)


{ // Affiche : Salut
printf("\n%s",ucMessage[i]); // Bonjour
} // Bye Bye !

F.B. / D.C. / P.C. 31 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
6.5. Localisation de nos tableaux (variables)
Comme les tableaux peuvent prendre énormément de place en mémoire : UI uiGrandTab[10000];
Il sera justifié d'indiquer l'endroit ou localiser le tableau pour ne pas remplir les différentes zone de
mémoire du microcontrôleur.
Il existe dans le compilateur Keil des mots réservés qui nous permettent de spécifier à quel endroit
nous voulons placer les variables (memory type: data, idata, xdata, pdata et code).
Lorsqu'aucun type n'est spécifié, le type par défaut du model de mémoire du projet est utilisé.
À date vous avez toujours utilisé le model Small qui place les variables en data.

Nous allons garder ce modèle:

Si on veut placer nos variables ailleurs, il faut le spécifier lors de la déclaration de la variable.
Exemple: unsigned char data ucToto; // ucToto sera place dans la zone data.
Voici les différentes zones de mémoires accessibles présentement avec votre Kit (interne):

idata

Type de mémoire: data, idata xdata, pdata code

Dans les faits, nous allons spécifier le type de mémoire seulement si nous voulons les placer dans la
mémoire flash (code), ou en RAM externe (xdata).
Exemples:
UI uiTab1[10]; // Localisé en RAM interne du Dallas (data <= 128 octets).
UI xdata uiTab1[10]; // En RAM externe du Dallas ou dans le 1ko interne.
UI code uiTab[3]= {4,7,1}; // En R0M(ou Flash) interne du Dallas (constant).

Pour le xdata, il y a 1024 octets disponible à l'intérieur du Dallas.


Pour pouvoir l'utiliser, il faut placer le bit0 du registre PMR à 1.
PMR = PMR | 0x01; // Active xdata interne (1ko de 0000 à 03FF).
Et il faut avertir le compilateur en cochant dans les options de la cible.
Ainsi, vous pourrez utiliser le type xdata même si vous n'avez pas de circuits de mémoires externes
(limité à 1024 octets si pas de circuits externe).
F.B. / D.C. / P.C. 32 Cours 247-236 Programme de TSO
06-12-2019 NotesCours236_FB.docx
Notez que l'accès à la mémoire xdata est plus lent que dans la mémoire data.
(data: 2 clk, xdata interne: 3 clk, xdata externe: 9 clk).

Exemple avec l'instruction ucToto = ucToto + 1;


Déclaration: UC ucToto; // ucToto en mémoire data.

ucToto = ucToto + 1;
// 050C INC 0x0C // 2 clk, 0x0C: Adresse de ucToto.

Déclaration: UC xdata ucToto; // ucToto en mémoire xdata.

ucToto = ucToto + 1;
// 900000 MOV DPTR,#0x0000 // 3 clk, 0x0000: Adr ucToto.
// E0 MOVX A,@DPTR // 3 clk int, 9 clk ext.
// 04 INC A // 1 clk.
// F0 MOVX @DPTR,A // 3 clk int, 9 clk ext.
____ ____
// Total: 10 clk int, 22 clk ext.

MOVX externe: 9 SYSCLK (par défaut 1 stretch cycle):

1 9

Note: Pour un accès plus rapide on peut mettre le stetch à 0.


CKCON = CKCON & 0xF8; // Met le strech à 0.
// MOVX dure 5 CLK.

Pourquoi utiliser le type de mémoire "code"?


Souvent dans nos programmes nous voudrons utiliser une grande quantité de données comme
références. Ces données ne seront jamais modifiées. Il sera alors judicieux de les placer dans la
mémoire ROM (code).
Exemple:
int i;
UC code ucMenu[4][25] = {" 1- Mode Manuel ",
" 2- Mode Automatique ",
" 3- Vide la mémoire ",
" 4- Affiche les données "};
for(i=0; i<4; i++)
{ //
printf("%s\n",ucMenu[i]); // Affiche le menu.
}

Le tableau contient 100 octets (4*(24+1)). Si on ne met pas "code" lors de la déclaration, nous
utiliserons une très grande partie de la mémoire RAM interne (data: 128 octets).

F.B. / D.C. / P.C. 33 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
7. Les claviers
7.1. Lecture du clavier de l'ordinateur (PC):
À date, avec notre kit Dallas, nous avons lu les touches du clavier de l'ordinateur à l'aide des fonctions
getchar( ) et getkey( ). La première fonction fait l'écho du caractère entré sur l'écran de l'ordinateur et
la seconde ne fait pas d'écho.

Les deux fonctions attendent qu'une touche soit pesée avant de nous retourner son code ASCII.
Donc nos programmes "bloquent" si on appelle getchar( ) ou getkey( ). Ceci n'est pas idéal pour faire
du contrôle.

Dans la bibliothèque de fonctions utilisée sur l'ordinateur (PC), il y avait une fonction "kbhit( )" qui
vérifiait si une touche était pesée sans bloquer le programme. Elle retournait VRAI si une touche était
pesée et FAUX si aucune touche n'était pesée. Ainsi on pouvait appeler getchar( ) ou getkey( )
seulement si une touche était pesée. Notre programme ne bloquait donc pas.

Malheureusement, cette fonction n'existe pas pour notre kit, car elle fait accès direct au matériel et
évidemment le matériel de notre kit est différent du matériel du PC.

Il est cependant possible d'écrire une fonction qui fera l'équivalent de kbhit( ). Pour notre kit les
caractères tapés au clavier nous arrivent par le port série. Il s'agit donc de vérifier si le port série a reçu
un caractère. Tout comme le Timer qui a un flag qui nous avertit qu'il y a eu débordement, le port
série a un flag qui nous dit qu'un caractère est arrivé. Ce flag s'appelle RI_0 pour le port série 0. Si ce
flag est actif (1), ça indique qu'un caractère est arrivé et qu'on peut le lire avec getchar( ) ou getkey( ).

Voici donc la fonction équivalente à kbhit( ):

#define VRAI 1
#define FAUX 0

UC ucKbHit(void)
{
if(RI_0) return VRAI; // Octet reçu par le port série?
else return FAUX;
}

Et voici comment l'utiliser:

if(ucKbHit())
{
ucTouche = getkey( );
}

Ainsi, on peut aller lire le clavier seulement si une touche a été pesée ce qui permet à notre
programme de rouler sans bloquer.

F.B. / D.C. / P.C. 34 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
7.2. Clavier à brancher:
Souvent dans une application de contrôle, aucun ordinateur n'est branché sur notre kit. Si on veut
entrer des informations il nous faudra un autre moyen que le clavier du PC.

Il y a, évidemment, les boutons mais on est vite limité. Le clavier à matrice comportant 10, 12, 16
touches et même plus, sera un outil très intéressant.

Principe:

Un clavier multitouche comporte plusieurs broches. Il est possible avec des circuits électroniques
simples de réduire ce nombre. Ceci nous permettra de les brancher sur un processeur sans trop utiliser
de bits de port.

Exemple (clavier 10 touches):


VCC

*
4k7

Code binaire de la touche


9 (inverse)

8 Encodeur
7 10
6 5
4
9
8 14 1
5 3
2
7
6
D
C
6
7
2
3
4 1
13
5
4
B
A
9 4
8
3 12
11
3
2
5
6
LS30

2 1 11
12 Detecteur
LS147
1 de touche

0
VCC

* Il y a une resistance de "Pull-up" par entree

CLAVIER

L'encodeur sort la valeur 0xF si aucune touche n'est pesée. Si on pèse sur une des touches (1 à 9), on
retrouve à la sortie du LS147 la valeur binaire inversée de la touche.

Aussitôt qu'une des touches (0 à 9) est pesée, le détecteur de touche sort un 1.

Il nous reste à brancher les 5 fils sur un port du processeur et à vérifier si la sortie du LS30 passe à 1.

Ceci indique qu'une touche est pesée, il nous reste à lire la sortie du LS147 et à l'inverser pour avoir le
numéro de la touche.

F.B. / D.C. / P.C. 35 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
7.3. Clavier à matrice:

7.3.1. Décodé avec des circuits de bases.

Un clavier à matrice comporte plusieurs touches qui sont disposées en lignes et en colonne. Dans
l'exemple suivant le clavier a 16 touches organisées en 4 colonnes et 4 lignes donc 8 broches. Ici
aussi, avec des circuits logiques de base, il est possible de réduire les nombre de broches à brancher au
processeur.
Exemple (clavier 16 touches):

Ici, la sortie du LS13 indique qu'une touche a été pesée (pulse à 1).

Le numéro de la touche se trouve à la sortie du compteur modulo 4 (S3 et S2 numéro de la colonne) et


de l'encodeur 4 à 2 (S1 et S0 numéro de la ligne).

Exercices: Quel est le code de la touche 'B'?

Quel est le code de la touche 'L'?

F.B. / D.C. / P.C. 36 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
7.3.2. Décodé avec un port du microcontrôleur.

Il est possible de remplacer les 4 circuits de l'exemple précédent avec un seul port du Dallas
VCC

A B C D P1_0

E F G H P1_1

I J K L P1_2

M N O P P1_3

P1_4

P1_5

P1_6

P1_7

Le principe ressemble au circuit précédant.

On va mettre une colonne à 0 et vérifier si une ligne est à 0. Ceci va indiquer qu'une touche a été
pesée. Si aucune touche n'est pesée, on met la colonne suivante à 0 et ainsi de suite pour les 4
colonnes. Le tout pourrait être fait dans une fonction ucLireClavier( ).

Fonctionnement de la fonction:
 Si une touche est pesée, la fonction retourne son code (0 à 15 ou encore le code ASCII).
 Si aucune touche n'est pesée, la fonction retourne un code spécial (exemple 0xFF).
 Si plus d'une touche est pesée, la fonction fait comme si aucune touche n'est pesée.

Si on veut retourner le code ASCII, on peut se servir d'un tableau comme celui-ci en utilisant le
numéro de la touche pour aller chercher un élément dans le tableau.
UC code ucTouche[4][4]={'1','2','3','A', // Definition des touches
'4','5','6','B', // du clavier.
'7','8','9','C',
'*','0','#','D'};

ou

UC code ucTouche[16] = {'1','2','3','A', // Definition des touches


'4','5','6','B', // du clavier.
'7','8','9','C',
'*','0','#','D'};

F.B. / D.C. / P.C. 37 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
7.3.3. Décodé avec un 74C922.

Le 74C922 est un circuit utilisé pour décoder un clavier à matrice pouvant aller jusqu'à 16 touches.

Il contient une horloge interne, des résistances de pull-up internes, un circuit d'anti rebondissement,
une sortie 3-states pouvant être relié à un bus de donnée, une sortie indiquant qu'une touche a été
pesée, un registre interne qui mémorise la dernière touche (même si elle est relâchée).

MM54C922/MM74C922 16-Key Encoder


General Description
These CMOS key encoders provide all the necessary logic An internal register remembers the last key pressed even
to fully encode an array of SPST switches. The keyboard after the key is released. The TRI-STATE outputs provide
scan can be implemented by either an external clock or for easy expansion and bus operation and are LPTTL
external capacitor. These encoders also have on-chip pull- compatible
up devices which permit switches with up to 50k on resis-
tance to be used. No diodes in the switch array are needed Features
to eliminate ghost switches. The internal debounce circuit  50 kΩ maximum switch on resistance
needs only a single external capacitor and can be defeated  On or off chip clock
by omitting the capacitor. A Data Available output goes to  On-chip row pull-up devices
a high level when a valid keyboard entry has been made.
 2 key roll-over
The Data Available output returns to a low level when the
 Keybounce elimination with single capacitor
entered key is released, even if another key is depressed.
The Data Available will return high to indicate acceptance  Last key register at outputs
of the new key after a normal debounce period; this two-  TRI-STATE outputs LPTTL compatible
key roll-over is provided between any two switches.  Wide supply range 3V to 15V
 Low power consumption

Connection Diagrams
Pin Assignment for
Dual-In-Line Package

11 17
10 X1 DOA 16
8 X2 DOB 15
7 X3 DOC 14
X4 DOD
1 12
2 Y1 DA
3 Y2
4 Y3
Y4
13
OE
5
6 OSC
KBM

74C922

F.B. / D.C. / P.C. 38 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Diagramme Bloc (922):

MSB
Key
Array

Table de vérité (922):


0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Y1,X1 Y1,X2 Y1,X3 Y1,X4 Y2,X 1 Y2,X2 Y2,X3 Y2,X4 Y3,X1 Y3,X2 Y3,X3 Y3,X4 Y4,X1 Y4,X2 Y4,X3 Y4,X4
A 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
O B 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
U C 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
T D 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

Formes d'ondes:

F.B. / D.C. / P.C. 39 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Caractéristiques typiques:

Typical FSCAN vs COSC Typical Debounce Period vs CKBM

Applications typiques:

Échange synchrone Accès avec polling (Port)

Accès synchrone au BUS Accès asynchrone au BUS

___
RD + CS

F.B. / D.C. / P.C. 40 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
8. La structure conditionnelle "switch"
Le langage C présente une structure conditionnelle particulière, le switch. Cette structure est
particulière dans le sens où elle permet de comparer une variable à plusieurs valeurs entières.
switch(Variable_A_Tester)
{
case Valeur1: Instructions à exécuter si la variable vaut Valeur1
break;

case Valeur2: Instructions à exécuter si la variable vaut Valeur2


break;

default: Instructions si la variable différente de Valeur1 et Valeur2


break;
}

 Une structure switch peut avoir autant de case que vous le souhaitez.
 Le cas "default" est optionnel.
Si vous le mettez, les instructions lui correspondant seront exécutées si la variable ne vaut aucune
des valeurs précisées dans les autres cas.
Si vous ne le mettez pas et que la variable est différente des valeurs précisées dans les autres cas,
rien ne se passera.
 Il est important de préciser que le switch permet de comparer une variable qu'à des valeurs
ENTIERES! Il est impossible d'utiliser cette structure conditionnelle pour comparer une variable à
un point flottant, par exemple!

Et voici un exemple d'utilisation de la structure conditionnelle switch, sans le cas default:


UI uiVarTest = 10;

switch(uiVarTest)
{
case 5:
printf("uiVarTest vaut 5\n");
break;

case 10:
printf("uiVarTest vaut 10\n");
break;

case 15:
printf("uiVarTest vaut 15\n");
break;
}

Étant donné que la variable uiVarTest vaut 10, et que l'on a un cas qui correspond à cette valeur, on
affichera le message: "uiVarTest vaut 10".

Note : Pour l’algo, on va utiliser une syntaxe semblable : switch(variable)


case 1:
break.
FIN switch.

F.B. / D.C. / P.C. 41 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
À présent, si la variable ne correspond à aucune des valeurs proposées, toujours sans cas default :
int iTest = 20;

switch(iTest)
{
case 5:
printf("iTest vaut 5\n");
break;

case 10:
printf("iTest vaut 10\n");
break;
}

Ici, iTest vaut 20, mais aucun cas correspondant à cette valeur. On n'affichera donc rien à l'écran.

Pour pallier à tous les cas non traités individuellement, il est possible d'utiliser une option par défaut,
qui s'écrit de la manière suivante :
int iTest = 20;

switch(iTest )
{
case 5:
printf("iTest vaut 5\n");
break;

case 10:
printf("iTest vaut 10\n");
break;

default:
printf("iTest ne vaut ni 5 ni 10\n");
break;
}

Ici encore, aucun cas ne correspond de manière précise à la valeur 20. Puisque l'on a un cas default,
c'est donc celui-ci qui sera exécuté, et on affichera le message: "iTest ne vaut ni 5 ni 10".

F.B. / D.C. / P.C. 42 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Notez que l'instruction break à la fin de chaque cas est optionnelle.
Le break permet de quitter une structure de contrôle.
Donc, quand on le met à la fin d'un "case", ça termine le "case" et donc le "switch".
Si on ne le met pas, il se passera quelque chose expliqué dans l'exemple suivant:
int iPasBreak = 5;

switch(iPasBreak)
{
case 5:
printf("iPasBreak vaut 5\n");
// Volontairement, on omet le break !

case 10:
printf("iPasBreak vaut 10\n");
break;

case 15:
printf("iPasBreak vaut 15\n");
break;
}

Qu'est-ce qui se passe ici ?


La variable "iPasBreak" vaut 5. On entrera donc dans le cas correspondant, et on affichera le message
"iPasBreak vaut 5". Puisqu'on n'a pas d'instruction break à la fin de ce cas, on ne quittera pas la
structure de contrôle et on continuera à exécuter les instructions du "case: 10". On affichera donc le
message "iPasBreak vaut 10" ! Une fois ceci fait, on parviendra à une instruction break, et on quittera
la structure "switch".

Il est parfois utile de ne pas mettre l'instruction break. Mais, souvent, en particulier pour les débutants,
c'est un oubli qui entraîne de drôles de résultats à l'exécution du programme ! Soyez prudents.

Exemple où le break est omis de façon intentionnel.


cTouche = getkey();
switch(cTouche)
{
case 'a':
case 'A':
// Instructions a faire si 'a' ou 'A' est entrée au clavier.
break;

case 'b':
case 'B':
// Instructions a faire si 'b' ou 'B' est entrée au clavier.
break;
...
}

Comme on peut le voir, la structure de contrôle "switch" sera utile pour la lecture d'un clavier.

F.B. / D.C. / P.C. 43 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
9. La boucle "do while"
La structure de boucle, que nous allons maintenant étudier, est "do...while", que l'on pourrait, en
français, appeler "FAIRE... TANT QUE".
Ici encore, les instructions constituant la boucle sont exécutées tant que la condition de boucle est
vraie. Cela dit, contrairement à while, avec do...while, la condition est évaluée à la fin de la
boucle ; cela signifie que les instructions correspondant au corps de la structure de contrôle seront
toujours exécutées au moins une fois, même si la condition est toujours fausse !
La syntaxe correspondant à cette structure répétitive est la suivante :
do
{
Instruction à exécuter tant que la condition est vraie.
}while( Condition ); // Notez le ;

Prenons un exemple, qui affiche les valeurs décimales et hexadécimales du code ASCII des touches
que nous entrons au clavier, en utilisant une boucle itérative de la forme do...while.
On quitte la boucle une fois que nous entrons la touche 'ESC'
#define ESC 27
do
{
a = getchar();
printf("%c = %bu = %bX\n", a, a, a);
}while(a != ESC);

Pour obtenir le même résultat avec une boucle while, nous devrions ajouter une instruction avant
d'entrer dans la boucle pour s'assurer d'y entrer.
#define ESC 27
a = 0;
while(a != ESC)
{
a = getchar();
printf("%c = %bu = %bX\n", a, a, a);
}

Ainsi la boucle do...while garantie que les instructions ci trouvant seront exécuté au moins une fois.

Dans l'exemple suivant, nous pouvons constater que l'on entre dans la boucle, alors que la condition
n'est pas vraie. Ce qui nous montre bien que la condition de boucle est testée en fin de boucle, et que
les instructions de la boucle sont toujours exécutées, au moins une fois.
a = 0;
do
{
printf("On est dans la boucle");
}while(a > 10);

Certes, il sera toujours possible d'utiliser le while au lieu du do...while, mais dans le but de simplifier
le code ou d'en améliorer la lisibilité et la compréhension et dans les occasions où ce comportement
correspond à ce que l'on recherche, il sera plus intéressant d'utiliser un do...while au lieu d'un while !

F.B. / D.C. / P.C. 44 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
10. Variable locale, globale et statique.
C'est ce qu'on va appeler la visibilité de nos variables.

10.1. Les variables locales et globales


Jusqu'à maintenant, l'ensemble de nos variables était déclarées au début de nos fonctions, tout de suite
après l'accolade ouvrante '{'. Cette déclaration donnait le statut de "locales" à nos variables. C’est-à-
dire que les variables n'existent qu'à l'intérieur des fonctions dans lesquelles elles sont déclarées.

Variables locales : (déclarées au tout début des fonctions)


Lorsqu'une variable est déclarée dans une fonction (même pour le main), elle est reconnue dans toute
cette fonction (localement), mais pas dans les autres fonctions. Des variables se trouvant dans des
fonctions différentes peuvent porter le même nom. Type de variable à utiliser le plus souvent possible.
Les variables locales sont éphémères (sauf pour celles du main( )).
Variables globales : (Déclarées avant le ‘main’)
Une variable globale est une variable déclarée à l'extérieur de toutes fonctions. Une telle variable est
utilisable par l'ensemble des fonctions du programme. Elle est donc connue par chaque fonction du
programme qui n'utilise pas une variable locale du même nom (Ce qui peut être Dangereux).
Les variables globales ne sont pas éphémères.
Une bonne pratique est d'utiliser le moins possible les variables GLOBALES.
En fait, à moins d'indications contraire, il est interdit d'utiliser les variables globales.
Exemple: Utilisation d'une fonction qui affiche des valeurs locales et globales passées en paramètres
void vInitPortSerie(void); // Pour utiliser le "printf".
void vAffiche(UC ucX, UC ucY); //

UC ucgA = 1; // Variable Globale.


UC ucgB = 2; // "

void main(void) //
{ //
UC ucC = 3; // Variable Locale au main( ).
UC ucD = 4; // "

vInitPortSerie() ; //
vAffiche(ucgA, ucgB); // Var Globales passées en paramètres.
vAffiche(ucC, ucD); // Var Locales passées en paramètres.
while(1); //
} //

void vAffiche(UC ucX, UC ucY) // 2 variables locales: ucX et ucY.


{ //
printf("%u, %u. ", (int)ucX, (int)ucY); // Affiche les Variables Locales
printf("%u, %u. ", (int)ucgA, (int)ucgB);// Affiche les Variables Globales
} //

Résultat : 1, 2. 1, 2. 3, 4. 1, 2.
Note: Utiliser le même nom de variable locale et globale, peut causer des problèmes de "Shadowing" avec certains
compilateurs.

F.B. / D.C. / P.C. 45 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
10.2. Variables statiques
Les variables locales sont déclarées au début des fonctions et sont éphémères. Donc à chaque fois
qu'on appelle une fonction, les variables sont à nouveau créées et réinitialisées au besoin. Dans certain
cas ça peut être embêtant.

Exemple:
UC ucCompteBoite(void) // Lit Capteur et compte les boites.
{ //
UI uiBoite = 0; // Pour compter les boites.

if(P3_0 == 0) // Une boite présente?


{
uiBoite++; // Une boite de plus.
}
return uiBoite;
}

Malheureusement, cette fonction retournera toujours 1 si une boite est détectée et 0 si aucune boite
n'est détectée. Ce n'est pas le but recherché.

La variable uiBoite est créée et remis à 0 à chaque début de la fonction.

Il est possible, avec l'attribut "static" de dire au compilateur que la variable doit continuer d'exister
même si la fonction se termine. Ceci nous permettra d'accumuler le nombre de boite.

UC ucCompteBoite(void) // Lit Capteur et compte les boites.


{ //
static UI uiBoite = 0; // Pour compter les boites.
// Initialisée, une fois, au début du programme.
if(P3_0 == 0) // Une boite présente?
{
uiBoite++; // Une boite de plus.
}
return uiBoite;
}

Avec cette déclaration, la variable uiBoite ne sera pas placée sur la pile et ne sera pas éphémère.

Si on rappelle la fonction une deuxième fois la variable uiBoite aura au départ de la fonction sa
dernière valeur (elle sera initialisée à 0, seulement, lors du premier passage dans la fonction).

10.3. Variables globale static


Une variable globale déclarée "static", sera globale à l'intérieur du fichier dans lequel elle est déclarée.

Si le projet comporte plusieurs fichiers, les autres fichiers ne pourront voir la variable globale static.

F.B. / D.C. / P.C. 46 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11. Les Bus, la mémoire.
11.1. Organisation mémoire du Dallas
Voici un schéma qui indique l'ensemble de la mémoire accessible par le Dallas 89C450.

PMR = PMR | 0x01;

En plus de la mémoire interne, deux blocs de 64 kilo octets se sont ajoutés.

Le premier bloc est un bloc de mémoire ROM externe qui pourrait contenir notre programme, à
condition que la broche EA du processeur soit mise à 0 (EA = 0).

On ne peut utiliser qu'un des deux blocs de ROM (Interne EA = 1 ou externe EA = 0).

L'autre bloc, peut contenir jusqu'à 64 kilo octets de mémoire RAM (xdata).
Si la mémoire RAM xdata interne est utilisée (PMR0 = 1 et ) , les
premiers 1024 octets de la RAM externe ne seront pas utilisés.

Dans les faits, cette espace de 64 ko de RAM sera partagé entre de la mémoire RAM et d'autres
circuits électroniques (exemple: Clavier sur un 922, écran LCD, convertisseur, etc…).

Ces circuits électroniques seront vus par le Dallas comme de la mémoire externe (xdata).

Pour accéder à ces deux blocs de mémoires externes (Flash et RAM), nous auront besoin de signaux
électriques pour sélectionner un des circuits et pour échanger les données.

Pour choisir le ou les circuits ont utilisera le bus d'adresse.

Pour échanger les informations ce sera le bus de donnée.

Finalement, pour synchroniser le tout, nous aurons besoin des signaux du bus de contrôle.

F.B. / D.C. / P.C. 47 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.2. Bus d'adresse, de donnée et de contrôle.
Comme on peut le voir sur le schéma précédent, nous pouvons accéder à des zones de 65536 octets
différents (64 ko).
Afin de sélectionner un seul de ces octets on doit utiliser une donnée à 16 bits (216 = 65536).
Cette donnée portera un nom spécial, c'est une adresse.
Donc pour aller chercher une instruction ou accéder à une donnée à l'extérieur, la première chose à
faire sera de mettre l'adresse sur le bus d'adresse (16 bits). Avec cette adresse, une seule case sera
sélectionnée et nous pourrons lire son contenu ou écrire dans la case.
C'est grâce au bus de donnée (8 bits) que nous pourrons échanger de l'information avec cette case.
Finalement, il faudra indiquer si on veut une instruction (signal PSEN), lire une donnée (signal RD)
ou écrire une donnée (signal WR). Une variable xdata, active RD ou WR (instruction movx).
Nous avons donc besoin d'au minimum 27 broches (16 + 8 + 3) pour réussir un échange avec des
circuits extérieurs. Le Dallas possède 40 broches dont 16 pour P1 et P3, 2 pour l'alimentation, 1 pour
le reset et 2 pour l'horloge. Ce qui nous laisse un maximum de 19 broches disponibles. Il en manque!
Solution utilisée par les concepteurs du circuit:
 Utilisation de P3_6 pour le signal WR (écriture d'une donnée).
 Utilisation de P3_7 pour le signal RD (lecture d'une donnée).
 Un nouveau signal, PSEN (Program Store ENable) pour lire une instruction.
 Utilisation du port 2 pour la partie haute de l'adresse (A8 – A15).
 Utilisation du port 0 pour la partie basse de l'adresse (A0-A7) ET pour les données (D0-D7).
Les signaux s'appelleront donc (AD0-AD7).
 Un signal ALE (Address Latch Enable) nous permet de mémoriser l'adresse au début du cycle.

Le port 0 sera donc multiplexé.


C’est-à-dire qu'au début du cycle, il contiendra l'adresse et à la fin du cycle, il contiendra la donnée.

F.B. / D.C. / P.C. 48 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.2.1. Le 74HCT573 (même fonction qu'un LS373):
Afin de faire le démultiplexage (adresse et donnée) du Port 0, nous utiliserons un circuit qui sera
activé par le signal ALE et qui mémorisera l'adresse pour l'ensemble du cycle.
Ce circuit, le 74HCT573, est un "Octal D-Type Latch with 3-STATE Output".
C’est-à-dire un ensemble de 8 bascules D qui peuvent mémoriser un octet.
De plus la sortie peut être mis en 3-state.

U1
2 19
3 D1 Q1 18 Broches:
4 D2 Q2 17
5 D3 Q3 16
6 D4 Q4 15
7 D5 Q5 14
8 D6 Q6 13
9 D7 Q7 12
D8 Q8
11
1 C
OC
74HCT573
Symbole Orcad Data Sheet

Table de vérité (Transparent Latch):

Diagramme fonctionnel:

Transparent

Utilisé pour mémoriser avec un signal actif à '1' LE = ALE:


/OE = '0' Latch

Transparent

Utilisé pour lire une donnée (avec CS à '0'): LE = /CS:


Latch
Read
2
/CS
/OE = /CS ET /RD: /RD
3
1
/OE

F.B. / D.C. / P.C. 49 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.3. Lecture d'un programme dans une ROM externe:
Programme en externe (EA = 0) (64K  CE = 0 circuit toujours actif)

Lecture des codes d'instructions (Opcode):

Opcode Opcode

F.B. / D.C. / P.C. 50 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.4. Accès à une donnée dans une RAM externe:

RAM (64K CE = 0 circuit toujours actif)

RAM si RD
CPU si WR

Normalement, le signal CE de la mémoire n'est pas au GND. Ici ça va, car la mémoire occupe
l'ensemble du 64 ko. Si on veut utiliser d'autres circuits, nous devront découper la plage de 64 ko en
plusieurs morceaux (plages).

Un circuit décodeur, nous permettra alors de sélectionner un circuit parmi plusieurs.

F.B. / D.C. / P.C. 51 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.5. Décodeur d'adresse.
Afin de sélectionner une plage d'adresses (donc un circuit), il faudra utiliser quelques bits du bus
d'adresse. Normalement les bits les plus significatifs seront utilisés.

Exemple: Je veux un ordinateur avec de la RAM de 0x0000 à 0x7FFF, soit 32 ko et un écran LCD
qui sera activé avec toutes les autres adresses (0x8000 à 0xFFFF).

Carte d'assignation de mémoire (Memory Map):


A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
RAM 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x0000
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x7FFF
LCD 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x8000
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xFFFF

Sert à sélectionner un circuit.


Sert à sélectionner une case dans le circuit.
Ne sert pas au décodage (Don't care: souvent indiqué par un X)

Comme on peut le constater seul A15 reste inchangé pour la RAM (0) et pour l'écran LCD (1).
Nous pourrions donc utiliser A15 pour différencier les deux circuits.

Pour l'écran LCD, plusieurs lignes d'adresses ne sont pas utilisées. Cette méthode de décodage
s'appelle décodage partiel, car pour activer l'écran, on utilise 32768 adresses différentes. On n'utilise
pas toutes les lignes d'adresses disponibles.
Évidemment ce n'est pas très efficace, mais c'est simple à implanter, car on sauve beaucoup de circuits
électroniques.

Note: Seulement deux (2) lignes d'adresses, de la partie basse (A1, A0), se rendent à l'écran LCD car
il y a seulement quatre cases accessibles pour la contrôler. Pour accéder à l'écran il y aura donc
4 adresses de bases: 0x8000, 0x8001, 0x8002 et 0x8003. Les autres adresses seront des images.

F.B. / D.C. / P.C. 52 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Exemple2: Je veux un ordinateur avec de la RAM de 0x0000 à 0x7FFF, soit 32 ko, un écran LCD et
un clavier sur un 922.

Ici la mémoire RAM occupe encore la moitié de la plage de 64ko. Les deux autres circuits pourrait se
partager l'autre moitié (16 ko chaque).

A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0


RAM 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x0000
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x7FFF
LCD 1 0 X X X X X X X X X X X X 0 0 0x8000
1 0 X X X X X X X X X X X X 1 1 0xBFFF
Clavier 1 1 X X X X X X X X X X X X X X 0xC000
1 1 X X X X X X X X X X X X X X 0xFFFF

Dans cet exemple on utilisera A15 et A14 pour différencier l'écran LCD et le Clavier.

(A1,A0)

Note: Le circuit 922 ne possède pas de broche CE pour le sélectionner. Nous devrons donc mettre
ensemble le décodeur d'adresse et le signal de contrôle RD pour activer le OE.
0xFFFF

Images Clavier
Donc voici la liste des adresses des différents circuits: 0xC001
0xC000 Clavier (922)
Circuit: Adresses de base: Adresses images: 0xBFFF

RAM de 0x0000 à 0x7FFF Images LCD


0x8004
LCD 0x8000, 0x8001, 0x8002 et 0x8003 de 0x8004 à 0xBFFF 0x8003
922 0xC000 de 0xC001 à 0xFFFF 0x8000 LCD
0x7FFF

RAM

0x0000

F.B. / D.C. / P.C. 53 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Comme on peut le voir dans l'exemple 2, on a une grosse perte d'espace mémoire avec le décodage
partiel. Pour diminuer ces pertes on pourrait utiliser un circuit décodeur bien connu, le 74LS138.

11.5.1. Décodeur d'adresse avec le 74LS138.

A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0


1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x8000
Y0
1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0x8FFF
1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0x9000
Y1
1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0x9FFF
1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0xA000
Y2
1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0xAFFF
1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0xB000
Y3
1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xBFFF
1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0xC000
Y4
1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0xCFFF
1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0xD000
Y5
1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0xDFFF
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0xE000
Y6
1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0xEFFF
1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0xF000
Y7
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0xFFFF

Ici on peut voir qu'avec un seul circuit, nous avons 8 blocs de 4096 (4ko) octets.

A15 sert à activer le 74LS138 et les lignes A14, A13 et A12 servent à activer une des sorties du 138.

Si on veut des blocs plus petits, on n'a qu'à utiliser plus de lignes pour activer le 138:

Dans cet exemple, on utilise 3 lignes d'adresses


pour activer le 138. Donc le 138 décodera
8 blocs qui totaliseront 8ko (213).
Chaque bloc aura 1024 (1ko) octets.

Pour utiliser les autres adresses (0xA000 à 0xFFFF), on n'a qu'à utiliser des portes logiques pour
sélectionner les différentes combinaisons de A14 et A13 afin d'activer le 74LS138.

Dans un cours de la prochaine session, vous verrez en détail, comment concevoir un décodeur
d'adresse complet.

F.B. / D.C. / P.C. 54 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.6. Carte mémoire complète du Kit Dallas.
Pour analyser la carte mémoire du Kit Dallas, nous analyserons son décodeur d'adresse.

Voici la carte mémoire du Kit:

En externe (XDATA) on constate la présence de 32ko de RAM et d’un espace de 32ko pour des
circuits externes.

Pour les circuits externes (I/O) on a un bloc de 32ko. Nous devrons diviser ce bloc pour nous
permettre d'avoir plusieurs circuits différents.

Le décodeur implanté dans le Kit Dallas permet d'utiliser les adresses de 0x0000 à 0x7FFF pour la
RAM (A15 = 0) et les adresses 0x8000 à 0xFFFF (A15 = 1) pour des circuits externes différents.

A15 à 0 active le *CE de la RAM :

F.B. / D.C. / P.C. 55 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.6.1. Circuit Décodeur des I/O du Kit Dallas.

Carte Mémoire (RAM externe):


A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 A5 A4 A3 A2 A1 A0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x0000
0 |
0 |
0 |
0 |
0 |
CSRAM+
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x7FFF
CS0 1 X X X X 0 0 0 0 0 0 0 0 0 0 0 0x8000
1 X X X X 0 0 0 1 1 1 1 1 1 1 1 0x80FF
CS1 1 X X X X 0 0 1 0 0 0 0 0 0 0 0 0x8100
1 X X X X 0 0 1 1 1 1 1 1 1 1 1 0x81FF
CS2 1 X X X X 0 1 0 0 0 0 0 0 0 0 0 0x8200
1 X X X X 0 1 0 1 1 1 1 1 1 1 1 0x82FF
CS3 1 X X X X 0 1 1 0 0 0 0 0 0 0 0 0x8300
1 X X X X 0 1 1 1 1 1 1 1 1 1 1 0x83FF
CS4 1 X X X X 1 0 0 0 0 0 0 0 0 0 0 0x8400
1 X X X X 1 0 0 1 1 1 1 1 1 1 1 0x84FF
CS5 1 X X X X 1 0 1 0 0 0 0 0 0 0 0 0x8500
1 X X X X 1 0 1 1 1 1 1 1 1 1 1 0x85FF
CS6 1 X X X X 1 1 0 0 0 0 0 0 0 0 0 0x8600
1 X X X X 1 1 0 1 1 1 1 1 1 1 1 0x86FF

Attention: Pour un bon décodage, RD et WR ne devraient pas activer le 74LS138.


De 0x8800 à 0xFFFF on retrouve des images des CS.

F.B. / D.C. / P.C. 56 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.6.2. Schéma de la partie Décodeur / Mémoire du Kit Dallas.

F.B. / D.C. / P.C. 57 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
11.7. Accès à la RAM externe.
Pour accéder à la mémoire RAM externe nous n'avons qu'à déclarer nos variables "xdata". Le compi-
lateur s'occupe alors de définir des adresses pour y mettre nos variables et de générer les instructions
pour accéder à ces adresses.

Le compilateur, par défaut, va mettre les variables à partir de l'adresse 0x0000. C’est l’endroit où se
trouve notre mémoire RAM. Il serait bon de spécifier au compilateur que notre mémoire ne dispose
que de 32ko de RAM.

Dans les options du projet (Bouton de droite sur Target1), nous allons mettre les informations
suivantes:

(+ PMR = PMR | 1;  1Ko plus rapide)

Il n’est pas vraiment nécessaire de spécifier la grosseur de notre RAM si on gère bien nos données.

Maintenant, si je veux accéder à l'écran LCD comment puis-je procéder?

Si je déclare une variable UC xdata ucLCD, elle sera placée n'importe où dans la RAM externe.

L'écran du Kit Dallas est, elle, placée à l'adresse 0x8000 (CS0). Je devrais donc forcer le compilateur
à mettre ma variable à cette adresse. Et il faudrait faire ça pour chaque circuit branché sur le Kit.

Dans les faits, le langage C a prévu un type de variable spécial.

Ce type de variable va contenir une adresse. On va nommer ces variables des pointeurs.

F.B. / D.C. / P.C. 58 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12. Les pointeurs
Un pointeur est une variable qui contient l'adresse mémoire d'une donnée particulière.
Il pointe l'endroit de la mémoire où est stockée une information.

Un pointeur sera toujours associé à un type de données particulier. Le compilateur doit savoir si le
pointeur pointe sur des variables de 1, 2, 4 octets...

Les pointeurs peuvent pointer sur tous les types de données existantes ou définis par le programmeur.

12.1. Déclaration
TypePointé *NomDuPointeur;

Exemple: int *ipPointeur; // Pointe une zone mémoire remisant des entiers.

char *cpPtr; // Pointe une zone mémoire remisant des octets.

12.2. Initialisation
L'initialisation d'un pointeur s'effectue en lui affectant la valeur de l'adresse d'une variable déjà
existante. Pour y arriver nous utilisons l'opérateur d'adressage &.

Ex.: &ucTotal représente l'adresse où est stockée la valeur de la variable du nom de ucTotal.

Pour initialiser un pointeur il suffit de lui affecter cette valeur.

Ex.: unsigned char ucVal = 12; // Déclare et initialise la variable ucVval.


unsigned char *ucpPteur; // Déclare un pointeur du nom de ucpPteur
// pointant sur un octet non signé.

ucpPteur = &ucVal; // Initialise un pointeur du nom de ucpPteur


// à l'adresse de la variable ucVal.
// ucpPteur pointe sur la variable ucVal.

ATTENTION: Un pointeur qui n'est pas initialisé est l'une des sources d'erreurs les plus difficiles à
régler.

12.3. Utilisation
Pour pouvoir manipuler la valeur stockée à l'adresse contenue par le pointeur nous utilisons l'opérateur
de déréférencement noté * (astérisque).

*ucpPteur = 0x0d; // Remise la valeur 0x0d dans la variable ucVal.


ucTotal = *ucpPteur; // Permet de remiser dans ucTotal la valeur pointée
// par le pointeur ucpPteur.

* ucpPteur représente alors la valeur stockée à l'adresse indiquée par le pointeur ucpPteur.

F.B. / D.C. / P.C. 59 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.4. Opérations sur les pointeurs
Pour incrémenter le pointeur d'une position nous pouvons utiliser l'expression suivante:

ipPointeur = ipPointeur + 1; // Pointe l'élément suivant.

Si nous voulons décrémenter le pointeur de 5 positions;

ipPointeur = ipPointeur - 5; // Pointe cinq éléments avant.

NOTE: Ne pas initialiser un pointeur avec un nombre numérique. Ex.: ipPointeur = 0x2345;
Nous ne connaissons pas ce qu'il y a à l'adresse 0x2345.
Nous laissons au compilateur le soin de nous allouer une location de mémoire disponible.

Pour accéder à nos circuits externes, il en sera autrement.

12.5. Les pointeurs et les tableaux


Du point de vue du type, le nom d'un tableau est considéré comme une constante d'adresse définissant
le début de l'enregistrement.
Le nom du tableau représente en fait l'adresse du premier élément. Nous pouvons donc l'associer à
une variable pointeur sans avoir à recourir au symbole &.

Exemple:
void main(void)
{
int i = 2;
int iVal; // Déclaration d'un entier de nom iVal.
int iTableau[5]; // d'un vecteur de 5 entiers.
int *ipPointeur; // d'un pointeur sur des entiers.

ipPointeur = iTableau; // Initialisation du pointeur sur le


// vecteur du nom de iTableau.
// ipPointeur = adresse du tableau.

*ipPointeur = 8; // Met 8 dans le premier element 0 du tableau.


*(ipPointeur+1) = 10; // 10 dans deuxieme element.
000E ipPointeur
ipPointeur++; // Pointe le deuxieme element.
000C
*ipPointeur = 12; // 12 dans le deuxieme element. 000A
*(ipPointeur+1) = 87; // 87 dans le troisieme element. 0008 iTableau
0006
*(ipPointeur+i) = 45; // 45 dans le quatrieme element. 0004
i++; 0002 iVal
0000 i
*(ipPointeur+i) = 68; // 68 dans le cinquieme element.
i++;
*(ipPointeur+i) = 18; // Attention! On pointe sur le pointeur!
}

// iTableau contient: {8, 12, 87, 45, 68}

F.B. / D.C. / P.C. 60 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.6. Quand utiliser les pointeurs? (3 exemples).

12.6.1. Utiliser les pointeurs pour permettre à une fonction de modifier plus d'une
variable de la fonction appelante.
void vFonctionQuiModifieDeuxVariables(UC *ucpVar1, UC *ucpVar2);

void main(void)
{
UC ucVariable1 = 'A';
UC ucVariable2 = 'B';
vFonctionQuiModifieDeuxVariables(&ucVariable1, &ucVariable2);
printf("%c%c",ucVariable1, ucVariable2); // Affiche: OK
while(1);
}

void vFonctionQuiModifieDeuxVariables(UC *ucpVar1, UC *ucpVar2)


{
*ucpVar1 = 'O';
*ucpVar2 = 'K';
}

12.6.2. Utiliser les pointeurs pour passer de "gros" éléments à une fonction.

void vFonctionModifieTableau(int *ipVar);

void main(void)
{
int iTab[20] = {0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9}; // 40 octets.

vFonctionModifieTableau(iTab); // Passe adresse du tableau (2 octets).


printf("%d,%d",iTab[10],iTab[11]); // Affiche: 100,121
while(1);
}

void vFonctionModifieTableau(int *ipVar) // Recoit l'adresse d'un "int".


{
int i;
for(i=0; i<20; i++)
{ // Il faut que l'adresse recu, pointe sur bloc
*(ipVar+i) = i*i; // d'au moins 20 "int".
}
}

On ne passe jamais un tableau à une fonction. On passe toujours l'adresse du tableau.

F.B. / D.C. / P.C. 61 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.6.3. Utiliser les pointeurs pour accéder à nos circuits externes.

Ce qu'on a vu à date, ce sont des pointeurs logiques. C’est-à-dire qu'ils pointent sur des cases
mémoires sans que l'on connaisse les adresses physiques exactes.

Pour nos circuits externes, l'adresse du circuit doit être connue. Cette adresse nous servira à initialiser
le pointeur externe.

Exemple (Écran LCD texte du Kit Dallas (adresses: 0x8000 à 0x8003)):


#define UC unsigned char

#define BUSY 0x80 // Bit du Busy Flag de l'ecran LCD.

#define LIGNE0 0x80 // Localisation des differentes lignes du LCD.


#define LIGNE1 0xC0
#define LIGNE2 0x94
#define LIGNE3 0xD4
// Adresse des differents registres du LCD.
#define REG_CTRL 0x8000 // Ecrire mot de contrôle.
#define REG_BUSY 0x8001 // Lire le Busy Flag du LCD.
#define REG_DATA 0x8002 // Ecrire une donnee a l'ecran.

void vAffCarLCD(UC ucLigne, UC ucCol, UC ucCar);

void main(void)
{
vAffCarLCD(0, 0, 'A');
while(1);
}

void vAffCarLCD(UC ucLigne, UC ucCol, UC ucCar)


{
UC xdata *ucpCtrl = REG_CTRL; // Initialisation des pointeurs
UC xdata *ucpBusy = REG_BUSY; // sur l'ecran LCD.
UC xdata *ucpData = REG_DATA;

switch(ucLigne)
{
case 0: ucLigne = LIGNE0 + ucCol; break;
case 1: ucLigne = LIGNE1 + ucCol; break;
case 2: ucLigne = LIGNE2 + ucCol; break;
default: ucLigne = LIGNE3 + ucCol; break;
}

while(*ucpBusy & BUSY); // Attendre que l'ecran soit prete.

*ucpCtrl = ucLigne; // Positionne le curseur.

while(*ucpBusy & BUSY); //

*ucpData = ucCar; // Affiche le caractere.

}//Fin vAffCarLCD

F.B. / D.C. / P.C. 62 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.6.4. Quelques informations sur l'écran LCD.

Branchement sur Kit Dallas Broches dans les spécifications

Positions des caractères dans la RAM de l'écran LCD

Timing pour une écriture (R/W à 0) dans un registre

F.B. / D.C. / P.C. 63 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.6.5. Exemple : Accès externe et tableau contenant diverses informations.

#define OFF 0
#define ON 1

#define START !P3_2 // Bouton pese = 0.

#define ETATFAN 0
#define VITFANLUE 1

void vControlFan(UC *ucpTab);


void vLireBouton(UC *ucpTab);
void vLireEtat(UC *ucpTab);

void main(void)
{
UC ucTableau[2] = {OFF,0}; // ETATFAN, VITFANLUE

while(1)
{
vLireBouton(ucTableau); // ou vLireBouton(&ucTableau[0]);
vLireEtat(ucTableau);
vControlFan(ucTableau);
}
}

////////////////////////////////////////////////////////////////////
void vControlFan(UC *ucpTab)
{
UC xdata *ucpAdrFan = 0x8300; // CS3.

if( (*(ucpTab + ETATFAN) == ON) && (*(ucpTab + VITFANLUE) == 0) )


{
*ucpAdrFan = 100; // Ventilateur a 100%
}

if( (*(ucpTab + ETATFAN) == OFF) && (*(ucpTab + VITFANLUE) != 0) )


{
*ucpAdrFan = 0; // Ventilateur a 0%
}
}

////////////////////////////////////////////////////////////////////
void vLireBouton(UC *ucpTab)
{ // Autre méthode:
if(START) { *(ucpTab + ETATFAN) = ON; } // ucpTab[ETATFAN] = ON;
else { *(ucpTab + ETATFAN) = OFF; } // ucpTab[ETATFAN] = OFF;
}

////////////////////////////////////////////////////////////////////
void vLireEtat(UC *ucpTab)
{
UC xdata *ucpAdrFan = 0x8300; // CS3.

*(ucpTab + VITFANLUE) = *ucpAdrFan; // Lit vitesse du ventilateur.


// ucpTab[VITFANLUE] = *ucpAdrFan; // Même chose.
}

F.B. / D.C. / P.C. 64 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.6.6. Exercices sur les pointeurs
// Déclaration: (modèle Large) // Instructions:

int iNombre = 123; ucA = 12;


UC ucA, ucB; ucpPtr = &ucB;
UC *ucpPtr; *ucpPtr = 0x41;
UC ucTab[8] = {"abcdefg"}; ucA = ucB;
UC ucTemp = 0xAA; ipPtr = &iNombre;
int *ipPtr; *ipPtr = *ipPtr + 5;
float fDiv = 1.2; ucpPtr = ucTab;
float *fpPtr; for(i=0; i<8; i++, ucpPtr++)
UC ucC = 0x23; {
UC i; *ucpPtr = i * i;
UC xdata *ucpLcd = 0x8000; }
ucC = ucTab[3];
fpPtr = &fDiv;
*fpPtr = *fpPtr * 2;
fpPtr++;
*ucpLcd = 0x31;
Contenu mémoire après les déclarations: Contenu mémoire après exécution:
Adresse Contenu Variable Adresse Contenu Variable

0x202 ucA 0x202 ucA

0x200 123 iNombre 0x200 iNombre

F.B. / D.C. / P.C. 65 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.7. La fonction scanf( ) :
Maintenant que nous avons vu les pointeurs, il est possible de regarder une nouvelle fonction de
lecture au clavier pré-déclarée dans stdio.h.
- La fonction "scanf( )" permet de lire des valeurs de type entier, réel (float), et même une chaine de
caractères sur l'entrée standard (clavier).
- La lecture d'une variable se termine avec Enter, Espace ou un caractère non valide.

Le format des arguments ressemble un peu à celui du printf.


Il y a deux sections:
- La première (entre " ") indique le format utilisé pour lire les données.
- La seconde donne la liste des variables que nous voulons lires.
Comme nous voulons modifier des variables de la fonction appelante, nous
devons fournir l'adresse (&) des variables.
Exemple:
UI uiVal;
...
scanf("%u", &uiVal); // Où &uiVal représente l'adresse de uiVal.

Les spécifications de format (simplifiées) on la forme suivante :


% «width» «{b|h|l}» type

- Le champ type est un caractère qui spécifie si l'entrée est de type nombre, caractère ou chaine de
caractères, comme l'indique la table suivante (les plus communs):
Character Argument Type Input Format
d int * Nombre entier signé.
u unsigned int * Nombre entier non signé.
x unsigned int * Nombre hexadécimal non signé.
f float * Nombre point flottant.
c char * Un simple caractère. (getchar fait mieux la job)
s char * Une chaine de caractères terminés par "\0".

- Le champ «width» est un nombre qui spécifie le maximum de caractère lue sur l'entrée standard.
- Les options b, h, et l doivent immédiatement précéder le type pour spécifier char, short, ou long
pour des entiers de types d, u, et x.

Notes:
 Avec Keil le nombre total d'octets qui peuvent être passé à la fonction "scanf" est limité due à
l'espace mémoire restreinte du 8051. Ainsi un maximum de 15 octets peut être passé en model
SMALL ou COMPACT et un maximum de 40 octets peut être passé en model LARGE.
 Un caractère qui suit le symbole "%" et qui n'est pas reconnu comme un format spécifié sera traité
comme un caractère ordinaire. Par exemple, "%%" scanf va s'attendre à recevoir le symbole "%"
en entrée.
 La fonction "scanf" retourne le nombre d'entrée correctement lues. EOF si une erreur est
rencontrée.

F.B. / D.C. / P.C. 66 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
12.7.1. Exemples de scanf (tiré du manuel du compilateur Keil page 303):
#include <stdio.h>
#include "_DeclarationGenerale.h"

void vTestScanf(void);
void vInitPortSerie(void);

void main(void)
{
vInitPortSerie();
vTestScanf();
while(1);
}

void vTestScanf(void)
{
char a; // Variables signees.
int b;
long c;
unsigned char x; // Variables non signees.
unsigned int y;
unsigned long z;
float f, g; // Variables float.
char buf [10]; // Variables char.
int argsread; // Nombre d'arguments lus.

printf ("\nEnter a signed byte, int, and long: "); // Signees.


argsread = scanf ("%bd %d %ld", &a, &b, &c);
printf("\nA= %bd, B= %d C= %ld", a, b, c);
printf ("\n%d arguments read\n", argsread);

printf ("\nEnter an unsigned byte, int, and long: "); // Non signees.
argsread = scanf ("%bu %u %lu", &x, &y, &z);
printf("\nX= %bu Y= %u Z= %lu", x, y, z);
printf ("\n%d arguments read\n", argsread);

printf ("\nEnter a string: "); // Caracteres.


argsread = scanf (" %9s ", buf); // Pour 1 seul Car, utiliser getchar.
getchar(); // Pour vider buffer de lecture!
printf("\nBuf= %s", buf);
printf ("\n%d arguments read\n", argsread);

printf ("\nEnter two floating-point numbers: "); // Floats.


argsread = scanf ("%f %f", &f, &g);
printf("\nF= %f G= %5.2f", f, g);
printf ("\n%d arguments read\n", argsread);
}

void vInitPortSerie(void)
{
T2CON = T2CON & 0xCF; // RXClk et TXClk viennent du Timer 1
SCON0 = 0x50; // Port Serie 0: mode 1, 8-bit UART avec reception.
TMOD = TMOD | 0x20; // TMOD: Timer 1, mode 2, 8-bit auto reload.
TH1 = 0xFF; // TH1: reload value for 57600 baud @ 11.059MHz.
PCON = PCON | 0x80; // SMOD_0 = 1 (doubleur de baud).
TR1 = 1; // TR1: Part le Timer 1.
TI_0 = 1; // TI a 1 pour permettre transmission 1er charactere.
} // Fin vInitPortSerie().

F.B. / D.C. / P.C. 67 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
13. Utilisation de plusieurs fichiers dans un projet.
Présentement, dans vos programmes, vous avez divisés les tâches à faires entre la fonction main( ) et
des fonctions ayant une tâche spécifique. Ceci permet d'avoir un programme facile à lire et à analyser.

Jusqu'à maintenant, nous avons toujours utilisé un seul fichier pour nos programmes. On y retrouvait
l'ensemble des tâches à faire. Cette approche fonctionne bien si on a un projet de petite envergure.
Cependant, lorsque que le problème à résoudre devient plus important, la division des tâches entre les
fonctions devient inefficace. Il faut faire une autre division.

Dans un projet de moyenne et de grande envergure, les tâches sont séparées entre différents fichiers.
Chaque fichier contient un ensemble de fonctions œuvrant sur une partie du problème à résoudre.
Exemple, un projet pourrait contenir un fichier avec les fonctions qui ont traits à la configuration du
microcontrôleur (Ex: InitPortSerie. InitTimer), un autre fichier pourrait s'occuper des fonctions
d'affichages, un troisième aurait les fonctions gérant un circuit de conversion quelconque et
finalement un dernier contiendrait la fonction principal (main( )) et quelques fonctions de gestion de
l'application.

Dans l'exemple qui suit, le projet a trois fichiers .C.


Un pour l'écran LCD, un pour le microcontrôleur et le
dernier pour la fonction main( ) et les fonctions de
l'application.

Il est aussi possible d'ajouter des fichiers textes dans le


projet afin de le documenter.

Il est aussi possible d'ajouter d'autres types de fichiers.


Dans l'exemple qui suit, un fichier, Convertisseur.OBJ a été ajouté.

Il contient des fonctions déjà compilées. Ces fonctions ne


peuvent pas être modifiées par l'utilisateur du fichier. Le
fichier source en texte (.C) n'étant pas disponible.
Cependant, on peut utiliser les fonctions qui sont dans le
fichier dans notre projet. La seule condition à respecter et
d'avoir les prédéclarations de ces fonctions. C'est
pourquoi, un fichier d'entête (.H) sera toujours fournit
avec le fichier .OBJ.
Dans le fichier .H, on trouvera les prédéclarations des fonctions.
Il nous suffira alors d'inclure ce fichier (#include "Convertisseur.H") au début de nos fichiers .C.
Notez l'utilisation des guillemets, elles indiquent que le fichier doit se trouver dans notre répertoire de
travail.

D'autres types de fichiers peuvent aussi être ajoutés au projet. Exemple, les fichiers .LIB contiennent
une librairie de fonctions qui gèrent différentes tâches.

F.B. / D.C. / P.C. 68 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
14. Convertisseur.
Jusqu'à maintenant nous avons activé et lue des éléments exclusivement numériques (0 ou 1).
Malheureusement, la vie n'est pas numérique. Elle est plutôt analogique.

C'est pourquoi il existe des circuits permettant de faire le pont entre le monde extérieur (analogique)
vers le monde des ordinateurs (numérique).

Le premier que nous allons voir est le circuit qui permet de convertir une tension analogique en
signaux numériques pouvant être lus par un ordinateur. Il s'agit du Convertisseur Analogue
Numérique (CAN) ou en anglais, Analog Digital Converter (ADC). La plage de tensions à convertir
est différente d'un circuit à un autre, mais elle est souvent ajustable. Dans notre cas nous convertirons
une tension qui se situe entre 0 et 5 volts.

Le principe de la conversion consiste à donner un numéro à chacune des tensions possibles. Comme la
tension peut prendre une infinité de valeur, les concepteurs de circuit ont limité le nombre d'états
possibles. C'est ce qu'on va appeler la résolution du convertisseur. Dans les faits, il s'agit du nombre
de bits utilisés pour donner un numéro.

Exemple: Avec 3 bits on peut donner 8 numéros différents. Si la tension est 0 volts on donne le
numéro 0 et si la tension est 7 volts on donne le numéro 7. Entre ces deux tensions on donne
les numéros 1 à 6.

Le passage d'un niveau à l'autre s'appelle un PAS.


Dans notre exemple il y a 7 pas (2n – 1).

La tension maximale de 7 volts est notre tension


de référence (Vref).

Dans nos programmes, nous pourrons connaitre la


tension du signal en multipliant la lecture
numérique par Vref / (2n – 1).

Exemple: J'ai un convertisseur 4 bits. La tension de référence est de 5 volts.


Mon programme lit 0x0C (12) sur le convertisseur.
Quel est la tension du signal?

Solution: Tension à l'entrée = 12 * 5V / (24 -1)  60V / 15  4.0 volts

Il existe plusieurs types de convertisseur analogique à numérique. Ils diffèrent par la technique
employée (Simple rampe, Double rampe, Approximations successives, Flash, Semi-flash, Sigma-
Delta). Le but du cours n'étant pas de vous décrire l'ensemble de ces techniques, nous allons nous
contenter de décrire un type très répandu et abordable, le convertisseur à approximations successives.

F.B. / D.C. / P.C. 69 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
14.1. Le circuit ADC0804
14.1.1. Fonctionnement du ADC0804

 Fonctionnement de base du convertisseur ADC0804 en 6 points :


- Alimentation : Vcc et DGND (ne pas oublier AGND)
- Entrées: Vin(+) => Signal, Vin(-) => AGND,
- Sorties sur D0 à D7,
- Contrôle : /RD, /WR, /CS,
- Conversion terminée : /INTR,
- Horloge : CLK R et CLK in.

 Mode Free-running.
(Utilisé pour le tester au lab)

F.B. / D.C. / P.C. 70 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
 Mode Normal: Partir une conversion: Écrire => /WR et /CS
Ensuite, Lire après /INTR ou un Délai.

Branchement à un CPU:
Dallas

Programme pour lire une donnée au convertisseur:

UC xdata *ucpADC = 0x8400; // Convertisseur place a l'adresse 0x8400 (CS4).


UC ucValeurLue;

*ucpADC = 0; // Part une conversion.


while(P1_0 == 1); // Attendre la fin de la conversion (INTR sur P1_0).
ucValeurLue = *ucpADC; // Lecture du convertisseur.

Note: Pour attendre la fin de conversion, on aurait pu mettre un délai d'au moins 100us.

Exemple de lectures à intervalle régulier:


UC xdata *ucpADC = 0x8400; // Convertisseur place a l'adresse 0x8400 (CS4).
UC xdata ucData[2000]; // Tableau des lecture.
UI i;

vInitTimer0(); // Ajuste le Timer0 pour l'intervalle voulu.

for (i = 0; i < 2000; i++) // Remplit tableau (Temps = Intervalle * 2000).


{
if(TF0 == 1) // Intervalle passer, on fait une lecture.
{
// Reload le Timer0 et remet Flag à 0.
*ucpADC = 0; // Part une conversion.
while(P1_0 == 1); // Attendre fin conversion (INTR sur P1_0).
ucData[i] = *ucpADC; // Lecture du convertisseur.
}
else
{
i--; // Pas de conversion, donc on reste au même i;
}
// On continu en interpretant les donnees du tableau.
}

Pour éviter d'attendre la fin de la conversion, on pourrait modifier le if:


if(TF0 == 1) // Intervalle passer, on fait une lecture.
{
ucData[i] = *ucpADC; // Lecture du convertisseur.
// Reload le Timer0 et remet Flag à 0.
*ucpADC = 0; // Part une conversion.
}

F.B. / D.C. / P.C. 71 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
14.1.2. Timing du ADC0804

Paramètres Conditions Min Typ Max Unités


TC Conversion Time fCLK = 640 kHz 103 114 us
fCLK Clock Frequency VCC = 5V 100 640 1460 kHz
CR Conversion Rate in Free-Running INTR = WR, CS =0 8770 9708 conv/s
fCLK = 640 kHz
tW(WR)L Width of WR (Start Pulse) * CS = 0 100 ns

tACC Access Time (Delay from Falling CL = 100pf 135 200 ns


edge of RD to Output Data Valid)
t1H, t0H Delay from Rising Edge of RD to 125 200 ns
Hi-Z state
tWI, tRI Delay from Falling Edge of WR or 300 450 ns
RD to Reset of INTR

*Voir la section 11.4 Accès à une donnée dans une RAM externe,
pour trouver la largeur du signal WR du Dallas.

F.B. / D.C. / P.C. 72 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15. Organigramme.
Lors de l'écriture de programme il est impératif de faire un brouillon papier afin de mieux cerner le
problème à résoudre. Mais comment mettre sur papier ses idées. Plusieurs options s’offrent à vous.

À date nous avons utilisé le pseudocode pour représenter nos algorithmes. Il existe d'autres
techniques. Une technique répandue est l'organigramme de programmation.

Un organigramme de programmation (souvent appelé Organigramme traditionnel (OT), logigramme


ou encore ordinogramme) est une représentation graphique normalisée de l'enchaînement des
opérations et des décisions effectuées par un programme d'ordinateur.

15.1. Organigramme Traditionnel ou Pseudocode?


Comme nous l'avons dit plutôt, nous retrouvons principalement deux techniques pour représenter un
algorithme: l'organigramme traditionnel et le pseudocode. Pour chacune il y a des avantages et des
inconvénients (rien n'est parfait). Voici donc un résumé de ces deux techniques.

L'organigramme traditionnel: Représentation graphique avec pictogrammes (icônes) reliés par des
traits, pour faciliter la lisibilité du programme. Comme chaque
pictogramme a une fonction bien définie, de simples connaissances
de bases permettent de les réaliser. (Ces connaissances de base
seront décrites plus loin)

Avantage: Simple à lire et à suivre, représente clairement l'idée exprimée, modification


relativement facile à faire (si on a un bon logiciel).

Inconvénient: Permet de faire des programmes non structurés, ce qui devient impossible à traduire
dans un langage évolué.

Pseudocode: Représentation des programmes avec du texte seulement. Ainsi le texte décrit
fidèlement ce qui doit être fait (LIRE les entrées, Incrémenter le compteur...).
Comme c'est du texte, et qu'il n'y a pas de lien nous devons écrire de manière très
structurée pour que le tout reste lisible et il faut aussi connaître les structures de base
en programmation.

Avantage : Simple à traduire vers un langage évolué, représente clairement l'idée exprimée, se
fait facilement sur plusieurs niveaux. Très facile à modifier.

Inconvénient : Vue d’ensemble plus difficile à faire, la traduction vers un langage de base
(assembleur) peut être difficile.

F.B. / D.C. / P.C. 73 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15.2. Organigramme Traditionnel
Les organigrammes traditionnels sont constitués de pictogrammes. Ces pictogrammes sont des
formes géométriques qui représentent visuellement une action ou un état propre à chaque dessin. Il y a
plusieurs pictogrammes différents mais pour les programmes de base, 6 sont suffisants, et les voici:

Indique le DÉBUT et la FIN d'un programme ou


d'une fonction. On y inscrit le nom de la fonction et
DÉBUT le type de retour s'il s’agit d'une fonction.

Indique une référence de sortie ou d'entrée lors


d'un GOTO (JMP) (ne pas utiliser avec un langage
RÉF.
évolué) ou d'un changement de page (OK pour
langage évolué).

Indique une action à effectuer (opération


mathématique, logique, de mouvement et
ACTION
autre).

Indique un choix à prendre ainsi que les différentes


Oui possibilités (2 possibles: VRAI ou FAUX).
DÉCISION

Non

Indique une action d'entrée/sortie à effectuer sur un


ENTRÉE port extérieur (Clavier, Écran, Port, etc…).
SORTIE

Indique l’appel d'une fonction.


FONCTION

Avec ces différents pictogrammes il est ainsi possible de faire l'organigramme traditionnel de vos
programmes (des plus simples aux plus compliqués).

F.B. / D.C. / P.C. 74 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15.2.1. Exemple:
Ordinogramme d'un programme pour faire un chenillard contrôlé par 2 interrupteurs
Méthode non structuré
DEBUT Tourne a
gauche
Initialisation
1 Décale de 1 bit
vers la gauche
sur la variable
Compteur -1 de sortie

Compteur=0 Oui
Sort la variable
Non Compteur = xx sur le port
Oui
Switch1=1
Non Tourne a Retour
gauche
Oui
Switch2=1
Tourne a
Non droite

En Pseudocode voici ce que ça donnerait:


DEBUT
Initialisation.
TANT QUE(VRAI)
Compteur = Compteur – 1.
SI(Compteur == 0)
Compteur = XX.
SI(Switch1 == 1)
Faire "Tourne A Gauche".
Recommence. // Difficile de coder en 'C'
SINON // Switch1 != 1.
SI(Switch2 == 1)
Faire "Tourne A Droite".
FIN SI.
FIN SI.
FIN SI.
FIN TANT QUE.
FIN.

DEBUT Fonction "Tourne a Gauche"


Décale Variable de 1 bit vers la gauche.
Port = Variable.
FIN Fonction "Tourne a Gauche".

Le grand inconvénient des organigrammes traditionnels, est qu’ils permettent de faire des
représentations non structurées et donc impossible à coder en ‘C’.

Pour réaliser un organigramme structuré, chaque partie du programme doit respecter la règle
suivante:
une entrée = une sortie.

F.B. / D.C. / P.C. 75 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Voici donc le même ordinogramme mais cette fois de manière structuré. La différence n’est pas
importante, mais elle prend tout son sens, lorsque vient le temps de codé le programme.

Ordinogramme d'un programme pour faire un chenillard contrôlé par 2 interrupteurs

Méthode structuré
DEBUT Tourne a
gauche
Initialisation
1 Décale de 1 bit
vers la gauche
Compteur -1 sur la variable
de sortie

Compteur=0 Oui
Sort la variable
Non Compteur = xx
sur le port
Oui
Switch1=1
Non Tourne a Retour
gauche
Oui
Switch2=1
Tourne a
Non droite

En Pseudocode voici ce que ça donnerait:

DEBUT
Initialisation.
TANT QUE(VRAI)
Compteur = Compteur – 1.
SI(Compteur == 0)
Compteur = XX.
SI(Switch1 == 1)
Faire "Tourne A Gauche".
SINON // Switch1 != 1.
SI(Switch2 == 1)
Faire "Tourne A Droite".
FIN SI.
FIN SI.
FIN SI.
FIN TANT QUE.
FIN.

DEBUT Fonction "Tourne a Gauche"


Décale Variable de 1 bit vers la gauche.
Port = Variable.
FIN Fonction "Tourne a Gauche".

F.B. / D.C. / P.C. 76 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15.2.2. Raison des organigrammes traditionnels.

Il est ainsi possible de représenter graphiquement la démarche logique de n’importe quels


programmes, processus, développement ou mécanisme.

Avec un organigramme traditionnel, on voit facilement les étapes (décisions et actions) à suivre pour
arriver au résultat escompté.

L’organigramme traditionnel permet de mettre sur papier ce que l’on conçoit dans sa tête. Il permet
aussi de faire visualiser à nos coéquipiers ce que l’on veut faire. S’il est fait de manière systématique,
au début de la conception d’un programme, vous sauverez un temps fou.

Faites toujours un organigramme traditionnel, même pour les programmes les plus simples. Un petit
bout de papier et c’est fait. Avec cette pratique, ce sera plus facile lorsque vous aurez de grands
programmes à réaliser (par contre, ça devient difficile à représenter).

L’organigramme traditionnel peut être fait de manière très générale ou très précise. La première
approche donne une vue d’ensemble, mais la seconde est plus facile à coder.

En voici un autre exemple :


Ordinogramme de processus de résolution de problème

DEBUT

Est-ce que Oui


la patente
a marche ?
Non
Touches-y
Oui surtout pas !!!
Y as-tu touché ?
T'as l'air fin là ! ! !
Non

Y a-t-il Oui
quelqu'un qui t'a
Est-ce que Oui vue
ça t'emmerde
vraiment ? Non
Pauvre toi ! ! !
Peux-tu Oui
Non la réparer ?
Tiens ça
Peux-tu Oui
mort !!!
Non blâmer quelqu'un
d'autre ?
Non
Fais de l'air !!!
Peux-tu Oui
la réparer ?

Non

Y-en a pas de problème !!!

FIN

F.B. / D.C. / P.C. 77 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15.3. Exemples de programmes:
Voici quelques exemples de programmes pour microcontrôleurs à cœur de 8051 en 'C' avec
organigramme traditionnel et le pseudocode.

Programme qui fait osciller le bit O du PORT 1 sans délai.


Ordinogramme : Pseudocode : Code Source : C

Prog 1 DEBUT Prog1 void Prog1(void)


{
TANT QUE(VRAI) while(1)
P1_0 = 1. {
Active B0 P1_0 = 0. P1_0 = 1;
FIN TANT QUE. P1_0 = 0;
FIN Prog1. }
Désactive B0 }

Programme qui retourne le PORT 3 sur le PORT 1


Ordinogramme : Pseudocode : Code Source : C

Prog 2 DEBUT Prog2 void Prog2(void)


{
TANT QUE(VRAI) while(1)
P1 = P3. {
Port 1 égale Port 3
FIN TANT QUE. P1 = P3;
FIN Prog2. }
}

Programme qui compte sur le PORT1 en binaire sans délai.


Ordinogramme : Pseudocode : Code Source : C

Prog 3 DEBUT Prog3 void Prog3(void)


{
TANT QUE(VRAI) while(1)
P1 = P1 + 1. {
Port 1 = Port 1 + 1
FIN TANT QUE. P1 = P1 + 1;
FIN Prog3. }
}

F.B. / D.C. / P.C. 78 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Routine de Délai.
Ordinogramme : Pseudocode : Code Source : C
Delai DEBUT Delai void Delai(UC i)
{
FAIRE do
iDelai = iDelai – 1. {
vDelai - 1 TANT QUE(iDelai != 0). i--;
FIN Delai. }while(i>0);
vDelai =0 Oui }
Non
//-----------------
ou encore:
Return
for(;i>0;i--);

Programme qui compte sur le PORT 1 en binaire avec un délai.


Ordinogramme : Pseudocode : Code Source : C
Prog 4
DEBUT Prog4 void Prog4(void)
{
TANT QUE(VRAI) while(1)
Port 1 = Port 1 + 1 P1 = P1 +1. {
iDelai = 10. P1++;
vDelai(iDelai). iDelai = 10;
vDelai = 10
FIN TANT QUE. vDelai(iDelai);
FIN Prog4. }
Call Delai }

Programme qui compte en binaire en + ou - selon l’état de l’entrée P3.3 sans délai.
Ordinogramme : Pseudocode : Code Source : C
Prog 5
DEBUT Prog5 void Prog5(void)
{
Oui
TANT QUE(VRAI) UC ucVar
P3.3 = 1 SI(P3_3 == 1) while(1)
Non VAR = VAR – 1. {
SINON if(P3_3 == 1)
VAR +1 VAR -1
VAR = VAR + 1. ucVar--;
FIN SI. else
P1 = VAR. ucVar++;
Port 1 = VAR FIN TANT QUE. P1 = ucVar;
FIN Prog5. }
}

F.B. / D.C. / P.C. 79 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
Programme qui fait la rotation d'un bit sur le PORT 1 vers la gauche ou la droite, selon l’entrée P3.3
(avec délai).

Ordinogramme : Pseudocode : Code Source : C


DEBUT Rout6 void Rout6(void)
Rout 6 {
Var = 0x08. UC ucVar = 0x08;
Var = 00001000
while(1)
TANT QUE(VRAI) {
SI(P3_3 == 1) if(P3_3 == 1)
Var = Var >> 1. {
Oui
P3.3 = 1 SI(Var == 0) ucVarA = ucVarA >> 1;
Non Var = 0x80. if(ucVar == 0)
FIN SI. ucVar = 0x80;
Rotation Rotation
Gauche Droite SINON }
de Var de Var
Var = Var << 1. else // P3_3 == 0.
SI(Var == 0) {
Var = 0x01. ucVarA = ucVarA << 1;
PortB = Var FIN SI. if(ucVar == 0)
FIN SI. ucVar = 0x01;
Delai 255
P1 = Var. }
vDelai(255). P1 = ucVar;
FIN TANT QUE. Delai(255);
FIN Rout6. }
}

F.B. / D.C. / P.C. 80 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
15.4. Organigramme traditionnel: If, then, else, for, while et switch case.
If-Then-Else While
Si (Condition vraie) tant que (Condition vraie)
{Faire si Oui} {Instruction}
Sinon {Faire si Non}

Oui Oui
Condition Condition vraie
Non Instructions
Non
Faire si non Faire si oui

Do-While
If-Then-Else imbriqué
faire
Si (Condition A) {Instructions}
{Si (Condition B) {Faire si A et B} tant que (Condition est vraie)
Sinon {Faire si A et non B}}
Sinon {Faire si non A}
Instructions
Oui
Condition A
Oui
Condition vraie
Non Oui
Condition B Non
Non
Faire si Faire si A Faire si Switch-Case
non A et non B A et B
switch (paramètre) {
case: 'A'{Faire si 'A'; break;}
case: 'B'{Faire si 'B'; break;}
case: 'C'{Faire si 'C'; break;}
For default: {Faire si rien; break;}
Pour (départ; Condition vraie ; incrément) }
{Instruction}
Si Oui
'A' Faire si 'A'
départ Non
Si Oui
'B' Faire si 'B'
Oui Non
Condition vraie Si Oui
'C' Faire si 'C'
Non Instructions
Faire si rien
incrément

F.B. / D.C. / P.C. 81 Cours 247-236 Programme de TSO


06-12-2019 NotesCours236_FB.docx
F.B. / D.C. / P.C. 82 Cours 247-236 Programme de TSO
06-12-2019 NotesCours236_FB.docx

Vous aimerez peut-être aussi