Académique Documents
Professionnel Documents
Culture Documents
Microcontrôleurs
PIC16FXX
2006/2007
1
Sommaire
A- Les microprocesseurs et les microcontrôleurs
1- Historique ………………………………………………………………………. 3
2- L’actualité………………………………………………………………………. 3
3- Architectures d'une Machine…………………………………………………… 3
4- Différence entre un microprocesseur et un microcontrôleur ………………….. 4
5- Quelques microcontrôleurs connus ……………………………………………. 4
6- Microcontrôleurs et applications ……………………………………………….. 9
2
D- Le PIC 16F877
1- Introduction ……………………………………………………………………. 5
2- Les Ports du 16F877 …………………………………………………………… 0
3- La mémoire du Pic ……………………………………………………………… 5
4- Les modules internes du 16F877……………………………………………….. 2
4.1. Les Trois timers / compteurs……………………………………………. 5
4.2. Un convertisseur analogique-numérique (CAN) 10 bits ………………. 4
4.3. Les deux modules CCP et la génération (PWM) ……………………… 5
4.4 Le module MSSP en mode SPI …………………………………………. 5
4.5. Le module USART …………………………………………………… 5
4.6. Port D en mode PSP…………………………………………………… 6
4.7. liaison I2C ……………………………………………………………… 6
5- Les Interruptions du 16F877 …………………………………………………… 0
6
8
7
2
7
3
7
9
8
0
8
5
3
2-L’actualité
Deux marques se partagent le marché des processeurs destinés aux ordinateurs :
• INTEL, grâce à IBM, ses PC et tous les compatibles qui sont apparus.
• MOTOROLA, grâce à Apple Computers et son Macintosh.
Le 80C552 est un microcontrôleur de 8bits. Il est fabriqué dans un processus supérieur de CMOS
et c'est un dérivé de la famille des microcontrôleurs 80C51. Il utilise les instructions mises pour
le 80C51 en addition des registres de la fonction spéciale qui sont incorporés pour contrôler les
périphériques.
5
Le schéma synoptique de la figure au dessous, présente les différents blocs internes du
microcontrôleur qui sont les suivants:
- 8 koctets de ROM interne (mémoire programme) extensible jusqu'à 64Koctets.
- 256 octets de RAM (mémoire données) interne extensible jusqu'à 64Koctets.
- 2 Timers 16 bits standard (T0 et Tl).
- Un troisième Timer de 16 bits (T2) couplé à quatre registres de capture et à trois
registres de comparaison.
- Convertisseur analogique numérique équipé de huit entrées dont la résolution est de 10
bits.
- 2 sorties (PWM) de résolution 8 bits.
- 5 ports bidirectionnels.
- Un port unidirectionnel.
6
5.2 Le 68HC11 de Motorola :
• Structure interne
Le microcontrôleur Motorola 68HC11 peut fonctionner avec des horloges allant jusqu'a
12MHz. Tous les registres étant statiques, une coupure d'horloge n'entraîne pas de perte de
donnée. Le 68HC11 intègre de puissants périphériques :
• Jusqu'a 12KO de ROM ou d'EPROM (mémoire programme)
• Jusqu'a 1 KO de RAM (mémoire donnée)
• Jusqu'a 8KO d'EEPROM (mémoire donnée)
• Ports paralélles
• Port de communication série asynchrone
• Port de communication série synchrone
• Ports analogiques
• Timers
• Chien de garde
• Génération d'interruptions temps réel
7
Le 68HC11 est disponible suivant les versions en boîtier DIP ou PLCC. Le modèle
fonctionnel du 68HC11Ex est donné ci-dessus. Des blocs fonctionnels peuvent être
différents ou absents dans certaines versions.
• Modes de fonctionnement
• Description :
Le microcontrôleur AT90S8535 est produit par ATMEL. Il s’agit d’un microcontrôleur 8 bits,
qui intègre de nombreux périphériques, ainsi que différents types de mémoire.
La figure suivante présente l’architecture interne du microcontrôleur.
• Constitution:
- CPU 8 Bits capable d’exécuter une instruction par cycle d’horloge.
- 8 Koctets de mémoire programme EEPROM FLASH programmable in situ.
- 512 Octets d’EEPROM (Stockage de données non volatiles)
- 512 Octets de RAM (données) statique.
- Convertisseur Analogique Numérique 10 bits à 8 entrées multiplexées.
- Liaisons séries synchrone (SPI) et asynchrone (SCI)
- 2 TIMERS 8 bits (dont 1 utilisable en RTC a l’aide d’un oscillateur externe)
- 1 TIMER 16 bits.
- 1 Comparateur de tensions analogiques.
- 2 entrées d’interruptions externes et une entrée de RESET.
- 4 Ports d’entrées/sorties 8 bits.
La fréquence maximale d’horloge est de 8MHz ce qui donne 8MIPS (8 Milliers d’Instructions
Par Seconde). Le boîtier peut être un boîtier DIL ou PLCC.
7
• Espace mémoire :
La taille du bus de données est de 8 bits, les mémoires de données (SRAM et EEPROM) sont
donc organisées en mot de 8 bits.
Les instructions exécutables par l’unité arithmétique et logique sont codées sur 16 bits, la
mémoire de programme (FLASH) est donc organisée en mots de 16 bits.
• Le CPU :
Le microprocesseur du AT90S8535 comporte :
- Un bloc de 32 registres, contenant les données à traiter,
- Une unité arithmétique et logique (ALU) rapide, qui est capable d’exécuter une instruction,
(de registre à registre) par cycle d’horloge,
- Un compteur programme,
- Un registre d’instruction et un décodeur d’instruction, et un cache d’instruction.
8
6- Microcontrôleurs et applications
D’autres fabricants proposent d’autres microcontrôleurs. Par exemple :
– Parallax (Série Basic Stamp)
– Arizona Microchip (Série PIC)
– Rabbit Semiconductor (Série Rabbit)
Il s’agit d’un marché gigantesque : Arizona Microchip déclare avoir vendu plus de 1,5
milliards d’unités (2001).
Les caractéristiques principales d’un microcontrôleur sont :
– De nombreux périphériques d’E/S
– Une mémoire de programme
– Une mémoire vive (en général de type SRAM)
– Eventuellement une mémoire EEPROM destinée à la sauvegarde par programme de
données à la coupure de l’alimentation.
– Un processeur 8 ou 16 bits.
– Faible consommation électrique
Les tailles mémoire sont en général réduites, de l’ordre de :
– 16 koctets pour la mémoire programme
– Quelques octets à 16 koctets pour la RAM
La puissance de calcul est aussi limitée :
– 0.012 MIPS pour les Basic Stamp
– 1 à 5 MIPS pour les PIC
– 20 MIPS pour les Rabbit
Pour référence, un 80286 à 6MHz permet d’atteindre 0.9 MIPS, alors que la puissance de
calcul d’un Pentium 4 est de l’ordre de 5000 MIPS. Les nouvelles gammes de
microcontrôleurs présentent des capacités importantes de calcul.
Les microcontrôleurs sont conçus pour l’usage dans plusieurs applications :
• Instrumentation
9
• Contrôle industriel
• Contrôle des automobiles
• Commande des machines
• Electroménager, Hi-Fi…
Nous allons durant ce cours étudier les microcontrôleurs PIC. Ces microcontrôleurs
présentent trois avantages majeurs : Le coût, le jeux d’instruction réduit et la facilité de
chargement du programme avec, bien sûr, toutes les fonctionnalités d’un microcontrôleur
moderne.
Nous allons maintenant nous pencher sur un PIC, et en particulier sur le 16F84.
Rassurez-vous, tout ce que nous verrons sur le 16F84 pourra être directement utilisé sur les
PIC16F877, qui ne sont rien d’autre que des 16F84 améliorées. Chaque PIC dispose des
fonctionnalités des modèles inférieurs, augmentées de nouvelles fonctions.
Tout d’abord, vous devez télécharger le datasheet du 16F84, car c’est un document qui
facilite la compréhension de ce cours. Durant ce cours, on vous référe à des pages à voir sur le
datasheet, pour plus d’informations. Il est vivement conseillé de l’imprimer, car vous en aurez
toujours besoin quand vous vous lancerez dans la réalisation de vos propres programmes. Le
datasheet du 16F877 vous serais aussi utile dans le cas ou vous aimeriez approfondir vos
connaisances sur ce qui se passe rééllement à l’intérieur du PIC, pour les périphériques qui
sont intégrés dans le microcontrôleur évolué 16F877.
Un PIC n’est rien d’autre qu’un microcontrôleur, c’est à dire une unité de traitement
de l’information de type microprocesseur à laquelle on a ajouté des périphériques internes
permettant de réaliser des montages sans nécessiter l’ajout de composants externes.
La dénomination PIC est sous copyright de Microchip, donc les autres fabricants ont
été dans l’impossibilité d’utiliser ce terme pour leurs propres microcontrôleurs.
Les PICs sont des composants dits RISC (Reduce Instructions Construction Set), ou
encore composant à jeu d’instructions réduit. Pourquoi ? Et bien, sachez que plus on réduit le
nombre d’instructions, plus facile et plus rapide en est le décodage, et plus vite le composant
fonctionne.
Toutes les PICs Mid-Range ont un jeu de 35 instructions, stockent chaque instruction
dans un seul mot de programme, et exécutent chaque instruction (sauf les sauts) en 1 cycle.
On atteint donc des très grandes vitesses, et les instructions sont de plus très rapidement
assimilées.
L’horloge fournie au PIC est prédivisée par 4 au niveau de celui-ci. C’est cette base de
temps qui donne le temps d’un cycle.
10
Si on utilise par exemple un quartz de 4MHz, on obtient donc 1000000 de
cycles/seconde, or, comme le pic exécute pratiquement 1 instruction par cycle, hormis les
sauts, cela vous donne une puissance de l’ordre de 1MIPS (1 Million d’Instructions Par
Seconde).
Pensez que les pics peuvent monter à 20MHz. C’est donc une vitesse de traitement
plus qu’honorable.
La famille des PICs est subdivisée en 3 grandes familles : La famille Base-Line, qui
utilise des mots d’instructions de 12 bits, la famille Mid-Range, qui utilise des mots de 14
bits (et dont font partie le 16F84 et 16F877), et la famille High-End, qui utilise des mots de 16
bits.
Nous nous limiterons dans cet ouvrage à la famille Mid-Range, sachant que si vous
avez tout compris, vous passerez très facilement à une autre famille, et même à un autre
microcontrôleur.
Notez dès à présent que les datasheets du 16F84 et 16F877 n’est qu’une petite partie
de la documentation complète. Pour obtenir la documentation complète, vous ajoutez encore
plus de 600 pages en téléchargeant chez Microchip les datasheets pour la gamme Mid-Range.
Cependant, la documentation de base suffit pour 99,9% des applications, et, de plus,
les datasheets Mid-Range sont disponibles sous la forme d’un fichier par chapitre.
Notez dès à présent que les PICs sont des composants STATIQUES, c’est à dire que la
fréquence d’horloge peut être abaissée jusqu’à l’arrêt complet sans perte de données et sans
dysfonctionnement. Une version –10 peut donc toujours être employée sans problème en lieu
et place d’une –04. Pas l’inverse, naturellement.
Ceci par opposition aux composants DYNAMIQUES, donc la fréquence d’horloge
doit rester dans des limites précises. N’essayez donc pas de faire tourner votre PIII/500 à
166MHz, car c’est un composant dynamique.
Nous allons traiter dans ce cours le PIC16f84 qui est le microcontroleur de base de
cette famille. Par la suite, on étudiera le 16F877 en décrivant surtout ses périphériques.
- Microcontrôleur 8 bits.
- Unité centrale de traitement avec une architecture RISC (Reduced Instruction
Set Computer).
- 1024 mots mémoire programme (flash).
11
- 68 octets mémoire de donnée (RAM).
- 64 octets mémoire de donnée (EEPROM).
- Bus programme (mot d’instruction) ; 14 bits.
- Bus de donnée ; 8 bits.
- 15 registres à fonction spéciale (SFR).
- 35 instructions : toutes les instructions prennent un seul cycle sauf les
branchements, ils prennent deux cycles.
- Adressage direct, indirect et relatif.
- 4 sources d'interruptions.
- Fréquence maximum d'horloge : 10Mhz.
- Temps de cycle d'instruction : 200ns.
- 13 entrées/sorties avec contrôle individuel de direction.
- Courant maximum d'entrée (par broche) : 25mA.
- Courant maximum de sortie (par broche) : 20mA.
- TMR0:8 bits temporisateurs/compteur programmable.
- Chien de garde interne avec son propre oscillateur.
- Programmation en mode série à travers deux broches (RB7 et RB6).
- Plage de tension de Vdd entre 2V et 5.5V.
- Boîtier de 18 broches (PDIP) : voir figure 1.2.
Fig1.Brochage du 16f84
2-Architecture interne:
La famille des microcontrôleurs PIC 16F8X utilise l'architecture RISC, cette architecture
permet une accessibilité séparée de la mémoire programme et celle de donnée .Par
conséquent on à deux bus : bus pour la mémoire donnée et un autre pour la mémoire
programme. L'UAL (Unité Arithmétique et Logique) est de 8 bits, elle communique avec un
12
registre de travail (W). Elle peut affecter le contenu des bits ; carry (C), digit carry (DC) et
Zéro (Z) du registre d'état (status), tout cela dépend du type d'instruction.
La figure 2 montre la structure interne du 16f84 :
3-Organisation du 16F84
Pour ceux qui veulent tout comprendre, la figure 3-1 de la page 8 montre
l’organisation interne d’un 16F84.
13
En effet, il faut 2 octets pour coder 14 bits. Ceci explique également pourquoi, lorsque
vous lisez une PIC vierge, vous allez lire des 0x3FFF. Cela donne en binaire
B’11111111111111’, soit 14 bits.
Notez à ce point qu’une instruction est codée sur 1 mot. Donc, 1K donne 1 bon millier
d’instructions possibles (c’est déjà pas si mal). Quand vous en serez à écrire des programmes
de 1K, vous serez sans aucun doute autonome pour vos applications.
La mémoire RAM est celle que nous allons sans cesse utiliser. Toutes les données qui y
sont stockées sont perdues lors d’une coupure de courant. La mémoire RAM est organisée en
2 banques pour la 16F84. La RAM est subdivisée de plus en deux parties. Dans chacune des
banques nous allons trouver des « cases mémoires spéciales » appelées REGISTRES
SPECIAUX et des cases mémoires « libres » dont vous pouvez vous servir à votre guise.
Pour le cas du 16F84, vous disposerez de 68 octets libres. L’organisation de la RAM est
montrée dans le tableau 4-2 page 13. Vous voyez la séparation verticale en 2 banques, et tout
en bas vous voyez deux banques de 68 octets de RAM.
14
banques (FSR par exemple). Cela signifie qu’y accéder depuis la banque 0 ou 1 ne fait pas de
différence.
Remarquez que la banque 0 utilise les adresses de 0x00 à 0x7F, la banque 1 allant de
0x80 à 0xFF (voir Fig4). Les zones en grisé sont des emplacements non utilisés (et non
utilisables). L’emplacement 0x00 est un emplacement auquel on ne peut pas accéder.
Pour la grande majorité des registres, chaque bit a une fonction spéciale. Page 14, tableau
4-1, vous trouverez les noms des bits utilisés dans ces registres.
4-1- Le PORTA :
Le PORTA est un port bidirectionnel et qu’il possède cinq pins dont la fonction de
chacune est :
RA0 (bit 0) : broche E/S.
RA1 (bit 1) : broche E/S.
RA2 (bit 2) : broche E/S.
RA3 (bit 3) : broche E/S.
RA4 (bit 4) : broche E/S et multiplexé avec une entrée d’horloge pour TMR0.
4-2- Le PORTB:
Le PORTB est un port bidirectionnel de huit broches dont la fonction de chacune est :
RB0 (bit 0) : broche E/S ou aussi une source d’interruption externe.
RB1 (bit 1) : broche E/S.
RB2 (bit2) : broche E/S.
RB3 (bit 3) : broche E/S.
RB4 ( bit 4 ) : broche E/S.
RB5 ( bit 5 ) : broche E/S.
RB6 (bit 6) : broche E/S et entrée horloge pour la programmation série du µC.
RB7 (bit 7) : broche E/S et entrée données pour la programmation série du µC.
15
Remarques :
- Toutes les broches du PORTB possèdent des résistances à + VDD (pullups). Ces résistances
sont mises en œuvre par programmation (le bit /RBPU du registre OPTION_REG), elles sont
automatiquement désactivées quand le port est en sortie..
- Les broches RB4:RB7 peuvent générer par programmation une interruption en cas de
changement d’état.
Les registres à fonction spéciale ou les SFR sont contenus dans la mémoire de données.
Ils sont utilisés par l'unité centrale (CPU) .L’emplacement mémoire de ces registres est donné
Dans la figure 4.Ces registres seront étudiés ultérieurement.
16
Allez, courage, cela devient de plus en plus concret. On va faire un petit survol du jeu
d’instructions des PICs. On saute directement page 55 du datasheet, au chapitre 9. Et oui,
comme cet ouvrage n’est pas un manuel de référence technique, mais un apprentissage, il faut
voir les chapitres dans le désordre.
Sur cette page, vous trouvez un petit encadré grisé qui fait allusion à deux anciennes
instructions qui ne sont plus utilisées. Nous ne nous en servirons donc pas. Par contre, vous
trouvez un tableau 9-1 qui indique comment les instructions sont codées dans le PIC. Et la,
vous voyez enfin à quoi correspondent nos 14 bits de mémoire programme.
Ce sont des instructions qui manipulent les données sous forme d’octets. Elles sont codées
de la manière suivante :
- 6 bits pour l’instruction : logique, car comme il y a 35 instructions, il faut 6 bits pour
pouvoir les coder toutes
- 1 bit (d) pour indiquer si le résultat obtenu doit être conservé dans le registre de travail de
l’unité de calcul (W pour Work) ou sauvé dans l’opérande (F pour File).
Aie, premier problème, 7 bits ne donnent pas accès à la mémoire RAM totale, donc voici
ici l’explication de la division de la RAM en deux banques.
En effet, il faudra bien trouver une solution pour remplacer le bit manquant. Vous avez dit
« un bit d’un des registres ? « BRAVO, vous avez tout compris. Il s’agit en réalité du bit RP0
du registre STATUS.
Ah, vous avez remarqué qu’il y a un RP1 ? Et oui, le 16F877 a 4 banques. Vous veillerez
à laisser RP1 à 0 pour la 16F84, afin de pouvoir « porter » votre programme sans problème
vers une PIC supérieure.
Ce sont des instructions destinées à manipuler directement des bits d’un registre
particulier. Elles sont codées de la manière suivante :
- 4 bits pour l’instruction (dans l’espace resté libre par les instructions précédentes)
- 3 bits pour indiquer le numéro du bit à manipuler (bit 0 à 7 possible), et de nouveau 7 bits
pour indiquer l’opérande.
17
6.2.3 Les instructions générales
Ce sont les instructions qui manipulent des données qui sont codées dans l’instruction
directement. Nous verrons ceci plus en détail lorsque nous parlerons des modes d’adressage.
Elles sont codées de la manière suivante :
- Elle est suivie d’une valeur IMMEDIATE codée sur 8 bits (donc de 0 à 255).
Ce sont les instructions qui provoquent une rupture dans la séquence de déroulement
du programme. Elles sont codées de la manière suivante :
Nous pouvons déjà en déduire que les sauts ne donnent accès qu’à 2K de mémoire
programme (211).
Rappelez-vous que l’espace mémoire programme est de 1Kmots. Pour coder une adresse
de saut à l’intérieur de la mémoire programme, il faut donc 10 bits (210 = 1024 = 1K).
Par convention, en effet, 1Kbytes correspond à 210 = 1024 octets. Ce qui explique que si
vous avez 16K de mémoire, en réalité vous avez 16*1024 = 16384 bytes. Par extension,
1Mbyte = 1024 Kbytes, donc 1048576 octets.
Maintenant vous voyez pourquoi vous voyez plus que votre mémoire théorique lors du
test mémoire au démarrage de votre ordinateur. Une petite parenthèse qui n’a rien à voir ici :
les fabricants de disques durs considèrent que 1Mbytes = 1000000 bytes. Comme Windows
indique la taille en Mbytes de 1048576 bytes, cela vous explique pourquoi la plupart de vos
disques durs semblent plus petits que prévus.
Le tableau suivant présente les instructions de la famille 16F(C)xxx :
18
Fig5.Le jeux d’instructions de la famille 16F8xx
Lisez donc attentivement ce qui suit. Tous les indicateurs sont des bits du registre
STATUS. Voyez le tableau page 15. Nous aborderons ici les flags Z et C. Les autres seront
traités lors de l’étude des registres.
19
Donc, si vous faites une addition avec ADDWF et que le résultat obtenu est 0, le bit Z
sera à 1. Si le résultat est <>0 (différent de 0), le bit Z vaudra 0. Dans les 2 cas il est modifié.
Par contre, si vous stockez une valeur avec l’instruction MOVWF, le bit Z ne sera pas
modifié, même si la valeur vaut 0. Ces remarques sont valables pour les autres flags.
Petit exemple :
Comme les registres de la PIC ne font que 8 bits, vous obtiendrez B’00000001’ (1) et
C positionné à 1 (en fait le 9ème bit, donc le bit 8, donc 28 = 256). Donc le résultat final est de
256 + 1 = 257.
Remarquez que si vous aviez ajouté B’11111110’ et B’00000010’, vous auriez obtenu
B’00000000’.
Dans ce cas, vous auriez eu C à 1 ET Z à 1, ce qui signifie résultat nul, mais avec
report (donc résultat = 256).
On utilisera pour l’édition d’un fichier « .asm » l’éditeur de MPLAB. MPLAB est
l’outil logiciel gratuit fournit par MICROCHIP pour l’édition, la compilation et la simulation
d’un programme en assembleur.
Tout d’abord, cliquez n’importe où à l’intérieur d’un fichier asm quelconque. Vous
êtes à l’intérieur d’un simple traitement de texte. Dans le coin inférieur gauche, vous verrez
un numéro de ligne et de colonne. C’est la position actuelle de votre curseur. Nous nous
servirons de cette position pour vous guider. N’ajoutez donc pas de lignes pour l’instant, pour
garder la correspondance correcte avec ce texte.
Si vous n’arrivez pas à effectuer des modifications dans votre fichier, et que votre
clavier semble inactif, c’est que vous avez utilisé un caractère étendu dans le nom de votre
fichier. MPLAB est allergique à certains caractères, comme le « ç ».
20
suit étant considéré comme zone de commentaire, vous pouvez y mettre tout ce que vous
voudrez.
Prenez l’habitude de toujours commenter vos programmes. Soyez sûr que dans 6
mois, vous ne vous rappellerez plus ce que vous avez voulu faire, les commentaires vous
seront alors d’une grande utilité si vous décidez de modifier votre programme.On prendra
l’exemple du fichier <<Ledcli.asm>> qui fera le sujet de notre premier programme.
A la ligne 8, nous trouvons une DIRECTIVE destinée à MPASM pour indiquer quel
type de processeur est utilisé dans ce programme.
Les DIRECTIVES ne font pas partie du programme, elles ne sont pas traduites en
OPCODE, elles servent à indiquer à l’assembleur de quelle manière il doit travailler. Ce sont
donc des COMMANDES destinées à l’assembleur en lui-même.
Cette ligne signifie tout simplement que FSR EGAL 0x0004. Autrement dit, lorsque
vous utiliserez FSR dans une instruction, MPASM interprétera FSR comme étant 0x04. 0x04
étant tout simplement l’adresse de FSR dans la mémoire du PIC.
H’0004’ est une autre méthode autorisée pour exprimer un nombre hexadécimal, tout
comme 04h
Si vous prenez votre tableau 4-2 page 13, vous constaterez que c’est bien le cas. Ce
fichier est donc principalement destiné à vous éviter d’avoir à mémoriser toutes les adresses,
un nom est bien plus simple à utiliser. Fermez le fichier p16F84.inc pour ne pas encombrer
votre fenêtre.
La ligne suivante, commence par « __CONFIG ». Cette ligne contient les fameux
« fusibles » qui fixent le fonctionnement du PIC.
Les valeurs écrites ici seront intégrées dans le fichier « .hex » pour signaler au
programmateur les valeurs à encoder aux adresses spécifiques du PIC. Nous y reviendrons.
21
On trouve dans le fichier toutes les valeurs possibles de ces paramètres, avec les
explications correspondantes. Il suffit de remplacer une des valeurs par celle souhaitée.
Par exemple, activons le Code Protect (protection en lecture) :
Par
Faites-le. Remarquez que les différentes valeurs sont liées par le symbole « & »
(AND). Ils fonctionnent donc en plaçant des bits à « 0 », si vous avez tout suivi. Les valeurs
exactes sont de nouveau dans le fichier « P16F84.INC ».
Il est vivement conseillé d’utiliser les ASSIGNATIONS et autres méthodes que nous
allons voir plus bas. La syntaxe est simple puisqu’il s’agit de EQU (égal à)
Exemple d’assignation :
Par exemple nous pourrons utiliser un PORT suivi d’un numéro de bit, ou bien
carrément une instruction avec ses paramètres.
22
Une définition est construite de la manière suivante : La directive #DEFINE, suivie
par le nom que l’on désire utiliser, puis la chaîne à substituer. Par exemple :
Une macro remplace donc un morceau de code que nous utilisons souvent. La macro
fonctionne également uniquement comme un simple traitement de texte.
La macro simplifie donc l’écriture, mais ne raccourci pas la taille du fichier .hex
obtenu, puisque les 2 lignes seront écrites dans le PIC.
Notez que l’on peut utiliser des macros plus complexes, avec passage de paramètres,
mais nous n’entrerons pas dans ces fonctions particulières pour l’instant.
Notez également que vous disposez d’une aide dans le menu « help->MPASM Help ».
En effet, l’aide de MPLAB concerne l’utilisation du logiciel. Les aides concernant le langage
sont dans MPASM, puisque c’est ce langage que MPLAB utilise (revoyez l’édition des
nœuds).
Toute zone définie par l’utilisateur commence avec la DIRECTIVE CBLOCK, suivie
par l’adresse du début de la zone.
Pour placer nos variables, qui sont des emplacements mémoires auxquels on a donné
un nom, nous consultons de nouveau le tableau 4-2. Nous voyons que la zone RAM librement
utilisable commence à l'adresse 0x0C. Notre zone de variable contiendra donc la directive
23
7.9 Les étiquettes
Vous trouverez dans les programmes en 1ere colonne ce que nous appellerons des
ETIQUETTES. Ce sont des noms que vous choisissez et qui sont des REPERES pour le
programme.
La directive ORG, suivie de l’adresse, précise à quelle adresse les instructions qui
suivent seront placées dans le PIC. Il est important de savoir 2 choses :
- Après un reset ou une mise sous tension, le PIC démarre toujours à l’adresse 0x00. Le
début de votre programme doit donc se situer là.
- L’adresse 0x04 est l’adresse utilisée par les interruptions (nous verrons le principe plus
tard). Il ne vous reste donc pas une grande place pour placer votre programme. Nous
commencerons donc par un saut vers l’emplacement du programme principal où nous
aurons plus de place. Allons donc voir ligne 70 comment tout ceci fonctionne :
La première ligne est une DIRECTIVE qui indique que la ligne suivante sera placée à
l’adresse 0x00.
La seconde ligne est une INSTRUCTION, expliquée page 62 du datasheet, qui indique
au PIC que le programme doit SAUTER à l’adresse « init ». « init » est une ETIQUETTE.
Après le reset, le PIC exécute donc l’instruction goto init qui se trouve à l’adresse
0x00, suivie par l’instruction qui se trouve à l’adresse init plus bas dans le programme (donc
juste en dessous de l’étiquette init).
Cette directive précise l’endroit où doit cesser l’assemblage de votre programme. Elle
est obligatoire dans tout programme, sous peine d’une erreur qui vous signalera que la fin de
fichier (End Of File) a été atteinte avant de rencontrer la directive END.
Toutes les instructions situées après la directive END seront tout simplement ignorées.
Vous voici prêt à lancer une simulation. Mais à quoi cela pourrait-il vous servir si vous
ne comprenez pas les changements qui vont s’opérer dans les registres spéciaux ? On va donc
24
commencer par vous expliquer les registres de base nécessaires à la compréhension du
processus.
Un processeur, quel qu’il soit est un composant qui exécute SEQUENTIELLEMENT une
série d’INSTRUCTIONS organisées selon un ensemble appelé PROGRAMME.
Il existe donc dans le processeur un SEQUENCEUR, c’est à dire un compteur qui permet
de pointer sur la PROCHAINE instruction à exécuter. Ce séquenceur est appelé suivant les
processeurs « compteur ordinal », « Pointeur de programme » etc. Dans le cas des PICs, il
s’appelle PC, pour Program Counter. Le PC n’est pas accessible directement par l’utilisateur.
Le principe de base est toujours le même. Dans les PICs, les registres ne font que 8 bits,
on ne peut donc stocker qu’une adresse maximale de 255. Il faudra donc 2 registres pour
accéder à une adresse. Les PICs ont un fonctionnement un peu particulier à ce sujet.
Nous trouvons tout d’abord un registre qui contient l’adresse basse du PC, c’est à dire les
8 bits de poids faibles. Ce registre est accessible en lecture et en écriture. Il est appelé PCL
(PC Low)
Le PC complet étant codé sur 13 bits, il faudra donc compléter PCL avec 5 bits
supplémentaires. Il existe deux cas possibles :
- Lors d’un saut, par exemple, le contenu du PC est chargé directement avec les 11 bits de
destination contenus dans l’instruction en elle-même. Les 2 bits manquants sont extraits
du registre PCLATH. Les bits 3 et 4, qui doivent être positionnés par l’utilisateur, sont
placés directement dans les bits 11 et 12 du PC afin de compléter l’adresse de destination.
Comme la 16F84 ne gère que 1K de mémoire programme, nous n’aurons pas besoin de ce
registre dans le cas des sauts. Le 16F84 ne gère que 10 des 13 bits du PC.
Remarquez que la limite du PC est de 13 bits, ce qui implique que les PICs de la
famille mid-range auront une capacité de mémoire programme de 8K mots maximum (soit
213).
Il est très important de se rappeler que le PC pointe toujours sur l’instruction suivante,
donc l’instruction qui n’est pas encore exécutée. C’est indispensable de bien comprendre ceci
pour analyser les programmes en cours de debbuggage.
25
7.12.2 Le registre « W »
Ce registre est un registre utilisé par les pics pour réaliser toutes sortes de calculs. Dans
une instruction la destination d’un résultat (d) peut en général être un emplacement RAM (f)
ou le registre de travail (w). C’est un donc un registre fondamental.
C’est un registre dont chaque bit a une signification particulière. Il est principalement
utilisé pour tout ce qui concerne les tests. Il est donc également d’une importance
fondamentale. Il est décrit dans le tableau de la page 15 du datasheet..
Voici les différents bits qui le composent, en commençant par le bit0 (b0), donc le bit le
plus à droite, ou encore le moins significatif. Remarquez qu’on utilise le terme LSB, parfois
comme byte le moins significatif, parfois comme bit le moins significatif. C’est également un
abus de langage, mais le contexte permet très bien de les distinguer.
26
8- Les modes d’adressage
Les instructions utilisent toutes une manière particulière d’accéder aux informations
qu’elles manipulent. Ces méthodes sont appelées « modes d’adressage ».
Exemple
Exemple
Cet adressage fait appel à 2 registres, dont un est particulier, car il n’existe pas vraiment.
Examinons-les donc :
27
8.3.1 Les registres FSR et INDF
Ceux qui suivent sont déjà en train de chercher dans le tableau 4-2 après INDF.
INDF signifie INDirect File. Vous le voyez maintenant ? Et oui, c’est le fameux
registre de l’adresse 0x00. Ce registre n’existe pas vraiment, ce n’est qu’un procédé d’accès
particulier à FSR utilisé par le PIC pour des raisons de facilité de construction électronique
interne.
Le registre FSR est à l’adresse 0x04 dans les 2 banques. Il n’est donc pas nécessaire de
changer de banque pour y accéder, quelle que soit la banque en cours d’utilisation.
On peut donc dire que INDF est en fait le registre FSR utilisé pour accéder à la case
mémoire. Donc, quand on veut modifier la case mémoire pointée, on modifie FSR, quand on
veut connaître l’adresse de la case pointée, on accède également à FSR. Si on veut accéder au
CONTENU de la case pointée, on accède via INDF. Nous allons voir tout ceci par un petit
exemple, mais avant,
ATTENTION
Le contenu du registre FSR pointe sur une adresse en 8 bits. Or, sur certaines PICs, la
zone RAM contient 4 banques (16F877). L’adresse complète est donc une adresse sur 9 bits.
L’adresse complète est obtenue, en adressage DIRECT, par l’ajout du bit 7 et 8 sous forme de
RP0¨et RP1 (RP1 est inutilisé pour le 16F84 car seulement 2 banques) et par l’ajout du bit
IRP dans le cas de l’adressage INDIRECT (inutilisé sur le 16F84). Veillez donc à toujours
laisser IRP (dans le registre STATUS) et RP1 à 0 pour assurer la portabilité de votre
programme.
Exemple
28
8.4 Quelques exemples
On va répéter, mais les modes d’adressages doivent impérativement être compris. Pour
les habitués des processeurs divers, excusez ces répétitions. Les registres sont intialisés avec
les valeurs précédentes.
movlw mavariable
movf mavariable , w
movf INDF , w
movf FSR , w
Ceci est un piège. C’est en effet de l’adressage DIRECT. On placera donc dans (W) le
CONTENU du registre FSR, donc 0X0E sera mis dans (W).
;*********************************************************************************
; PROGRAMME DE CLIGNOTEMENT D'UNE LED CONNECTEE SUR LE PORTA.2 *
; D'UN PIC16F84. PROGRAMME D'ENTRAINEMENT AU FONCTIONNEMENT *
; DES PICS.LA FREQUENCE DE CLIGNOTTEMENT EST DE 1 HZ (avec un quartz de 4MHz)*
;*********************************************************************************
29
; '__CONFIG' précise les paramètres encodés dans le processeur au moment de
; la programmation du processeur. Les définitions sont dans le fichier include.
; Voici les valeurs et leurs définitions :
Remarquez qu’on effectue un AND (&) entre les différentes valeurs, les niveaux actifs
sont donc des niveaux 0
Le premier paramètre précise si votre PIC sera protégée ou non contre la lecture à la
fin de la programmation. Laissez ici ce paramètre sur « CP_OFF » = non protégée.
Ensuite, laissez PWRTE sur ON pour préciser que vous utilisez un reset « sécurisé »,
donc avec un allongement du temps avant démarrage. Ceci vous met à l’abri des alimentations
un peu lentes à démarrer.
Enfin, vient le fonctionnement de l’oscillateur que vous allez utiliser. Le tableau 8-1
page 40 donne les valeurs recommandées en fonction des fréquences utilisées pour un PIC de
10MHz. Retenez que la valeur _HS_OSC convient pour les fréquences élevées. Sinon utiliser
XT_OSC pour les fréquences ≤ 4MHz.
Il est important de ne pas utiliser _RC_OSC si on utilise un quartz. Ce paramètre est
réservé à un fonctionnement par réseau R/C tel que dessiné figure 8-7 page 41.
Même, si en pratique, les PICs sont des composants très solides, évitez de vous
tromper à ce niveau. Et voilà, vous connaissez parfaitement _Config. Vous avez maintenant la
ligne suivante :
30
9.3 Le registre OPTION
Si vous regardez le tableau 4-2, vous constaterez que ce registre se trouve à l’adresse
0x81, donc dans la banque1. Dans les fichiers include de MPLAB, ce registre est déclaré avec
le nom OPTION_REG.
C’est donc ce nom que vous devrez utiliser. Nous allons le détailler ici. Ce registre est un
registre de bits, c’est à dire que chaque bit a un rôle particulier :
Le tableau de la page 16 représente le contenu de ce registre :
b7 : RBPU
Quand ce bit est mis à 0 (actif niveau bas en italique), une résistance de rappel au +5
volt est placée sur chaque pin du PORTB.
b6 : INTEDG
Donne, dans le cas où on utilise les interruptions sur RB0, le sens de déclenchement de
l’interruption. Si b6 = 1, on a interruption si le niveau sur RB0 passe de 0 vers 1. Si b6 = 0,
l’interruption s’effectuera lors de la transition de 1 vers 0.
b5 : TOCS
Ce bit détermine le fonctionnement du timer0, que nous verrons bientôt. Retenez que
le timer0 est incrémenté soit en fonction de l’horloge interne (synchronisé au programme),
dans ce cas b5 = 0, soit il compte les impulsions reçues sur la pin RA4, dans ce cas b5=1.
b4 : TOSE
Comme nous avons placé b5=0, b4 est alors inutilisé. Nous laisserons donc b4 = 0.
b3 : PSA
Nous avons dans le PIC un prédiviseur. Qu’est-ce que c’est ? Et bien tout simplement,
ceci indique le nombre de pulses qui devra être reçu pour provoquer une incrémentation de la
destination. Nous y reviendrons en détail avec le fonctionnement du tmr0.
A ce niveau, sachez simplement que ce prédiviseur peut servir à une des deux
fonctions suivantes (et pas les deux) : soit il effectue une prédivision au niveau du timer du
watchdog (b3 = 1), soit il effectue une prédivision au niveau du tmr0 (timer0) (b3=0). Dans
notre cas, mettez b3 = 1 (nous verrons ci-dessous pourquoi).
31
b2, b1,b0 : PS2,PS1,PS0
Ces trois bits déterminent la valeur de prédivision pour le registre déterminé ci-dessus. Il y
a donc 8 valeurs possibles, montrées dans le petit tableau de la page 16.
Remarquez que les valeurs sont différentes pour le watchdog et pour tmr0. En effet, il n’y
a pas de ‘division par 1’ pour ce dernier registre.
Si vous désirez ne pas utiliser de prédiviseur du tout, la seule méthode est de mettre b3=1
(prédiviseur sur watchdog) et PS2 à PS0 à 0. Dans ce cas : pas de prédiviseur sur tmr0, et
prédiviseur à 1 sur watchdog, ce qui correspond à pas de prédiviseur non plus. Nous mettrons
donc b2=b1=b0= 0.
Nous utiliserons donc la valeur B’00001000’ pour notre programme d’essai, soit 0x08.
Il faut avoir l’habitude de ne pas traîner des valeurs fixes à travers mes programmes, afin d’en
faciliter la maintenance. On place ces valeurs en début de programme en utilisant des
assignations.
L’assignation est déjà créée plus bas dans le programme. On a créé une CONSTANTE
qu’on a appelé OPTIONVAL et qui contiendra la valeur à placer plus tard dans le registre
OPTION_REG. On rappelle que les CONSTANTES n’occupent pas de place dans le PIC,
elles sont simplement remplacées par l’assembleur au moment de la compilation. Elles
servent à faciliter la lecture du programme.
Cherchez donc plus bas dans le programme après les assignations, et remplacez la
valeur affectée à OPTIONVAL par celle que nous avons trouvée et ajoutez vos commentaires.
Supprimez l’assignation concernant INTERMASK, car nous ne nous servirons pas des
interruptions dans ce premier programme. Dans la zone des assignations, il vous reste donc
ceci :
;*********************************************************************
; ASSIGNATIONS *
;*********************************************************************
Descendons encore jusqu’à la zone des définitions. Nous allons donner un nom à notre
bouton-poussoir et à notre LED. Les instructions bcf et bsf que nous allons utiliser pour
mettre ou lire des 1 ou des 0 dans les registres ont la syntaxe suivante bsf f, n et comme le
registre d’accès s’appelant PORTA (pour le port A) et PORTB (pour le port B), nous
utiliserons des DEFINE permettant d’intégrer f et n en même temps.
32
Nous voyons sur le schéma que la LED est connectée sur le bit 2 du port A. Le
bouton-poussoir est connecté sur le bit 2 du port B. Nous effaçons donc les définitions de
l’exemple, et nous les remplaçons par les nôtres. Nous obtenons alors ceci :
;*********************************************************************
; DEFINE *
;*********************************************************************
Notez que LED et BOUTON sont des noms que nous avons librement choisis, à
condition qu’il ne s’agisse pas d’un mot-clé. Pas question par exemple d’utiliser STATUS ou
encore MOVLW, bien que ces exemples soient tirés par les cheveux, cela pourrait vous arriver
un jour d’utiliser un mot réservé par inadvertance.
A quoi servent les définitions ? Et bien supposons que vous décidez de connecter la
LED sur le PORTB bit 1 (RB1), par exemple. Et bien, nul besoin de rechercher partout dans
le programme, il suffira de changer dans la zone DEFINE.
On descend encore un peu, et on arrive dans la zone des macros. Nous n’en avons pas
vraiment besoin ici, mais nous allons quand même les utiliser à titre d’exemple.
Effacez la macro donnée à titre d’exemple et entrons celles-ci.
;*********************************************************************
; MACRO *
;*********************************************************************
LEDON macro
bsf LED
endm
LEDOFF macro
bcf LED
endm
La première colonne donne le nom de la macro (ici, 2 macros, une LEDON et une
LEDOFF). La directive macro signifie ‘début de la macro’ la directive endm signifie ‘fin de la
macro’. Notez que les macros peuvent évidemment comporter plusieurs lignes de code.
Prenons notre exemple : quand nous utiliserons la ligne suivante dans notre
programme (attention, ne pas mettre la macro en première colonne) :
LEDON
bsf LED
Il remplacera également LED par PORTA,2. Ce qui fait qu’en réalité nous obtiendrons :
33
bsf PORTA , 2
Nous avons donc obtenu une facilité d’écriture et de maintenance. Gardez à l’esprit
que les macros sont des simples substitutions de traitement de texte. Si votre macro se
compose de 50 lignes de code, les 50 lignes seront copiées dans le PIC à chaque appel de la
macro.
Nous arrivons dans la zone des variables. Nous ajouterons celles-ci au fur et à mesure
de leur nécessité. Effacez donc les 2 variables présentes, car elles sont utilisées dans les
routines d’interruption que nous n’utiliserons pas ici.
;*********************************************************************
; DECLARATIONS DE VARIABLES *
;*********************************************************************
Comme nous n’utiliserons pas les interruptions, supprimez tout ce qui suit jusqu’à la routine
d’initialisation, vous obtenez :
**********************************************************************
; DEMARRAGE SUR RESET *
;**********************************************************************
;*********************************************************************
; INITIALISATIONS *
;*********************************************************************
suite du programme
A ce stade, avant de poursuivre, nous allons étudier les registres dont nous allons nous
servir, et tout d’abord :
Ce registre est un peu particulier, puisqu’il donne directement accès au monde extérieur.
C’est en effet ce registre qui représente l’image des pins RA0 à RA4, soit 5 pins. Si vous
suivez toujours, c’est ce registre qui va servir à allumer la LED.
Ce registre se situe à l’adresse 05H, dans la banque0. Chaque bit de ce registre représente
un pin. Donc, seuls 5 bits sont utilisés. Pour écrire sur un pin en sortie, on place le bit
correspondant à 1 ou à 0, selon le niveau souhaité.
34
Par exemple :
bsf PORTA , 1 ; envoyer niveau 1 sur RA1
place un niveau +5V sur la pin RA1. Notez qu’il faut pour cela que cette pin soit
configurée en sortie (voir TRISA). Pour tester une entrée, on pourra par exemple utiliser
Ce registre est situé à la même adresse que PORTA, mais dans la banque 1. Son adresse
complète sur 8 bits est donc 0x85.
Ce registre est d’un fonctionnement très simple et est lié au fonctionnement du PORTA.
Au reset du PIC, tous les pins sont mis en entrée, afin de ne pas envoyer des signaux non
désirés sur les pins. Les bits de TRISA seront donc mis à 1 lors de chaque reset.
Notez également que, comme il n’y a que 5 pins utilisées sur le PORTA, seuls 5 bits
(b0/b4) seront utilisés sur TRISA.
Ces registres fonctionnent exactement de la même manière que PORTA et TRISA, mais
concernent bien entendu les 8 pins RB. Tous les bits sont donc utilisés dans ce cas.
Voyons maintenant les particularités du PORTB. Nous en avons déjà vu une, puisque
les entrées du PORTB peuvent être connectées à une résistance de rappel au +5V de manière
interne.
Note :
Après un reset, vous vous demandez peut-être quel est l’état de tel ou tel registre.
Vous trouverez ces explications dans le tableau de la page 14. Vous voyez qu’après un reset, le
registre OPTION_REG a tous ses bits à 1. Vous devez donc spécifier l’effacement du bit7
pour valider les résistances de rappel au +5V.
35
;*********************************************************************
; SOUS-ROUTINE DE TEMPORISATION *
;*********************************************************************
;---------------------------------------------------------------------
; Cette sous-routine introduit un retard de 500.000 µs.
; Elle ne reçoit aucun paramètre et n'en retourne aucun
;---------------------------------------------------------------------
tempo
movlw 2 ; pour 2 boucles
movwf cmpt3 ; initialiser compteur3
boucle3
clrf cmpt2 ; effacer compteur2
boucle2
clrf cmpt1 ; effacer compteur1
boucle1
nop ; perdre 1 cycle
decfsz cmpt1 , f ; décrémenter compteur1
goto boucle1 ; si pas 0, boucler
decfsz cmpt2 , f ; si 0, décrémenter compteur 2
goto boucle2 ; si cmpt2 pas 0, recommencer boucle1
decfsz cmpt3 , f ; si 0, décrémenter compteur 3
goto boucle3 ; si cmpt3 pas 0, recommencer boucle2
return ; retour de la sous-routine
;*********************************************************************
; PROGRAMME PRINCIPAL *
;*********************************************************************
DEBUT
bsf STATUS,RP0
clrf TRISA ; port A en sortie
bcf STATUS,RP0
LEDON ; allumer la LED :
call tempo ; appeler la tempo de 0.5s
LEDOFF ; éteindre LED
call tempo ; appeler la tempor de 0.5s
goto DEBUT ; boucler
END ; directive fin de programme
Imaginez une conversation normale. Chaque interlocuteur prend la parole quand vient son
tour de parler. Survient alors un événement extérieur dont le traitement est urgent. Par
exemple, un piano tombe du 3ème étage de l’immeuble au pied duquel vous discutez. Vous
imaginez bien que votre interlocuteur ne va pas attendre la fin de votre phrase pour vous
36
signaler le danger. Il va donc vous INTERROMPRE durant le cours normal de votre
conversation afin de pouvoir TRAITER IMMEDIATEMENT l’EVENEMENT extérieur.
Les interlocuteurs reprendront leur conversation où elle en était arrivée, sitôt le danger écarté.
Et bien, pour les programmes, c’est exactement le même principe. Votre programme se
déroule normalement. Survient un événement spécifique. Le programme principal est
interrompu (donc, subit une INTERRUPTION), et va traiter l’événement, avant de reprendre
le programme principal à l’endroit où il avait été interrompu.
Vous voyez ici l’opposition avec les ruptures de séquences synchrones, provoquées par le
programme lui-même (goto, call, btfss…).
Nous pouvons dire, sans nous tromper de beaucoup, qu’une routine d’interruption est un
sous-programme particulier, déclenché par l’apparition d’un événement spécifique. Cela a
l’air un peu ardu, mais vous allez voir que c’est très simple.
Il va bien sûr de soi que n’importe quel événement ne peut pas déclencher une
interruption. Il faut que 2 conditions principales soient remplies :
37
Fig 6. Méchanisme général d’une interruption
Que pouvons-nous dire en voyant cet ordinogramme ? Et bien, nous pouvons déjà nous
dire que le programme principal ne sait pas quand il est interrompu, il est donc crucial de lui
remettre ses registres dans l’état où ils étaient avant l’interruption.
En effet, supposons que l’instruction xxx ait positionné un flag (par exemple, le bit
d’indicateur Z). Si par malheur, la routine d’interruption a modifié ce bit, le programme ne
pourra pas se poursuivre normalement.
Nous voyons également que l’instruction xxx termine son exécution avant de se
brancher sur la routine d’interruption. Une instruction commencée n’est donc jamais
interrompue.
Bien entendu, les PICs répondent au fonctionnement général ci-dessus, mais elles ont
également leurs particularités. Voyons maintenant le principe des interruptions sur les PICs
- Tout d’abord, l’adresse de début de toute interruption est fixe. Il s’agit toujours de
l’adresse 0x04. Toute interruption provoquera le saut du programme vers cette adresse.
38
- Le contenu du PC est sauvé sur la pile interne (8 niveaux). Donc, si vous utilisez des
interruptions, vous ne disposez plus que de 7 niveaux d’imbrication pour vos sous-
programmes. Moins si vous utilisez des sous-programmes dans vos interruption.
- Une interruption ne peut pas être interrompue par une autre interruption. Les
interruptions sont donc invalidées automatiquement lors du saut à l’adresse 0x04 par
l’effacement du bit GIE (que nous allons voir).
- TMR0 : Débordement du timer0 (tmr0). Une fois que le contenu du tmr0 passe de 0xff
à 0x00, une interruption peut être générée.
- EEPROM : cette interruption peut être générée lorsque l’écriture dans une case
EEPROM interne est terminée.
- RB0/INT : Une interruption peut être générée lorsque, la pin RB0, encore appelée
INTerrupt pin, étant configurée en entrée, le niveau qui est appliqué est modifié.
39
- PORTB : De la même manière, une interruption peut être générée lors du changement
d’un niveau sur une des pins RB4 à RB7. Il n’est pas possible de limiter l’interruption
à une seule de ces pins. L’interruption sera effective pour les 4 pins ou pour aucune.
Ce registre se situe à l’adresse 0x0B, dans les 2 banques. Il est donc toujours accessible. Il est
détaillé figure 4-5 page 16. C’est un registre de bits, donc, chaque bit a une fonction
particulière. Voici le détail de ces bits :
b7 : GIE
Global Interrupt Enable bit. Il permet de valider ou d’invalider toutes les interruptions
d’une seule fois. Ce bit correspond donc à notre interrupteur de validation générale.
b6 : EEIE
Eeprom write complete Interrupt Enable bit. Ce bit permet de valider l’interruption de fin
d’écriture en eeprom (nous étudierons plus tard le mécanisme d’écriture eeprom).
b5 : T0IE
Tmr0 Interrupt Enable bit : Valide l’interruption générée par le débordement du timer0.
b4 : INTE
INTerrupt pin Enable bit : Valide l’interruption dans le cas d’une modification de niveau
de la pin RB0.
ATTENTION : rappelez-vous le bit 6 du registre OPTION, qui détermine quel est le sens
de transition qui provoque l’interruption. On pourra donc choisir si c’est une transition 0->1
ou 1->0 qui provoque l’interruption, mais pas les deux ensemble.
b3 : RBIE
b2 : T0IF
Tmr0 Interrupt Flag bit. C’est un Flag, donc il signale. Ici c’est le débordement du timer0
b1 : INTF
INTerrupt pin Flag bit : signale une transition sur la pin RB0 dans le sens déterminé par
INTEDG du registre OPTION (b6)
b0 : RBIF
Port Interrupt Flag bit : signale qu’une des entrées RB4 à RB7 a été modifiée.
40
Remarque
Rappelez-vous que les flags ne se remettent pas à 0 tout seuls. C’est votre
programme qui doit s’en charger, sous peine de rester indéfiniment bloqué dans
une routine d’interruption. Nous dirons que ces flags sont des FLAGS REMANENTS
Pour qu’une nouvelle interruption puisse avoir lieu une fois celle en cours terminée, il
faut remettre GIE à 1. Ceci est exécuté automatiquement par RETFIE.
11-Timer0 :
Nous avons vu que le timer0 est en fait un compteur. Mais que compte-t-il ? Et bien, vous
avez deux possibilités.
- En premier lieu, vous pouvez compter les impulsions reçues sur le pin RA4/TOKI.
Nous dirons dans ce cas que nous sommes en mode compteur
- Vous pouvez aussi décider de compter les cycles d’horloge du PIC lui-même. Dans ce
cas, comme l’horloge est fixe, nous compterons donc en réalité du temps. Donc, nous
serons en mode « timer ».
La sélection d’un ou l’autre de ces deux modes de fonctionnement s’effectue par le bit
5 du registre OPTION : T0CS pour Tmr0 Clock Source select bit.
Dans le cas où vous décidez de travailler en mode compteur, vous devez aussi préciser
lors de quelle transisition de niveau le comptage est effectué. Ceci est précisé grâce au bit 4
du registre OPTION : T0SE pour Timer0 Source Edge select bit.
41
11.2 Le registre tmr0
Ce registre, qui se localise à l’adresse 0x01 en banque0, contient tout simplement la valeur
actuelle du timer0. Vous pouvez écrire ou lire tmr0. Si par exemple vous avez configuré tmr0
en compteur, la lecture du registre tmr0 vous donnera le nombre d’événements survenus sur le
pin RA4/TOKI.
Comment utiliser le timer0, et quelles sont les possibilités offertes à ce niveau, voilà de
quoi nous allons parler ici.
La première méthode qui vient à l’esprit est la suivante : Nous lisons le registre tmr0
pour voir ce qu’il contient. La valeur lue est le reflet du nombre d’événements survenus, en
prenant garde au fait que le tmr0 ne peut compter que jusque 255. En cas de dépassement, le
tmr0 recommence à 0. C’est donc à vous de gérer cette possibilité.
Petit exemple :
Nous devons savoir à ce niveau, que tout débordement du timer0 (passage de 0xFF à
0x00) entraîne le positionnement du flag T0IF du registre INTCON. Vous pouvez donc
utiliser ce flag pour déterminer si vous avez eu débordement du timer0, ou, en d’autres
termes, si le temps programmé est écoulé. Cette méthode à l’inconvénient de vous faire perdre
du temps inutilement
Petit exemple :
Mais vous pourriez vous dire que vous ne désirez pas forcément attendre 256
incrémentations de tmr0. Supposons que vous désiriez attendre 100 incrémentations. Il suffit
dans ce cas de placer dans tmr0 une valeur telle que 100 incrémentations plus tard, tmr0
déborde.
exemple
42
movlw 256-100 ; charger 256 – 100
movwf tmr0 ; initialiser tmr0
bcf INTCON , T0IF ; effacement du flag
loop
btfss INTCON , T0IF ; tester si compteur a débordé
goto loop ; non, attendre débordement
; suite du programme ; oui, poursuivre : 100 événements écoulés
C’est évidemment le mode principal d’utilisation du timer0. En effet, lorsque T0IE est
positionné dans le registre INTCON, chaque fois que le flag T0IF passe à 1, une interruption
est généree.
Supposons que vous vouliez, par exemple, mesurer un temps entre 2 impulsions sur le
broche RB0. Supposons également que ce temps soit tel que plusieurs débordements du tmr0
puissent avoir lieu. Une méthode simple de mesure du temps serait la suivante :
On a donc utilisé les interruptions pour les multiples de 256, et la lecture directe de tmr0 pour
les « unités ».
11.4 Le prédiviseur
Supposons que nous travaillons avec un quartz de 4MHz. Nous avons donc dans ce cas
(4000000/4) = 1.000.000 de cycles par seconde. Chaque cycle d’horloge dure donc
1/1000000ème de seconde, soit 1µs.
Si nous désirons réaliser une LED clignotante à une fréquence de +- 1Hz, nous aurons
besoin d’une temporisation de 500ms, soit 2000 fois plus. Ce n’est donc pas pratique.
Nous disposons pour améliorer ceci d’un PREDIVISEUR .
Qu’est-ce donc ? Et bien, tout simplement un diviseur d’événements situé AVANT
l’entrée de comptage du timer0. Nous pourrons donc décider d’avoir incrémentation de tmr0
tous les 2 événements par exemple, ou encore tous les 64 événements.
Regardez tableau de la page 16. Vous voyez en bas le tableau des bits PS0 à PS2 du
registre OPTION qui déterminent la valeur du prédiviseur.
43
Ces valeurs varient, pour le timer0, entre 2 et 256. Le bit PSA, quand à lui, détermine
si le prédiviseur est affecté au timer0 ou au watchdog. Voici un tableau exprimant toutes les
possibilités de ces bits :
PSA PS2 PS1 PS0 /tmr0 /WD Temps tmr0 Temps typique Watchdog
(minimal)
0 0 0 0 2 1 512 µs 18 ms (7ms)
0 0 0 1 4 1 1024 µs 18 ms (7ms)
0 0 1 0 8 1 2048 µs 18 ms (7ms)
0 0 1 1 16 1 4096 µs 18 ms (7ms)
0 1 0 0 32 1 8192 µs 18 ms (7ms)
0 1 0 1 64 1 16384 µs 18 ms (7ms)
0 1 1 0 128 1 32768 µs 18 ms (7ms)
0 1 1 1 256 1 65536 µs 18 ms (7ms)
1 0 0 0 1 1 256 µs 18 ms (7ms)
1 0 0 1 1 2 256 µs 36 ms (14 ms)
1 0 1 0 1 4 256 µs 72 ms (28 ms)
1 0 1 1 1 8 256 µs 144 ms (56 ms)
1 1 0 0 1 16 256 µs 288 ms (112 ms)
1 1 0 1 1 32 256 µs 576 ms (224 ms)
1 1 1 0 1 64 256 µs 1,152 Sec (448 ms)
1 1 1 1 1 128 256 µs 2,304 Sec (996 ms)
Remarques importantes :
- Il n’y a qu’un prédiviseur, qui peut être affecté au choix au timer du watchdog (que nous
verrons plus tard) ou au timer0. Il ne peut être affecté aux deux en même temps.
- La valeur contenue dans le prédiviseur n’est pas accessible. Par exemple, si vous décidez
d’utiliser un prédiviseur de 64, et qu’il y a un moment donné 30 événements déjà
survenus, vous n’avez aucun moyen de le savoir. Le prédiviseur limite donc la précision
en cas de lecture directe.
- L’écriture dans le registre tmr0 efface le contenu du prédiviseur. Les événements survenus
au niveau du prédiviseur sont donc perdus.
44
12- Les accès en mémoire « eeprom »
Ce paragraphe décrit l’accès dans l’eeprom interne du PIC. Il ne faut pas confondre
ceci avec l’écriture dans une eeprom externe type 2416. Pour ce type d’eeprom, il suffit de
suivre les directives du datasheet du composant concerné.
L’adresse physique de la zone eeprom commence, pour les PICs mid-range, à l’adresse
0x2100. Cette adresse se situe hors de l’espace d’adressage normal des PICs, donc nous
pouvons déjà en déduire qu’il nous faudra utiliser une procédure spéciale pour y accéder.
Notez déjà que si ces emplacements ne sont pas accessibles directement par le
programme, par contre ils le sont au moment de la programmation. Vous pourrez donc
initialiser votre zone eeprom au moment de programmer votre composant.
Ceci est également vrai pour des registres spéciaux des PICs. Par exemple, l’adresse
0x2007 contient les paramètres que vous écrivez dans _CONFIG. Vous pourriez donc
remplacer cette directive par une initialisation directe à l’adresse 0x2007. Nous vous le
déconseillons cependant pour des raisons de portabilité et d’évolution rapide vers une autre
famille de PICs.
La 16F84 dispose de 64 emplacements eeprom disponibles pour votre libre usage. Nous
allons voir comment les utiliser.
C’est dans ce registre que va transiter la donnée à écrire vers ou la donnée lue en
provenance de l’eeprom. Ce registre est situé à l’adresse 0x08 banque0.
Dans ce registre, situé à l’adresse 0x09 banque0, nous allons préciser sur 8 bits l’adresse
concernée par l’opération de lecture ou d’écriture en eeprom. Nous voyons déjà que pour cette
famille de PICs, nous ne pourrons pas dépasser 256 emplacements d’eeprom. Pour la 16F84,
la zone admissible va de 0x00 à 0x3F, soit 64 emplacements.
Ce registre, situé à l’adresse 0x88 en banque1, contient 5 bits qui définissent ou indiquent
le fonctionnement des cycles de lecture/écriture en eeprom. Voici son contenu :
bits 7/6/5
non utilisés
45
bit 4 : EEIF
Eeprom write operation Interrupt Flag bit. C’est le flag qui est en liaison avec
l’interruption EEPROM. Il passe à 1 une fois l’écriture en eeprom terminée. Si le bit EEIE du
registre INTCON est à 1, une interruption sera générée
bit 3 : WRERR
WRite Error. C’est un bit d’erreur. Il passe à 1 si une opération d’écriture en eeprom a été
interrompue, par exemple par un reset.
bit 2 : WREN
bit 1 : WR
WRite. Démarrage du cycle d’écriture. Est remis à 0 automatiquement une fois l’écriture
terminée.
bit 0 : RD
ReaD. Démarrage d’un cycle de lecture. Reste à 1 durant un cycle, puis est remis à 0
automatiquement
Nous revoici en présence d’un registre « fantôme », puisque ce registre n’existe pas. Il
s’agit tout simplement d’une adresse 0x89 banque1, qui sert à envoyer des commandes au PIC
concernant les procédures eeprom. Vous ne pouvez l’utiliser qu’en vous servant des
instructions expliquées plus bas.
Pour lire une donnée en eeprom, il suffit de placer l’adresse concernée dans le registre
EEADR. Ensuite, vous positionnez le bit RD à 1. Vous pouvez ensuite récupérer la donnée lue
dans le registre EEDATA. Il ne faut pas bien sûr oublier les différents changements de
banques.
Comme cette procédure est courte et toujours la même, nous allons créer une macro à
cette intention. Comme la macro doit contenir l’adresse de lecture, nous réaliserons une macro
avec passage de paramètre. Voici la macro à ajouter. Vous devrez être dans la banque0 pour
appeler cette macro, et elle vous retourne la valeur lue dans le registre W.
46
movf EEDATA , w ; charger valeur lue dans W
endm ; fin de la macro
Vous remarquez que vous passez un argument à la macro. Vous désignez cet ou ces
arguments après la directive macro. Nous avons utilisé ici l’argument adeeprom pour indiquez
l’adresse eeprom. Chaque utilisation de adeeprom dans notre macro sera remplacée par
l’argument reçu au moment de l’appel de la macro.
Pour utiliser cette macro, nous devons donc lui passer un argument. Par exemple :
Maintenant vous allez nous dire avec raison que cela ne sert à rien de lire en eeprom si
on n’arrive pas à y écrire. Notre programme ne présente donc rien de plus que ce que nous
avions auparavant. C’est tout à fait justifié. Aussi allons-nous étudier la méthode d’écriture.
Comme vous vous en doutez, cette méthode utilise les mêmes registres.
13- Le watchdog
Le fonctionnement du watchdog est lié à un timer interne spécifique, qui n’est pas
synchronisé au programme, ni à un événement extérieur. La durée spécifique de débordement
de ce timer est approximativement de 18ms. Cette valeur est à prendre avec précaution, car
elle varie en fonction de différents paramètres comme la tension d’alimentation ou la
température. La valeur minimale de 7ms est celle que vous devrez utiliser dans la pratique.
Chaque fois que l’instruction clrwdt est envoyée au PIC, le timer du watchdog est
remis à 0, ainsi que la valeur contenue dans son prédiviseur. Si par accident cette instruction
n’est pas reçue dans le délai prévu, le PIC est redémarrée à l’adresse 0x00 et le bit TO du
47
registre STATUS est mis à 0. En lisant ce bit au démarrage, vous avez donc la possibilité de
détecter si le PIC vient d’être mise sous tension, ou si ce démarrage est du à un « plantage »
de votre programme.
Nous avons vu dans les leçons précédentes que le prédiviseur pouvait être affecté au tmr0
ou au watchdog, via le bit PSA du registre OPTION. Si nous décidons de mettre le prédiviseur
sur le watchdog (PSA = 1), le tableau de la page 16 nous donnera les valeurs du prédiviseur
obtenues suivant les bits PS0/PS2. En réalité, pour le watchdog, il s’agit d’un postdiviseur,
mais cela ne concerne que l’électronique interne du PIC. Vous n’avez pas à vous tracasser
pour cette différence.
Le watchdog est destiné à vérifier que votre programme ne s’est pas « égaré » dans une
zone non valide de votre programme (parasite sur l’alimentation par exemple), ou s’il n’est
pas bloqué dans une boucle sans fin (bug du programme). Il sert également à réveiller un PIC
placée en mode « sleep », ce que nous verrons plus tard.
La première chose à faire, si vous désirez profiter de cette protection intégrée, est de
paramètrer la _CONFIG pour la mise en service du watchdog. La première chose à constater,
c’est que si vous indiquez « _WDT_ON » pour un programme qui ne gère pas le watchdog,
celui-ci redémarrera sans arrêt, et donc ne fonctionnera pas, car il ne contiendra aucune
instruction « clrwdt ».
C’est une erreur fréquente pour ceux qui ne maîtrisent pas les bits de configuration de
leur programmateur. Les bits de configurations indiqués dans le fichier sont en effet
modifiables sur la plupart des softs de programmation, qui sont capables de forcer une valeur
de _CONFIG différente de celle prévue par le concepteur du logiciel.
Ensuite, vous devez placer une ou plusieurs instructions « clrwdt » dans votre
programme en vous arrangeant pour qu’une instruction « clrwdt » soit reçue dans les délais
requis par votre PIC. A ce propos, ne tenez pas compte du temps nominal de 18ms, mais
plutôt du temps de la situation la plus défavorable. Ce temps est de minimum 7ms. En prenant
ce temps comme temps maximum autorisé, vous êtes certain que votre programme
fonctionnera dans toutes les conditions.
Nous allons étudier dans ce paragraphe un mode très particulier des PICs, qui leur
permet de se mettre en sommeil afin de limiter leur consommation.
48
14.1 Principe de fonctionnement
Le mode « sleep » ou « power down » est un mode particulier dans lequel vous pouvez
placer votre PIC grâce à l’instruction « sleep ». Une fois dans ce mode, le PIC est placée en
sommeil et cesse d’exécuter son programme. Dès réception de cette instruction, la séquence
suivante est exécutée :
Une fois dans cet état, le PIC est à l’arrêt. La consommation du circuit est réduite au
minimum. Si le tmr0 est synchronisé à l’horloge interne, il est également mis dans
l’incapacité de compter.
Par contre, il est très important de se rappeler que le timer du watchdog possède son
propre circuit d’horloge. Ce dernier continu à compter comme si de rien n’était.
Le passage en mode « sleep » n’a réellement d’intérêt que s’il est possible d’en sortir. La
16F84 ne réagit dans ce mode qu’aux événements suivants, qui sont seuls susceptibles de
replacer la 16F84 en mode de fonctionnement normal. Ces événements sont les suivants :
- Application d’un niveau 0 sur le pin MCLR. Ceci provoquera un reset de la 16F84. La
PIC effectuera un reset classique à l’adresse 0x00. L’utilisateur pourra tester les bits
TO et PD lors du démarrage pour vérifier l’événement concerné (reset, watch-dog, ou
mise sous tension).
- Ecoulement du temps du timer du watchdog. Notez que pour que cet événement
réveille le PIC, il faut que le watchdog ait été mis en service dans les bits de
configuration.
Dans ce cas particulier, le débordement du watchdog ne provoque pas un reset du PIC,
il se contente de la réveiller. L’instruction qui suit est alors exécutée au réveil.
- Une interruption RB0/INT, RB, ou EEPROM est survenue. Notez que pour qu’une
telle interruption puisse réveiller le processeur, il faut que les bits de mise en service
de l’interruption aient été positionnés. Par contre le bit GIE n’a pas besoin d’être mis
en service pour générer le réveil du PIC. Vous pouvez donc décider par exemple de
réveiller le PIC à la fin du cycle d’écriture EEPROM. Pour ce faire, vous devez mettre
49
le bit EEIE de INTCON à 1 et lancer le cycle d’écriture, suivi par l’instruction
« sleep » Une fois l’écriture terminée, le PIC est réveillée et poursuit son programme.
Si votre PIC est réveillée par une interruption alors que le BIT GIE de INTCON est mis à
0, le programme se poursuivra tout simplement à l’instruction qui suit l’instruction « sleep ».
Dans le cas où votre bit GIE est positionné, un réveil suite à une interruption entraînera la
séquence suivante.
Une autre application typique est un programme dans lequel le PIC n’a rien à faire dans
l’attente d’un événement extérieur particulier. Dans ce cas, plutôt que d’utiliser des boucles
sans fin, une instruction « sleep » pourra faire efficacement l’affaire.
D- LE PIC16F877:
1-Introduction
50
Fig9.Brochage du 16f877
1.1 Les broches d’alimentation :
Comme pour tout micrcontrôleur, le 16F877 a besoin d’une horloge pour fixer la
vitesse d’exécution des instructions. On utilise pour ce faire un quartz dont le rôle est de créer
une impulsion de fréquence élevée.
Le micrcontrôleur se base sur cette fréquence pour son fonctionnement interne. Notons
que le 16F877 exécute une instruction élémentaire du langage assembleur en 4 cycles
d’horloge. Il va donc pouvoir exécuter 1millions d’opérations élémentaires par seconde.Le
quartz est connecté sur les 2 broches OSC1 et OSC2.
51
Fig10.Architecture interne du 16f877
2- Les Ports du 16F877 :
- le port A (6 broches)
- le port B (8 broches)
- le port C (8 broches)
52
- le port D (8 broches)
- le port E (3 broches)
2.1 Le PORTA :
Le port A contient 6 pins I/O (input/output) numérotées de RA0 à RA5. On a donc 6 bits
dans le registre PORTA et 6 bits dans le registre TRISA. Les bits 6 et 7 de ces registres ne
sont pas implémentés. Le port A est configuré comme un ensemble d’entrées analogiques.
Donc, il faut forcer une valeur dans le registre ADCON1 dans la routine d’initialisation pour
pouvoir utiliser ce port comme port d’entrée/sortie de type général.
2.2 Le PORTB :
2.3 Le PORTC :
Ce port utilise un registre TRISC localisé dans la banque 1, qui permet de décider
quelles sont les entrées et quelles sont les sorties. le positionnement d’un bit à « 1 » place la
pin en entrée, le positionnement de ce bit à « 0 » place pin en sortie.
Nous trouvons également le registre PORTC, qui se trouve dans la banque 0.
2.4 Le PORTD :
2.5 Le PORT E :
Il ne comporte que 3 pins, RE0 à RE2 ces pins peuvent également être utilisées comme
pins d’entrées analogiques. Le registre ADCON1 détermine si ce port sera utilisé comme port
I/O ou comme port analogique.
53
b7 : IBF : Input Buffer Full status bit
b6 : OBF : Output Buffer Full status bit
b5 : IBOV : Input Buffer OVerflow detect bit
b4 : PSPMODE : Parallel Slave Port MODE select bit
b3 : 0 : Non implémenté (lu comme « 0 »)
b2 : Direction I/O pour RE2 (1 = entrée)
b1 : Direction I/O pour RE1
b0 : Direction I/O pour RE0
C’est la mémoire programme proprement dite. Chaque « case » mémoire unitaire fait 13
bits. La mémoire FLASH est un type de mémoire stable, qu’on peut reécrire à volonté cette
mémoire flash fait 8Koctet.
54
Fig11.La RAM du 16F877
Le 16F877 comprend :
55
2. Un convertisseur analogique-numérique (CAN) 10 bits 8 canaux
5. Une USART
7. Un « chien de garde »
L’intérêt des modules de comptage, c’est qu’ils permettent de tenir compte d’évènements
qui surviennent de façon répétée sans que le microprocesseur soit monopolisé par cette tâche.
Dans la plupart des cas, une interruption a lieu que lorsque le compteur déborde (overflow).
4.1.1. Le timer 0 : fonctionne exactement comme décrit dans la partie du microcontroleur
16F84.
4.1.2. Le timer 1 :c’est un compteur 16 bits qui peut compter (de 0 à 65535) :
- soit les impulsions de l’horloge
- soit les impulsions externes, et en particulier les impulsions d’un quartz externe.
Le débordement provoque aussi une interruption.
56
- Il faut que le bit d’autorisation générale des interruptions (GIE) soit positionné dans le
registre INTCON.
Une interruption sera alors générée à chaque débordement du timer1. Cet événement sera
indiqué par le positionnement du flag TMR1IF dans le registre PIR1.
b7 : Inutilisé
b6 : Inutilisé
b5 : T1CKPS1 : Timer 1 oscillator ClocK Prescale Select bit 1
b4 : T1CKPS0 : Timer 1 oscillator ClocK Prescale Select bit 0
b3 : T1OSCEN : Timer 1 OSCillator ENable control bit
b2 : T1SYNC : Timer 1 external clock input SYNChronisation control bit
b1 : TMR1CS : TiMeR 1 Clock Source select bit
b0 : TMR1ON : TiMeR 1 ON bit
Les bits T1CKPS1 et T1CKPS0 déterminent à eux deux la valeur du prédiviseur. Le
prédiviseur, permet, de ne pas compter chaque événement reçu, mais seulement chaque
multiple de cet événement. La valeur de ce multiple est la valeur du prédiviseur.
0 0 1
0 1 2
1 0 4
1 1 8
T1OSCEN : ce bit permet de mettre en service l’oscillateur interne, et donc permet d’utiliser
un second quartz pour disposer d’un timer précis travaillant à une fréquence distincte de la
fréquence de fonctionnement du PIC.
Si on place ce bit à « 1 », on met en service l’oscillateur interne.
T1SYNC : permet de choisir si le comptage des événements sera effectué de façon synchrone
ou asynchrone avec l’horloge principale du PIC. Lorsque TMR1 est utilisé en mode « timer »,
57
T1SYNC n’a aucune influance. Le comptage étant déterminé par l’oscillateur principal du
PIC, il est automatiquement synchrone.
TMR1CS : permet de définir quel est le mode de comptage du timer 1 :
- Soit on place TMR1CS à « 0 », dans ce cas, le timer compte les cycles d’instructions du
PIC.
- Soit, on place ce bit à « 1 », et TMR1 compte alors les flancs montants du signal appliqué
sur le pin RC0/T1OSO/T1CKI .
TMR1ON, si ce bit est mis à « 1 », autorise le timer 1 à fonctionner, alors que s’il est mis à «
0 », fige le contenu du timer 1, et donc, le met à l’arrêt.
- lors d’une écriture dans TMR1L ou TMR1H.
4.1.3. Le timer 2 :
Le timer 2 est un timer couplé au module CCP. Il est utilisé essentiellement pour la
génération d’impulsions à période ajustable (PWM).
Le timer2 dispose de certaines caractéristiques différentes des deux autres timers.
Le timer 2 est un compteur sur 8 bits. Il possède lui aussi un prédiviseur. Celui-ci peut
être paramètré avec une des 3 valeurs suivantes : 1,4, ou 16.
Cependant, le timer 2 dispose également d’un postdiviseur, qui effectue une seconde
division après l’unité de comparaison. Ce postdiviseur peut prendre n’importe quelle valeur
comprise entre 1 et 16.
La valeur du diviseur total est obtenue en multipliant la valeur du prédiviseur par celle du
postdiviseur.On obtient alors les valeurs suivante du diviseur :
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20,24,28,32,36,40,44,48,52,56,60,64,80,96,112,128,14
4,160,176,192,208,224,240,256
Le timer 2 incrémente le registre TMR2, (un seul registre comptage sur 8 bits).
Les valeurs de division minimale et maximale sont identiques à celles du timer 0.
Le registre TMR2 est remis automatiquement à 0 lors d’un reset, contrairement au timer 1.
Ce timer ne dispose d’aucune entrée extérieure via un pin du PIC. Il ne peut donc fonctionner
qu’en mode « timer » avec l’horloge interne.
58
avoir, par exemple, un débordement de 0x56 à 0x00, en plaçant la valeur 0x56 comme valeur
maximale dans le registre PR2.
On peut donc dire que le fonctionnement du timer est le suivant :
- On incrémente le contenu du prédiviseur à chaque cycle d’instruction
- Chaque fois que ce contenu correspond à un multiple de la valeur du prédiviseur, on
incrémente TMR2.
- Chaque fois que le contenu de TMR2 dépasse le contenu de PR2, on remet TMR2 à 0, et on
incrémente le contenu du postdiviseur.
- Chaque fois que le contenu du postdiviseur correspond à un multiple de la valeur du
postdiviseur, on positionne le flag TMR2IF.
- Chaque fois que le contenu du postdiviseur est égal à un multiple de la valeur de ce celui-ci,
paramètrée par TOUTPS0 à TOUTPS3, le flag TMR2IF est forcé à 1, et une interruption est
éventuellement générée.
Une écriture dans le registre TMR2 efface le contenu du prédiviseur et du postdiviseur.
b6 b5 b4 b3 Postdiviseur
0 0 0 0 1
0 0 0 1 2
0 0 1 0 3
0 0 1 1 4
0 1 0 0 5
0 1 0 1 6
0 1 1 0 7
0 1 1 1 8
1 0 0 0 9
59
1 0 0 1 10
1 0 1 0 11
1 0 1 1 12
1 1 0 0 13
1 1 0 1 14
1 1 1 0 15
1 1 1 1 16
b1 b0 Prédiviseur
0 0 1
0 1 4
1 0 16
1 1 16
Le CAN présent dans le 16F877 est un CAN 10bits. Un CAN convertit une tension
analogique en une valeur numérique binaire, qui pourra ensuite être utilisée pour des calculs
ou autres. Sa résolution de 10bits donne une précision en 5V de 5mv environ.
60
C’est le temps qu’il faut pour que le condensateur interne atteigne une tension proche de
la tension à convertir. Cette charge s’effectue à travers une résistance interne et la résistance
de la source connectée au pin.
Ce temps est incrémenté du temps de réaction des circuits internes, et d’un temps qui dépend
de la température (coefficient de température). En effet que les résistances augmentent avec la
température, donc les temps de réaction des circuits également.
Donc, si on pose :
La valeur 2047 vient du fait que pour numériser avec une précision de ½ bit, la
numérisation utilisant une valeur maximale de 1023, la charge du condensateur doit être au
minimum de 2046/2047 ème de la tension à mesurer.
Comme « C » et « ln(1/2047) » sont des valeurs fixes la constante sera :
-C * ln(1/2047) = 0,914895 * 10 -9
61
Tc maximal = 20,12 µs
Dans le cas typique, à savoir une tension d’alimentation de 5V et une résistance de source de
10 Kohms, nous aurons :
4.2.3. La conversion :
- Tad : avant le début de conversion (le temps de connexion du condensateur est inclus)
- 10 * Tad pour la conversion des 10 bits du résultat
- Tad supplémentaire pour la fin de la conversion de b0
62
Un temps équivalent à 2 * Tad est nécessaire avant de pouvoir effectuer une nouvelle
conversion. Donc le temps nécessaire pour effectuer l’ensemble des opérations :
F = 1/T =23747 Hz
Cette fréquence permet d’échantillonner des signaux sinusoïdaux d’une fréquence maximale
de 11874 Hz (la moitié de la fréquence d’échantillonnage).
Le PIC ne contient qu’un seul convertisseur, mais il dispose de plusieurs pins sur
lesquelles connecter les signaux analogiques. Un circuit de commutation sélectionne laquelle
des pins sera reliée au condensateur de maintien interne durant le temps Tacq. Les différentes
entrées sont des canaux différents d’un seul convertisseur.
Le 16F877 dispose de 8 canaux d’entrée analogique. On peut échantillonner jusque 8 signaux
différents sur les pins AN0 à AN7. Les pins AN0 à AN4 sont les dénominations analogiques
des pins RA0 à RA5 (RA4 non inclus), tandis que les pins AN5 à AN7 sont les dénominations
analogiques des pins RE0 à RE2.
63
RA5/AN4
- Utilisation de Vss (masse du PIC) comme tension Vref- et de Vdd (alimentation positive
du PIC) comme tension Vref+. Dans ce mode, les tensions de références sont éxtraites en
interne de la tension d’alimentation.
64
La broche Vref+ est une dénomination alternative de la broche RA3/AN3, tandis que la
broche Vref- est une dénomination alternative de la broche RA2/AN2.
La procédure de numérisation, pour le cas où plusieurs canaux sont utilisés, est la suivante :
Le résultat de la conversion est sauvegardé dans 2 registres. Ces registres sont ADRESL
et ADRESH.
Le résultat étant sur dix bits le résultat peut être soit justifié à gauche, soit à droite.
La justification à droite complète la partie gauche du résultat par des « 0 ». Le résultat obtenu
est de la forme :
ADCON1
- b7 : ADFM : A/D result ForMat select
- b6 : Inutilisé : lu comme « 0 »
- b5 : Inutilisé : lu comme « 0 »
- b4 : Inutilisé : lu comme « 0 »
- b3 : PCFG3 : Port ConFiGuration control bit 3
- b2 : PCFG2 : Port ConFiGuration control bit 2
- b1 : PCFG1 : Port ConFiGuration control bit 1
- b0 : PCFG0 : Port ConFiGuration control bit 0
Le bit ADFM permet de déterminer si le résultat de la conversion sera justifié à droite (1)
ou à gauche (0).
65
On trouve dans ce registre 4 bits de configuration des pins liés au convertisseur
analogique/numérique. Ces bits permettent la détermination du rôle de chaque pin.
Il y a 16 combinaisons possibles, et autant de possibilités de configuration.
66
CHS2 CHS1 CHS0 Canal Pin
0 0 0 0 AN0/RA0
0 0 1 1 AN1/RA1
0 1 0 2 AN2/RA2
0 1 1 3 AN3/RA3
1 0 0 4 AN4/RA5
1 0 1 5 AN5/RE0
1 1 0 6 AN6/RE1
1 1 1 7 AN5/RE2
Cette partie ne comporte aucune difficulté particulière. En effet, l’interruption générée par
le convertisseur est une interruption périphérique, et doit donc être traitée comme telle. Les
différentes étapes de sa mise en service sont donc :
- Positionnement du bit ADIE du registre PIE1
- Positionnement du bit PEIE du registre INTCON
- Positionnement du bit GIE du registre INTCON
Moyennant quoi, toute fin de conversion analogique entraînera une interruption. Il vous
suffira de remettre à « 0 » le flag ADIF après traitement de cette interruption.
67
temporisation. En effet, 2 temporisations qui se suivent peuvent être remplacées par une
temporisation unique de temps cumulé.
Si donc, nous prenons un PIC cadencée à 20Mhz, sous une tension d’alimentation de
5V, nous aurons :
1) Configurez ADCON1 en fonction des pins utilisées en mode analogique, ainsi que les
registres TRISA et TRISE si nécessaire.
2) Validez, si souhaité, l’interruption du convertisseur (PEIE, ADIE, GIE)
3) Paramètrez le diviseur 32 sur ADCON0 (B’10000000’)
4) Choisissez le canal en cours de digitalisation sur ADCON0 et lancez le convertisseur
(B’10xxx001’)
5) Attendez le temps (Tacq+2Tad), soit 19,7µs + 3,2µs = 22,9µs
6) Démarrez la conversion en positionnant le bit GO du registre ADCON0
7) Attendez la fin de la conversion
8) Lisez les registres ADRESH et si nécessaire ADRESL
9) Recommencez au point 4
Notez que vous pouvez, comme montré, réaliser plusieurs opérations en même temps.
Cependant, fort logiquement, vous ne pouvez pas positionner les bits ADON et
GO/DONE en même temps, puisque le temps Tacq doit impérativement les séparer.
Remarque :
Lorsque vous disposez de beaucoup de temps entre 2 lectures de la valeur analogique, nous
vous conseillons d’effectuer plusieurs mesures intermédiaires, et d’effectuer la moyenne de
ces mesures. Ainsi, un parasite éventuel, ou une légère fluctuation de votre tension sera
fortement atténuée.
Il est pratique dans ce cas d’effectuer un nombre de mesures qui est une puissance de 2 (2
mesures, 4,8,16…). Ainsi, pour effectuer votre moyenne, il suffira d’effectuer la somme de
toutes les valeurs, la division s’effectuant par simple décalage.
Ce module est très puissant pour créer des impulsions à des fréquences élevées. On pourra
définir la période, la durée du niveau haut pendant cette période.
Le 16F877 dispose de deux modules CCP. CCP signifie Capture, Compare, and PWM.
Ces modules CCP sont fortement liés et dépendant des timers 1 et 2. Ils sont également liés au
convertisseur A/D.
Les 2 modules CCP1 et CCP2 sont strictement identiques, excepté la possibilité, pour le
module CCP2, de démarrer automatiquement la conversion A/D.
Ces registres ont la même fonction, CCP1CON concerne le module CCP1, tandis que
CCP2CON concerne le module CCP2.
Le registre CCPxCON permet de déterminer le mode de fonctionnement du module.
CCPxCON
b7 : Inutilisé : Lu comme « 0 »
b6 : Inutilisé : Lu comme « 0 »
b5 : CCPxX : module Capture Compare and Pwm x bit X
b4 : CCPxY : module Capture Compare and Pwm x bit Y
b3 : CCPxM3 : module Capture Compare and Pwm x Mode select bit 3
b2 : CCPxM2 : module Capture Compare and Pwm x Mode select bit 2
68
b1 : CCPxM1 : module Capture Compare and Pwm x Mode select bit 1
b0 : CCPxM0 : module Capture Compare and Pwm x Mode select bit 0
Les bits CCPxX et CCPxY. Ces bits sont en fait les 2 bits de poids faible qui complètent le
nombre de 10 bits utilisé pour le mode de fonctionnement PWM. Dans les autres modes, ces
bits sont donc inutilisés.
Les bits CCPxM3 à CCPxM0 servent à déterminer quel sera le mode de fonctionnement
du module concerné. Les possibilités sont les suivantes :
CCPM Fonctionnement
Ce mode fait intervenir un pin comme événement déclencheur. Il s’agit donc d’une
entrée. Il est donc impératif de configurer le pin CCPx en entrée via le registre TRISC avant
de pouvoir utiliser le module CCPx en mode « capture ».
Principe de fonctionnement :
Le mode capture est en étroite liaison avec les pins RC1/CCP2 et RC2/CCP1 du PIC. le
principe est le suivant :
- Au moment de l’apparition de l’événement déclencheur sur le pin concernée, la valeur
(16bits) du timer 1 contenue dans les registres TMR1H et TMR1L est copiée dans les
registres CCPR1H et CCPR1L.
- Simultanément, le bit CCP1IF du registre PIR1 est validé, et une interruption intervient si
elle est configurée.
L’événement déclencheur est une variation du signal sur le pin CCP1/RC2 pour le module
CCP1, et sur le pin CCP2/RC1 pour le module CCP2. L’événement qui provoque la capture
dépend des bits CCPxM3 à CCPxM0.
69
PWM signifie « Pulse Width Modulation », ce qu’on pourrait traduire par modulation de
largeur d’impulsion. Il s’agit d’un signal binaire de fréquence fixe dont le rapport cyclique
peut être modulé par logiciel.
Le module « PWM » a besoin d’un pin du PIC configurée en sortie.
Le rapport cyclique d’un signal binaire à fréquence fixe peut être défini comme étant le
rapport entre le temps où il se trouve à l’état « 1 » par rapport au temps total d’un cycle. Un
cycle n’étant constitué, par définition, que d’un état « 1 » suivi d’un état « 0 », la somme des
temps des 2 états étant constante.
Il y a donc deux paramètres qui définissent un signal « PWM » :
- La durée d’un cycle complet
- Le rapport cyclique
Donc, si on pose :
Alors :
Un signal avec un rapport cyclique de 0% est un signal dont le temps à l’état haut occupe
0% du temps total. C’est donc un signal qui est constamment à l’état bas. De même, un signal
avec un rapport cyclique de 100% est un signal qui est constamment à l’état haut.
70
Le rapport cyclique d’un signal PWM doit donc être supérieur à 0% et inférieur à 100%.
Le PIC permet donc de créer un signal périodique dont il est possible de faire varier (moduler)
le rapport cyclique.
71
Fig14.Fonctionnement en mode PWM
4.3.4.3. Remarques :
La valeur de référence encodée dans CCPRxL ne peut être supérieure à la valeur contenue
dans PR incrémentée de 1. Dans le cas contraire, le signal ne pourrait jamais atteindre cette
consigne, et la pin CCPx resterait bloquée au niveau haut (rapport cyclique > 100%) Il est
possible d’utiliser le timer 2 à la fois comme timer classique et comme générateur pour le
module PWM.
Le registre CCPRxH n’est pas accessible en écriture lors du fonctionnement en mode
PWM.
La prise en compte du changement de valeur du rapport cyclique ne se fera qu’après la fin du
cycle en cours.
En fait, vous utiliserez ce module chaque fois que vous aurez besoin d’un signal de
fréquence fixe, mais de rapport cyclique variable. L’application typiquee est la variation de
vitesse d’un moteur à courant continu.
Le module MSSP, pour Master Synchronous Serial Port, permet l’échange de données du
PIC avec le mode extérieur, en utilisant des transmissions série synchrones.
Il n’est pas le seul à proposer des communications, nous avons déjà vu la liaison parallèle
dans le module PSP, et nous verrons d’autres communications série avec le module USART.
Vous verrez que l’ensemble de ces modules vous ouvre toutes les voies royales de la
communication, que ce soit avec votre PC, avec des afficheurs, des mémoires de type
72
eeprom, des détecteurs de température, des horloges en temps réel, etc…
Avec ces modules, nous n’avons plus à nous occuper de la transmission des bits en eux-
mêmes, mais nous les recevrons et émettrons directement sous forme d’octet, toute la
procédure de sérialisation étant alors automatique.
Outre le gain en taille programme qui en découlera, vous vous rendez compte que les
vitesses de transmission pourront être améliorées de façon notable. Au lieu que votre
programme ne soit interrompu lors de la réception ou de l’émission de chaque bit, il ne le sera
plus que pour chaque octet. Ceci multiplie allègrement la vitesse maximale possible dans un
facteur de plus de 10.
Il y a tellement de modes possibles de fonctionnement, qu’il ne nous sera pas possible de
donner systématiquement des exemples. Nous nous limiterons donc à des situations courantes.
Nous verrons que le module MSSP peut travailler selon 2 méthodes. Soit en mode SPI (Serial
Peripherial Interface), soit en mode I2C.
73
- Le premier bit émis
- Un bit de niveau toujours celui opposé au niveau de repos (donc 0 dans notre cas).
- Un bit d’une durée, par convention, la même que celle d’un bit de donnée.
4.5.1.2 Les bits de donnée :
Nous avons envoyé notre start-bit, il est temps de commencer à envoyer nos bits de
donnée. En général, les bits de donnée seront au nombre de 7 ou de 8 (cas les plus courants).
La durée d’un bit est directement liée au débit choisi pour la connexion. En effet, le débit
est exprimé en bauds, autrement dit en bits par seconde. Donc, pour connaître la durée d’un
bit, il suffit de prendre l’inverse du débit.
Tb = 1 / Débit.
Par exemple, si on a affaire à une connexion à 9600 bauds, nous aurons :
Tb = 1 / 9600 bauds = 104,16 µs.
4.5.1.3 La parité :
Directement après avoir envoyé nos 7 ou 8 bits de données, nous pouvons décider
d’envoyer ou non un bit de parité. Ce bit permet d’imposer le nombre de bits à « 1 » émis
(donnée + parité), soit comme étant pair (parité paire), soit comme étant impair (parité
impaire).
Le récepteur procède à la vérification de ce bit, et, si le nombre de bits à « 1 » ne
correspond pas à la parité choisie, celui-ci saura que la réception ne s’est pas effectuée
correctement. La parité permet de détecter les erreurs simples, elle ne permet ni de les réparer,
ni de détecter les erreurs doubles. Par contre, la détection fréquente d’erreurs de parité indique
un problème dans l’échange des données.
Voici par exemple, un cas de parité paire :
La donnée vaut : B’00110100’
Nous avons 3 bits à « 1 ». Donc, pour avoir notre parité paire, notre bit de parité devra donc
valoir « 1 », ce qui forcera le nombre total de bits « 1 » à valoir « 4 », ce qui est bien pair.
On enverra donc :
B’001101001’ : 4 bits à « 1 » = parité paire
4.5.1.4 Le stop-bit :
Nous avons maintenant émis tous nos bits de donnée et notre parité éventuelle. Reste à
permettre à notre ligne de transmission de revenir à l’état de repos. Ce passage est dénommé
« stop-bit ».
On peut dire à son sujet que :
- Il sera le dernier bit émis de l’octet
- Son niveau est toujours celui du niveau de repos (donc 1 dans notre cas).
- Sa durée sera par convention la même que celle d’un bit de donnée.
Le stop-bit n’est pas comptabilisé (pas plus que le start-bit) pour le calcul du bit de parité.
Notez qu’il est permis d’imposer l’utilisation de 2 « stop-bits ».
Une autre façon de voir les choses, est de dire qu’on ne pourra envoyer l’octet suivant
qu’après une durée Tb après l’émission du premier stop-bit. Où, dit encore autrement, on doit
ménager une pause d’une durée de 1 bit, entre l’émission de 2 octets consécutifs.
Un mot concernant la lecture d’un bit par le récepteur. Il est évident que la lecture de
chaque bit doit s’effectuer dans la zone présentant la plus grande probabilité de stabilité du
bit. En général, les électroniques sont construites pour mesurer le bit au milieu de sa durée.
Voici, pour illustrer tout ceci, l’émission d’une donnée codée sur 8 bits, avec parité paire
et un stop-bit. Les lectures sont indiquées par les flèches inférieures :
74
4.5.2 Mise en œuvre :
Le PIC est capable de travailler en mode full-duplex. Ceci vous laisse plusieurs
possibilités de configuration. Voyons tout d’abord comment interconnecter les circuits dans
ce mode :
Dans ce cas, notre PIC est privilégié, puisqu’il peut parler avec le circuit 2 et 3, alors que
ces circuits ne peuvent dialoguer entre eux.
Vous voyez donc que le mode full-duplex procure un inconvénient. Soit on se contente de
communiquer entre 2 composants, soit, si on a plus de 2 composants, un seul se trouve en
situation privilégiée.
Notez que nous représentons toujours des liaisons directes, connectées directement sur votre
PIC. On peut aussi, pour diverses raisons, dont la principale est d’allonger les distances de
communication, utiliser des pilotes de lignes spécifiques.
Prenons par exemple le cas de la communication half-duplex entre votre PIC et le port
série d’un PC :
Vous notez la présence d’un driver de ligne, de type MAX232 qui convertit les niveaux
75
0v/5V de votre PIC en niveaux +12V/-12V à destination de la RS232 de votre PC, et
réciproquement. La norme RS232 est également une norme qui précise un mode de
fonctionnement full-duplex.
76
CREN : lance la réception continue. Les octets seront reçus sans interruption jusqu’à ce que
ce bit soit effacé par vos soins. Dans le mode asynchrone, il n’existe que ce mode de
réception.
FERR : indique une erreur de trame. Ceci se produit lorsqu’au moment où devrait
apparaître le stop-bit en mode lecture, l’USART voit que la ligne de réception est à « 0 ». Ce
bit est chargé de la même façon que le bit RX9D. Autrement dit, ce n’est pas un flag à effacer,
c’est un bit qui est lié à la donnée qu’on vient de lire. On doit donc lire ce bit, tout comme
RX9D : avant de lire RCREG. Une fois ce dernier lu, le nouveau FERR écrase l’ancien. Si
vous suivez, on a donc un bit FERR vu comme s’il était le 10ème bit du mot reçu.
OERR : indique une erreur de type overflow. Il se retrouve positionné si vous n’avez pas lu
RCREG suffisamment vite. L’effacement de ce bit nécessite le reset de CREN. Le non
effacement de OERR positionné bloque toute nouvelle réception d’une donnée.
RX9D : contient le 9ème bit de votre donnée reçue, pour autant, bien entendu, que vous ayez
activé le bit RX9.
Pour l’émission :
- Vous validez la mise en service de l’émission en positionnant le bit TXEN.
77
- La validation de TXEN positionne le flag TXIF à « 1 » puisque le registre TXREG est
vide.
- Si vous utilisez le format de donnée sur 9 bits, vous devez commencer par placer la valeur
du 9ème bit dans le bit TX9D.
- Ensuite, vous placez la donnée à émettre dans le registre TXREG (TRANSMITT
REGister).
- Cette donnée est transférée dès le cycle d’instruction suivant, ainsi que le bit TX9D, dans
son registre TSR. TRMT passe à « 0 » (TSR plein).
- Vous recommencez éventuellement le chargement de TX9D + TXREG, le flag TXIF
passe à « 0 » (TXREG plein)
- Dès que le premier octet est émis, le second est transféré dans TSR, le registre TXREG est
vide, TXIF est positionné. Vous pouvez charger le troisième octet, et ainsi de suite.
- Quand le dernier octet est transmis, TRMT passe à 1 (TSR vide, émission terminée)
Avec toujours les mêmes remarques :
- Vous avez toujours un octet en cours de transmission dans TSR, le suivant étant déjà prêt
dans TXREG.
- Si on a besoin de 9 bits, on place toujours TX9D avant de charger TXREG.
- Chaque fois que TXIF passe à 1 (avec interruption éventuelle), vous pouvez recharger
TXREG avec l’octet suivant (tant que vous en avez).
- Quand la communication est terminée, TRMT passe à 1.
- TXIF passe à « 1 » dès que TXEN est positionné
Pour qu’il y ait une émission, il faut donc que, le module étant initialisé et lancé :
- TXEN soit positionné
- Le registre TXREG soit chargé
Pour la réception :
- Vous positionnez CREN, ce qui a pour effet que votre PIC est prêt à recevoir une donnée.
- L’octet reçu est transféré dans le registre RCREG (le 9ème bit éventuel est transféré vers
RX9D), et le flag RCIF est positionné
- Vous lisez alors le bit RX9D éventuel puis le registre RCREG, ce qui provoque
l’effacement du bit RCIF.
De nouveau, nous retrouvons notre file FIFO, qui nous permettra de recevoir 2 octets
(plus un en cours de réception), avant d’obtenir une erreur d’overflow. Pour rappel :
- Le premier octet est en cours de réception dans RSR, RCREG est vide, donc RCIF est à
«0»
- Le premier octet est reçu et transféré dans la file de RCREG, RCIF est positionné, le
second octet est en cours de réception dans RSR
- Si vous ne réagissez pas de suite, le second octet est reçu et transféré dans la file de
RCREG, RCIF reste positionné, le troisième octet est en cours de réception dans RSR.
Comme on n’a que 2 emplacements, vous devrez réagir avant la fin de la réception du
troisième octet, mais vous voyez que votre temps de réaction peut être allongé. Voici alors ce
que vous allez faire dans ce cas :
- Vous lisez, si vous travaillez sur 9 bits, le 9ème bit, RX9D, puis RCREG, donc le premier
octet reçu. Cette lecture ne provoque pas l’effacement de RCIF, puisque RCREG n’est pas
vide. Par contre, le 9ème bit du second octet est transféré à ce moment dans RX9D, ce qui
explique que vous deviez le lire en premier.
- Vous lisez alors, si vous travaillez sur 9 bits, le bit RX9D qui est le 9ème bit de votre
second octet, puis RCREG, donc le second octet reçu. Le flag RCIF est effacé, le registre
RCREG est vide.
L’erreur d’overflow sera générée si un troisième octet est reçu alors que vous n’avez pas
lu les deux qui se trouvent dans la file FIFO. Le positionnement empêche toute nouvelle
78
réception. L’effacement de ce bit nécessite d’effacer CREN.
Votre réaction devrait donc être du style :
- Tant que RCIF est positionné
- Je lis RX9D puis RCREG
- RCIF n’est plus positionné, OERR est positionné ?
- Non, pas de problème
- Oui, je sais que j’ai perdu un octet et je coupe CREN
Evidemment, vous avez beaucoup moins de chance d’avoir une erreur d’overflow si vous
travaillez via les interruptions, ce que nous vous conseillons fortement dans ce mode.
4.5.6 L’erreur de frame :
L’erreur de frame survient si, au moment où l’USART s’attend à recevoir un stop-bit, il
voit que le niveau de sa ligne de réception est positionnée à « 0 ». Dans ce cas, quelque chose
s’est mal déroulé.
Le bit FERR sera positionné, mais, attention, ce bit fait partie de la file FIFO, tout comme
le bit RX9D. Donc, vous lisez RX9D et FERR, et, ensuite, vous lisez RCREG. A ce
moment, le nouveau bit FERR concernant l’octet suivant écrasera le FERR actuel, exactement
comme le nouveau bit RX9D effacera l’actuel.Vous n’avez donc pas à effacer FERR, qui est,
du reste, en lecture seule.
79
n’est donc plus d’aucune utilité : inutile de le configurer.
Notez que dans la plupart des cas, vous utiliserez les interruptions pour gérez ce mode de
fonctionnement, vous devez donc mettre en service l’interruption PSP .
En effet, les échanges d’information se font sur décision du microprocesseur maître, et
donc de façon asynchrone avec le programme de votre PIC. Les interruptions restent la
meilleure façon de traiter rapidement les événements asynchrones.
4.7.1 Introduction
On passe directement à l’étude du module MSSP en mode I²C, et par conséquent on doit
commencer par expliquer en quoi consiste ce bus.
En effet, bien que le module prenne en charge tous les aspects de gestion de ce bus, il m’a
semblé important de comprendre ce qui se passait effectivement au niveau des lignes qui le
constituent.
De plus, l’absence de ces informations vous poserait problème au moment d’établir une
communication avec un composant I²C. En effet, lorsque vous rencontrerez un nouveau
composant, tout ce dont vous disposerez à son sujet sera, en général, son datasheet. Sans une
connaissance assez précise du bus I²C, vous vous retrouveriez confronté à quelque chose qui
risquerait bien de vous paraître incompréhensible.
Et puis, mieux vaut avoir une information qu’on n’utilisera éventuellement pas qu’une
absence d’information au moment où on en a besoin.
4.7.2 Caractéristiques fondamentales :
Le bus I²C permet d’établir une liaison série synchrone entre 2 ou plusieurs composants.
Il a été créé dans le but d’établir des échanges d’informations entre circuits intégrés se
trouvant sur une même carte. Son nom, d’ailleurs, traduit son origine : Inter Integrate Circuit,
ou I.I.C., ou plus communément I²C (I carré C). Ce bus est le descendant du CBUS, qui est de
moins en moins utilisé.
Son domaine d’application actuel est cependant bien plus vaste, il est même, par exemple,
utilisé en domotique.
Il comporte des tas de similitudes avec le SMBUS d’Intel (System Management BUS),
80
mais on ne parlera pas de ce bus ici. Si cela vous intéresse, vous trouverez des informations
sur Internet. Sachez simplement que nos 16F87x peuvent créer des signaux compatibles avec
cette norme.
L’ I²C permettait, à ses débuts, de travailler à des fréquences maximales de 100
Kbits/seconde, vitesses assez rapidement portées à 400 Kbits/seconde. Il existe maintenant
des familles de circuits pouvant atteindre des vitesses de 3.4 Mbits/seconde. Le
fonctionnement théorique reste identique.
Le bus I²C est constitué de 2 uniques lignes bidirectionnelles :
- La ligne SCL (Serial Clock Line), qui, comme son nom l’indique, véhicule l’horloge de
synchronisation
- La ligne SDA (Serial DAta line), qui véhicule les bits transmis.
Il est important de vous souvenir que :
- la ligne SCL est gérée par le maître (nous verrons que par moment, l’esclave peut prendre
provisoirement le contrôle de la ligne).
- la ligne SDA, à un moment donné, est pilotée par celui qui envoie une information (maître
ou esclave).
Tous les circuits sont connectés sur ces 2 lignes. Il existe 2 sortes de circuits pouvant être
connectés :
- Les circuits maîtres, qui dirigent le transfert et pilotent l’horloge SCL.
- Les circuits esclaves, qui subissent l’horloge et répondent aux ordres du maître.
Chacun de ces 2 types peut émettre et recevoir des informations.
Une particularité est qu’on peut placer plusieurs maîtres sur le même bus I²C. Ceci
implique que, sans précautions, si 2 maîtres désirent prendre le contrôle du bus en même
temps, on encourrait, sans précautions particulières, 2 risques :
- La destruction de l’électronique des circuits, pour le cas où l’un d’entre eux impose un
niveau haut et l’autre un niveau bas (court-circuit).
- La corruption des données destinées ou en provenance de l’esclave.
Fort heureusement, vous vous doutez bien que Philips (l’inventeur de l’I²C) a trouvé des
solutions pour parer à ces éventualités.On commence par les contraintes électroniques.
Pour la partie électronique, la parade est simple. On travaille avec des étages de sortie qui
ne peuvent imposer qu’un niveau 0, ou relâcher la ligne, qui remonte d’elle-même au niveau 1
via des résistances de rappel.
De cette façon, on n’aura jamais de court-circuit, puisque personne ne peut placer la
tension d’alimentation sur la ligne. Ces étages sont des montages que vous trouverez dans la
littérature sous la dénomination de « collecteur ouvert » ou de « drain ouvert » suivant la
technologie utilisée. Le pin RA4 de votre PIC utilise d’ailleurs la même technique.
Le schéma d’interconnexion des circuits sur un bus I²C est donc le suivant :
81
Fig15. Connexion de plusieurs composants sur le bus I2C
82
Vous constatez que le bit présent sur la ligne SDA doit être présent avant la montée du
signal SCL, et continuer un certain temps avant sa redescente.
4.7.3.2 Le start-condition :
Comme pour tout signal synchrone, le protocole ne définit pas de start et de stop-bit. Mais
il intègre toutefois les notions de « start-condition » et de « stop-condition ». Ces séquences
particulières, obtenues en modifiant la ligne SDA alors que la ligne SCL est positionnée à
l’état haut permettent de définir début et fin des messages. Nous verrons que ceci est
indispensable pour repérer le premier octet du message, qui a un rôle particulier.
Si on se souvient qu’au repos, SCL et SDA se trouvent relâchés, et donc à l’état haut, le
start-condition (symbole conventionnel : S) est réalisé simplement en forçant la ligne SDA à
0, tout en laissant la ligne SCL à 1.
Il existe un dérivé de cette condition, appelé « repeated start condition », qui est utilisé
lorsqu’un message en suit directement un autre. Il s’agit donc en fait d’un second
startcondition au sein d’un même message. Sachez à ce niveau qu’un « repeated start-
condition » peut être considéré comme identique à un « startcondition ».
4.7.3.3 Le stop-condition :
Nous venons d’y faire allusion. Cette condition indique la fin du message en cours. Elle
remet les lignes SDA et SCL au repos, mais en respectant la chronologie suivante :
La ligne SDA est ramenée à 1, alors que la ligne SCL se trouve déjà à 1. Voici ce que cela
donne :
4.7.3.4 L’acknowledge :
L’acknowledge (ACK) est en fait l’accusé de réception de l’octet envoyé. C’est donc le
récepteur qui émet ce bit pour signaler qu’il a bien lu l’octet envoyé par l’émetteur. Cet accusé
de réception est lu comme un bit classique. Il vaudra 0 si l’accusé de réception signifie « OK
», et 1 pour toute autre raison (récepteur dans l’impossibilité de répondre, par exemple).
Voici un ACK (OK) :
83
Et voici une absence d’accusé de réception, encore dénommée NOACK. En effet, un
NOACK équivaut à une absence de réaction, puisque seul le niveau bas est imposé, le niveau
haut étant lié à la libération de la ligne (ou à sa non appropriation).
Nous avons vu tous les signaux possibles, il nous faut maintenant étudier le protocole de
communication des intervenants en I²C.
Nous savons déjà que la transmission commence par un « start-condition » (S).
Vient ensuite l’adresse, codée sur 7 ou 10 bits, complétée par l’octet R/W qui précise si
les données qui vont suivre seront écrites ou lues par le maître.
Chaque octet envoyé est toujours accompagné d’un accusé de réception de la part de celui
qui reçoit. Un octet nécessite donc 8 + 1 = 9 impulsions d’horloge sur la pin SCL.
On distingue dès lors 2 cas : soit l’adresse est codée sur 7, soit sur 10 bits. Voici comment
se présente le début d’une trame (à partir ce cet instant, dans les chronogrammes concernant
l’I²C.
Notez donc, et c’est logique, que c’est toujours le maître qui envoie l’adresse, qu’il soit
récepteur ou émetteur pour le reste du message.
Pour coder une adresse sur 10 bits, on utilisera comme premier octet l’adresse réservée
84
B’11110xy0’ qui précise qu’un second octet est nécessaire. Voici ce que donne l’envoi d’une
adresse sur 10 bits :
85