Chapitre 2
Chapitre 2
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.
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.
Pensez que les pics peuvent monter à 20MHz. C’est donc une vitesse de traitement
plus qu’honorable.
2- Les différentes familles des PICs
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).
- 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.
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
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
La mémoire du 16F84 est divisée en 3 parties. Page 4 du datasheet, vous trouverez la
table 1-1 qui donne un aperçu de la famille 16F8X. Les numéros de pages peuvent varier en
fonction des mises à jour de Microchip. Vous devrez peut-être chercher un peu.
Pour ceux qui veulent tout comprendre, la figure 3-1 de la page 8 montre
l’organisation interne d’un 16F84.
La mémoire programme est constituée de 1K mots de 14 bits. C’est dans cette zone que
vous allez écrire votre programme. Ceci explique pourquoi vos fichiers sur PC font 2Kbytes.
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.
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.
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.
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.
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 :
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.
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).
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 « ç ».
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.
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.
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
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
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.
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.
Exemple
Exemple
Cet adressage fait appel à 2 registres, dont un est particulier, car il n’existe pas vraiment.
Examinons-les donc :
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
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)*
;*********************************************************************************
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 :
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).
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.
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 :
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é.
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.
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