214
LA PROGRAMMATION DES PIC® PAR BIGONOFF Cinquième PARTIE Migration vers 18F : mode d’emploi Révision 1

Part5_r1

  • Upload
    ngt881

  • View
    30

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Part5_r1

LA PROGRAMMATION DES PIC®

PAR BIGONOFF

Cinquième PARTIE

Migration vers 18F : mode d’emploi

Révision 1

Page 2: Part5_r1

2

Page 3: Part5_r1

1. INTRODUCTION............................................................................................................................................. 7

2. PRÉSENTATION GÉNÉRALE...................................................................................................................... 9 2.1 RAPPEL .......................................................................................................................................................... 9 2.2 GÉNÉRALITÉS................................................................................................................................................. 9 2.3 ORGANISATION DES INSTRUCTIONS................................................................................................................ 9 2.4 ORGANISATION DE LA MÉMOIRE PROGRAMME ............................................................................................. 10 2.5 SAUTS RELATIFS ET ABSOLUS....................................................................................................................... 10 2.6 ORGANISATION DE LA MÉMOIRE DE DONNÉES.............................................................................................. 12 2.7 UN MOT SUR LE REGISTRE WREG............................................................................................................... 13

3. LES MODES D’ADRESSAGE...................................................................................................................... 17 3.1 L’ADRESSAGE LITTÉRAL OU IMMÉDIAT........................................................................................................ 17 3.2 L’ADRESSAGE DIRECT DE TYPE « BANKED » ................................................................................................ 17 3.3 L’ADRESSAGE DIRECT EN ACCESS BANK ...................................................................................................... 18 3.4 L’ACCÈS DIRECT LONG................................................................................................................................. 21 3.5 L’ADRESSAGE INDIRECT SIMPLE................................................................................................................... 22 3.6 L’ADRESSAGE INDIRECT POST-DÉCRÉMENTÉ ............................................................................................... 24 3.7 L’ADRESSAGE INDIRECT POST-INCRÉMENTÉ ................................................................................................ 25 3.8 L’ADRESSAGE INDIRECT PRÉ-INCRÉMENTÉ .................................................................................................. 26 3.9 L’ADRESSAGE INDIRECT PRÉ-INDEXÉ........................................................................................................... 27

4. REGISTRES PARTICULIERS ET PARTICULARITÉS.......................................................................... 29 4.1 LE PC........................................................................................................................................................... 29 4.2 LES REGISTRES PCL,PCLATH, ET PCLATU .............................................................................................. 30 4.3 LES SAUTS CALCULÉS .................................................................................................................................. 31 4.4 LE REGISTRE « STATUS »........................................................................................................................... 33 4.5 LE CAS DES INSTRUCTIONS 32 BITS .............................................................................................................. 34 4.6 LA PROCEDURE « READ/MODIFY/WRITE » .................................................................................................... 36 4.7 LES REGISTRES LATX .................................................................................................................................. 39 4.8 MANIPULATIONS DE LA PILE ........................................................................................................................ 40 4.9 LES RETOURS RAPIDES « FAST »................................................................................................................... 44

5. LE JEU D’INSTRUCTIONS ......................................................................................................................... 49 5.1 CONVENTIONS.............................................................................................................................................. 49 5.2 L’INSTRUCTION « ADDLW » ...................................................................................................................... 49 5.3 L’INSTRUCTION «ADDWF » ....................................................................................................................... 50 5.4 L’INSTRUCTION « ADDWFC ».................................................................................................................... 51 5.5 L’INSTRUCTION «ANDLW » ....................................................................................................................... 52 5.6 L’INSTRUCTION «ANDWF » ....................................................................................................................... 53 5.7 L’INSTRUCTION «BC »................................................................................................................................. 54 5.8 L’INSTRUCTION «BCF »............................................................................................................................... 54 5.9 L’INSTRUCTION «BN » ................................................................................................................................ 55 5.10 L’INSTRUCTION «BNC »............................................................................................................................ 56 5.11 L’INSTRUCTION «BNN »............................................................................................................................ 56 5.12 L’INSTRUCTION «BNOV »......................................................................................................................... 57 5.13 L’INSTRUCTION «BNZ » ............................................................................................................................ 57 5.14 L’INSTRUCTION «BRA »............................................................................................................................ 58 5.15 L’INSTRUCTION «BSF » ............................................................................................................................. 58 5.16 L’INSTRUCTION «BTFSC »........................................................................................................................ 59 5.17 L’INSTRUCTION «BTFSS » ........................................................................................................................ 60 5.18 L’INSTRUCTION «BTG » ............................................................................................................................ 61 5.19 L’INSTRUCTION «BOV »............................................................................................................................ 61 5.20 L’INSTRUCTION «BZ » ............................................................................................................................... 62 5.21 L’INSTRUCTION «CALL ».......................................................................................................................... 62 5.22 L’INSTRUCTION «CLRF ............................................................................................................................. 63 5.23 L’INSTRUCTION «CLRWDT » ................................................................................................................... 64 5.24 L’INSTRUCTION «COMF »......................................................................................................................... 64

3

Page 4: Part5_r1

5.25 L’INSTRUCTION «CPFSEQ » ..................................................................................................................... 65 5.26 L’INSTRUCTION «CPFSGT » ..................................................................................................................... 66 5.27 L’INSTRUCTION «CPFSLT »...................................................................................................................... 66 5.28 L’INSTRUCTION «DAW »........................................................................................................................... 67 5.29 L’INSTRUCTION «DECF ».......................................................................................................................... 68 5.30 L’INSTRUCTION «DECFSZ » ..................................................................................................................... 69 5.31 L’INSTRUCTION «DCFSNZ »..................................................................................................................... 70 5.32 L’INSTRUCTION «GOTO » ......................................................................................................................... 70 5.33 L’INSTRUCTION «INCF » ........................................................................................................................... 71 5.34 L’INSTRUCTION «INCFSZ » ...................................................................................................................... 72 5.35 L’INSTRUCTION «INFSNZ » ...................................................................................................................... 72 5.36 L’INSTRUCTION «IORLW » ....................................................................................................................... 73 5.37 L’INSTRUCTION «IORWF » ....................................................................................................................... 74 5.38 L’INSTRUCTION «LFSR »........................................................................................................................... 74 5.39 L’INSTRUCTION «MOVF »......................................................................................................................... 75 5.40 L’INSTRUCTION «MOVFF » ...................................................................................................................... 76 5.41 L’INSTRUCTION «MOVLB »...................................................................................................................... 77 5.42 L’INSTRUCTION «MOVLW »..................................................................................................................... 77 5.43 L’INSTRUCTION «MOVWF »..................................................................................................................... 78 5.44 L’INSTRUCTION «MULLW » ..................................................................................................................... 78 5.45 L’INSTRUCTION «MULWF » ..................................................................................................................... 79 5.46 L’INSTRUCTION «NEGF ».......................................................................................................................... 80 5.47 L’INSTRUCTION «NOP » ............................................................................................................................ 80 5.48 L’INSTRUCTION «POP »............................................................................................................................. 81 5.49 L’INSTRUCTION «PUSH ».......................................................................................................................... 82 5.50 L’INSTRUCTION «RCALL » ....................................................................................................................... 83 5.51 L’INSTRUCTION «RESET »........................................................................................................................ 83 5.52 L’INSTRUCTION «RETFIE »....................................................................................................................... 84 5.53 L’INSTRUCTION «RETLW » ...................................................................................................................... 85 5.54 L’INSTRUCTION «RETURN » .................................................................................................................... 85 5.55 L’INSTRUCTION «RLCF » .......................................................................................................................... 86 5.56 L’INSTRUCTION «RLNCF » ....................................................................................................................... 87 5.57 L’INSTRUCTION «RRCF ».......................................................................................................................... 88 5.58 L’INSTRUCTION «RRNCF » ....................................................................................................................... 88 5.59 L’INSTRUCTION «SETF »........................................................................................................................... 89 5.60 L’INSTRUCTION «SLEEP » ........................................................................................................................ 90 5.61 L’INSTRUCTION «SUBFWB ».................................................................................................................... 90 5.62 L’INSTRUCTION «SUBLW » ...................................................................................................................... 91 5.63 L’INSTRUCTION «SUBWF » ...................................................................................................................... 92 5.64 L’INSTRUCTION «SUBWFB ».................................................................................................................... 93 5.65 L’INSTRUCTION «SWAPF »....................................................................................................................... 94 5.66 L’INSTRUCTION «TBLRD » ....................................................................................................................... 94 5.67 L’INSTRUCTION «TBLWT » ...................................................................................................................... 95 5.68 L’INSTRUCTION «TSTFSZ » ...................................................................................................................... 95 5.69 L’INSTRUCTION «XORLW » ..................................................................................................................... 96 5.70 L’INSTRUCTION «XORWF »...................................................................................................................... 97

6. LECTURES ET ÉCRITURES EN MÉMOIRE PROGRAMME............................................................... 99 6.1 LES NOUVEAUX MECANISMES ...................................................................................................................... 99 6.2 LES REGISTRES CONCERNES ......................................................................................................................... 99

6.2.1 Le registre EECON1 .......................................................................................................................... 100 6.2.2 Le registre EECON2 .......................................................................................................................... 100 6.2.3 Le registre « TBLPTR » ..................................................................................................................... 101 6.2.4 Le registre TABLAT ........................................................................................................................... 101

6.3 LA LECTURE............................................................................................................................................... 101 6.3.1 L’instruction « TBLRD ».................................................................................................................... 102 6.3.2 La lecture en pratique ........................................................................................................................ 103

6.4 PROCÉDURE D’EFFACEMENT ...................................................................................................................... 103 6.5 L’ÉCRITURE ............................................................................................................................................... 106

6.5.1 L’écriture dans le buffer interne ........................................................................................................ 106

4

Page 5: Part5_r1

6.5.2 L’instruction « TBLWT ».................................................................................................................... 106 6.5.3 L’écriture du buffer en mémoire flash................................................................................................ 107 6.5.4 Elaboration de la routine complète.................................................................................................... 109 6.5.5 Ecriture avec vérification et effacement............................................................................................. 114

6.6 DÉCLARATIONS DE CONSTANTES EN MÉMOIRE FLASH................................................................................ 115 6.7 PARTICULARITÉ DE L’ORGANISATION DES MOTS........................................................................................ 116 6.9 ORGANISATION D’UN FICHIER HEX............................................................................................................. 118

6.9.1 Organisation générale ....................................................................................................................... 119 6.9.2 Constitution générale d’une ligne hex................................................................................................ 119 6.9.3 Analyse d’un fichier réel .................................................................................................................... 121 6.9.4 Traitement par un programme PC ..................................................................................................... 125

6.10 ZONES CONCERNÉES ET REGISTRES DE CONFIGURATIONS ........................................................................ 127 7. LES ACCÈS EN MÉMOIRE EEPROM .................................................................................................... 131

7.1 GÉNÉRALITÉS............................................................................................................................................. 131 7.2 LECTURE EN EEPROM.............................................................................................................................. 131 7.3 DÉCLARATIONS EN ZONE EEPROM.............................................................................................................. 132 7.4 ECRITURE EN EEPROM............................................................................................................................. 135

8. LES INTERRUPTIONS............................................................................................................................... 139 8.1 INTRODUCTION .......................................................................................................................................... 139 8.2 LES REGISTRES CONCERNÉS ....................................................................................................................... 139 8.3 INTERRUPTIONS SIMPLES............................................................................................................................ 141 8.4 LES INTERRUPTIONS HIÉRARCHISÉES ......................................................................................................... 147 8.5 LES SOURCES D’INTERRUPTION .................................................................................................................. 157

8.5.1 Flanc détecté sur la pin INT0 (INT)................................................................................................... 157 8.5.2 Flanc détecté sur la pin INT1............................................................................................................. 158 8.5.3 Flanc détecté sur la pin INT2............................................................................................................. 158 8.5.4 Changement de niveau sur les pins RB4/RB7 .................................................................................... 158 8.5.5 Débordement du Timer0..................................................................................................................... 159 8.5.6 Débordement du Timer1..................................................................................................................... 159 8.5.7 Débordement du Timer2..................................................................................................................... 159 8.5.8 Débordement du Timer3..................................................................................................................... 160 8.5.9 Buffer de réception de l’USART plein................................................................................................ 160 8.5.10 Buffer d’émission de l’USART vide.................................................................................................. 160 8.5.11 Emission ou réception SSP terminée................................................................................................ 161 8.5.12 Interruption du module compare et capture 1.................................................................................. 161 8.5.13 Interruption du module enhanced compare and capture 1 .............................................................. 161 8.5.14 Fin de numérisation ......................................................................................................................... 162 8.5.15 Fin de transfert de donnée en mode parallèle.................................................................................. 162 8.5.16 Chute de tension détectée sur le PIC®............................................................................................. 162 8.5.17 Collision détectée sur le bus I²C ...................................................................................................... 163 8.5.18 Fin d’écriture en eeprom interne ..................................................................................................... 163 8.5.19 Détection d’un changement de la sortie d’un comparateur ............................................................. 163 8.5.20 Réception d’une trame CAN sur le buffer 0 ..................................................................................... 163 8.5.21 Réception d’une trame CAN sur le buffer 1 ..................................................................................... 164 8.5.22 Fin d’émission d’une trame CAN sur le buffer 0.............................................................................. 164 8.5.23 Fin d’émission d’une trame CAN sur le buffer 1.............................................................................. 164 8.5.24 Fin d’émission d’une trame CAN sur le buffer 2.............................................................................. 165 8.5.25 Erreur sur le bus CAN...................................................................................................................... 165 8.5.26 Activité détectée sur le bus CAN ...................................................................................................... 165 8.5.27 Message invalide sur le bus CAN..................................................................................................... 166

9. LES REGISTRES DE CONFIGURATION ............................................................................................... 167 9.1 INTRODUCTION .......................................................................................................................................... 167 9.2 MPLAB® V7 ET DIRECTIVE CONFIG ...................................................................................................... 167 9.3 OÙ TROUVER LES INFORMATIONS ?............................................................................................................ 168

10. MISE EN CIRCUIT.................................................................................................................................... 171 10.1 TYPES D’OSCILLATEUR ............................................................................................................................ 171

5

Page 6: Part5_r1

10.2 UTILISATION D’UN QUARTZ OU D’UN RÉSONATEUR ................................................................................. 171 10.3 UTILISATION D’UN RÉSEAU RC................................................................................................................ 173 10.4 UTILISATION D’UNE HORLOGE EXTERNE .................................................................................................. 176 10.5 UTILISATION DE 2 OSCILLATEURS ............................................................................................................ 176

11. LE RESET ................................................................................................................................................... 181 11.1 TYPES DE RESET ....................................................................................................................................... 181 11.2 UTILITÉ DE LA DÉTECTION DU TYPE DE RESET.......................................................................................... 181 11.3 MÉCANISMES DE DÉTECTION.................................................................................................................... 182 11.4 DÉTECTION DE LA MISE SOUS TENSION..................................................................................................... 184 11.5 RESET SUR CHUTE D’ALIMENTATION........................................................................................................ 184 11.6 RESET SOFTWARE..................................................................................................................................... 186 11.7 RESET PAR DÉBORDEMENT DE PILE .......................................................................................................... 186 11.8 RESET PAR DÉPILEMENT EXCESSIF ........................................................................................................... 186 11.9 RESET PAR DÉBORDEMENT DU WATCHDOG .............................................................................................. 187 11.10 RESET PAR MCLR ................................................................................................................................. 188 11.11 RÉALISATION DE LA ROUTINE DE TEST COMPLÈTE ................................................................................. 188 11.12 RESET SUR ALIMENTATIONS LENTES ...................................................................................................... 190 11.13 RESET ET PROGRAMMATION ICSP® ...................................................................................................... 191

12. L’ÉTUDE DU FICHIER MAQUETTE.................................................................................................... 193 12.1 LE FICHIER MAQUETTE ............................................................................................................................. 193 12.2 LE FICHIER DES MACROS .......................................................................................................................... 204 12.2 CONCLUSIONS.......................................................................................................................................... 210

UTILISATION DU PRESENT DOCUMENT ............................................................................................... 213

6

Page 7: Part5_r1

1. Introduction Et bien, nous nous retrouvons de nouveau, suite à l’intérêt croissant que portent fort logiquement les utilisateurs de PIC® à cette nouvelle famille. J’ai longtemps hésité entre écrire un cours spécifique pour ceux qui démarrent l’apprentissage des PIC® par la famille 18F, ou écrire un cours expliquant les différences avec la famille 16F pour ceux qui ont déjà étudié cette famille. J’ai choisi la seconde solution pour plusieurs raisons :

- D’une part, vu le nombre incroyable de fonctionnalités existant sur les 18F, écrire un cours complet aussi détaillé que les cours 1 et 2, avec exemples pour chaque fonction, m’aurait nécessité beaucoup trop de temps, et n’aurait jamais vu le jour. Imaginez qu’il y a déjà plus de 750 pages pour l’apprentissage des 16F via les cours-part1 et part2.

- D’autre part, la majorité des internautes désirant étudier les 18F a déjà fait

l’apprentissage des 16F, il serait donc dommage de les encombrer de renseignements inutiles et de faire l’impasse sur les explications concernant les différences

- La famille 18F fait toujours partie des PIC® 8 bits, c’est donc le dernier maillon de

toute une gamme de produits, dont les modules hardwares restent assez semblables.

- Enfin, de mon point de vue, l’apprentissage des PIC® via le 16F84 reste quand même la façon la plus simple et qui risque moins de décourager le débutant.

Ce cours s’adresse donc aux lecteurs des deux premiers cours traitant spécifiquement de la

famille 16F. Les cours 3 et 4 ne sont pas nécessaires à la compréhension de cet ouvrage. A la limite, le lecteur peut se contenter d’avoir assimilé le cours-part1, le second traitant de modules complémentaires dont beaucoup se retrouvent cependant sur les 18F.

Il m’est impossible d’aborder les modules hardwares spécifiques des 18F pour la bonne

raison qu’il y en a trop et qu’ils évoluent sans arrêt. Le lecteur abordant cet ouvrage est sensé pouvoir lire un datasheet et avoir assimilé les précédents cours, il comprendra donc aisément les explications techniques du datasheet.

J’ai travaillé dans cet ouvrage sur base du 18Fxx8 (18F248, 258, 448 et 458), remplacé

actuellement par le 18Fxx80. Il est identique au 18Fxx2 excepté la présence d’un module CAN supplémentaire.

A ce propos, plutôt que d’intégrer les explications sur certains modules très spécifiques

(comme le gestionnaire de bus CAN) dans ce document, ce qui nécessiterait d’incessantes mises à jour, j’ai choisi d’expliquer ces techniques dans des documents séparés. Le lecteur désirant dès à présent comprendre le bus CAN sur PIC® pourra se référer à mon application « Domocan » (domotique sur bus CAN) dont les sources sont disponibles sur mon site, ainsi que toutes les explications détaillées nécessaires, dans l’attente d’un cours spécifique.

7

Page 8: Part5_r1

Il m’est également impossible de reprendre des exemples détaillés comme pour les cours précédents, toujours par manque de temps disponible. Etant donné que le lecteur est déjà sensé avoir compris le fonctionnement des PIC16F, le côté plus théorique de cet ouvrage ne devrait pas poser de problème.

N’hésitez jamais à me faire part de vos remarques par mail via [email protected], ni à me signaler les erreurs qui m’ont inévitablement échappées.

Répercutez les infos que vous trouverez ici, traduisez le document dans une autre langue ou un autre format, bref utilisez ce document à votre guise. Simplement, dans ce cas, veuillez respecter les désirs de l’auteur en fin d’ouvrage et faites moi parvenir un exemplaire de votre travail. Ceci pour permettre d’en faire profiter le plus grand nombre. Pour l’apprentissage de MPLAB®, qui est l’outil nécessaire, je vous renvoie au cours part 1, dans lequel vous apprendrez à maîtriser l’éditeur, l’assembleur, et le simulateur. Ce cours tient cependant compte des particularités de MPLAB® en version 7.

8

Page 9: Part5_r1

2. Présentation générale 2.1 Rappel Je rappelle à ceux qui ne prennent pas la peine de lire les introductions, que ce cours est destiné à ceux qui ont une bonne connaissance des microcontrôleurs PIC® de la famille mid-range (16Fxxx). Pour les autres, veuillez lire au minimum le cours-part1, et, si possible, le cours-part2. Sans ça, vous risquez bien de ne rien comprendre et de vous lasser, à moins bien entendu que vous ne disposiez déjà des connaissances nécessaires. 2.2 Généralités Nous traiterons ici des PIC18Fxx8. Un PIC® est un microcontrôleur fabriqué par Microchip®, et les modèles identifiés comme 18F font partie de la gamme high-end de ce constructeur. Cette famille comporte plusieurs types de PIC®, les 18xx8 sont très représentatifs de cette famille. Les très connus 18Fxx2 (18F252 et autres) sont pratiquement identiques, exceptés qu’ils ne disposent pas de la fonctionnalité de gestion du bus CAN. Lorsque vous aurez étudié ce PIC®, il vous sera très simple de passer à une autre version, les adaptations étant mineures. Je fournis un fichier maquette, m18F258.asm, qui contient le squelette (template) d’un programme de départ d’une application réelle. Un copier/coller de ce fichier accélérera la réalisation de vos propres programmes. Les PIC® high-end 18F sont, comme les PIC® mid-range 16F, des microcontrôleurs 8 bits. Cependant, le bus d’instruction passe de 14 à 16 bits, ce qui a fait dire à tort par certains que les 18F étaient des PIC® 16 bits. Cette confusion a été entretenue jusqu’il y a peu par les forums de Microchip®, c’est maintenant clairement rectifié depuis que sont apparus les vrais PIC 16 bits. Il existe donc maintenant des PIC® 16 bits, mais ils font partie d’autres familles, comme les DsPIC30F, DsPIC33F, PIC24H et PIC24F (à l’heure actuelle).Leur étude fera l’objet d’un ouvrage spécifique dès que ça me sera possible. 2.3 Organisation des instructions Cette famille se caractérise donc par des mots d’instruction d’une largeur de 16 bits, chaque mot étant composé de deux octets. Les octets sont stockés dans l’ordre poids faible / poids fort. Lorsque vous lisez un fichier hexadécimal à l’aide d’un éditeur, il vous faudra donc inverser les deux octets lus afin de former le mot intégral. Notez ici une grosse différence par rapport aux PIC® de la gamme mid-range : Dans cette gamme, les instructions sont codées sur 14 bits, mais en un seul mot d’une largeur de 14 bits.

9

Page 10: Part5_r1

Une instruction est donc mémorisée dans une et une seule adresse mémoire d’une largeur de 14 bits. Sur les 18F, au contraire, une instruction sera codée au minimum sur deux emplacements mémoire, chaque emplacement contenant donc un seul octet. Les instructions sont toujours alignées sur une valeur paire, c’est à dire que le début d’une instruction doit toujours se trouver à l’adresse paire, alors que le second octet, qui contient l’octet de poids fort, se trouvera toujours à une adresse impaire. De toutes façons, les registres sont organisés de façon à vous éviter de sauter à une adresse impaire, n’oubliez cependant pas de compter chaque mot d’instruction comme 2 adresses. Notez aussi qu’alors que sur un 16F la taille mémoire programme est donnée en nombre de mots, sur un 18F elle est donnée en nombre d’octets. Or, il faut deux octets pour former une instruction. Moralité, une taille de 16Kmots d’un PIC16F correspond à une taille de 32Koctets d’un PIC18F. Faites-y attention en comparant les caractéristiques. 2.4 Organisation de la mémoire programme Bonne nouvelle pour les habitués des 16F, vous pouvez maintenant accéder à l’intégralité de la mémoire programme sans avoir à gérer des modifications de PCLATH. L’espace adressable total d’un PIC18F est de 2Mbytes (2 Moctets, ou Mo). Les 18F248 et 18F448 (18Fx48) disposent de 16Ko de mémoire programme, tandis que les 18F258 et 18F458 (18Fx58) disposent de 32Ko de mémoire programme. Attention, n’oubliez pas : il s’agit de Kilo-octets, donc de « demi-instructions ». Dans le meilleur des cas, ceci vous autorise donc respectivement 8192 et 16384 instructions d’un mot de 16 bits, sachant de plus que certaines instructions sont codées sur 32 bits. 2.5 Sauts relatifs et absolus Une instruction de saut pourra prendre, sur les 18F, trois formes : - Soit un saut relatif long - Soit un saut relatif court - Soit un saut absolu

Le saut absolu, vous le connaissez déjà, sous la forme du « goto », par exemple, utilisé sur les 16F. Ce goto est suivi de l’adresse directe à laquelle on désire sauter. goto adresse_absolue

On était du reste limité à un saut dans une page, l’adresse de destination effective était

alors complétée par la valeur contenue dans le registre PCLATH.

10

Page 11: Part5_r1

Rien de tout ceci sur les 18F, vous pouvez préciser l’adresse directement dans tout

l’espace d’adressage, sans aucun recours à PCLATH. Terminées donc les limites de pages. Bien entendu, étant donné que l’instruction est codée sur 16 bits, il n’y a pas suffisamment

de place pour coder l’adresse de destination, puisque coder une adresse sur 2Mo nécessite déjà 20 bits.

La solution a donc été de coder les instructions de sauts absolus sur 32 bits. Lorsque vous

écrivez « goto destination », cette instruction est donc codée sur 2 mots de 16 bits, et nécessite 2 cycles pour son exécution.

Le saut relatif long compense cet encombrement, en limitant le saut à une distance de –

2048 à +2046. Pour ce faire, l’équivalant du « goto » s’appellera « bra » pour « branch always » ou saut inconditionnel. L’instruction est donc codée en donnant la distance du saut par rapport à l’emplacement de l’instruction suivante (en effet, souvenez-vous, au moment de l’exécution d’une instruction, le PC, ou compteur ordinal, pointe déjà sur l’instruction suivante). La syntaxe au niveau langage machine serait donc : bra distance La distance sera exprimée par un nombre de 11 bits codé en complément à 2. Ceci donne une valeur comprise entre –1024 et + 1023 (voir cours-part1). Or, puisqu’on ne peut sauter qu’à une adresse paire, le PIC® multiplie cette valeur de lui-même par 2, ce qui donne la formule : Nouvel emplacement = emplacement actuel + 2 + (2 * distance) L’emplacement actuel + 2 s’explique par le fait qu’au moment d’exécuter l’instruction, le compteur de programme pointe déjà sur l’instruction suivante, et contient donc déjà l’emplacement actuel + 2. Pour l’utilisateur, tout ceci est transparent, l’assembleur se charge de convertir l’étiquette en une valeur de déplacement. Bref, la syntaxe en langage d’assemblage est tout simplement : bra étiquette Autrement dit, pour sauter à l’étiquette « label », vous aurez le choix entre : goto label Et bra label

Ces deux instructions donneront le même résultat à condition que le bra saute à un emplacement compris dans les limites expliquées ci-dessus. Dans le cas contraire, MPASM®

11

Page 12: Part5_r1

vous signalera simplement une erreur de limite de saut. Il vous suffira alors de remplacer le « bra » par un « goto ».

Remarquez que pouvoir sauter de plus de 1000 instructions en avant et en arrière couvre

déjà les 99% des sauts rencontrés dans un programme. Vous allez me dire : « alors, autant utiliser le goto ». Et bien non, car le goto utilise 32 bits

pour être codé, et le bra n’en utilise que 16. Autrement dit : utilisez systématiquement des sauts relatifs (nous verrons qu’il y en a

d’autres), et ne remplacez par des sauts absolus que si vous obtenez des erreurs lors de l’assemblage. Pour terminer, certaines instructions utilisent un saut relatif court, c’est à dire dont la destination relative est codée sur 8 bits. Ceci permet alors un déplacement de (-128 à +127) * 2 octets à partir de l’emplacement suivant l’instruction. De nouveau, les mécanismes sont les mêmes, et vous vous ne préoccupez de cette limite que si MPASM® vous avertit à l’aide d’une erreur. Un exemple typique est l’instruction « bc » qui permet de sauter (brancher) si le carry vaut 1. bc n ; saut relatif, avec n compris entre –128 et +127 Comparez cette «simple » instruction avec la séquence suivante pour vous convaincre de son utilité btfsc STATUS,C ; tester si carry vaut 1 bra etiquette ; oui, sauter Les sauts relatifs courts sont utilisés dans les instructions de sauts conditionnels. Vous allez voir que ce qui est génial avec les 18F, c’est que chaque fois que je vous indique des possibilités supplémentaires nécessitant des paramètres supplémentaires, je vais également vous indiquer que ces paramètres sont pris en charge de façon automatique. 2.6 Organisation de la mémoire de données Si je commence par vous dire que la zone de données est divisée en 15 banques, vous allez hurler en me disant que c’est encore pire que sur les 16F avec leurs 4 banques, sur lesquels les changements de banques se révèlent souvent pénibles. En fait, comme promis, il n’en est rien dans la pratique. Une série de mécanisme, d’instructions, et une organisation intelligente permet d’écrire de gros programmes sans nécessiter un seul changement de banque. Dans toutes mes applications domotiques écrites à ce jour, il n’y a aucun changement de banque dans aucun source, c’est dire qu’on n’est pas sur la même longueur d’onde que pour les 16F. En somme, pour la plupart de vos applications, vous n’aurez pas besoin de faire de changement de banques. Je vais expliquer pourquoi.

12

Page 13: Part5_r1

La mémoire RAM utilisateur d’un 18Fx48 comporte 768 octets, tandis que celle d’un 18Fx58 en contient 1536. Vous voyez déjà qu’on se trouve dans un autre ordre de grandeur que la mémoire RAM des 16F. Sachant qu’on place 256 adresses dans une seule banque, ça fait déjà pas mal de possibilités, mais c’est encore mieux que ça. La première chose à savoir, c’est que tous les SFR (registres spéciaux) sont situés dans la même banque, à savoir la banque 15. Donc, c’est déjà fini les changements de banques pour passer de TRISB à PORTB, par exemple.

Avoir tous les registres dans la même banque équivaut déjà à une simplification, et non des moindres, puisqu’il s’agit d’une des principales causes d’erreur chez le débutant sur les 16F. A ce stade, je vous conseille de télécharger le datasheet du 18Fxx8. Je place sur mon site la version qui a servi à écrire ce cours, comme ça tout le monde aura les mêmes numéros de page. Par contre, une fois le cours terminé, je vous conseille de charger la dernière version disponible chez Microchip®. Vous verrez page 47 et 48, table 4-1, les 256 emplacements de la banque 15, avec les noms des registres correspondants.La seconde partie située page 48 n’existe que pour les 18Fxx8 disposant du module CAN, et non pour les 18Fxx2 par exemple. Ne vous intéressez pas à cette seconde partie, elle sera traitée dans un ouvrage sur le bus CAN. Mais en fait, nous allons voir dans le chapitre 3 que les registres de la page 47 se trouvent en fait dans une zone très particulière de la mémoire RAM, zone qui ne nécessite même pas de passer en banque 15 pour y accéder. 2.7 Un mot sur le registre WREG Il s’agit de notre registre de travail, dénommé « W » dans les instructions, et que nous connaissons bien sur le 16F. Lorsque vous exécutez par exemple : movf variable,w ; charger le contenu de variable dans W Vous retrouvez le contenu de la variable « variable » dans le registre de travail W. Or, grande nouveauté, sur les 18F, et contrairement aux 16F, ce registre est maintenant accessible comme tout autre registre via le nom « WREG ». Bref, vous pouvez maintenant faire des opérations directement sur le registre de travail, ce qui présente un grand plus et se révèle très pratique à l’usage. Pas convaincu ? Prenons l’énoncé suivant : Vous avez une valeur dans W, et vous désirez la décaler vers la gauche. En 16F, vous devriez écrire : movwf temporaire ; sauver W dans une variable temporaire bcf STATUS,C ; effacer le carry rlf temporaire,w ; charger la valeur décalée vers la gauche

13

Page 14: Part5_r1

Pourquoi avons-nous du sauver W ? Tout simplement parce que le registre « W » n’étant pas adressable, il est impossible de faire « rlf w,w ». On ne peut faire un décalage que sur un emplacement RAM adressable. C’est toujours identique pour le 18F, mais comme WREG est un registre à part entière, il est accessible en tant que tel. Vous pouvez donc écrire : rlncf WREG,w ; décalage direct de W Ecrire « ,w » ou « ,f » ne change strictement rien, puisque dans ce cas très particulier l’emplacement RAM concerné est justement « W ». rlncf WREG,w ; décalage direct de W, résultat dans w rlncf WREG,f ; décalage direct de W, résultat dans WREG (=W) Vous voyez ici que le décalage s’effectue directement dans W, ce qui ouvre pas mal de nouvelles possibilités et de facilités d’écriture. Ne vous inquiétez pas pour la syntaxe de l’instruction « rlncf », nous allons en reparler en étudiant le jeu d’instructions. Remarquez que ceci rend inutile certaines instructions spécifiques au registre W. Par exemple, on notera la disparition de l’instruction « clrw »: clrw ; effacement du registre W En effet, étant donné que le registre W est accessible comme un registre ordinaire, l’instruction générale « clrf » fonctionne également pour ce registre : clrf WREG ; effacer le registre WREG = effacer W Mais vous constaterez que si vous écrivez « clrw », vous ne serez pas gratifiés d’une erreur. Simplement, MPASM® considère clrw comme une pseudo-instruction (genre de macro intégrée) et fait la conversion automatiquement pour vous. Il n’empêche que cette instruction a bel et bien disparu. Notez un point que je soulève régulièrement, à savoir que le nombre d’instructions donné sur papier ne caractérise pas la puissance d’un microcontrôleur, il faut analyser l’environnement en intégralité. Vous voyez très bien ici que la suppression de cette instruction ne diminue en rien ni la facilité d’utilisation, ni la puissance du microcontrôleur. Au contraire, dans ce cas, cette diminution est le résultat d’une augmentation de l’efficacité générale, puisque le registre W est maintenant accessible pour l’intégralité des instructions. Autrement dit, rien qu’en plaçant le registre W dans la zone des registres accessibles, vous avez en fait une multitude d’instructions supplémentaires non répertoriées, et non comptabilisées dans le nombre d’instructions. Dans l’exemple que nous avons vu concernant le décalage, ceci équivaut par exemple à avoir ajouté l’instruction « rlw », soit « rotation du registre w », c’est ce que vous auriez rencontré dans certaines autres marques de microcontrôleurs.

14

Page 15: Part5_r1

Certaines familles de microcontrôleurs travaillent avec des instructions spécifiques, et donc leur jeu d’instructions est plus étendu. Méfiez-vous des comparaisons directes dont certains sont friands sur les forums.

15

Page 16: Part5_r1

Notes :

16

Page 17: Part5_r1

3. Les modes d’adressage 3.1 L’adressage littéral ou immédiat Cet adressage précise que la valeur est immédiatement (littéralement) comprise dans l’instruction elle-même. Aucune autre donnée n’est nécessaire à son exécution. movlw 0x55 ; charger la valeur 0x55 dans W Aucune variable n’est précisée, tout est dit dans l’instruction. C’est le mode le plus simple. 3.2 L’adressage direct de type « banked » La mémoire RAM est en fait accessible de plusieurs façons : - Soit par accès « banked », qui correspond aux possibilités rencontrées sur le 16F. - Soit par « access bank », ce qui est une nouvelle possibilité. - Soit par accès direct long - Soit par accès indirect

Nous allons voir ou revoir ces différentes possibilités, en commençant par l’adressage « banked » Cette méthode est semblable dans son principe à celle utilisée par les 16F. Vous précisez une adresse, et l’adresse accédée est complétée par la banque actuellement sélectionnée. Sur un 16F, on écrivait : bsf STATUS,RP0 ; passer en banque 1 movf variable,w ; charger variable en banque 1 dans w bcf STATUS,RP0 ; revenir éventuellement en banque 0 La syntaxe pour le 18F, si on veut être complet, est un peu différente. Vous précisez que vous désirez utiliser un adressage de type « banked » en ajoutant « ,banked » à l’instruction, et vous sélectionnez préalablement la banque, comme nous faisions pour le 16F. Si donc nous imaginons la banque 1 actuellement sélectionnée, et « variable » étant un emplacement RAM de la banque 1, nous pourrons écrire : movf variable,w,banked ; placer B1 dans variable de la banque courante ou movf variable,w,1 ; placer B1 dans variable de la banque courante Ah là là, allez-vous me dire, encore un paramètre en plus !

17

Page 18: Part5_r1

Rassurez-vous, de nouveau et comme je vous l’avais annoncé, MPASM® prend ce paramètre en charge automatiquement. Si vous accédez à une variable qui n’est accessible qu’en mode « banked », alors le suffixe « ,banked » est ajouté automatiquement. Bref, vous pouvez continuer à écrire : movf variable,w ; charger variable dans w à condition évidemment qu’on pointe bien sur la bonne banque, rien n’a changé à ce niveau. Il vous suffit simplement de vous rappeler que puisque votre variable est en banque1, MPASM® ajoutera automatiquement le suffixe. Il nous reste à savoir comment préciser la banque. Puisque nous avons 15 banques, plus question ici de RP0 et de RP1, il nous faut plus de bits. En fait, nous disposons d’un registre spécial, BSR, dont les 4 bits de poids faibles donnent le numéro de la banque concernée. On pourrait donc se dire qu’on va exécuter : movlw 0x01 ; pour banque 1 movwf BSR ; dans BSR movf variable,w ; charger variable en banque 1 dans w Mais c’est plus simple que ça, vous disposez d’une instruction spéciale, movlb, qui vous permet de charger en une seule opération la banque désirée. Le programme final sera donc : movlb 0x01 ; pointer en banque 1 movf variable,w ; charger variable en banque 1 dans w Vous voyez que c’est simple, mais vous allez me dire qu’on en est toujours avec ces maudites banques. Un peu de patience, que diable, je vous ai dit que vous alliez en être débarrassé dans la pratique. 3.3 L’adressage direct en access bank Si vous regardez page 46 du datasheet, figure 4-6, vous voyez un petit cadre à droite, intitulé «access bank ». Ce petit cadre est en fait la clé de la simplification de la gestion des banques sur les 18F. En fait, vous imaginez que vous pouvez à tout instant, et quel que soit la banque actuellement pointée, avoir accès en direct à 256 emplacements toujours identiques. Ces 256 emplacements sont en fait les adresses 0 à 0x5F des variables en banque 0, complétées par les registres SFR 0x60 à 0xFF de la banque 15.

18

Page 19: Part5_r1

Autrement dit, vous obtenez une sorte de banque spéciale, nommée « access bank », qui vous donne accès aux 96 premiers emplacements RAM de la banque 0 et aux 160 derniers emplacements de la banque 15. Autrement dit, vous avez accès en access bank à 160 sur 256 registres FSR possibles. Vous allez penser que c’est dommage de ne pas avoir accès à tout, mais en fait, nous allons voir que le problème n’existe pas. Si vous jetez un œil à la table 4-1 page 47 et 48 (attention, ce tableau est inversé, il commence par les adresses les plus hautes), vous voyez que les 96 premières adresses non accessibles (donc les dernières dans le tableau) sont représentées en grisé. Ce sont tous des registres concernant le module CAN (et qui n’existent pas sur les 18Fxx2 et Cie), mais l’accès à ces registres dispose également d’un mécanisme qui évite les changements de banque, nous en parlerons dans l’étude du module CAN dans un autre ouvrage. Considérez donc pour l’instant que ces registres n’existent pas. Notez dans la note dans le cadre du dessous que les registres en grisé sont accessibles en banque 15, alors que les autres sont accessibles en « access bank », comme je viens de vous le dire. Ne vous préoccupez pas de la note numéro 3, j’en parlerai également dans le chapitre consacré au bus CAN. Bref, TOUS les registres SFR sont accessibles directement en access-bank, et donc il ne faudra JAMAIS changer de banque pour accéder à n’importe quel registre SFR du PIC®. Fini donc les changements de banques pour les registres spéciaux. Revenons à nos moutons. Il nous faut maintenant savoir comment préciser que nous désirons un accès à l’ « access bank ». Comme pour le mode « banked », nous ajoutons un suffixe, qui sera cette fois « ,ACCESS », ou « ,A », ou « ,0 ». Et attendez la suite avant de râler sur ce nouveau paramètre … Imaginons un programme fictif qui charge le contenu de la variable1, située à l’adresse 0x10 de la banque 0, et place cette valeur dans le registre TRISB situé en banque 15. Nous pourrons écrire : movf variable1,w,A ; charger variable1 de l’access bank dans W movwf TRISB,A ; mettre la valeur dans TRISB en access bank De nouveau, nous avons des termes supplémentaires, mais de nouveau MPASM® nous simplifie la vie. En effet, il connaît l’adresse de variable1 et il sait qu’elle est accessible en access bank. Dans ce cas, il place automatiquement le suffixe « ,A » à la fin de votre instruction. Pratique, non ? En effet, toute variable se trouvant dans l’espace accessible par l’access-bank peut toujours être accédée de cette façon. MPASM® fait donc l’opération suivante pour vous : - Si l’emplacement RAM se trouve en access-bank, MPASM® ajoute « ,A » - Sinon, MPASM® ajoute « ,BANKED », puisque c’est la seule façon d’y accéder.

19

Page 20: Part5_r1

De cette façon, vous ne verrez jamais le troisième terme dans un source de 18F, pour

la simple raison qu’il est inutile. Par contre, si vous désassemblez un fichier, ou que vous examinez la mémoire programme via MPLAB®, alors vous verrez que ce terme est bel et bien présent.

Bref, tout ceci est transparent pour l’utilisateur.

Votre programme devient donc : movf variable1,w ; charger variable1 de l’access bank dans W movwf TRISB ; mettre la valeur dans TRISB en access bank MPASM® s’occupe de tout. Tout ce que vous devez vérifier, c’est que soit votre variable se trouve en access bank, soit si elle ne s’y trouve pas, que BSR est correctement configuré. Vous vous dites à présent : « Super, j’ai 96 variables accessibles directement en plus de mes registres ». Mais, en fait, c’est mieux que ça. En effet, durant ce temps, votre registre BSR contient une valeur, et donc toutes les variables contenues dans la banque sont également directement accessibles. Si BSR contient la valeur 0 (valeur par défaut à la mise sous tension), alors vous avez accès en réalité aux 256 emplacements de la banque 0, et aux 160 registres FSR de la banque 15. En effet, en imaginant variable1 située à l’adresse 0x10 de la banque 0, et variable2 située à l’adresse 0x80 de la banque 0, si vous écrivez : movf variable1,w ; charger variable1 movwf TRISB ; sauver dans TRISB movf variable2 ; charger variable2 movwf PORTB ; mettre dans PORTB En fait, MPASM® convertira en : movf variable1,w,A ; la variable1 est accessible en access bank movwf TRISB,A ; TRISB est accessible en access bank movf variable2,BANKED ; la variable2 n’est PAS accessible en access bank movwf PORTB,A ; PORTB est accessible en access bank Il vous faut donc être attentif au fait que l’accès à la variable2 nécessite que BSR soit correctement configuré à l’aide d’une instruction « movlb ». A la mise sous tension, BSR pointe sur la banque 0. Vous devez déjà vous dire que vous avez accès maintenant à 256 variables sans changement de banque, mais vous pouvez faire mieux. En effet, si vous décidez de faire pointer BSR sur la banque1 dès le début du programme, par exemple, vous avez maintenant accès sans aucun changement : - Aux 96 variables de la banque 0 situées en access bank

20

Page 21: Part5_r1

- Aux 256 variables de la banque 1 pointées par BSR. - Aux 160 registres FSR de la banque 15.

Autrement dit, vous voici maintenant en possession de 352 variables accessibles sans changement de banques, en plus de tous vos registres SFR. C’est pas beau, ça, madame ?

Mais ce n’est de nouveau pas fini.

3.4 L’accès direct long Les 18F disposent d’instructions supplémentaires, et non des moindres, qui permettent d’accéder à l’intégralité de l’espace RAM sans avoir besoin ni de l’access ram, ni des banques. Ces instructions sont codées sur 32 bits, et contiennent l’adresse complète de la variable de façon explicite. Nul besoin donc de recourir à un changement de banque, même si BSR ne pointe pas sur la banque concernée. Examinons une instruction qui permet cette manipulation. movff source,destination Permet de copier le contenu de l’emplacement source vers l’emplacement destination, sans nécessiter de recourir aux banques. Les emplacements visés sont donc toujours justes, et peuvent se trouver dans des banques différentes. Imaginons par exemple que votre variable1 se trouve en banque2, que votre variable2 se trouve en banque3, et que votre BSR pointe sur la banque1 Vous pouvez parfaitement écrire : movff variable1,TRISB ; mettre le contenu de variable1 dans TRISB movff variable2,PORTB ; mettre le contenu de variable2 dans PORTB movff variable2,variable1 ; copier variable2 dans variable1 Cerise sur le gâteau, l’instruction movff ne modifie aucun bit du registre STATUS (génial pour sauvegarder les registres dans les routines d’interruption). Vous voyez que ça commence à devenir sérieusement intéressant. Donc, intelligents comme vous êtes, vous vous dites que : - Votre access bank peut contenir 96 variables accessibles en permanence - Votre registre BSR pointe sur 256 autres variables - Vous avez accès à toutes les autres variables par accès direct long.

Autrement dit, vous placez dans l’access bank et dans la banque pointée par BSR vos variables générales, et vous placez dans les autres banques les variables de sauvegarde, par exemple, et toutes les variables auxquelles vous n’accéderez que par accès direct long.

En effet, une sauvegarde d’un registre, lors d’une interruption, par exemple, peut s’effectuer à l’aide d’une instruction movff registre, variable. Vous n’accédez donc à ces

21

Page 22: Part5_r1

variables qu’à l’aide d’accès directs longs, ce qui vous permet d’économiser de la place dans les banques accessibles de façon traditionnelle. Comparons un bout de code 16F et 18F chargé de copier le contenu de «variable1 » situé en banque 3 vers « variable2 » situé en banque 0. Voici le code 16F : bsf STATUS,RP0 ; pointer banque 3 bsf STATUS,RP1 movf variable1,w ; charger variable1 en banque 3 bcf STATUS,RP0 ; pointer banque 0 bcf STATUS,RP1 movwf variable2 ; sauver dans variable2 en banque 0

Pas glop, pas glop (pour les anciens qui se souviennent de « Pifou » dans « Pif et Hercule »). Voici maintenant le code 18F : movff variable1,variable2 ; variable1 en banque 3 dans variable2 banque 0

Avouez qu’il n’y a pas photo. Cerise sur la gâteau : Vous allez dire que si vous voulez sauver le contenu de W dans une variable qui ne se trouve ni en acess-bank ni dans la banque pointée (vous êtes difficile, avouez-le) vous devrez quand même changer de banque. Et bien, même pas. Puisque W est accessible en RAM par son nom WREG, il vous suffit d’écrire, à la place de : movlb banque ; pointer sur la banque de la variable movwf variable ; sauver W dans variable tout simplement ceci ; movff WREG,variable ; sauver W dans variable de n’importe quelle banque et la réciproque est vraie, pour recharger w : movff variable,WREG Certes, le movff est une instruction 32 bits, mais avouez que c’est génial. Mais, de nouveau, ce n’est pas encore tout. 3.5 L’adressage indirect simple La première chose à dire, comparé aux 16F, c’est que les registres d’accès indirect accèdent maintenant à l’intégralité de la zone RAM, sans aucun changement de banque nécessaire. Fini donc le bit « IRP » à positionner avant d’utiliser INDF.

22

Page 23: Part5_r1

De fait, si vous avez des tableaux et autres zones auxquels vous accédez en accès indirect, placez-les dans une banque non pointée par BSR, ce qui vous donne encore plus de possibilités de variables.

Vous commencez à comprendre que dans un programme bien conçu, vous aurez peu, voire pas du tout d’instructions de changement de banque. En général, les programmes utilisent un certain nombre de variables directes, et lorsqu’on use beaucoup de mémoire c’est généralement pour des tableaux ou des buffers, qui sont donc accessibles par l’adressage indirect. Moralité : les changements de banques dans les programmes 18F sont plus l’exception que la règle. L’adressage indirect, comme son nom l’indique, vous permet d’accéder à une adresse pointée par un registre. Si vous ne connaissez pas le mécanisme, je vous renvoie au cours-part1. Les PIC18Fxx8 disposent de 3 registres d’accès indirect, contre 1 pour les 16F. De plus, comme je l’ai indiqué, ces registres pointent vers l’adresse complète de la variable, et donc ne sont pas limités à 256 emplacements, comme sur les 16F. Pour qui réfléchit un peu, cela veut dire que ces registres contiennent plus de 8 bits, la mémoire du PIC® étant organisée sur des registres de 8 bits, chaque registre d’indirection contient donc en réalité deux registres. Les registres d’indirection sont dénommés FSR0, FSR1, et FSR2. Chacun de ces noms « générique » représente en fait deux registres physiques « FSRxL et FSRxH » contenant poids faible et poids fort du registre générique. Imaginons que nous voulions faire pointer FSR0 sur la variable1, dans la banque 3. Vu que le registre FSR0 est constitué de 2 registres FSR0L (poids faible) et FSR0H (poids fort), on pourrait se dire que la méthode est la suivante : movlw LOW(variable1) ; charger poids faible de l’adresse de variable1 movwf FSR0L ; dans pointeur poids faible movlw HIGH(variable1) ; charger poids fort (donc la banque) movwf FSR0H ; dans pointeur poids fort. Cette méthode semble logique, mais peu pratique, et une fois de plus, Microchip® vient à notre secours avec une nouvelle instruction spéciale, « lfsr » (load FSR). lfsr FSR0,variable1 ; faire pointer FSR0 sur la variable1 Et voilà, tout s’effectue en une seule ligne. Notez que vous ne précisez plus FSR0L ou H, mais uniquement le nom générique. Cerise sur le gâteau, l’initialisation de FSR ne nécessite pas non plus la modification de W via une instruction movlw, comme sur les 16F. Bien évidemment, cette instruction est codée sur 32 bits (2 mots). De nouveau, beaucoup de simplifications par rapport aux 16F. Les spécialistes savent bien que plus un microcontrôleur est complexe à étudier, plus il est simple à utiliser. De nouveau, vous en avez la preuve.

23

Page 24: Part5_r1

Les PIC® disposent d’une méthode particulière pour faire de l’adressage indirect. Au lieu d’utiliser une syntaxe spéciale, ils définissent un registre virtuel, qui sera interprété par le PIC® comme étant un adressage indirect. Le registre est INDFx, avec « x » représentant un chiffre de 0 à 2, en rapport avec FSR0 à 2. Un exemple de syntaxe est le suivant : lfsr FSR1,variable1 ; pointer sur variable1 movf INDF1,w ; charger la variable1 dans W Ceci constitue l’adressage indirect simple. Mais les PIC18F disposent d’autres possibilités d’adressage indirect. Les différentes possibilités sont : - L’adressage indirect simple - L’adressage indirect post-décrémenté - L’adressage indirect post-incrémenté - L’adressage indirect pré-incrémenté - L’adressage indirect avec offset

Ceci vous ouvre toute une série de nouvelles possibilités, que je vais décrire. Sachez que le PIC® est un processeur orthogonal, c’est à dire que tous les modes d’adressage s’appliquent à toutes les instructions et à tous les registres. C’est très pratique et très puissant.

La méthode utilisée par Microchip® pour gérer ses modes d’adressage et ses instructions

est très puissante, mais peu commerciale. En effet, lorsqu’on compare avec d’autres familles, on dénombre beaucoup plus d’instructions et de modes d’adressage dans ces autres familles, mais c’est principalement parce que Microchip® a choisi d’ouvrir le champ d’application de ses instructions en utilisant des registres.

Lorsque vous comparez des processeurs, ne vous fiez pas uniquement aux

caractéristiques annoncées, regardez plutôt comment le processeur est mis en œuvre. Ceci évitera d’âpres discussions stériles sur les forums.

Ceci étant dit, nous venons de voir l’adressage indirect simple, voyons les autres modes

d’adressage indirect 3.6 L’adressage indirect post-décrémenté Cet adressage fonctionne exactement comme l’adressage indirect simple, mais le contenu du FSR utilisé est décrémenté après l’instruction. Résultat, après l’opération, FSR pointe vers l’emplacement précédent en RAM. La syntaxe est : POSTDECx, avec « x » numéro du registre FSR de 0 à 2. POSTDECx s’utilise comme un registre fictif, exactement comme pour INDFx. Imaginons deux variables : var1 située à l’adresse 0x10, et var2 située à l’adresse 0x11. Nous pouvons écrire : lfsr FSR2,var2 ; FSR2 pointe sur 0x11

movff POSTDEC2,PORTB ; envoyer le contenu de var2 dans PORTB, FSR2

24

Page 25: Part5_r1

; est ENSUITE décrémenté et pointe sur 0x10 movff INDF2,PORTC ; on envoie le contenu de var1 sur PORTC Vous constatez que l’instruction contenant POSTDEC2 a réalisé deux opérations en une seule instruction : - Elle a transféré la variable pointée par FSR2 - ENSUITE (post), elle a décrémenté FSR, qui pointe alors sur l’adresse précédente (0x10,

soit var1)

Vous voyez que ce genre d’instructions permet de gérer facilement des tableaux, d’autant que vous disposez de 3 registres d’indirection. 3.7 L’adressage indirect post-incrémenté Ici, c’est exactement identique, à part qu’après l’accès, on incrémente le registre FSR concerné au lieu de le décrémenter. La syntaxe est : POSTINCx, avec « x » numéro du registre FSR de 0 à 2. Je ne résiste pas à vous donner un simple exemple de la puissance de ce que nous avons vu jusqu’à présent. Imaginez d’écrire pour un 16F un programme qui copie un certain nombre de variables consécutives d’une banque à l’autre. Vous allez voir que vous aller devoir faire des sauvegardes de FSR, des changements de banque avec IRP, des sauvegardes de l’octet en cours de transfert, etc. Bref, une belle routine en perspective (simplifiées cependant si vous arrivez à utiliser une des astuces données dans les précédents cours). Voyons ce que donnerait un programme qui copierait toute les variables de la banque 1 vers la banque 2 sur un 18F : lfsr FSR0,0x100 ; FSR0 pointe sur le début de la banque 1 lfsr FSR1,0x200 ; FSR1 pointe sur le début de la banque 2 boucle movff POSTINC0,POSTINC1 ; transférer un octet, pointer sur suivants btfss FSR0H,1 ; tester si terminé (FSR0 pointe sur 0x200) bra boucle ; non, octet suivant Simple, pas vrai ? On ne peut rêver plus court. Quelques mots d’explication pour ne pas vous tracasser : Les initialisations se font avec les adresses 0x100 et 0x200. En effet, souvenez-vous que pour les initialisations de pointeurs indirects, le pointeur accède à l’intégralité de la mémoire, et n’est pas limité à une banque de 256. Si vous regardez la figure 4-6 page 46, vous voyez que les adresses relatives à l’intérieur de la banque sont données à gauche du tableau, et varient chaque fois dans l’intervalle 0x00 à 0xFF, alors que les adresses complètes sont données à droite, et varient de 0x00 à 0xFFF.

25

Page 26: Part5_r1

Imaginons que vous vouliez accéder à la variable située à l’emplacement 5 de la banque 2. Si vous utilisez une instruction de type « banked », l’adresse est précisée sur 1 octet. Vous aurez donc, par exemple : movlb 0x02 ; pointer sur la banque 2 movf 0x05,w ; accéder à la variable 5 de la banque 2 Par contre, si vous utilisez une instruction de type direct long, vous aurez : movff 0x205, variable ; copier la variable de l’adresse 205 autre part. En fait, tant que vous utilisez des symboles, l’assembleur fait la distinction pour vous. Mais comme dans notre exemple, nous pointons en début de banque, il nous faut bien préciser l’adresse complète. Si vous avez compris ça, alors, vu qu’un registre FSR est composé en réalité de 2 registres FSRxH et FSRxL, vous comprendrez que la ligne suivante : lfsr FSR0,0x100 ; FSR0 pointe sur le début de la banque 1 a placé en réalité la valeur 0x01 dans FSR0H, et la valeur 0x00 dans FSR0L. Partant de là, le teste de fin de boucle : btfss FSR0H,1 ; tester si terminé se contente de tester si le bit 1 du poids fort est passé à 1, autrement dit, si FSR0 est passé de la valeur 0x100 à la valeur 0x200, ce qui signifie qu’on a atteint la fin de la banque 1. Naturellement, pour détecter la fin d’une autre banque, il faudra jouer sur d’autres bits.

Notez donc que les incrémentations et décrémentations se moquent des limites de banques, vous pouvez donc manipuler des zones de plus de 256 octets sans aucun problème. Notez pour finir l’utilisation systématique du « bra » au lieu du « goto » pour les sauts, chaque fois que c’est possible. 3.8 L’adressage indirect pré-incrémenté Il fonctionne de façon strictement identique aux précédents, si ce n’est que la valeur de FSRx est incrémentée AVANT de réaliser l’opération précisée par l’instruction. La syntaxe est : PREINCx, avec « x » numéro du registre FSR de 0 à 2. Pour mieux comprendre, regardez l’exemple suivant : lfsr FSR0,0x208 ; FSR0 pointe sur la variable 0x208 (banque 2) movf PREINC0,w ; charge la variable 0x209 dans W Notez donc qu’après l’opération, FSR0 pointe sur l’adresse 0x209, comme pour POSTINC0, mais à la différence que l’incrémentation a été faite avant de réaliser le chargement de la variable concernée.

26

Page 27: Part5_r1

Pour information, il n’existe pas de mode indirect pré-décrémenté. 3.9 L’adressage indirect pré-indexé Ce mode d’adressage est appelé « adressage PlusW » par Microchip®. La valeur pointée est celle dont l’adresse est contenue dans le registre FSR concerné, augmentée de la valeur contenue dans W (en complément à 2), et qui constitue un déplacement. La syntaxe est : PLUSWx , avec « x » numéro du registre FSR de 0 à 2 Ceci permet d’exécuter des lectures d’emplacements calculés avec une grande facilité. Si vous regardez cet exemple, vous verrez que sa mise en œuvre est très simple. Imaginons que la variable à l’adresse 0x215 contienne 0x50, et que la variable 0x255 contienne 0x10 : lfsr FSR0,0x205 ; pointer sur l’adresse 0x205 movlw 0x10 ; déplacement = 0x10 movf PLUSW0,w ; W contient maintenant le contenu de l’adresse 0x215, ; soit 0x50 movf PLUSW0,w ; W contient maintenant le contenu de l’adresse 0x255, ; soit 0x10 Ceci permet des opérations complexes. N’oubliez pas non plus qu’une opération de type « movff » ne modifie pas le contenu de « W », ce qui permet d’autres types d’opérations. Cette nouvelle possibilité permet de travailler sur des variables dont l’adresse relative peut être calculée. L’adresse de la variable pointée est donc elle-même une variable. Vous voyez pour conclure que les modes d’adressage se sont nettement enrichis avec cette nouvelle famille.

ATTENTION : le registre W est considéré dans ce cas comme un registre signé. C’est-à-dire que si sa valeur est supérieure à 0x7F, elle sera considérée comme un déplacement négatif. lfsr FSR0,0x205 ; pointer sur l’adresse 0x205 movlw 0x85 ; déplacement = 0x85 = -0x7B movf PLUSW0,w ; W contient maintenant le contenu de l’adresse 0x18A Notez que les modes d’incrémentations et décrémentations automatiques agissent sur l’intégralité des registres 16 bits. Le débordement, par exemple, de FSR1L suite à une incrémentation se traduit par une incrémentation de FSR1H. Ainsi, vous pouvez manipuler des tables d’une taille quelconque. lfsr FSR1,0x1FE ; FSR1H contient 0x01, FSR1L contient 0xFE movf POSTINC1,w ; FSR1H contient 0x01, FSR1L contient 0xFF movf POSTINC1,w ; FSR1H contient 0x02, FSR1L contient 0x00 movf POSTDEC1,w ; FSR1H contient 0x01, FSR1L contient 0xFF

27

Page 28: Part5_r1

Notes :

28

Page 29: Part5_r1

4. Registres particuliers et particularités Nous allons examiner ici quelques registres spécifiques importants pour le fonctionnement des PIC® de cette famille 4.1 Le PC Le PC est le compteur ordinal de notre PIC®. C’est lui qui séquence le déroulement des instructions. A chaque chargement d’une instruction, le PC est incrémenté pour pointer sur l’instruction suivante. Etant donné que nos instructions sont codées sur 16 bits, et que la mémoire est organisée en octets, le PC sera donc incrémenté de 2 lors de chaque chargement d’instructions. En outre, le premier octet de chaque instruction doit impérativement être placé à une adresse paire. On dira que le processeur aligne ses instructions sur les adresses paires. Vous retrouverez cette procédure sur pas mal de processeurs 16 bits, c’est un grand classique, même s’il s’agit ici d’un cas particulier, le processeur étant un 8 bits avec bus d’instructions sur 16 bits. Ecrire ceci, par exemple : ORG 0x01 instruction Est illégal sur un 18F. Si, maintenant, vous êtes un adepte de l’utilisation de la directive « $ », qui précise, je vous le rappelle, l’adresse du début de la ligne actuelle, vous utilisiez pour les 16F une syntaxe du style : nop goto $-1 Dans ce petit bout de code, le « goto » renvoie sur l’instruction « nop » qui se situe un mot de programme (-1) avant la ligne « goto ». Si vous faites ça pour un 18F, ça ne fonctionnera pas. Pourquoi ? Si vous avez trouvé, bravo. En fait, vous devez vous rappeler que les instructions sont alignées sur des octets pairs, et que chaque instruction est composée au moins de 2 octets. Comme l’instruction « nop » est constituée d’un mot de 2 octets, remonter du début de la ligne « goto » au début de la ligne « nop » nécessite de décrémenter PC de 2 unités. La syntaxe correcte sera donc : nop goto $-2 De même, faites attention dans ce cas-ci par exemple

29

Page 30: Part5_r1

Label nop call test bra $- ?

Quelle valeur faut-il placer derrière « $- » pour sauter à l’étiquette label ? Et bien, remonter au début de l’instruction « call » nécessite de remonter de 4 octets, puisqu’un « call » est codé sur 32 bits, plus 2 octets pour l’instruction « nop ». Ceci nous donne donc : Label nop call test bra $- 6 Retenez donc déjà que si vous utilisez « $ », la valeur ajoutée ou soustraite devra toujours être multiple de 2. De plus, vous devrez prendre en compte le fait que certaines instructions comportent 2 mots et non 1, et vous devrez calculer en conséquence (movff, call, goto etc.). Franchement, n’est-ce pas plus simple d’écrire : Label nop call test goto label ou mieux : Label nop call test bra label ? Si vous n’êtes pas convaincu, tant pis, on ne pourra pas dire que je ne vous ai pas prévenu. Et faites attention à cette particularité lorsque vous « portez » des routines du 16F vers le 18F et réciproquement. 4.2 Les registres PCL,PCLATH, et PCLATU Le compteur ordinal, PC, est subdivisé en 3 registres : - PCL : contient les 8 octets de poids faible de l’adresse. Comme les adresses sont toujours

paires, le bit 0 de ce registre vaudra toujours 0. Ce registre est accessible en lecture et en écriture.

- PCH : contient les bits 8 à 15 de l’adresse. Ce registre n’est ni accessible en lecture, ni en

écriture, sauf en lisant PCL

30

Page 31: Part5_r1

- PCU : contient les bits 16 à 21 de l’adresse. Ce registre n’est ni accessible en lecture, ni en écriture, sauf en lisant PCL.

Lorsque vous réalisez des sauts, branchements, retours, etc., vous n’avez pas à vous

préoccuper de ces registres, ils sont gérés de façon transparente. Par contre, si vous réalisez des opérations sur PCL, comme des lectures de table, ou des sauts calculés, alors il vous faudra en tenir compte.

Lors d’une écriture (movwf, addwf etc.) d’une valeur dans le registre PCL, le contenu du

registre PCLATH est transféré dans le registre PCH, et le contenu du registre PCLATU est transféré dans le registre PCU. Autrement dit, comme sur les 16F, vous devez initialiser correctement PCLATH avant toute opération sur PCL.

Vous n’aurez pas à vous préoccuper de PCLATU sur les 18Fxx8. En effet, l’espace

d’adressage de 32Ko permet des adresses codées sur 15 bits. PCLATU et PCU vaudront donc toujours 0.

A l’inverse, une lecture de PCL transfère automatiquement la valeur de PCH dans PCLATH, et celle de PCU dans PCLATHU. C’est donc une façon de pouvoir lire l’adresse en intégralité, et, accessoirement d’initialiser PCLATH.

Attention, j’ai trouvé plusieurs exemples dans la documentation de Microchip® sur les sauts calculés, qui « oublient » la gestion de PCLATH. Si vous appliquez ces exemples, vous courrez un grand risque qu’ils ne fonctionnent pas.

Le déroulement normal du programme, ainsi que les sauts, ne mettent pas à jour

automatiquement PCLATH et PCLATU. Vous devrez soit les mettre à jour manuellement, soit en lisant PCL.

Remarque : il est interdit d’utiliser une opération codée sur 32 bits pour transférer

une valeur dans PCL. Vous ne pouvez donc pas écrire : « movff variable,PCL ». Si vous voulez faire ça, vous devez procéder en deux instructions 16 bits :

movf variable,w movwf PCL

Autre remarque : n’oubliez pas que chaque instruction compte au minimum 2 octets, tenez-en compte dans vos calculs. 4.3 Les sauts calculés A titre d’exemple, je vous donne une routine qui exécute une routine dont le numéro, de 0 à 255, est contenu dans la variable numroutine. main . . . . ; programme principal rcall traiter ; traiter la sous-routine dont le N° se trouve dans ; numroutine . . . . ; suite du programme après traitement traiter

31

Page 32: Part5_r1

movlw HIGH (tablesaut) ; poids fort adresse de la table de saut movwf PCLATH ; dans PCLATH rlcf numroutine,w ; charger numéro routine * 2 btfsc STATUS,C ; tester si numéro > 127 incf PCLATH,f ; oui, incrémenter PCLATH addlw LOW (tablesaut) ; ajouter poids faible adresse table de saut btfsc STATUS,C ; tester si on déborde incf PCLATH,f ; oui, incrémenter PCLATH movwf PCL ; provoquer un saut dans la table qui suit : tablesaut bra routine0 ; sauter à la routine 0 bra routine1 ; sauter à la routine 1 bra routine2 ; sauter à la routine 2 bra routine3 ; sauter à la routine 3 . . . . ; et ainsi de suite pour les 256 routines routine0 . . . . return ; retour au main routine1 . . . . return ; retour au main etc. Les routines peuvent se trouver n’importe où en mémoire programme, chaque bloc se trouvant à un emplacement différent. Si les routines sont trop éloignées de la table de sauts, alors il faudra remplacer les « bra » par des « goto ». Cependant, comme les « goto » sont des instructions à 4 octets, vous devrez multiplier numroutine par 4 au lieu de 2. Nul doute que vous ferez sans problème les modifications nécessaires. La ligne « rlcf » (rotation avec carry, équivalente de rlf sous 16F) n’est pas précédée d’un « bcf STATUS,C ». On fait donc entrer n’importe quelle valeur dans le bit b0, qui sera par la suite écrit dans PCL. Cela n’a aucune importance, le bit 0 de PCL est figé à 0, il est impossible d’y écrire une autre valeur. Attention que si vous remplacez les « bra » par des « goto » qu’il vous faudra alors effacer le carry, qui se retrouverait alors multiplié par deux, et donc pris en compte. Si vous désirez effectuer des sauts, et non des sous-routines, il vous suffit d’appeler la routine « traiter » à l’aide d’un « bra » ou d’un « goto » dans le programme principal, et, bien entendu, de ne pas terminer vos routines par un « return ». Vous pouvez utiliser la même méthode pour la création de tables de « retlw », sachant que vous sacrifiez deux octets (l’instruction) pour un seul octet retourné. Nous verrons que le PIC18F dispose de fonctions spécifiques à cette intention, plus performantes, et plus simples à mettre en œuvre. Une nouvelle fois, méfiez-vous des exemples « simplifiés » donnés dans le datasheet du PIC®, qui ne fonctionnent que dans certains cas particuliers, comme par exemple l’exemple 19-1 de la page 201. Si vous appliquez tel quel, vous risquez de gros problèmes.

32

Page 33: Part5_r1

Si vous avez un tableau comportant moins de 128 instructions, vous pouvez supprimer les deux lignes suivantes : btfsc STATUS,C ; tester si numéro > 127 incf PCLATH,f ; oui, incrémenter PCLATH Notez qu’il existe une méthode puissante pour effectuer des sauts calculés sur les 18Fxxx, c’est l’utilisation de la pile via l’instruction PUSH. Je parlerai de cette méthode dans le chapitre consacré aux manipulations de la pile. 4.4 Le registre « STATUS »

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.

Ce registre comporte 5 bits utiles :

b7 : Non utilisé b6 : Non utilisé b5 : Non utilisé b4 : N b3 : OV b2 : Z b1 : DC b0 : C

Commençons par le bit le moins significatif : C Carry (report)

Ce bit est en fait le 9ème bit d’une opération. Par exemple, si une addition de 2 octets

donne une valeur >255 (0xFF), ce bit sera positionné. Il est également utilisé comme 9ème bit dans les opérations de rotation (décalage) sur 9 bits. Le carry a également un rôle d’emprunt (borrow) dans les soustractions. Dans ce cas, passe à 0 si l’emprunt a été nécessaire.

DC Digit Carry (report de quartet)

Ce bit est utilisé principalement lorsque l’on travaille avec des nombres BCD. Il indique un report du bit 3 vers le bit 4. Pour information, un nombre BCD est un nombre dont chaque quartet représente un chiffre décimal. Le nombre BCD 0x34 par exemple représente le nombre décimal « 34 » (et non 52). Evidemment si vous faites des opérations mathématiques sur des nombres BCD, les résultats seront faux, et le bit DC vous indique l’utilité de faire un correctif. Z Zéro

33

Page 34: Part5_r1

Ce bit est positionné à 1 si le résultat de la dernière opération susceptible de modifier Z engendre un résultat nul.

Limite d’utilisation : Ces flags ne sont positionnés que pour certaines instructions. Dans l’étude du jeu d’instructions, les bits susceptibles d’être modifiés seront indiqués pour chacune des instructions. OV Overflow (débordement). Ce bit est utilisé principalement pour les opérations sur les nombres signés. Le positionnement de ce bit indique qu’un débordement du bit 6 du nombre vers le bit 7 a entraîné de ce fait un changement de signe du résultat. En effet, dans un nombre signé en complément à deux (voir part1), le bit 7 représente le signe. Dans ce cas, l’addition de deux nombres signés positifs pourrait entraîner un résultat comportant le bit 7 positionné à 1, ce qui serait interprété comme un nombre négatif par erreur, si on ne tenait pas compte du bit OV. N Negative (résultat négatif) Indique que le résultat de la dernière opération concernée a produit un résultat qui doit être considéré comme négatif si on travaille en complément à 2. Comme en complément à deux, un nombre est négatif si son bit 7 vaut 1, le bit N indique en fait que le résultat de l’opération concernée a produit un résultat dont le bit 7 vaut 1. Cette particularité peut être utilisée pour tester le bit 7 d’un registre. 4.5 Le cas des instructions 32 bits Nous avons vu que certaines instructions demandaient d’être encodées sur 2 mots de 16 bits, soit 32 bits. Nous avons également vu que ceci nécessitait d’être pris en compte dans certains cas particuliers. Il reste un cas dont nous n’avons pas parlé. Imaginons la portion de code suivante : btfss STATUS,Z ; tester si bit Z = 1 bra routine1 ; non, sauter à la routine 1 bsf variable,4 ; oui, mettre le bit 4 de variable à 1 Cet exemple est purement imaginaire, mais va nous permettre de regarder en détails ce qui se passe. Imaginons que Z = 0. Dans ce cas, nous exécutons l’instruction de test (btfss), le PC a été à ce moment incrémenté de 2, et pointe donc sur le début de l’instruction « bra ». Au cycle suivant, cette instruction sera exécutée. Le temps consommé par l’instruction « btfss » est donc de 1 cycle, ce qui est normal pour une instruction sur un mot sans rupture de séquence.

34

Page 35: Part5_r1

Imaginons que Z = 1. Dans ce cas, nous chargeons l’instruction de test (btfss). A ce moment, PC a été incrémenté de 2, et pointe donc sur l’instruction « bra ». On exécute l’instruction, qui, vu que le test est vrai, va incrémenter de nouveau le PC de 2. On pointe donc sur l’instruction « bsf ». Comme il y a rupture de séquence, le traitement prendra 2 cycles. Tout est conforme à nos habitudes. Imaginons maintenant que la routine1 se trouve à plus de 2048 octets de distance de notre test. Nous allons être contraints d’utiliser un « goto » au lieu d’un « bra ». btfss STATUS,Z ; tester si bit Z = 1 goto routine1 ; non, sauter à la routine 1 bsf variable,4 ; oui, mettre le bit 4 de variable à 1 L’instruction « goto » étant constituée de 2 octets, la mémoire programme est donc remplie de la façon suivante : Instruction btfss Premier mot de l’instruction goto Second mot de l’instruction goto Instruction bsf Vous voyez tout de suite que si Z vaut 1, nous allons sauter tout droit sur le second mot de l’instruction goto, au lieu de sauter à l’instruction bsf. Que va-t-il se passer si on tente d’exécuter une demi-instruction ? Et bien, ce cas a été prévu, vous vous en doutez bien. En fait, toute instruction codée sur 32 bits à son second mot d’instruction dont le poids fort commence toujours par : B’1111’ . Or, le PIC® interprête toute instruction commençant par B’1111’ comme une instruction « nop ». Somme toutes, pour le cas où Z vaut 1, le PIC® va « voir » la séquence suivante en mémoire programme : Instruction btfss Premier mot de l’instruction goto Nop (reste de l’instruction goto, non interprétable) Instruction bsf Autrement dit, votre PIC® va sauter à cet octet particulier, « exécuter » le « nop », puis continuer et exécuter l’instruction « bsf ». L’instruction « btfss » avec saut a donc bien duré 2 cycles, mais l’exécution du « nop » supplémentaire en a duré un également. Au niveau de votre programme, tout se passe donc comme si le « btfss » avait nécessité 3 cycles. C’est pourquoi on vous dit dans le datasheet que les sauts conditionnels suivis d’une instruction 32 bits durent 3 cycles en cas d’exécution. Ce n’est pas tout à fait vrai, mais maintenant vous savez pourquoi c’est vu comme ça.

35

Page 36: Part5_r1

Prenez donc garde au fait que si une instruction conditionnelle de type « passer si condition » est suivie par une instruction de 2 mots, l’exécution du saut nécessitera un cycle supplémentaire. Ce peut être important si vous devez calculer des temps d’exécutions précis. Notez donc qu’il y a deux façons de coder un « nop » dans la mémoire programme : B’0000 0000 0000 0000’ correspond à l’instruction nop officielle. B’1111 xxxx xxxx xxxx’ correspond au second mot d’une instruction 32 bits Autrement dit, si vous tentez d’exécuter le second mot d’une instruction 32 bits, l’opération réalisée sera un « nop ».

Mais notez également que si votre programme s’égare dans une zone non programmée de votre PIC® (ne contenant que des 0xFFFF), il n’y exécutera que des nop (au contraire des 16F qui y voyaient des addlw). 4.6 La procédure « read/modify/write » Il importe de savoir que toute opération sur un bit particulier est traitée en plusieurs séquences dans le PIC®. Ceci peut avoir des répercussions importantes sur le fonctionnement d’un programme, principalement si vous travaillez avec les PORTs d’entrées/sorties. La séquence est la suivante (en interne, je le rappelle) : - Le PIC® lit l’intégralité du registre précisé - Il modifie le bit concerné - Il réécrit l’intégralité du registre précisé

Puisqu’on procède par une lecture suivie d’une modification et enfin d’une écriture, le cycle s’appelle dans les datasheets : cycle read/modify/write.

Vous allez me dire : « oui, mais, puisque c’est exécuté en interne, nous on voit que le bit a été modifié, et c’est suffisant ». Et bien, détrompez-vous ! Imaginons le programme suivant, on imagine que le niveau sur chaque RB0 est amené au +5V par une résistance : movlw B’11111110’ ; préparer valeur movwf TRISB ; RB0 en sortie, les autres en entrée (exemple) bcf PORTB,7 ; préparer RB7 à 0 bcf TRISB,7 ; passer RB7 en sortie. Que constaterez-vous lors de l’exécution de ce programme ? En réalité, en écrivant dans PORTB,7, vous avez écrit dans le buffer du port. Or, comme ce port est en entrée, rien ne se répercute au niveau des pins. Par contre, une fois que vous allez passer la pin en sortie (TRISB,7), le niveau placé dans le buffer va être transféré vers la sortie, et donc la pin RB7 va passer à l’état bas.

36

Page 37: Part5_r1

Tout est donc logique, et sans problème. C’est une technique qu’on utilise par exemple lorsqu’on veut piloter par software des lignes qui ne peuvent pas êtres forcées au niveau haut (gestion I²C par exemple). L’exemple n’a donc rien d’utopique. Modifions donc de façon qui semble anodine notre programme, en ajoutant une ligne : movlw B’11111110’ ; préparer valeur movwf TRISB ; RB0 en sortie, les autres en entrée bcf PORTB,7 ; préparer RB7 à 0 bcf PORTB,0 ; passer RB0 à 0. bcf TRISB,7 ; passer RB7 en sortie. Qu’avons-nous sur nos sorties ? Réfléchissez avant de poursuivre : Ceux qui ont dit : RB7 et RB0 = 0 ont tout faux. Pour ceux qui ont trouvé le piège, bravo ! Si vous appliquez la façon dont le PIC® exécute les opérations sur les bits, le PIC® a en réalité exécuté en interne la séquence suivante : movlw B’11111110’ ; préparer valeur movwf TRISB ; RB0 en sortie, les autres en entrée bcf PORTB,7 ; préparer RB7 à 0 ; lecture du PORTB = B’11111111’(par exemple) ; modification du bit7 du PORTB, bit 7 : B’01111111’ ; écriture du PORTB : B’01111111’ bcf PORTB,0 ; passer RB0 à 0. ; lecture du PORTB = B’11111111’ ( ! ! ! ! !) ; modification du bit 0 = B’11111110’ ; écriture du PORTB = B’11111110’ bcf TRISB,7 ; passer RB7 en sortie. ; le buffer est placé sur la sortie ; PORTB : B’11111110’ Et bien, vous constatez que cette fois, RB7 ne vaut pas 0, mais bel et bien 1. Ceci a été provoqué par la lecture du PORTB provoquée par l’ajout de la ligne qui modifie RB0. Ceci a provoqué la copie de l’état de RB7 (qui est toujours en entrée, donc vaut 1 dans notre exemple) dans le buffer, ce qui a écrasé le niveau « 0 » que nous y avions précédemment placé. Faites très attention à ceci, car dans le cas contraire, vous risquez bien de ne pas comprendre ce qui vous arrive. Ce cas intervient lorsque vous inscrivez des valeurs dans une pin de port placée en sortie, ou, également, lorsque vous utilisez RA4, qui est en collecteur ouvert. Pour RA4, en effet, si vous écrivez « 1 », vous risquez que le niveau en sortie soit « 0 », suivant l’électronique qui se trouve derrière le PIC®. Imaginons que vous placiez un niveau « 1 » sur RA4. Imaginons que cette ligne soit utilisée, par exemple, pour gérer une ligne I²C de façon software. Une résistance de rappel au +5V est présente sur la ligne. Imaginons enfin que plusieurs périphériques utilisent la ligne I²C en question, ce qui est banal :

37

Page 38: Part5_r1

Et un moment donné dans votre programme, vous décidez de piloter une autre ligne du port, RA0 par exemple, pour allumer une led. L’allumage de cette led n’a donc strictement rien à voir avec la gestion de la ligne I²C. Vous avez dans votre source des instructions du type: bsf PORTA,0 ; allumer la LED . . . . . . . . bcf PORTA,0 ; éteindre la LED . . . . . . . . bsf PORTA,0 ; allumer la LED . . . . . . . . bcf PORTA,0 ; éteindre la LED Imaginons ce qui va se passer au niveau de la pin RA4, en fonction d’éléments extérieurs imaginaires sur la ligne concernée :

; Ici, aucun périphérique n’accède à la ligne I²C ;(elle est au repos –> forcée à +5V par la résistance ; de rappel)

bsf PORTA,0 ; allumer la LED . . . . . . . . bcf PORTA,0 ; éteindre la LED . . . .

; Ici, un périphérique extérieur a imposé un niveau ; bas sur la ligne

. . . . bsf PORTA,0 ; allumer la LED . . . .

; Et ici, ce périphérique a libéré la ligne . . . . . . . . bcf PORTA,0 ; éteindre la LED Voyons ce qui se passe au niveau de RA4 dans notre PIC®.

; Ici, aucun périphérique n’accède à la ligne I²C ;(elle est au repos –> forcée à +5V par la résistance ; de rappel)

bsf PORTA,0 ; allumer la LED . . . . ; on lit PORTA, on modifie RA0,

; RA0 = 1, RA4 = 1 . . . . bcf PORTA,0 ; éteindre la LED ; on lit PORTA, on modifie RA0 ; RA0 = 0, RA4 = 1 . . . .

; Ici, un périphérique extérieur a imposé un niveau ; bas sur la ligne

. . . . bsf PORTA,0 ; allumer la LED ; on lit PORTA, on modifie RA0 ; RA0 = 1, RA4 = 0 (car RA4 est lu comme 0 puisque

; forcé par le périphérique extérieur)

38

Page 39: Part5_r1

. . . . ; Et ici, ce périphérique a libéré la ligne . . . .

. . . . bcf PORTA,0 ; éteindre la LED ; on lit PORTA, on modifie RA0 ; RA0 = 0, RA4 = 0 Vous constaterez que le fait d’avoir modifié RA0 a bloqué la ligne RA4 à 0 définitivement. En effet, lors de la lecture/modification/écriture surlignée, la ligne RA4 a été lue comme étant à 0 (imposée par un périphérique extérieur) bien que dans votre programme vous ayez placé RA4 à 1 et ne l’avez plus modifié. Une fois que le périphérique libérera la ligne, RA4 restera bloqué à 0, car le niveau 0 présent maintenant dans le PORTA de votre PIC® bloque cette ligne à ce niveau. Donc, simplement en ajoutant la gestion d’une led, vous avez bloqué une application qui tournait auparavant. Pensez à tout ceci si un comportement sur une sortie vous semble curieux. Mettez-vous à la place du PIC®, et scindez vos opérations sur les bits en 3 opérations : lecture/modification/écriture. Comprenez donc bien que toute modification d’un bit sur un port entraîne une opération sur tous les bits de ce port. Mais de nouveau, et contrairement aux 16F, une solution pour résoudre ce problème est maintenant disponible : 4.7 Les registres LATx Pour contrer ce qui précède, il faudrait pouvoir accéder dans ces cas particuliers, non pas au contenu du PORT, mais au contenu qu’on a placé dans le buffer du PORT. Exprimé autrement, il faudrait, au lieu de : - Lire le port - Modifier la pin concernée - Ecrire le port

Pouvoir réaliser : - Lire la valeur qu’on a soi-même écrite dans le port - Modifier la valeur de la pin concernée - Réécrire le résultat

Bref, il faudrait pouvoir accéder non pas au port, mais au buffer de sortie du port. Avec les registres LATx, c’est désormais possible pour les 18F. Reprenons le cas suivant :

setf TRISB ; PORTB en entrée bcf PORTB,0 ; préparer RB0 à 0

39

Page 40: Part5_r1

bcf LATB,1 ; préparer RB1 à 0 clrf TTRISB ; PORTB en sortie Si vous testez ce code, vous constaterez qu’il n’induit pas la même anomalie que dans le chapitre précédent. En effet, un bcf sur PORTB a les conséquences suivantes : - On lit le PORTB - On modifie le bit concerné - On réécrit le tout sur le PORTB

Or, en écrivant dans le PORTB, on écrit en réalité dans le latch (buffer de sortie) du PORT, et non sur le port directement, puisque seules les pins en sortie sont reliées au buffer. Par contre, on lit toutes les pins placées en entrée.

Dit autrement :

- On lit l’état présent sur les pins du PORTB du PIC® - On modifie le bit concerné - On réécrit dans le buffer de sortie du PORTB, qui n’affectera que les pins configurées en

sortie

Par contre, un bcf sur LATB aura les conséquences suivantes : - On lit le buffer de sortie du PORTB (LATB) - On modifie le bit concerné - On réécrit dans le buffer de sortie du PORTB.

Moralité, la séquence « read/modify/write » ne sera pas affectée par l’électronique extérieure, ni pas le niveau réel des pins en entrée.

C’est une façon très simple de se prémunir des conséquences curieuses dans le genre de

circonstances spéciales décrites plus haut. Une fois de plus, un simple accès à un registre supplémentaire ouvre la porte à de nouvelles perspectives. Si vous utilisez des pins qui sont durent le fonctionnement de votre programme, placées successivement en entréeset en sortie, pensez à utiliser les opérations sur ce port via son LATx.

Ne tombez pas dans le travers inverse, en n’utilisant plus que des LATx au lieu de

PORTx, car la lecture d’une pin en entrée vous impose l’utilisation de PORTx. 4.8 Manipulations de la pile Comme dans la famille 16F, le 18Fxx8 dispose d’une pile, destinée principalement à mémoriser l’adresse de retour d’une sous-routine ou d’une interruption. Cependant, la pile passe maintenant à 31 emplacements, ce qui permet une plus grande souplesse dans la gestion des sous-routines.

40

Page 41: Part5_r1

Accompagnant cette augmentation de capacité, il est maintenant possible de manipuler directement la pile, dans certaines limites cependant. Malheureusement, on est encore loin des grandes capacités et des possibilités nécessaires pour réaliser des compilateurs C très efficaces (gros consommateurs de piles). Mais l’étude complète du 18F permet à un œil averti de constater que les modifications de la pile et de certaines instructions de test permettent plus aisément de réaliser des compilateurs de moyenne qualité. C’est pour ça que vous trouverez plus de compilateurs pour 18F que pour 16F, mais c’est un autre débat. Disons pour simplifier sur ce sujet que les PIC16F ne sont pas du tout adaptés à l’utilisation du C, les 18F un peu mieux, et, à partir des PIC® 16 bits, beaucoup plus. Ceci pour vous dire au passage que je continue toujours de programmer mes 18F en langage d’assemblage, et ce d’autant plus que si les nouvelles fonctionnalités ont facilité la tâche des concepteurs de compilateurs, elle facilite également la tâche des programmeurs en langage d’assemblage. En effet, programmer en langage d’assemblage sur des 18F est beaucoup plus agréable que sur des 16F. Mais revenons à notre pile. Tout d’abord, il est possible de modifier le sommet de la pile, c’est-à-dire le contenu du PC sauvegardé en dernier lieu, et donc qui contient en principe l’adresse de prochain retour. Le sommet de la pile est dénommé « TOS » pour (Top Of Stack), et, bien entendu, comme le PIC® peut adresser un maximum théorique de 2Mo, le PC (compteur ordinal) qui sera sauvé occupera 21 bits. Le TOS contient donc également 21 bits. Le TOS est ainsi accessible au travers de 3 registres : - TOSU : contient les bits 20 à 16 du TOS, ses bits 7 à 5 sont inutilisés - TOSH : contient les bits 15 à 8 du TOS - TOSL : contient les bits 7 à 0 du TOS

Lorsque nous avons des registres codant des valeurs sur plus de 8 bits, nous retrouverons souvent cette terminologie :

U : représente « Upper », soit l’octet qui contient les bits de poids les plus forts (16 à 23) H : représente « High », soit l’octet qui contient les bits de poids fort (8 à 15) L : représente « Lower », soit l’octet qui contient les bits de poids faible (7 à 0)

Ainsi, la première chose qu’il vous est possible de faire, c’est de modifier le TOS, et ainsi

de changer l’adresse de retour lors du prochain retour de sous-routine ou d’interruption. Soyez très prudent lorsque vous agissez ainsi, et posez-vous la question de savoir si votre programme est correctement structuré pour devoir recourir à des méthodes aussi peu élégantes (disons même barbares).

Bien entendu, ce qui va surtout vous intéresser, c’est de pouvoir placer une valeur sur la

pile, et de pouvoir l’en retirer. Ceci s’effectue à l’aide de deux instructions, POP et PUSH. PUSH permet tout simplement de copier le contenu du PC sur le TOS. Cette valeur sera

ensuite accessible via TOSU, TOSH, TOSL.

41

Page 42: Part5_r1

Autrement dit, si vous voulez ajouter une valeur sur la pile, vous utilisez PUSH, qui va :

- Incrémenter le pointeur de pile - Placer le contenu du PC sur la pile

Libre à vous ensuite de manipuler cette valeur comme vous l’entendez, et pour l’usage que vous voulez.

Maintenant, n’oubliez pas que dans tous les cas, ce sera cette valeur qui sera utilisée lors

du prochain retour. Autrement dit, si vous ne comptez pas utiliser cette nouvelle valeur dans ce but, n’oubliez surtout pas de la retirer de la pile, à l’aide de l’instruction POP, qui va : - Décrémenter le pointeur de pile.

Vous êtes donc revenu dans la position d’avant l’instruction PUSH. Attention, les programmateurs habituées des langages de haut niveau vont dire : « Super, je peux passer mes paramètres de fonctions sur la pile en utilisant cette technique ». Malheureusement, ça ne marchera pas. Pourquoi ? - Dans votre « main », vous écrivez une instruction PUSH - Vous modifiez TOSU, TOSH, et TOSL pour y placer des paramètres - Vous appelez votre sous-routine (rcall, ou call)

Si vous faites ça, votre sous-routine, lorsqu’elle voudra lire le TOS, lira en fait l’adresse de retour placée par votre appel de sous-routine. Vos paramètres, eux, ils sont dans l’emplacement juste inférieur, qui lui, n’est pas accessible. Si vous voulez y accéder, il faudra dépiler l’adresse de retour, la sauver, lire les paramètres, refaire un POP, et restaurer l’adresse de retour, le tout en interdisant les interruptions. Je ne vois guère l’intérêt, et c’est pourquoi il y a maintenant un registre supplémentaire accessible, le registre STKPTR. Ce registre contient en fait l’emplacement de la pile pointé actuellement dans ses 5 bits de poids faible. Vous pouvez donc incrémenter ou décrémenter STKPTR pour changer l’emplacement pointé, et donc profiter de la pile pour y passer vos paramètres, ce qui est fait sur les langages haut niveau. MAIS il y a une contrepartie. En effet, vous pouvez donc également dans votre routine appelante exécuter une séquence du style : - Incrémenter STKPTR - Placer un paramètre via TOSU - Placer un second paramètre via TOSH - Placer un troisième paramètre via TOSL - Appeler la sous-routine avec call ou rcall - Après le retour, redécrémenter STKPTR

De ce fait, lors de l’appel, la sous-routine a accès à la valeur placée sur le sommet de la pile, qui est l’adresse de retour vers la routine appelante. Pour récupérer les paramètres, elle doit donc procéder comme suit :

42

Page 43: Part5_r1

- décrémenter STKPTR - Lire le premier paramètre via TOSU et le traiter - Lire le second paramètre via TOSH et le traiter - Lire le troisième paramètre via TOSL et le traiter - Incrémenter STKPTR pour repointer sur l’adresse de retour.

Or, si vous faites ça, s’il y a la moindre interruption entre la décrémentation du pointeur et sa remise à la bonne valeur, l’adresse de retour vers la routine appelante sera remplacée par l’adresse de retour de l’interruption, c’est-à-dire par l’adresse suivant celle où l’interruption a eu lieu. Autrement dit, lorsque vous voudrez exécuter le return, vous vous retrouverez plus haut dans la même sous-routine. Et lorsque vous y rencontrerez le return pour la seconde fois, vous croirez que les paramètres que vous avez placé sur la pile représentera votre adresse de retour, d’où crash en perspective.

Moralité, si vous utilisez le passage de paramètres par la pile de cette façon, vous êtes

contraints de couper les interruption dans vos sous-routine avant de pouvoir décrémenter votre STKPTR, et ce, jusqu’à ce que vous ayez rétabli sa bonne valeur.

Il y a une alternative, qui consiste à placer les paramètres au-dessus de l’adresse de retour

en opérant comme suit : - Incrémenter STKPTR de 2 unités - Placer un paramètre via TOSU - Placer un second paramètre via TOSH - Placer un troisième paramètre via TOSL - Décrémenter STKPTR d’une unité - Appeler la sous-routine avec call ou rcall

Dans ce cas, les paramètres se retrouvent au-dessus de la pile et non en dessous, mais ça vous oblige quand même à couper les interruptions pour ne pas écraser les paramètres.

Bref, cette manipulation de pile est une bonne idée, mais malheureusement, son

exploitation souffre du grave défaut de n’avoir pas accès direct à l’avant-dernière valeur de la pile, ce qui résoudrait tout les problèmes. Mais bon, c’est déjà un premier pas.

Il y a encore une dernière possibilité en utilisant un goto ou un bra. Tout d’abord, vous

empilez l’adresse de retour de votre programme, puis les variables passées sur la pile, et ensuite vous sautez à votre sous-routine par un « goto ». La sous-routine dépile les variables sur la pile, exécute son travail, puis revient au programme principal par un return. De cette façon, pas besoin de couper les interruptions. Mais bon, il faut bien avouer que tout ceci est un peu lourd. On sent bien l’esprit qui a animé cette possibilité (surtout pour l’utilisation de compilateurs de langages hauts niveaux), mais l’idée n’a pas complètement abouti.

Notez que si vous voulez passer des paramètres sur la pile, ce qui est particulièrement

utile pour les fonctions réentrantes, il vous suffit de « déclarer » une zone de pile software en RAM, et d’utiliser les adressages indirects avec décrémentation et incrémentation automatiques, et vous avez une pile sans contrainte qui fonctionne parfaitement et de façon simple et rapide. Autrement dit, sans aucun des précédents inconvénients et avec la place qu’on désire.

43

Page 44: Part5_r1

Une utilisation plus intelligente de la pile, est d’utiliser l’instruction PUSH pour effectuer des sauts calculés.

En effet, plutôt que d’effectuer une série de lignes « goto » avec sélection suivant une

opération sur PCL, il est beaucoup plus simple maintenant de procéder de la façon suivante : - Vous créez une entrée sur la pile avec PUSH ou en incrémentant STKPTR - Vous calculez l’adresse du « goto » en fonction de ce que vous désirez - Vous modifiez TOSH et TOSL en fonction de l’adresse du saut - Vous lancez une instruction « return »

Au moment du return, le programme va sauter à l’adresse désignée par celle que vous avez placée sur la pile, donc à l’adresse de votre saut calculé. La pile est décrémentée de 1 et revient donc à son niveau d’origine. sautparpile push ; placer PC sur la pile movlw LOW(adresse) ; adresse basse du saut movwf TOSL ; dans TOSL movlw HIGH(adresse) ; adresse haute du saut movwf TOSH ; dans TOSH (TOSU reste à 0x00 pour les 18Fxx8) return ; « goto » à l’adresse placée sur la pile

Maintenant, il vous faudra veiller à ne pas déborder de la pile, comme d’habitude. Si vous faites déborder la pile, vous aurez tout d’abord les conséquences suivantes : - Si vous empilez trop, la valeur ne sera pas sauvée, et le bit STKFUL du registre STKPTR

sera positionné pour signaler l’erreur. - Si vous dépilez trop, la valeur dépilée sera « 0 », et donc votre programme reviendra à

l’adresse de départ. Le bit STKUNF du registre STKPTR sera positionné pour signaler l’erreur.

Attention, retour à l’adresse 0x00 ne veut pas dire « reset ». En effet, dans ce cas, aucun

registre n’est affecté, il s’agit d’un simple saut. En plus de ces actions, vous disposez d’une sécurité supplémentaire. En effet, il faut

comprendre qu’une erreur de débordement de pile n’est jamais normale, il s’agit donc soit d’une erreur de programmation, soit d’un plantage du PIC®.

Dans ce dernier cas, il est alors utile de provoquer un reset général, tout comme le

watchdog effectue un reset s’il n’est pas rechargé périodiquement. Ceci peut être mis en place en activant le bit STVREN dans les bits de configuration. Une

fois fait, tout débordement dans un sens ou dans un autre provoquera un reset complet du PIC®.

4.9 Les retours rapides « fast »

44

Page 45: Part5_r1

Lorsque vous traitez une interruption, ou parfois pour certaines sous-routines, vous devez souvent sauvegarder certains registres particuliers, afin de les replacer dans l’état où ils se trouvaient au moment de revenir au programme principal. Les 18F disposent d’un mécanisme particulier qui permet d’effectuer cette sauvegarde automatiquement dans certaines conditions. Les registres concernés par la sauvegarde automatique sont : - STATUS : contient les indicateurs principaux - WREG : est le registre de travail (W) - BSR : est le pointeur de banques pour l’adressage direct banked

Dans le cas de l’utilisation de cette possibilité, les registres sont sauvés et restaurés automatiquement à partir des registres se sauvegarde, dénommés respectivement : STATUSS, WS, et BSRS. Ces registres ne sont accessibles ni en lecture, ni en écriture. Vous ne pouvez les utiliser que par le biais des instructions spécifiques. La première méthode pour utiliser les registres est d’utiliser les instructions d’appel de sous-routine en précisant l’option « FAST » call sous-routine, FAST ; appel avec mémorisation de STATUS,WREG,BSR Si vous faites ça, en plus de sauver l’adresse de retour sur la pile, le PIC® va automatiquement, et sans nécessiter de cycle supplémentaire effectuer une sauvegarde de STATUS, WREG, et BSR. Bien évidemment, et vous l’aurez deviné, il faudra préciser lors du retour que l’on souhaite restaurer ces registres, ce qui se fait également avec le paramètre « FAST » placé en paramètre de l’instruction « RETURN » return FAST ; retour de sous-routine avec restauration des registres Voici un exemple typique : main . . . . call label,FAST ; appel de sous-routine avec sauvegarde . . . . ; ici, on continue avec BSR,W, et STATUS tels qu’ils

; étaient à l’instruction précédent le call label . . . . ; traitement de la sous-routine, les registres sont ; éventuellement modifiés return FAST ; retour au main avec restauration de BSR,W, et STATUS La seconde utilisation concerne les interruptions. Pour ces dernières, qu’elles soient de haute ou de basse priorité, la sauvegarde est évidemment automatique (puisqu’il n’y a pas, par définition, d’instruction d’appel d’interruption). Dit autrement, lors de chaque interruption, la sauvegarde de ces trois registres est toujours effectuée, il ne vous reste qu’à préciser lors du retfie correspondant si vous voulez ou non profiter de la restauration automatique.

45

Page 46: Part5_r1

Par contre, de nouveau, la restauration se fera en ajoutant « FAST » à l’instruction de retour d’interruption : retfie FAST ; retour d’interruption avec restauration des registres. Ce mécanisme est très pratique et très utile, et permet la restauration sans consommer le moindre temps CPU ni le moindre emplacement RAM. interrupt . . . . ; traitement de l’interruption, inutile de sauvegarder ; les 3 registres STATUS,W, et BSR retfie FAST ; retour avec restauration automatique. Cependant, il faut être conscient que puisqu’il n’y a qu’un seul niveau de sauvegarde, toute interruption écrasera les valeurs précédemment mémorisées. Moralité, si les interruptions sont en service dans votre programme, vous ne pouvez plus utiliser cette fonctionnalité dans les instructions « call », car les sauvegardes seraient perdues en cas d’interruption (car dans ce cas la sauvegarde est automatique). De plus, si vous utilisez les interruptions hiérarchisées (haute et basse priorité), vous ne pourrez utiliser le « retfie FAST » que pour l’interruption haute priorité, car une interruption basse priorité pourrait être interrompue ce qui provoquerait l’écrasement des valeurs précédemment sauvées, et avant le retour d’interruption. Il faut savoir en effet que le PIC® dispose d’interruptions basses-priorités qui peuvent être interrompues par d’autres interruptions hautes-priorités. Ce mécanisme d’interruptions sur deux niveaux peut être mis en ou hors service par l’utilisateur. S’agissant d’une nouveauté, j’y consacre évidemment un chapitre. De même, dans un programme sans interruption, une sous-routine appelée avec le paramètre FAST ne pourra pas elle-même appeler une autre sous-routine avec le paramètre FAST. Mais bon, en général, l’intérêt des sauvegardes réside dans les interruptions, et surtout dans les interruptions devant être traitées rapidement (haute priorité). Ce mécanisme devrait donc vous aider à gagner du temps et à faciliter vos sauvegardes.

Attention, cette possibilité de sauvegarde existe pour l’appel en adressage long (call) mais pas pour l’adressage relatif (rcall). Le « rcall adresse,FAST » n’existe donc pas.

De même, et bien évidemment, on ne peut pas utiliser le paramètre « FAST » pour un retlw, ce qui n’aurait du reste aucun sens (restaurer le registre W en voulant en même temps y renvoyer une autre valeur est absurde). Tout ceci nous donne une série de possibilités. Imaginons que nous avons une sous-routine pour laquelle nous souhaitons sauvegarder ces registres. Nous avons trois cas possibles : Si on n’utilise pas les interruptions : - On appelle la sous-routine avec « call FAST » et on en sort avec « return FAST »

46

Page 47: Part5_r1

Si on utilise les interruptions sur un seul niveau (sans priorité) : - On sauve manuellement les registres dans des variables (movff) avant l’appel de la sous-

routine, on les restaure avant le retour (movff) - On ne sauvegarde pas manuellement les registres dans la routine d’interruption, on sort de

l’interruption avec « retfie FAST » Si on utilise les interruptions prioritaires : - On sauve manuellement les registres dans des variables (movff) avant l’appel de la sous-

routine, on les restaure avant le retour (movff) - On sauvegarde manuellement les registres dans la routine d’interruption basse priorité

avant le traitement de l’interruption, on restaure manuellement avant le « retfie » - On ne sauvegarde pas les registres dans la routine d’interruption haute priorité, on sort de

l’interruption avec « retfie FAST »

Comprenez bien que vous disposez d’un seul endroit utilisable pour votre retour rapide, et c’est celui de priorité la plus élevée. C’est du reste logique, c’est l’endroit où vous voudrez en général traiter vos informations le plus rapidement possible.

47

Page 48: Part5_r1

Notes :

48

Page 49: Part5_r1

5. Le jeu d’instructions 5.1 Conventions Le jeu d’instructions de cette famille comporte pas moins de 75 instructions, qu’on pourrait classer par famille. Le résumé de ces instructions est donné dans le datasheet, table 25-2 pages 280 et 281 du datasheet. Je vais voir ces instructions une par une par ordre alphabétique, vous verrez que certaines existaient déjà sur le 16F, d’autres sont nouvelles, et d’autres, enfin, voient leur syntaxe légèrement modifiée. Enfin, et c’est le plus dangereux lors des portages, certaines semblent identiques mais leur influence sur les bits du registre STATUS est différente. Par convention, je représenterai une valeur par une lettre, et le contenu d’un emplacement par son nom entouré de parenthèses. Par exemple : « k » exprime une valeur, tandis que « (var) » Signifie la « valeur contenue à l’adresse var » « d » (destination) exprime l’endroit où le résultat de l’opération sera placé. Ce résultat pourra être soit « f », soit « w ». Ce paramètre est facultatif, dans ce cas, MPASM® considérera que le résultat doit être placé dans le registre « f ». Ne rien mettre équivaut donc à inscrire «,f » « f » indique que la destination est un « file », ce qui, en terme de dénomination Microchip®, signifie simplement que la destination est le registre précisé dans l’instruction. « w » précise que le résultat de l’opération se trouve dans le registre W (= WREG). Dans ce cas, l’opération ne modifie pas le registre éventuellement précisé dans l’instruction. « a » représente le type d’adressage direct utilisé, soit en access bank, soit en banked. Le type d’adressage est toujours facultatif, et donc est représenté entre « [] ». Je vous rappelle que dans un source 18F « normal », on n’utilise jamais ce troisième argument, on le laisse gérer par MPASM®. 5.2 L’instruction « ADDLW » Signification

ADD Litteral to W (ajouter valeur littérale au registre W) Bits de STATUS affectés

N, OV, C, DC, Z Description Effectue une addition sur 8 bits entre le registre W (= WREG) et une valeur littérale précisée dans l’instruction. Le résultat se trouve toujours dans le registre W.

49

Page 50: Part5_r1

Syntaxe addlw k ; (W) + k (W) avec k compris entre 0 et 0xFF Taille et durée 1 mot (16 bits) 1 cycle Exemple W contient 0x10 addlw 0x05 W contient 0x15 5.3 L’instruction «ADDWF » Signification ADD W to File (Ajouter le registre W à la destination) Bits de STATUS affectés N, OV, C, DC, Z Description Effectue une addition sur 8 bits entre le registre désigné (f) et le registre W. Le résultat sera placé soit dans W, soit dans f, selon le paramètre précisé comme destination. Syntaxe addwf f[,d,[a]] ; (W) + (f) (d) Taille et durée 1 mot (16 bits) 1 cycle Notez que cette syntaxe précise que le paramètre « ,d » est facultatif, ainsi que le paramètre « ,a ». Par contre, vous voyez que pour pouvoir préciser le paramètre « ,a », vous êtes contraint également de préciser le paramètre de destination. Vous pouvez donc écrire : addwf variable ; résultat dans f ou

50

Page 51: Part5_r1

addwf variable,f ; résultat dans f ou addwf variable,f,A ; résultat dans f, f est en access bank mais pas addwf variable,A ; interdit, car a est précisé et pas d Exemple W contient 0x50 variable contient 0x12 addwf variable,w ; 0x50 + 0x12, résultat dans W W contient 0x62 var contient 0x12 5.4 L’instruction « ADDWFC » Signification ADD W to File with Carry (Ajout de W au registre avec carry) Bits de STATUS affectés N, OV, C, DC, Z Description Ajoute le contenu de W avec le contenu de F, ajoute également la valeur du carry (0 ou 1) et place le résultat dans la destination. Cette instruction est très pratique lorsqu’on réalise des opérations sur plus de 8 bits. Syntaxe addwfc f[,d[,a]] ; (f) + (c) + (w) (d) Taille et durée 1 mot (16 bits) 1 cycle Exemple On désire réaliser une addition sur 16 bits entre deux variables 16 bits : Soit var1 = 0x9580 et var2 = 0xF290. Le résultat sera placé la variable2, le report sera dans report. L’organisation mémoire est la suivante, déclarée en zone CBLOCK :

51

Page 52: Part5_r1

var1 : 2 ; poids fort / poids faible de la variable1 var2 : 2 ; poids fort / poids faible de la variable2 report : 1 ; bit 16 du résultat clrf report ; effacer le report movf var1+1,w ; charger poids faible de var1 = 0x80 addwf var2+1,f ; ajouter au poids faible de var2, résultat dans var2 ; attention, on utilise addwf et non addwfc ; 0x80 + 0x90 = 0x10 avec carry = 1 movf var1,w ; charger poids fort de var1 = 0x95 addwfc var2,f ; ajouter au poids fort de var2, avec le carry. ; cette fois, on utilise addwfc ; 0x95 + 0xF2 + 1 = 0x88 et carry = 1 clrf WREG ; effacer W addwfc report ; ajouter 0 + C au report, donc ajouter carry en fait ; report = 0 + 0 + 1 = 1 Résultat final, nous avons dans Report : 0x01 Var2 : 0x88 Var2+1 : 0x10 Si vous effectuez l’opération 0x9580 + 0xF290, vous trouvez bien : 0x18810 Vous pouvez maintenant effectuer des opérations sur des nombres plus grands qu’un octet sans devoir faire sans arrêt des tests sur le bit C, comme c’était le cas avec les 16F.

Il arrive que dans une routine on veuille incrémenter une variable en fonction de l’état du bit C. Plutôt que d’écrire : btfsc STATUS,C ; bit C = 1 ? incf variable,f ;oui, incrémenter variable On peut écrire : clrf WREG ; effacer W addwfc variable,f ; ajouter carry à la variable C’est une façon différente de faire la même chose, la seconde méthode peut être pratique dans certaines circonstances (ajout d’une valeur >1 par exemple) 5.5 L’instruction «ANDLW » Signification AND Litteral with W (Et logique d’une valeur littérale avec W) Bits de STATUS affectés N, Z Description

52

Page 53: Part5_r1

Réalise un « et » logique entre le contenu du registre W et le nombre littéral précisé dans l’instruction. L’opération réalise un « et » bit par bit. En général, cette instruction est utilisée pour masquer des bits qu’on ne désire pas utiliser. Dans ce cas, les bits à masquer sont mis à 0 dans le littéral précisé. Syntaxe andlw k ; (w) and k (w) Taille et durée 1 mot (16 bits) 1 cycle Exemple W vaut 0x87 andlw 0x0F ; 0x87 and 0x0F W vaut maintenant 0x07. 5.6 L’instruction «ANDWF » Signification AND W with File (ET logique du registre W avec un autre registre) Bits de STATUS affectés N, Z Description Réalise un « et » logique entre le registre W et le registre F précisé. Syntaxe andwf f[,d[,a]] ; (f) and (w) (d) Exemple W contient 0x56 var contient 0xF0 andwf var,w ; 0x56 and 0xF0 W W contient 0x50 var contient 0xF0

53

Page 54: Part5_r1

5.7 L’instruction «BC » Signification Branch if Carry (brancher si carry est positionné) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si carry est positionné. Dans les 16F, si on désirait sauter plus loin lorsque C était positionné, on devait utiliser un « btfsc STATUS,C » suivi d’un « goto plusloin ». C’est désormais inutile sachant que la majorité des sauts de ce type s’effectuent sur de courtes distances. Syntaxe bc n ; Si C = 1, PC = PC + 2n ; avec n compris entre –128 et +127 Taille et durée 1 mot (16 bits) 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bc label ; sauter à l’étiquette label si C = 1 . . . . ; on continue ici si C = 0 . . . . . . . . label . . . . ; on continue ici si C = 1 Label doit être à une distance de –128 à +127 mots à partir de l’instruction qui suit l’instruction « bc ». Dans le cas contraire, vous aurez un warning de la part de MPASM®. Si c’était le cas, il vous reste à appliquer la méthode habituelle du « btfsc STAUTS,C », suivie par un « bra » ou par un « goto » selon la distance. Attention que la distance n’est pas PC + 2n à partir de la ligne courante, mais à partir de l’instruction suivante : PC pointe toujours sur l’instruction suivante dans le déroulement d’un programme. 5.8 L’instruction «BCF » Signification Bit Clear in File (placer le bit à 0 dans le registre)

54

Page 55: Part5_r1

Bit de STATUS affecté Aucun Description Force le bit indiqué du registre précisé à « 0 » Syntaxe bsf f,b,[a] ; positionne le bit « b » du registre « f » à « 0 » ; b est compris entre 0 et 7 Taille et durée 1 mot 1 cycle Exemple bsf var,2 ; positionne le bit 2 de la variable var à 0. 5.9 L’instruction «BN » Signification Branch if Negative (brancher si résultat négatif) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si le bit N du registre STATUS est positionné Syntaxe bn n ; Si N = 1, (PC) = (PC) + 2n ; n est compris entre –128 et +127 Taille et durée 1 mot (16 bits) 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bn label ; sauter à l’étiquette label si N = 1

55

Page 56: Part5_r1

5.10 L’instruction «BNC » Signification Branch if Not Carry (brancher si carry pas positionné) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si le bit carry n’est pas positionné Syntaxe bnc n ; Si C = 0, (PC) = (PC) + 2n

; n est compris entre –128 et +127 Taille et durée 1 mot (16 bits) 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bnc label ; sauter à l’étiquette label si C = 0 5.11 L’instruction «BNN » Signification Branch if Not Negative (brancher si résultat pas négatif) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si le bit N du registre STATUS n’est pas positionné Syntaxe bnn n ; Si N = 0, (PC) = (PC) + 2n

; n est compris entre –128 et +127 Taille et durée 1 mot (16 bits)

56

Page 57: Part5_r1

2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bnn label ; sauter à l’étiquette label si N = 0 5.12 L’instruction «BNOV » Signification Branch if Not OVerflow (brancher si pas de débordement) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si le bit OV du registre STATUS n’est pas positionné. Syntaxe bnov n ; Si OV = 0, (PC) = (PC) + 2n

; n est compris entre –128 et +127 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bnov label ; sauter à l’étiquette label si OV = 0 5.13 L’instruction «BNZ » Signification Branch if Not Zero (brancher si résultat non nul) Bit de STATUS affecté Aucun Description Branchement (saut relatif court) si le bit Z du registre STATUS n’est pas positionné.

57

Page 58: Part5_r1

Syntaxe bnz n ; Si Z = 0, (PC) = (PC) + 2n

; n est compris entre –128 et +127 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bnz label ; sauter à l’étiquette label si OV = 0 5.14 L’instruction «BRA » Signification BRanch Always (branchement inconditionnel) Bit de STATUS affecté Aucun Description Branchement inconditionnel long Syntaxe bra n ; (PC) = (PC) + 2n, avec n compris entre –1024 et +1023 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bra label ; sauter toujours à l’étiquette label Attention, dans le cas de bra, la distance du saut est codée sur 11 bits, et non sur 8. De ce fait, on peut sauter de –1024 à +1023 mots à partir de l’instruction qui suit le « bra » Le « bra » est donc un « goto » en adressage relatif long. 5.15 L’instruction «BSF » Signification

58

Page 59: Part5_r1

Bit Set in File (placer le bit à 1 dans le registre) Bit de STATUS affecté Aucun Description Force le bit indiqué du registre précisé à « 1 » Syntaxe bsf f,b,[a] ; positionne le bit « b » du registre « f » à « 1 » ; b est compris entre 0 et 7 Taille et durée 1 mot 1 cycle Exemple bsf WREG,2 ; positionne le bit 2 du registre W à 1. Remarquez de nouveau que le registre W est maintenant accessible à toutes les instructions, en précisant « WREG » comme nom de registre. 5.16 L’instruction «BTFSC » Signification Bit Test in File, Skip if Clear (tester le bit du registre, passer s’il vaut 0) Bit de STATUS affecté Aucun Description Vérifie dans le registre précisé le bit dont on précise le numéro. Si le bit vaut 0, on saute l’instruction suivante. Syntaxe btfsc f,b[,a] ; Si le bit b de f = 0, PC = PC+2 ; b est compris entre 0 et 7

59

Page 60: Part5_r1

Taille et durée 1 mot 2 cycles si on saute (voir chapitre sur les instructions 32 bits), 1 cycle si on ne saute pas . Exemple btfsc var,3 ; saute l’instruction suivante si le bit 3 de var = 0 . . . . ; on traite cette instruction si b3 = 1 . . . . ; on saute directement ici si b3 = 0 Attention, lisez attentivement le chapitre consacré aux instructions 32 bits. En effet, vu par l’utilisateur, si l’instruction de test est suivie d’une instruction 32 bits, 1 cycle supplémentaire sera perdu. 5.17 L’instruction «BTFSS » Signification Bit Test in File, Skip if Set (tester le bit du registre, passer s’il vaut 1) Bit de STATUS affecté Aucun Description Vérifie dans le registre précisé le bit dont on précise le numéro. Si le bit vaut 1, on saute l’instruction suivante. Syntaxe btfss f,b[,a] ; Si le bit b de f = 1, (PC) = (PC) + 2 ; b est compris entre 0 et 7 Taille et durée 1 mot 2 cycles si on saute (voir chapitre sur les instructions 32 bits), 1 cycle si on ne saute pas . Exemple btfss var,3 ; saute l’instruction suivante si le bit 3 de var = 1 movff var1,var2 ; on traite cette instruction si b3 = 0 . . . . ; on saute directement ici si b3 = 1 Attention, lisez attentivement le chapitre consacré aux instructions 32 bits. En effet, vu par l’utilisateur, si l’instruction de test est suivie d’une instruction 32 bits, 1 cycle supplémentaire sera perdu. C’est le cas dans cet exemple

60

Page 61: Part5_r1

5.18 L’instruction «BTG » Signification Bit ToGgle in file (inverser le bit du registre) Bit de STATUS affecté Aucun Description Inverse le bit désigné. Syntaxe btg f,b[,a] ; inverse le bit b du registre f.

; b est compris entre 0 et 7 Taille et durée 1 mot 1 cycle Exemple btg var1,3 ; inverse le bit 3 de la variable var1 Si le bit valait « 1 », il vaut maintenant 0, s’il valait « 0 », il vaut maintenant « 1 ». Voici une instruction qui nous manquait cruellement dans la famille des mid-range. 5.19 L’instruction «BOV » Signification Branch if OVerflow (brancher si débordement) Bit de STATUS affecté Aucun Description Branchement si le bit OV du registre STATUS vaut 1. Syntaxe bov n ; Si OV = 1, (PC) = (PC)+ 2n ; n est compris entre –128 et +127

61

Page 62: Part5_r1

Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bov label ; Si OV = 1, sauter à l’étiquette label 5.20 L’instruction «BZ » Signification Branch if Zero (brancher si résultat nul) Bit de STATUS affecté Aucun Description Branchement si le bit Z du registre STATUS vaut 1. Syntaxe bz n ; Si Z = 1, (PC) = (PC)+ 2n ; n est compris entre –128 et +127 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple bz label ; Si Z = 1, sauter à l’étiquette label N’oubliez pas que si le bit Z vaut 1, c’est que le résultat de la précédente opération concernée donnait un résultat nul. 5.21 L’instruction «CALL » Signification CALL (appel) Bit de STATUS affecté Aucun

62

Page 63: Part5_r1

Description Effectue un appel de sous-routine avec un adressage direct long. Syntaxe Call k[,FAST] ; appel de sous-routine à l’adresse k ; k représente n’importe quelle adresse de la zone mémoire

; programme ; le paramètre « ,FAST » précise si on utilise le mode ; de restauration automatique de W,STATUS, et BSR

Taille et durée 2 mots 2 cycles Exemple call label,FAST ; traite la sous-routine à l’adresse label avec retour rapide Le paramètre « ,FAST » permet de générer une sauvegarde automatique des registres W, BSR, et STATUS dans des registres internes au PIC® : WS, BSRS, et STATUSS. Ces registres pourront être restaurés automatiquement lors du retour de la sous-routine. Voyez le chapitre consacré aux retours de type « fast ». 5.22 L’instruction «CLRF Signification CLeaR File (effacer registre) Bit de STATUS affecté Z Description Place tous les bits du registre concerné à 0. N’agit évidemment pas sur les bits en lecture seule. Syntaxe clrf f[,a] ; 0x00 (f) Taille et durée 1 mot 1 cycle Exemple

63

Page 64: Part5_r1

clrf PORTA ; met tous les bits du buffer du PORTA à 0 Le bit Z sera automatiquement positionné après cette instruction 5.23 L’instruction «CLRWDT » Signification CLeaR WatchDog Timer (effacer timer du chien de garde) Bits de RCON affectés TO, PD Description Resette le timer du watchdog, afin d’éviter un reset par débordement. Syntaxe clrwdt ; resette le watchdog Taille et durée 1 mot 1 cycle Exemple clrwdt ; resette le watchdog Après l’exécution, le watchdog et son diviseur (le contenu, pas la valeur) sont remis à 0, et les bits TO et PD du registre RCON sont positionnés. 5.24 L’instruction «COMF » Signification COMplement File (complémenter le registre) Bits de STATUS affectés N, Z Description Effectue l’inversion de tous les bits du registre précisé, ce qui équivaut à effectuer un « complément à 1 ».

64

Page 65: Part5_r1

Syntaxe comf f[,d[,a]] ; effectue le complément de f Taille et durée 1 mot 1 cycle Exemple Si variable = 0x35 comf variable ; complément de variable Variable = 0xCA 5.25 L’instruction «CPFSEQ » Signification ComPare File with w register, Skip if EQual (comparer le registre avec W, sauter s’ils sont égaux) Bit de STATUS affecté Aucun Description Compare la valeur contenue dans W avec la valeur contenue dans le registre précisé. Si les valeurs sont identiques, on passe l’instruction suivante. Syntaxe cpfseq f[,a] ; si (f) = (w), alors (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple movlw 0x05 ; charger valeur de comparaison cpfseq variable ; tester si variable = 0 bra traiterno ; non, traiter différent . . . . ; oui, poursuivre ici

65

Page 66: Part5_r1

Notez qu’aucun bit de STATUS n’est positionné lors de cette comparaison. Cette méthode permet de faire facilement des tests en cascade, sans devoir utiliser la technique des xorlw. Ni le contenu de f, ni celui de W ne sont modifiés par cette opération. 5.26 L’instruction «CPFSGT » Signification ComPare File with w register, Skip if Greater Than (Comparer le registre avec W, sauter s’il est supérieur) Bit de STATUS affecté Aucun Description Compare la valeur contenue dans W avec la valeur contenue dans le registre précisé. La comparaison est faite avec des valeurs non signées (de 0 à 255). Si la valeur contenue dans le registre F est strictement supérieure à celle contenue dans le registre W, alors on saute l’instruction suivante. Syntaxe cpfsgt f[,a] ; si (f) > (w), alors (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple movlw 0x05 ; charger valeur de comparaison cpfsgt variable ; tester si variable > 5 bra traiterno ; non, traiter inférieure ou égale . . . . ; oui, poursuivre ici La comparaison s’effectue sur des nombres non signés. En cas d’égalité le saut n’aura pas lieu. 5.27 L’instruction «CPFSLT » Signification ComPare File with w register, Skip if Less Than (comparer le registre avec W, sauter s’il est inférieur)

66

Page 67: Part5_r1

Bit de STATUS affecté Aucun Description Compare la valeur contenue dans W avec la valeur contenue dans le registre précisé. La comparaison est faite avec des valeurs non signées (de 0 à 255). Si la valeur contenue dans le registre F est strictement inférieure à celle contenue dans le registre W, alors on saute l’instruction suivante. Syntaxe cpfslt f[,a] ; si (f) < (w), alors (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple movlw 0x05 ; charger valeur de comparaison cpfslt variable ; tester si variable < 5 bra traiterno ; non, traiter supérieure ou égale . . . . ; oui, poursuivre ici La comparaison s’effectue sur des nombres non signés. En cas d’égalité le saut n’aura pas lieu. 5.28 L’instruction «DAW » Signification Decimal Adjust in W register (ajustement décimal dans W) Bit de STATUS affecté C Description Ajuste la valeur contenue dans le registre W pour la rendre conforme à un format BCD. Syntaxe daw ; (w) corrigé (w)

67

Page 68: Part5_r1

Taille et durée 1 mot 1 cycle Exemple L’instruction n’est efficace que si vous avez précédemment effectué une addition de deux variables, chacune étant déjà correctement formatée en mode BCD. Lors de cette addition, vous pouvez obtenir une valeur qui n’est plus conforme à la norme BCD, l’instruction DAW résout ce problème. Si la valeur obtenue ne tient pas sur 2 digits BCD, le report se retrouvera dans le carry Imaginons après une addition, le résultat incorrect suivant : W = 0xC5 daw ; effectuer la correction W = 0x35 C = 1 Le résultat sera donc en réalité, au format BCD : 135 5.29 L’instruction «DECF » Signification DECrement File (décrémenter le registre) Bits de STATUS affectés C, DC, N, OV, Z Description Décrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0, sa valeur passe à 0xFF Syntaxe decf f[,f[,a]] ; (f)-1 (d) Taille et durée 1 mot 1 cycle

68

Page 69: Part5_r1

Exemple decf variable,w ; on place le contenu de la variable décrémentée dans W Attention, si vous précisez « ,w » comme destination, le contenu de la variable ne sera évidemment pas modifié. Par contre, w contiendra la valeur de la variable décrémentée de 1. ATTENTION : contrairement aux PIC® de la famille 16F, cette instruction modifie plusieurs flags en plus du flag Z. Soyez prudents sur la récupération d’anciennes routines. 5.30 L’instruction «DECFSZ » Signification DECrement File, Skip if Zero (décrémenter le registre, sauter si le résultat est nul) Bit de STATUS affecté Aucun Description Décrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0, sa valeur passe à 0xFF. Si le résultat de cette décrémentation induit un résultat nul, l’instruction suivante est passée Syntaxe decfsz f[,f[,a]] ; (f)-1 (d). Si (d) = 0 (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple decfsz variable ; décrémenter variable bra nozero ; si résultat > 0, alors on traite . . . . ; suite ici si résultat = 0 Attention, si vous précisez « ,w » comme destination, le contenu de la variable ne sera évidemment pas modifié. Par contre, w contiendra la valeur de la variable décrémentée de 1. Le test fonctionnera toujours cependant correctement. Attention, pour cette instruction, contrairement à l’instruction « decf », aucun bit du registre STATUS n’est modifié.

69

Page 70: Part5_r1

5.31 L’instruction «DCFSNZ » Signification DeCrement File, Skip if Not Zero (décrémenter le registre, sauter si le résultat n’est pas nul) Bit de STATUS affecté Aucun Description Décrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0, sa valeur passe à 0xFF. Si le résultat de cette décrémentation induit un résultat non nul, l’instruction suivante est passée Syntaxe dcfsnz f[,f[,a]] ; (f)-1 (d). Si (d) > 0 (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple dcfsnz variable ; décrémenter variable bra zero ; si résultat = 0, alors on traite . . . . ; suite ici si résultat > 0 Attention, si vous précisez « ,w » comme destination, le contenu de la variable ne sera évidemment pas modifié. Par contre, w contiendra la valeur de la variable décrémentée de 1. Le test fonctionnera toujours cependant correctement 5.32 L’instruction «GOTO » Signification GO TO (aller à) Bit de STATUS affecté Aucun Description Saut inconditionnel long à l’adresse spécifiée

70

Page 71: Part5_r1

Syntaxe goto k ; k peut être n’importe quelle adresse valide de la mémoire ; programme Taille et durée 2 mots 2 cycles Exemple goto label ; sauter à l’étiquette label Attention, pour des raisons d’élégance de programmation et de compacité du code, utilisez de préférence l’instruction « bra » chaque fois que c’est possible. 5.33 L’instruction «INCF » Signification INCrement File (incrémenter le registre) Bits de STATUS affectés C, DC, N, OV, Z Description Incrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0xFF, sa valeur passe à 0x00 Syntaxe incf f[,f[,a]] ; (f)+1 (d) Taille et durée 1 mot 1 cycle ATTENTION : contrairement aux PIC® de la famille 16F, cette instruction modifie plusieurs flags en plus du flag Z. Soyez prudents sur la récupération d’anciennes routines. Exemple incf variable,w ; on place le contenu de la variable incrémentée dans W

71

Page 72: Part5_r1

5.34 L’instruction «INCFSZ » Signification INCrement File, Skip if Zero (incrémenter le registre, sauter si le résultat est nul) Bit de STATUS affecté Aucun Description Incrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0xFF, sa valeur passe à 0x00. Si le résultat de cette décrémentation induit un résultat nul, l’instruction suivante est passée Syntaxe incfsz f[,f[,a]] ; (f)+1 (d). Si (d) = 0 (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple incfsz variable ; incrémenter variable bra nozero ; si résultat > 0, alors on traite . . . . ; suite ici si résultat = 0 5.35 L’instruction «INFSNZ » Signification INcrement File, Skip if Not Zero (incrémenter le registre, sauter si le résultat n’est pas nul) Bit de STATUS affecté Aucun Description Incrémente le registre f d’une unité sur 8 bits non signés. Si le registre valait 0xFF, sa valeur passe à 0x00. Si le résultat de cette décrémentation induit un résultat non nul, l’instruction suivante est passée

72

Page 73: Part5_r1

Syntaxe infsnz f[,f[,a]] ; (f)+1 (d). Si (d) > 0 (PC) = (PC) + 2 Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple infsnz variable ; incrémenter variable bra zero ; si résultat = 0, alors on traite . . . . ; suite ici si résultat > 0 5.36 L’instruction «IORLW » Signification Inclusive OR Litteral with W (OU logique entre la valeur littérale et W) Bits de STATUS affectés N, Z Description Effectue un « ou inclusif » bit à bit entre la valeur précisée et le registre W. Le résultat est placé dans W. Syntaxe iorlw k ; k or (W) (W). K est compris entre 0 et 255 Taille et durée 1 mot 1 cycle Exemple W = 0x37 iorlw 0x0F ; (W) or 0x0F W = 0x3F Cette instruction sert typiquement pour forcer des bits à 1.

73

Page 74: Part5_r1

5.37 L’instruction «IORWF » Signification Inclusive OR W with File (OU logique entre le registre et W) Bits de STATUS affectés N, Z Description Effectue un « ou inclusif » entre le registre W et la valeur contenue dans le fichier précisé Syntaxe iorwf f[,d[,a]] ; (f) or (W) (d) Taille et durée 1 mot 1 cycle Exemple iorwf variable,w ; mettre dans W le résultat de (W) or (variable) 5.38 L’instruction «LFSR » Signification Load adress to FSR register (charger adresse dans registre FSR) Bit de STATUS affecté Aucun Description Charge l’adresse de la variable précisée dans le registre FSR concerné Syntaxe lfsr f,k ; f est compris entre 0 et 2 et représente le numéro du

; registre FSR concerné ; k représente l’adresse de la variable, dans toute la ; zone RAM possible

74

Page 75: Part5_r1

Taille et durée 2 mots 2 cycles Exemple lfsr FSR0,variable ; place l’adresse de la variable dans FSR0 N ‘oubliez pas qu’un registre FSR est constitué en réalité de deux registres de 8 bits. Cette instruction effectue donc l’initialisation de deux registres en une seule instruction. 5.39 L’instruction «MOVF » Signification MOVe File to destination (copier le registre dans la destination) Bits de STATUS affectés N, Z Description Copie la valeur contenue dans le registre précisé vers la destination précisée. Syntaxe movf f[,d[,a]] ; (f) (d) Taille et durée 1 mot 1 cycle Exemple movf variable,w ; charger (variable) dans W Notez que l’instruction « movf variable,f » copie le contenu de variable dans variable, ce qui semble n’avoir aucun intérêt. Ce serait le cas si cette instruction ne positionnait pas en même temps le bit Z, ce qui peut être utile pour un test éventuel, quoi que moins nécessaire que pour la famille 16F. Il est impératif ici de bien faire la distinction entre

movlw k ; charge la valeur k dans le registre W et

movf f,w ; charge le contenu de la variable f dans le registre W

75

Page 76: Part5_r1

Dans le premier cas, c’est la valeur qui est chargée dans w, dans le second c’est le contenu de l’emplacement spécifié. 5.40 L’instruction «MOVFF » Signification MOVe File to File (copier le registre dans un autre registre) Bit de STATUS affecté Aucun Description Copie un registre dans un autre registre, à travers toute la mémoire RAM, et sans restriction de banque Syntaxe movff f1, f2 ; (f1) (f2) ; f1 et f2 sont compris entre 0 et 4095 Taille et durée 2 mots 2 cycles Exemple movff variable,PORTB ; envoyer variable sur le PORTB Il y a quelques restrictions à l’utilisation de cette puissance instruction : - Vous ne pouvez pas l’utiliser pour modifier les paramètres d’interruption si les

interruptions sont en service - Vous ne pouvez pas l’utiliser pour modifier PCL, TOSU, TOSH, ni TOSL

Notez que cette instruction ne modifie aucun flag ce qui la rend précieuse pour sauvegarder les registres lors des interruptions (terminés les doubles swaps du 16F).

ASTUCE : cette instruction appliquée au registre W permet à celui-ci d’être chargé

ou sauvé dans n’importe quelle variable de n’importe quelle banque.

Par exemple imaginons « variable » située dans la banque 3 : movf variable,w ; charge variable dans W, BSR doit pointer sur la banque 3 movff variable,WREG ; charge variable dans W indépendemment de BSR movwf variable ; sauver W dans variable, BSR doit pointer sur la banque 3 movff WREG,variable ; sauver W dans variable indépendemment de BSR

76

Page 77: Part5_r1

5.41 L’instruction «MOVLB » Signification MOVe Litteral to Bsr register (copier la valeur dans le registre BSR) Bit de STATUS affecté Aucun Description Copie la valeur contenue dans le registre précisé vers la destination précisée. Syntaxe movlb k ; k (BSR). k est compris entre 0 et 15 Taille et durée 1 mot 1 cycle Exemple movlb 0x2 ; BSR pointe sur la banque 2 5.42 L’instruction «MOVLW » Signification MOVe Litteral to W (charger la valeur littérale dans W) Bit de STATUS affecté Aucun Description Charge la valeur précisée dans le registre W. Syntaxe movlw k ; k (W) Taille et durée 1 mot 1 cycle

77

Page 78: Part5_r1

Exemple movlw 0x25 ; charger la valeur 0x25 dans le registre W. 5.43 L’instruction «MOVWF » Signification MOVe W to File (copier le contenu de W dans un registre) Bit de STATUS affecté Aucun Description Copie la valeur contenue dans le registre W vers le registre précisé. Syntaxe movwf f[,a] ; (W) (f) Taille et durée 1 mot 1 cycle Exemple movwf variable ; sauve le contenu de W dans variable Prenez garde au fait que cette instruction ne modifie pas les bits de STATUS. 5.44 L’instruction «MULLW » Signification MULtiply Litteral with W (multiplication de W par une valeur littérale) Bit de STATUS affecté Aucun Description Multiplie la valeur précisée avec le contenu de W. Le résultat sera placé dans deux registres spécifiques, PRODH et PRODL contenant respectivement le poids fort et le poids faible du résultat de la multiplication.

78

Page 79: Part5_r1

Il s’agit d’une multiplication non signée. L’utilisateur prendra en charge les modifications nécessaires à la réalisation d’une multiplication signée. Le registre W ne sera pas modifié par l’opération. Syntaxe mullw k ; (w) * k (PRODH)(PRODL) ; avec k compris entre 0 et 255 Taille et durée 1 mot 1 cycle Exemple mullw 0x12 ; multiplier W par 0x12, résultat dans PRODH PRODL 5.45 L’instruction «MULWF » Signification MULtiply W with File (multiplier W avec le registre) Bit de STATUS affecté Aucun Description Multiplie le registre W avec le registre F. Le résultat sera placé dans deux registres spécifiques, PRODH et PRODL contenant respectivement le poids fort et le poids faible du résultat de la multiplication. Il s’agit d’une multiplication non signée. L’utilisateur prendra en charge les modifications nécessaires à la réalisation d’une multiplication signée. Aucun autre registre n’est modifié par l’opération Syntaxe mulwf f[,a] ; (w) * (f) (PRODH)(PRODL) Taille et durée 1 mot 1 cycle

79

Page 80: Part5_r1

Exemple mulwf variable ; multiplier W par le contenu de variable

; résultat dans PRODH PRODL 5.46 L’instruction «NEGF » Signification Negate File (Inverser le signe du registre) Bits de STATUS affectés N, OV, C, DC, Z Description Réalise un complément à deux du registre désigné. Le complément à deux consiste à inverser tous les bits de l’octet, puis à ajouter 1. Le nombre représenté en signé devient alors le négatif du nombre précédemment représenté en notation signée. Syntaxe negf f[,a] ; -(f) (f) Taille et durée 1 mot 1 cycle Exemple W = 0x3A negf WREG ; complément W à 2 W = 0xC6 5.47 L’instruction «NOP » Signification No OPeration (pas d’opération) Bit de STATUS affecté Aucun

80

Page 81: Part5_r1

Description N’effectue aucune opération. Syntaxe nop ; pas d’opération Taille et durée 1 mot 1 cycle Exemple nop ; pas d’opération Cette instruction qui semble ne servir à rien, est utilisée la plupart du temps, soit pour perdre un cycle, soit dans certaines conditions spéciales qui sont traitées dans ce document. Vous retrouverez donc de temps en temps cette instruction. 5.48 L’instruction «POP » Signification POP top of stack (dépiler le dessus de la pile) Bit de STATUS affecté Aucun Description Retire les 21 bits sauvegardés au sommet de la pile. En réalité, se contente de décrémenter le pointeur de pile Syntaxe pop ; (pointeur de pile) –1 (pointeur de pile) Taille et durée 1 mot 1 cycle Exemple PILE : 0x001234 (TOS) 0x123456 (sommet – 1)

81

Page 82: Part5_r1

0x165432 (sommet – 2) PC : 0x02468A pop ; retrait d’une valeur de la pile PILE : 0x123456 (TOS) 0x165432 (sommet – 1) PC : 0x2468C 5.49 L’instruction «PUSH » Signification PUSH pc on top of stack (empiler sur le sommet de la pile) Bit de STATUS affecté Aucun Description Incrémente le pointeur de pile, et place le contenu du PC sur le sommet de la pile Syntaxe push ; (pointeur de pile) +1 (pointeur de pile). (PC) (TOS) Taille et durée 1 mot 1 cycle Exemple PILE : 0x001234 (TOS) 0x123456 (sommet – 1) PC : 0x02468A push ; placer le PC sur la pile PILE : 0x02468C 0x001234 (sommet – 1) 0x123456 (sommet – 2)

82

Page 83: Part5_r1

Prenez garde au fait qu’après avoir chargé l’instruction « push », le PC est alors incrémenté de 2. Autrement dit, le PC contient à ce moment la valeur qu’il avait avant le chargement de l’instruction + 2. Vous placez donc sur la pile l’adresse de l’instruction qui suit l’instruction « push ». 5.50 L’instruction «RCALL » Signification Relative CALL to subroutine (appel de sous-routine en adressage relatif) Bit de STATUS affecté Aucun Description Génère un saut relatif de type long vers une sous-routine. L’adresse de retour est mémorisée sur le sommet de la pile. Syntaxe rcall n ; (PC) TOS. (PC) + 2n (PC) ; n est compris entre –1024 et +1023 Taille et durée 1 mot 2 cycles Exemple rcall label ; saute à la sous-routine label . . . . ; après le retour, le déroulement se poursuit ici Utilisez l’instruction « RCALL » en place de l’instruction « CALL » chaque fois que c’est possible. Si vous tentez d’effectuer un « RCALL » à une adresse trop lointaine, vous serez de toutes façons prévenus par un warning de MPASM®. 5.51 L’instruction «RESET » Signification RESET software Bits affectés Voir tableau des registres pour connaître tous les registres affectés par un reset software.

83

Page 84: Part5_r1

Description Effectue un reset du PIC®. Ceci n’est utile réellement qu’en de rares circonstances. Normalement, un reset software peut toujours être évité avec une programmation structurée correcte. Cependant, pour certaines applications, il peut s’avérer pratique de disposer de cette instruction. Syntaxe reset ; reset du PIC et de tous les registres concernés Taille et durée 1 mot 1 cycle Exemple reset ; reset du PIC et de tous les registres concernés Notez que le datasheet de Microchip® indique un seul cycle nécessaire pour effectuer le reset. Il est assez compliqué de vérifier ceci en pratique, je vous donne donc l’information « comme telle ». 5.52 L’instruction «RETFIE » Signification RETurn From Interrupt Execution (retour d’interruption) Bits de INTCON affectés GIE/GIEH, PEIE/GIEL Description Effectue un retour de sous-routine Syntaxe retfie [FAST] ; (TOS) (PC) ; 1 GIE/GIEH ou dans PEIE/GIEL ; si FAST : (BSRS) (BSR), (WS) (W) ; (STATUSS) (STATUS) Taille et durée 1 mot 2 cycles

84

Page 85: Part5_r1

Exemple retfie FAST ; retour avec restauration rapide Voyez le chapitre consacré aux retours rapides et celui sur les interruptions pour plus de détails. 5.53 L’instruction «RETLW » Signification RETurn from subroutine with Litteral in W (retour de sous-routine avec valeur littérale dans W Bit de STATUS affecté Aucun Description Effectue un retour de sous-routine, avec la valeur précisée dans le registre W. Syntaxe retlw k ; (TOS) (PC). k (W) ; k est compris entre 0 et 255 Taille et durée 1 mot 2 cycles Exemple retlw 0x25 ; effectue un retour avec 0x25 dans le registre W.

Permet facilement de retourner une valeur représentant un état de l’exécution de la sous-

routine (code d’erreur par exemple) 5.54 L’instruction «RETURN » Signification RETURN from subroutine (retour de sous-routine) Bit de STATUS affecté Aucun

85

Page 86: Part5_r1

Description Effectue le retour de la sous-routine à l’adresse mémorisée dans le TOS. Syntaxe return [FAST] ; (TOS) (PC)

; 1 GIE/GIEH ou dans PEIE/GIEL ; si FAST : (BSRS) (BSR), (WS) (W) ; (STATUSS) (STATUS) Taille et durée 1 mot 2 cycles Exemple return ; retour de sous-routine Pour plus de détails sur les retours rapides, voyez le chapitre consacré à ce sujet. 5.55 L’instruction «RLCF » Signification Rotate Left through Carry on File (rotation à gauche du registre à travers le carry) Bits de STATUS affectés C, N, Z Description Effectue une rotation à gauche sur 9 bits du registre précisé. Le bit 7 passe dans le carry, l’ancien carry passe dans le bit 0. Syntaxe rlcf f[,d[,a]] ; Tous les bits sont décalés à gauche. (C) b0, b7 (C) Taille et durée 1 mot 1 cycle

86

Page 87: Part5_r1

Exemple var = B’01101101’ C = 1 rlcf var,f ; rotation de var sur 9 bits var = B’11011011’ C = 0 rlcf var,f ; rotation de var sur 9 bits var = B ‘10110110’ C = 1 5.56 L’instruction «RLNCF » Signification Rotate Left, No Carry, on File (rotation à gauche du registre sans utilisation du carry) Bits de STATUS affectés N, Z Description Effectue une rotation à gauche sur 8 bits du registre précisé. L’ancien bit 7 devient le nouveau bit 0 Syntaxe rlncf f[,d[,a]] ; Tous les bits sont décalés à gauche. (b7) b0 Taille et durée 1 mot 1 cycle Exemple var = B’01101101’ rlncf var,f ; rotation de var sur 8 bits var = B’11011010’ rlncf var,f ; rotation de var sur 8 bits var = B ‘10110101’

87

Page 88: Part5_r1

5.57 L’instruction «RRCF » Signification Rotate Right through Carry on File (rotation à droite du registre à travers le carry) Bits de STATUS affectés C, N, Z Description Effectue une rotation à droite sur 9 bits du registre précisé. Le bit 0 passe dans le carry, l’ancien carry passe dans le bit 7. Syntaxe rrcf f[,d[,a]] ; Tous les bits sont décalés à droite. (C) b7, b0 (C) Taille et durée 1 mot 1 cycle Exemple var = B’01101110’ C = 1 rrcf var,f ; rotation de var sur 9 bits var = B’10110111’ C = 0 rrcf var,f ; rotation de var sur 9 bits var = B ‘01011011’ C = 1 5.58 L’instruction «RRNCF » Signification Rotate Right, No Carry, on File (rotation à droite du registre sans utilisation du carry) Bits de STATUS affectés N, Z

88

Page 89: Part5_r1

Description Effectue une rotation à droite sur 8 bits du registre précisé. L’ancien bit 0 devient le nouveau bit 7 Syntaxe rrncf f[,d[,a]] ; Tous les bits sont décalés à droite. (b0) (b7) Taille et durée 1 mot 1 cycle Exemple var = B’01101101’ rrncf var,f ; rotation de var sur 8 bits var = B’10110110’ rrncf var,f ; rotation de var sur 8 bits var = B ’01011011’ 5.59 L’instruction «SETF » Signification SET File (place tous les bits du registre à 1) Bit de STATUS affecté Aucun Description Place tous les bits possibles du registre concerné à 1. N’agit évidemment pas sur les bits en lecture seule. Syntaxe setf f[,a] ; 0xFF (f) Taille et durée 1 mot 1 cycle

89

Page 90: Part5_r1

Exemple setf TRISB ; place toutes les lignes du PORTB en entrée 5.60 L’instruction «SLEEP » Signification enter PIC® in SLEEP mode (passer le PIC® en mode sommeil) Bits de RCON affectés TO, PD Description Place le PIC® en mode sommeil, resette le watchdog et le contenu de son diviseur. Syntaxe sleep ; 0x00 (WDT), 1 TO, 0 PD Taille et durée 1 mot 1 cycle Exemple sleep ; passer en mode sommeil Le PIC® restera en sommeil jusqu’à survenance d’un événement prévu (voir chapitre sur le mode sleep) 5.61 L’instruction «SUBFWB » Signification SUBstract File from W with Borrow (soustraire le registre de W en utilisant l’emprunt) Bits de STATUS affectés N, OV, C, DC, Z Description Effectue une soustraction du registre hors de W, avec emprunt. L’emprunt est en réalité l’inverse du bit carry

90

Page 91: Part5_r1

Syntaxe subfwb f[,d[,a]] ; (w) – (f) - !(C) (d) Taille et durée 1 mot 1 cycle Exemple var = 0x50 W = 0x02 C = 1 subfwb ; 0x02 – 0x50 – 0x00 (complément de 1) var = 0xB2 W = 0x02 C = 0 : un emprunt a du être effectué, le résultat doit être interprété en conséquence Le carry à 0 indique qu’il y aura un report sur la soustraction suivante, si la soustraction porte sur 16 bits ou plus. movlw 0xD4 ; charger D4 subfwb,w ; 0xD4 – 0xB2 – 0x01 (complément de 0) var = 0xB2 W = 0x21 C = 1 : aucun emprunt n’a été nécessaire 5.62 L’instruction «SUBLW » Signification SUBstract W from Litteral (soustraire W de la valeur littérale) Bits de STATUS affectés N, OV, C, DC, Z Description Soustrait (sans utilisation du report) la valeur du registre W de la valeur littérale Syntaxe sublw k ; k – (W) (W) ; avec k compris entre 0 et 255

91

Page 92: Part5_r1

Taille et durée 1 mot 1 cycle Exemple W = 0x5 sublw 0x25 ; effectue 0x25 – 0x05 W = 0x20 C = 1 ATTENTION, vous n’effectuez pas une soustraction de la valeur précisée hors de W, mais exactement le contraire. C’est W qui est soustrait de la valeur. C’est un peu curieux, d’autant que la syntaxe de l’instruction laisserait sous-entendre le contraire (SUBLW = substract L from W ?), ce qui n’est pas le cas. Si le bit C est positionné, alors l’opération n’a pas nécessité d’emprunt. 5.63 L’instruction «SUBWF » Signification SUBstract W from File (soustraire W du registre) Bits de STATUS affectés N, OV, C, DC, Z Description Soustrait la valeur contenue dans W de celle contenue dans le registre sans utiliser le carry Syntaxe subwf f[,d[,a]] ; (f) – (W) (d) Taille et durée 1 mot 1 cycle Exemple var = 0x53 movlw 0x20 ; valeur à soustraire subwf var,f ; 0x53 – 0x20

92

Page 93: Part5_r1

var = 0x33 C = 1 : aucun emprunt nécessaire 5.64 L’instruction «SUBWFB » Signification SUBstract W from File with Borrow (soustraire W du registre en utilisant l’emprunt) Bits de STATUS affectés N, OV, C, DC, Z Description Effectue une soustraction de W hors du registre, avec emprunt. L’emprunt est en réalité l’inverse du bit carry. Cette opération est l’inverse de SUBFWB. Syntaxe subwfb f[,d[,a]] ; (f) – (W) - !(C) (d) Taille et durée 1 mot 1 cycle Exemple var = 0x02 W = 0x50 C = 1 subwfb ; 0x02 – 0x50 – 0x00 (complément de 1) var = 0xB2 W = 0x02 C = 0 : un emprunt a du être effectué, le résultat doit être interprété en conséquence Le carry à 0 indique qu’il y aura un report sur la soustraction suivante, si la soustraction porte sur 16 bits ou plus. movlw 0x54 ; charger D4 subwfb,w ; 0xB2 – 0x54 – 0x01 (complément de 0) var = 0xB2 W = 0x5D C = 1 : aucun emprunt n’a été nécessaire

93

Page 94: Part5_r1

Attention, veillez à ne pas confondre « SUBWFB » avec « SUBFWB ». 5.65 L’instruction «SWAPF » Signification SWAP File (inverser les quartets du registre) Bit de STATUS affecté Aucun Description Effectue une permutation des deux quartets qui composent l’octet contenu dans le registre désigné. Syntaxe swapf f[,a] ; les bits 7 à 4 sont permutés avec les bits 3 à 0 Taille et durée 1 mot 1 cycle Exemple var = 0x35 swapf var,f ; « swapper » var var = 0x53 5.66 L’instruction «TBLRD » Signification TaBLe ReaD (lecture de table) Bit de STATUS affecté Aucun Description Effectue une lecture de l’octet en mémoire flash pointé par le registre TBLPTR et le copie dans le registre TABLAT.

94

Page 95: Part5_r1

Syntaxes possibles tblrd* ; octet pointé par TBLPTR (TABLAT) tblrd*+ ; octet pointé par TBLPTR (TABLAT), (TBLPTR)+1 (TBLPTR) tblrd*- ; octet pointé par TBLPTR (TABLAT), (TBLPTR)-1 (TBLPTR) tblrd+* ; (TBLPTR)+1 (TBLPTR), octet pointé par TBLPTR (TABLAT) Taille et durée 1 mot 2 cycles Exemple Je vous renvoie au chapitre concernant les accès en mémoire programme 5.67 L’instruction «TBLWT » Signification TaBLe WriTe (écriture dans la table) Bit de STATUS affecté Aucun Description Effectue une écriture de l’octet contenu dans TABLAT vers le buffer d’écriture flash, à l’emplacement pointé par les 3 bits de poids faible de TBLPTR. Syntaxes possibles tblwt* ; (TABLAT) -> buffer tblwt*+ ; (TABLAT) -> buffer, (TBLPTR)+1 (TBLPTR) tblwt*- ; (TABLAT) -> buffer (TBLPTR)-1 (TBLPTR) tblwt+* ; (TBLPTR)+1 (TBLPTR), (TABLAT) -> buffer Taille et durée 1 mot 2 cycles Exemple Je vous renvoie au chapitre concernant les accès en mémoire programme 5.68 L’instruction «TSTFSZ »

95

Page 96: Part5_r1

Signification TeST File, Skip if Zero (teste le registre, passe l’instruction suivante si nul) Bit de STATUS affecté Aucun Description Teste le registre précisé. Si ce registre vaut 0, alors l’instruction suivante n’est pas exécutée. Syntaxe tstfsz f[,a] ; if (f) = 0 (PC)+2 (PC) Taille et durée 1 mot 2 cycles si on saute, 1 cycle si on ne saute pas. Exemple tstfsz WREG ; teste si W = 0 bra nozero ; traiter W > 0 . . . . ; traiter W = 0 Je rappelle de temps en temps que toutes les instructions s’appliquent également au registre W. 5.69 L’instruction «XORLW » Signification eXclusive OR Litteral with W (OU exclusif logique entre la valeur littérale et W) Bits de STATUS affectés N, Z Description Effectue un « ou exclusif » bit à bit entre la valeur précisée et le registre W. Le résultat est placé dans W. Syntaxe xorlw k ; k xor (W) (W). K est compris entre 0 et 255

96

Page 97: Part5_r1

Taille et durée 1 mot 1 cycle Exemple W = 0xB5 iorlw 0x0F ; (W) xor 0x0F W = 0xBA Cette instruction sert typiquement pour inverser des bits ou effectuer des comparaisons. Je rappelle que : 0 xor 0 = 0 0 xor 1 = 1 1 xor 0 = 1 1 xor 1 = 0 Donc, effectuer un xor avec un bit à 1 inverse le bit, et réciproquement, un bit de résultat vaut 0 si les deux bits étaient identiques 5.70 L’instruction «XORWF » Signification eXclusive OR W with File (OU exclusif logique entre le registre et W) Bits de STATUS affectés N, Z Description Effectue un « ou exclusif » entre le registre W et la valeur contenue dans le fichier précisé Syntaxe xorwf f[,d[,a]] ; (f) xor (W) (d) Taille et durée 1 mot 1 cycle Exemple

97

Page 98: Part5_r1

xorwf variable,w ; mettre dans W le résultat de (W) xor (variable) Ceci clôture l’analyse de nos 75 instructions. Mais, allez-vous me dire, il n’y en a pas 75 ? En fait si, tout dépend de la façon dont on calcule. Vous en avez ici 69. Si vous vous souvenez que l’instruction tlbrd comporte en réalité 4 instructions différentes au lieu d’une, et que c’est le cas également pour tblwt, ça nous fait bien : 69 + 6 = 75 instructions.

98

Page 99: Part5_r1

6. Lectures et écritures en mémoire programme 6.1 Les nouveaux mécanismes Plutôt que les tableaux de « retlw » peu pratiques, et plutôt que les opérations de lecture en mémoire flash longues et contraignantes comme sur les 16F, nous avons maintenant une méthode puissante pour lire les données directement dans la mémoire flash. Pour l’écriture, cette même méthode va nous simplifier la vie, car elle permet l’écriture de 8 octets simultanément, et raccourci les temps nécessaires. Par contre l’obligation d’écriture par paquet nous compliquera un peu (beaucoup) la vie si nous ne désirons écrire qu’un seul et unique octet.

Microchip® a voulu manifestement privilégier la vitesse d’écriture en mémoire flash plutôt que la facilité, ce qui permet d’optimiser les mécanismes très prisés de bootloader (voir cours-part3). Quoi qu’il en soit, nous effectuerons probablement beaucoup plus de lectures en mémoire flash que d’écritures, ces mécanismes vont donc nous être précieux. Sur un PIC® du genre 18F258, la mémoire flash nous permet des écritures dans des plages de tension descendant jusque 2V (voir les détails réels dans les caractéristiques électriques). Le mémoire flash présente une endurance typique de l’ordre de 100.000 cycles d’écritures (ce qui devrait vous permettre de reprogrammer votre PIC® sans état d’âme). La mémoire flash se programme depuis l’extérieur (programmateur) avec un temps de 4ms par écriture (de 8 octets), alors que les opérations d’écriture en interne (objet de ce chapitre) s’effectuent en 2ms par groupe de 8 octets. L’accent a manifestement été mis sur la rapidité. Vos informations en flash seront mémorisées durant un temps minimum garanti de 40 ans, ce qui devrait vous laisser le temps de profiter de votre application. Notez qu’il s’agit d’un temps minimal garanti, nous verrons (enfin vous verrez) ce qu’il en sera passé ces 40 années.

En effet, si vous vous apercevez que vos données ont duré en réalité 60 ans, il ne faudra pas trop compter sur moi pour reporter l’information dans cet ouvrage. 6.2 Les registres concernés Les accès en mémoire flash nécessitent la mise en œuvre de 4 registres (EECON1, EECON2, TABLAT, et TBLPTR) et mettent en jeu deux instructions particulières (TBLRD et TBLWT) ainsi que leurs dérivés. Notez de plus que les accès en lecture et en écriture aux différentes zones de la mémoire flash sont soumis aux autorisations que vous avez données dans les registres de configuration. Nous reparlerons des configurations dans un autre chapitre, pour le moment, considérons que vous n’avez rien bloqué dans les configurations.

99

Page 100: Part5_r1

6.2.1 Le registre EECON1 Ce registre contrôle les accès en écriture dans la mémoire du PIC®, qu’elle soit la mémoire data (eeprom), programme (flash), ou même l’accès aux registres de configuration (en flash). Comme le datasheet l’indique, il est constitué de 7 bits utiles que je vais détailler. Registre EECON1 b7 b6 b5 b4 b3 b2 b1 b0 EEPGD CFGS N.U. FREE WRERR WREN WR RD EEPGD : Permet de sélectionner si vous voulez accéder à la mémoire eeprom (0) ou à la mémoire flash (1), et ce, uniquement si CFGS vaut 0. Si CFGS vaut 1 ce bit n’a pas d’utilité. CFGS : Détermine si vous voulez accéder au type de mémoire sélectionné par EEPGD (0), ou si vous voulez accéder aux registres de configuration (1). En effet, les accès en mémoire flash vous permettent dorénavant d’écrire vos registres de configuration en cours d’exécution du programme ( !! danger !!). FREE : Si vous accédez en écriture à la mémoire flash ET que vous positionnez ce bit (1), alors l’opération résultera non pas en une écriture mais en une séquence d’effacement. Le bit sera automatiquement remis à 0 une fois l’effacement du bloc terminé. WRERR : S’il vaut 1, indique que la dernière opération d’écriture ne s’est pas correctement terminée (le PIC® a été reseté par watchdog ou par un reset extérieur avant la fin de la durée d’écriture). En cas de terminaison normale ce bit vaudra 0. WREN : Si ce bit vaut 1 vous êtes autorisé à écrire en mémoire flash ou eeprom. S’il vaut 0 les mémoires sont protégées en écriture. C’est vous-même qui positionnez ce bit. WR : Permet de lancer la procédure d’effacement ou d’écriture (1). Il sera remis à 0 automatiquement une fois le cycle terminé. RD : Procède de façon identique pour la lecture. La mise à 1 déclenche la lecture, et vu que la lecture ne prend qu’un cycle, le bit sera directement remis à 0 automatiquement. 6.2.2 Le registre EECON2 Impossible pour moi de vous décrire le registre EECON2, il n’existe pas en tant que registre ordinaire. Ceux qui ne connaissent pas les 16F vont penser que je délire, mais en fait EECON2 est utilisé en interne du PIC® pour déclencher les opérations sur les types de mémoire.

Ne vous préoccupez donc pas de ce registre, les séquences le mettant en œuvre sont dictées par Microchip® et il n’y a rien à comprendre (sauf si vous désirez analyser l’intérieur du PIC®).

100

Page 101: Part5_r1

6.2.3 Le registre « TBLPTR » C’est le pointeur sur l’emplacement concerné en mémoire flash.. Puisqu’il permet de pointer sur l’intégralité de la mémoire programme, ce registre contient 21 bits, répartis dans 3 sous-registres : TBLPTRU : contient les bits 20 à 16 TBLPTRH : contient les bits 15 à 8 TBLPTRL : contient les bits 7 à 0 Etant donné que nos 18Fxx8 actuels n’adressent qu’un maximum de 32Ko de mémoire programme, TBLPTRU vaudra toujours 0, et pourra être ignoré dans tous nos programmes. Je vous l’indique cependant pour le cas où vous utiliseriez un jour un PIC® de cette famille disposant de plus de 64K de mémoire programme.

Les registres TABLAT et TBLPTR ont un fonctionnement lié au sens de la transmission (lecture ou écriture), et dès lors je vais en parler dans les chapitres correspondants suivants. 6.2.4 Le registre TABLAT Il s’agit du registre qui va concerner la valeur liée à l’opération en cours. Son rôle diffère donc à une lecture ou une écriture dans la mémoire programme. Commençons du reste avec la lecture. 6.3 La lecture La lecture d’un ou plusieurs octets en mémoire flash va dorénavant s’effectuer au travers de 2 registres particuliers et d’une instruction, et déclinable en plusieurs modes de fonctionnement. Le principe général est le suivant :

- On initialise un pointeur sur la mémoire flash - On lance une instruction de lecture - On récupère l’octet lu dans un registre spécifique.

Comme nous l’avons vu, la première étape consiste donc à initialiser notre pointeur TBLPTR. Petite remarque en passant, pour la lecture dans la mémoire flash vous n’êtes plus contraint d’utiliser des adresses multiples de 2. Autrement dit, vous pouvez lire 2 octets différents par mot d’instruction.

Vous ne perdez donc pas de place en utilisant cette méthode, au contraire des tableaux de « retlw » qui vous renvoient un octet par instruction (donc par mot écrit en mémoire programme). Voici une routine pour initialiser notre pointeur, en imaginant que l’octet à lire se trouve à l’adresse « add ».

101

Page 102: Part5_r1

movlw UPPER(add) ; charger bits 16 à 21 de l’adresse movwf TBLPTRU ; dans pointeur UPPER movlw HIGH(add) ; charger bits 8 à 15 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW Bien entendu, rien ne vous empêche de vous créer une petite macro pour effectuer cette opération. Pour les 4 sortes de 18Fxx8 disponibles actuellement, la zone mémoire étant limitée à 32Ko, vous pouvez raccourcir cette procédure en : movlw HIGH(add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW Vous allez penser que c’est bien contraignant d’effectuer toutes ces opérations pour lire un octet, surtout si vous avez toute une série d’octets à lire. Rassurez-vous, dans ce cas, Microchip® a prévu des options pour le moins intéressantes. 6.3.1 L’instruction « TBLRD » C’est cette instruction qui va permettre de lire l’octet que nous avons précédemment pointé avec notre registre TBLPTR. Le simple fait d’écrire : tblrd* ; lire un octet en mémoire flash provoque la lecture de l’octet. Notez le symbole « * » situé dans l’instruction. Cette étoile représente l’instant de la lecture, vous allez comprendre. En réalité, l’instruction TBLRD n’est pas unique, elle se répartit en 4 instructions qui exécutent la même fonction de base, mais avec des répercussions différentes. En fait, on pourrait dire qu’il s’agit d’une forme d’adressage dans le même style que celles concernant les registres FSR. Vous avez donc 4 possibilités de lecture : tblrd* ; effectue la lecture de l’octet pointé par TBLPTR tblrd*+ ; effectue la lecture, et ensuite incrémente TBLPTR tblrd*- ; effectue la lecture, et ensuite décrémente TBLPTR tblrd+* ; incrémente TBLPTR, et ensuite effectue la lecture Vous comprenez immédiatement les avantages de ces instructions. En effet, après ou avant avoir lu un octet, le pointeur TBLPTR est mis à jour pour pointer sur l’octet suivant, dans l’ordre où vous décidez de parcourir la table. Comme il s’agit d’une incrémentation ou d’une décrémentation sur 3 octets (TBLPTRx), ceci vous évite bien des instructions.

102

Page 103: Part5_r1

Nous avons initialisé le pointeur, puis effectué une lecture. Il faut maintenant récupérer l’octet lu. Où se trouve-t-il ? Tout simplement dans notre registre spécialisé, TABLAT. 6.3.2 La lecture en pratique Nous avons maintenant tout ce qu’il nous faut pour réaliser une lecture d’une table dans la mémoire flash. Imaginons un exemple simple : nous voulons transférer 30 octets contenus à partir de l’adresse flash « add » dans un tableau RAM qui commence à l’adresse 0x20 de la banque 2. Je vous laisse imaginer la procédure pour un 16F et le nombre de cycles nécessaires. Voici pour les 18F : ; initialisations ; --------------- movlw HIGH(add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW movlw .30 ; 30 octets à lire movwf cmpt ; dans la variable cmpt en access bank lfsr FSR0,0x220 ; pointer sur le premier emplacement 0x20 en banque 2 ; lectures ; -------- loop tblrd*+ ; lire un octet, pointer sur le suivant movff TABLAT,POSTINC0 ; transférer en RAM, pointer sur emplacement suivant decfsz cmpt ; décrémenter compteur d’octets bra loop ; pas dernier, suivant Vous avez vu la taille de la boucle (4 instructions) ? Vous voulez essayer de faire la même chose avec un 16F ? 6.4 Procédure d’effacement Nous avons vu comment lire en mémoire programme, lecture qui s’effectue à n’importe quelle adresse, et qui concerne un seul octet à la fois. Avant d’examiner comment écrire dans cette mémoire, nous allons apprendre comment l’effacer. En effet, malheureusement, il est impossible d’écrire dans la mémoire programme si l’emplacement dans lequel nous écrivons n’a pas été préalablement effacé. En fait, on peut forcer un bit à 0, mais pas le ramener à 1, ce qui explique cette procédure, l’effacement d’un octet en mémoire consistant à ramener tous ses bits à 1. Ceci est en fait lié à la technologie flash utilisée, et non à une limitation des PIC®. De nouveau, c’est une question de choix de technologie et Microchip® a fait le choix de l’optimisation en vitesse. Nous nous trouvons à ce niveau devant une contrainte, à savoir qu’on ne peut pas effacer un octet précis, mais uniquement un bloc constitué de 64 octets.

103

Page 104: Part5_r1

La zone à effacer est pointée de nouveau par notre pointeur flash TBLPTR. Le début de ce bloc a une adresse alignée sur un multiple de 64, et donc, les 6 bits de poids faible de TBLPTR seront ignorés (considérés comme étant à 0). Le bloc effacé sera composé des octets compris entre : Octet à l’adresse : B’000xxxxx xxxxxxxx xx000000’

Et Octet à l’adresse : B’000xxxxx xxxxxxxx xx111111’ Les 3 premiers bits à 0 s’expliquent par le fait, je vous le rappelle, que les adresses en mémoire flash sont constituées de 21 bits. Mieux, pour notre 18F258 disposant de « seulement » 32K de mémoire programme, l’adresse sera comprise entre : Octet à l’adresse : B’00000000 0xxxxxxx xx000000’ Et Octet à l’adresse : B’00000000 0xxxxxxx xx111111’ Les 8 bits de poids fort sont inutilisés (TBLPTRU) et le bit7 de TBLPTRH également. Mais restons dans le cas général. Les bits « x » peuvent avoir n’importe quelle valeur. Autrement dit, vous comprenez déjà la grosse contrainte qui vous attend si vous souhaitez écrire un seul octet en mémoire flash déjà utilisée :

- Vous commencez par sauver les 64 octets qui composent le bloc dans une zone RAM - Vous effacez le bloc complet - Vous modifier en RAM l’octet à écrire - Vous réécrivez les 64 octets en flash Je sais, c’est pénible, mais il n’y a pas moyen d’y couper. L’écriture en mémoire flash

n’est pas du tout facilitée, au contraire de la lecture. De toutes façons, il faut toujours éviter au maximum d’écrire en mémoire programme, car ça diminue la durée de vie du PIC®, et en plus ça le place à l’arrêt durant l’écriture.

En fait, cette procédure augmente considérablement les difficultés d’écriture d’un seul

octet, mais augmente de façon très significative les vitesses d’écriture lorsqu’on écrit des grandes portions de code dans l’ordre logique des adresses. Or, c’est justement ce que fait un bootloader, notre PIC® a donc manifestement été étudié dans le but d’optimiser les performances des bootloaders.

Souvenez-vous que durant l’opération d’écriture elle-même, le PIC® est complètement

stoppé (il n’exécute plus aucune opération ni interruption). Même si on n’avait pas les contraintes que je viens de décrire, écrire un seul octet en mémoire flash prenant 2ms, ça stopperait le PIC® durant l’équivalent de 20.000 instructions. Inutile donc de chercher à gagner du temps dans ce cas, les écritures en mémoire flash (excepté le bootloader) sont à

104

Page 105: Part5_r1

éviter autant que possible… au contraire des lectures qui sont particulièrement bien optimisées (et c’est ce qui est important).

Mais bon, revenons à l’effacement. Après avoir initialisé votre TBLPTR exactement comme pour la routine de lecture, vous devez utiliser une séquence imposée par Microchip® pour provoquer l’ordre d’effacement. Voici une routine complète pour un PIC® de maximum 64Koctets de mémoire programme : ; initialisations ; --------------- movlw HIGH(add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW ; séquence d’effacement ; --------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash bsf EECON1,WREN ; écritures autorisées bsf EECON1,FREE ; indique une procédure d’effacement STOPINT ; interdire les interruptions (imposé) movlw 0x55 ; séquence imposée movwf EECON2 ; il n’y a rien à comprendre, uniquement movlw 0xAA ; recopier tel quel movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture STARTINT ; remettre les interruptions si nécessaire STARTINT est une macro qui relance les interruptions. STOPINT est une macro qui coupe les interruptions. Pour l’instant, considérez que STARTINT exécute simplement un « bsf INTCON,GIE » et STOPINT un « bcf INTCON,GIE ». Vous aurez des explications supplémentaires dans le chapitre consacré aux interruptions. La procédure d’effacement dure à peu près 2ms, ce qui signifie qu’entre l’exécution de la ligne bsf EECON1,WR et la remise en service de vos interruptions, 2ms se seront écoulées, soit l’équivalant de 20.000 cycles d’instruction à 40Mhz. Il est important de s’en rendre compte car à la lecture du code ça semble rapide… ça ne l’est pas. L’instruction « nop » est obligatoire pour une exécution correcte. C’est du au fait que le PIC® peut « oublier » d’exécuter la première instruction qu’il rencontre lorsqu’il se « réveille » de sa léthargie. Oublier un « nop » n’a pas grande importance, pas vrai ? Quant au temps perdu par le nop au vu de l’arrêt durant 20.000 cycles d’instructions, inutile d’en faire allusion. Si les interruptions sont en service durant l’exécution de cette routine, il faudra les couper au plus tard juste avant la séquence imposée et les remettre après la procédure d’effacement. Ces contraintes se retrouveront de la même façon lors des opérations d’écriture.

105

Page 106: Part5_r1

6.5 L’écriture La chose la plus importante à comprendre est que l’écriture en mémoire flash s’effectue en deux étapes complètement séparées et obligatoirement par groupe de 8 octets :

1) L’écriture des 8 octets dans un buffer interne 2) L’écriture du buffer dans la mémoire flash Vous ne pouvez cependant écrire un seul octet en utilisant une astuce. Si vous devez

écrire 1 seul octet à une adresse préalablement effacée (voir chapitre précédent), il vous suffit de compléter par 7 autres octets placés à 0xFF. Souvenez-vous en effet que vous ne pouvez jamais forcer un bit à 1, l’écriture d’un octet à 0xFF ne modifie donc pas les octets déjà éventuellement présents.

La seconde chose à savoir, c’est que l’écriture d’un bloc de 8 octets se fait obligatoirement à partir d’une adresse multiple de 8. La zone flash est donc gérée en blocs de 8 octets, chaque bloc devant être écrit en une seule étape, et chaque écriture concernant obligatoirement l’intégralité d’un bloc. Abordons maintenant les deux étapes de façon distincte. 6.5.1 L’écriture dans le buffer interne Comme nous l’avons dit, avant de procéder à l’écriture en mémoire flash elle-même, nous devons enregistrer nos 8 octets dans un buffer interne spécifique. Ceci nécessite l’utilisation de l’instruction TBLWT et des registres TBLPTRL et TABLAT. Durant cette phase d’écriture, seuls les 3 bits de poids faibles de TBLPTRL sont utilisés. Le nombre, de 0 à 7, indiqué par ces 3 bits , détermine dans quel emplacement du buffer interne sera mémorisé l’octet en cours d’écriture. TBLPTR : xxxxx yyy Avec yyy variant de 0 à 7 pour chaque emplacement du buffer interne. Pour remplir ce petit buffer, nous allons faire appel à une instruction spéciale, TBLWT, qui est à l’écriture ce que TBLRD est à la lecture, à une différence près (et de taille) : TBLRD lit un octet à partir de la mémoire flash TBLWT écrit un octet dans le buffer interne, et NON dans la mémoire flash. 6.5.2 L’instruction « TBLWT » Comme c’était le cas pour TBLRD, l’instruction TBLWT s’utilise avec 4 syntaxes différentes : tblwt* ; effectue l’écriture de l’octet pointé par TBLPTR tblwt*+ ; effectue l’écriture, et ensuite incrémente TBLPTR tblwt*- ; effectue l’écriture, et ensuite décrémente TBLPTR tblwt+* ; incrémente TBLPTR, et ensuite effectue l’écriture

106

Page 107: Part5_r1

Bref, de nouveau vous disposez de mécanismes très intéressants, surtout si vous vous souvenez que vous êtes obligé d’écrire 8 octets à la fois. Cette instruction écrit dans le buffer d’écriture interne, à l’emplacement indiqué par les 3 bits de poids faible de TBLPTRL, le contenu du registre TABLAT. Vous devez donc placer chaque octet à écrire dans le registre TABLAT avant d’exécuter l’instruction TBLWT, et exécuter cette opération 8 fois, donc une fois par octet à écrire. Voici une routine permettant donc d’écrire 8 octets contenus en RAM à partir de l’adresse « oct_ram » dans le buffer d’écriture interne. oct_ram peut se trouver dans n’importe quelle banque (adressage indirect). ; initialisations ; --------------- clrf TBLPTRL ; on commence l’écriture dans le buffer 0 movlw .8 ; 8 octets à écrire movwf cmpt ; dans compteur (en access-bank) lfsr FSR0,oct_ram ; pointer sur adresse des octets en RAM ; écriture dans le buffer ; ----------------------- loop movff POSTINC0,TABLAT ; copier octet dans TABLAT, pointer sur suivant tblwt*+ ; écrire octet dans buffer, pointer sur buffer suivant decfsz cmpt,f ; décrémenter compteur d’octets bra loop ; pas dernier, suivant Cette petite routine a maintenant placé les 8 octets à écrire en flash dans le buffer d’écriture interne. D’après mon courrier, contrairement à ce que certains ont pensé à la première lecture du datasheet, à cet instant RIEN n’a encore été écrit en mémoire flash, les données sont simplement prêtes à être écrites. Corollaire : Jusqu’à présent le PIC® ne n’est pas arrêté, l’écriture en mémoire flash n’étant pas encore démarrée. Il est temps maintenant de passer à la seconde étape, l’écriture en elle-même. 6.5.3 L’écriture du buffer en mémoire flash Cette fois nous allons faire appel à nos registres EECON1, EECON2, ainsi qu’aux registres TBLPTRU (sur les PIC® disposant de plus de 64K de mémoire flash), TBLPTRH, et TBLPTRL. Nous ferons également appel à une séquence de démarrage de l’écriture, séquence imposée par Microchip®, et qui ne demande aucune logique explicative. Nous avons vu que l’écriture se faisait obligatoirement pour tout le buffer de 8 octets, et à un emplacement commençant obligatoirement à un multiple de 8. Notre registre TBLPTR (constitué de nos 3 registres U,H,L) contiendra donc l’adresse d’écriture du bloc. Comme l’adresse doit être multiple de 8, nos 3 bits de poids faible de TBLPTRL devraient être mis à 0. En fait, le PIC® s’assure que c’est le cas, en ignorant purement et simplement la valeur de ces 3 bits.

107

Page 108: Part5_r1

L’adresse d’écriture des 8 octets est donc comprise automatiquement entre :

B’000xxxxx xxxxxxxx xxxxx000’ Et B’000xxxxx xxxxxxxx xxxxx111’ Ou, pour le cas particulier de nos PIC18F258, entre : B’00000000 0xxxxxxx xxxxx000’ Et B’00000000 0xxxxxxx xxxxx111’ Pour écrire le buffer en mémoire flash, vous devez d’abord initialiser EECON1, puis exécuter la séquence imposée par Microchip® sur le pseudo-registre EECON2. Voici une routine type qui écrit le buffer d’écriture interne en mémoire flash à l’emplacement flash_add . Pour un PIC® disposant de plus de 64Ko de mémoire flash, n’oubliez pas d’ajouter l’intialisation de TBLPTRU : ; initialisations ; --------------- movlw HIGH(flash_add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(flash_add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW (bits 0/2 ignorés) ; séquence d’écriture ; -------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 ; il n’y a rien à comprendre movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture (sécurité) STARTINT ; remettre les interruptions si nécessaire Dès cet instant, vos 8 octets sont maintenant écrits en mémoire flash (à condition que la zone ait été préalablement effacée). Attention, la routine a l’air courte, mais entre le bsf EECON1,WR et le nop, le PIC® est mis à l’arrêt durant la procédure d’écriture, soit approximativement 2ms, ce qui correspond à 20.000 cycles d’instructions à 40Mhz. C’est donc une éternité dans l’échelle de temps du PIC®. Pour ceux qui se demandent à quoi peut bien servir la séquence imposée, et bien c’est entre autre pour éviter les écritures accidentelles lors d’une erreur de programmation.

Imaginez donc que la séquence imposée est l’armement de votre fusil, et le bit WR la détente. Les valeurs 0x55 et 0xAA n’ont pas non plus été choisies au hasard, puisque ce sont des suites alternées de 0 et de1, et inversées l’une par rapport à l’autre. Bref, il y a peu de chance pour qu’un plantage de votre programme envoie 0x55 et 0xAA successivement dans

108

Page 109: Part5_r1

votre registre EECON2. WREN est de plus le cran de sécurité de votre arme, vous ne risquez pas ainsi de tirer une balle n’importe où par accident. Comme pour l’effacement, le nop est nécessaire car le PIC® peut ne pas exécuter la première instruction qui suit son redémarrage.

Vous constatez en fait qu’il s’agit strictement la même séquence que pour l’effacement, excepté que nous ne positionnons pas le bit FREE 6.5.4 Elaboration de la routine complète En général dans un programme, vous devrez écrire une routine permettant d’écrire 8 octets présents en RAM dans la mémoire flash. Vous devrez donc combiner les deux méthodes précédentes afin de réaliser une opération d’écriture complète. Reprenons nos données et écrivons une routine destinée à écrire les 8 octets présents en RAM à partir de l’adresse oct_ram dans la mémoire flash à partir de l’adresse flash_add. Il nous suffit donc de mettre nos deux routines bout-à-bout : ; initialisations étape 1 ; ----------------------- clrf TBLPTRL ; on commence l’écriture dans le buffer 0 movlw .8 ; 8 octets à écrire movwf cmpt ; dans compteur (en access-bank) lfsr FSR0,oct_ram ; pointer sur adresse des octets en RAM ; écriture dans le buffer ; ----------------------- loop movff POSTINC0,TABLAT ; copier octet dans TABLAT, pointer sur suivant tblwt*+ ; écrire octet dans buffer, pointer sur buffer suivant decfsz cmpt ; décrémenter compteur d’octets bra loop ; pas dernier, suivant ; initialisations étape 2 ; ------------------------ movlw HIGH(flash_add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(flash_add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW (bits 0/2 ignorés) ; séquence d’écriture ; -------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 ; il n’y a rien à comprendre movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture (sécurité) STARTINT ; remettre les interruptions si nécessaire

109

Page 110: Part5_r1

Dans le but d’améliorer la phase d’initialisation, on peut partir des constations suivantes :

- Pour la première étape, seuls les bits 0/2 de TBLPTRL sont utilisés, et ils sont initialisés à 0 si on écrit dans l’ordre incrémental.

- Pour la seconde étape, ces trois bits sont justement inutilisés

- L’écriture en mémoire flash se fait sur une adresse multiple de 8.

On en déduit que si on initialise TBLPTRL dès le début avec l’octet de poids faible de

l’adresse de destination en mémoire flash, les 3 bits seront d’office à 0 (adresse multiple de 8), et donc TBLPTRL sera à la fois initialisé pour être prêt à l’écriture dans le buffer interne, et prêt pour l’écriture effective en mémoire flash.

Bref, on évite une initialisation, ce qui rend le programme plus clair. La première idée qui

vient à l’esprit (oui, je vous l’accorde, après un peu de réflexion quand même), c’est de mixer les deux initialisations. Essayez de le faire avant de lire ce qui suit : ; initialisations ; --------------- movlw HIGH(flash_add) ; charger bits 8 à 14 de l’adresse movwf TBLPTRH ; dans pointeur HIGH movlw LOW(flash_add) ; charger bits 0 à 7 de l’adresse movwf TBLPTRL ; dans pointeur LOW (et b0/b2 à 0) movlw .8 ; 8 octets à écrire movwf cmpt ; dans compteur lfsr FSR0,oct_ram ; adresse des octets en RAM ; écriture dans le buffer ; ----------------------- loop movff POSTINC0,TABLAT ; copier octet dans registre de transfert tblwt*+ ; écrire l’octet dans le buffer, pointer sur suivant decfsz cmpt ; décrémenter compteur d’octets bra loop ; pas dernier, suivant ; séquence d’écriture ; -------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash

bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture (sécurité) STARTINT ; remettre les interruptions si nécessaire C’est déjà plus élégant, car on a une seule phase d’initialisation en début de routine, ce qui est de plus plus pratique pour réaliser des sous-routines avec différents points d’entrée. MAIS il y a ici un piège. Si vous utilisez cette routine telle quelle, ça ne fonctionnera pas. Ceux qui ont trouvé pourquoi se voient attribuer un 10/10 avec félicitations du jury.

110

Page 111: Part5_r1

Si vous êtes arrivé à écrire votre propre routine sans aide et sans tomber dans le piège,

vous êtes très fort, toutes mes félicitations. Pour les autres, aucune inquiétude, le piège n’est pas si évident que ça à première vue. Explications : Souvenez-vous que je vous ai dit que la séquence d’écriture concernait les adresses pointées par TBLPTR, pour lequel les 3 bits de poids faible sont mis à 0. Or, si vous regardez ce qui se passe, lors du premier passage dans la boucle, TBLPTR pointe sur « flash_add ». A la fin de l’exécution de la première boucle, TBLPTR pointe sur flash_add+1, étant donné l’utilisation de « tblwt*+ ».

Or, si vous continuez de la sorte, au sortir de la dernière boucle, TBLPTR va pointer sur « flash_add+8 ». Autrement dit, si on regarde TBLPTRL en début de boucle :

B’xxxxx 000’ xxxxx représentent le poids faible de l’adresse d’écriture complétée par des « 000 », alors

que les « 000 » représentent le n° du buffer d’écriture interne. En sortie de boucle, vous avez incrémenter 8 fois ce registre, et donc vous avez modifié la

zone « xxxxx » au lieu de vous contenter de ne modifier que les 3 bits de poids faible concernant l’écriture en buffer interne.

Vous pointez donc non pas sur « B’xxxxx 111’ » mais bel et bien sur « B’xxxxx 111’ +

1 » Moralité, au moment de la séquence d’écriture réelle, vous n’écrirez pas à partir de

l’adresse B’00000000 0xxxxxxx xxxxx000’, mais bel et bien à partir de cette adresse +8, et donc vous écrirez dans le bloc de 8 adresses suivant.

Par contre, les 3 bits de poids faible étant ignorés lors de la procédure d’écriture réelle, la

procédure aurait fonctionné si on avait eu en sortie de la première étape une adresse augmentée de 7 au lieu de 8, soit :

B’00000000 0xxxxxxx xxxxx111’ Nous devons donc nous arranger pour sortir de la première boucle avec une adresse qui

n’a été incrémentée que de 7, afin de rester dans le même bloc d’écriture en mémoire flash que celui pointé lors de l’initialisation.

La solution ? Il y en a plusieurs. La plus simple est de décrémenter TBLPTR avant d’effectuer la séquence d’écriture. Ceci s’effectue simplement en utilisant par exemple une instruction : tblrd*-, qui effectue une lecture (dont on se moque), puis décrémente TBLPTR.

C’est la méthode utilisée dans les routines de Microchip®, qui présente cependant l’inconvénient d’exiger une instruction supplémentaire. ; séquence d’écriture ; --------------------

111

Page 112: Part5_r1

tbldr*- ; repointer dans la bonne page bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche Il y a une méthode plus élégante, qui consiste à pointer dès le départ sur l’adresse précédente, et de faire un accès pré-incrémenté au lieu de post-incrémenté. Je vous écris le programme. ; initialisations ; --------------- movlw HIGH(flash_add-1) ; charger bits 8 à 14 de l’adresse - 1 movwf TBLPTRH ; dans pointeur HIGH movlw LOW(flash_add-1) ; charger bits 0 à 7 de l’adresse - 1 movwf TBLPTRL ; dans pointeur LOW movlw .8 ; 8 octets à écrire movwf cmpt ; dans compteur lfsr FSR0,oct_ram ; adresse des octets en RAM ; écriture dans le buffer ; ----------------------- loop movff POSTINC0,TABLAT ; copier octet dans registre de transfert tblwt+* ; pointer sur suivant, puis écrire l’octet dans buffer decfsz cmpt ; décrémenter compteur d’octets bra loop ; pas dernier, suivant ; séquence d’écriture ; -------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash

bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture (sécurité) STARTINT ; remettre les interruptions si nécessaire Maintenant, si vous regardez la boucle, vous entrez dans la boucle en pointant sur add –1. La première écriture commence par incrémenter TBLPTR, puis écrit dans add, et ainsi de suite. Lors de la dernière sortie de la boucle, TBLPTR pointera cette fois sur add+7, donc sur la bonne page. CQFD. Vous pouvez extrapoler cette routine pour tout type de PIC18F disposant de plus de 64K de mémoire flash en ajoutant l’initialisation de TBLPTRU :

112

Page 113: Part5_r1

; initialisations ; --------------- movlw UPPER(flash_add-1) ; charger bits 15 à 21 de l’adresse movwf TBLPTRU ; dans pointeur UPPER movlw HIGH(flash_add-1) ; charger bits 8 à 14 de l’adresse - 1 movwf TBLPTRH ; dans pointeur HIGH movlw LOW(flash_add-1) ; charger bits 0 à 7 de l’adresse - 1 movwf TBLPTRL ; dans pointeur LOW movlw .8 ; 8 octets à écrire movwf cmpt ; dans compteur lfsr FSR0,oct_ram ; adresse des octets en RAM ; écriture dans le buffer ; ----------------------- loop movff POSTINC0,TABLAT ; copier octet dans registre de transfert tblwt+* ; pointer sur suivant, puis écrire l’octet dans buffer decfsz cmpt ; décrémenter compteur d’octets bra loop ; pas dernier, suivant ; séquence d’écriture ; -------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash

bsf EECON1,WREN ; écritures autorisées STOPINT ; interdire les interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture (sécurité) STARTINT ; remettre les interruptions si nécessaire Voici donc une routine universelle fonctionnelle pour l’écriture flash de tous vos PIC® 18F. Et au moins comme ça vous devriez comprendre la raison des routines trouvées dans les datasheets.

Reste une dernière question. Nous avons vu que nous pouvions écrire un seul octet en mémoire flash dans un emplacement préalablement effacé, simplement en affectant la valeur 0XFF aux 7 octets non concernés. Par contre, comment écrire un seul octet dans un emplacement flash non vierge, c’est-à-dire ne contenant pas la valeur 0xFF ? C’est simple, du moins en théorie :

- Vous copiez le bloc entier de 64 octets qui contient l’adresse concernée en RAM - Vous modifiez l’octet concerné en RAM - Vous effacez le bloc de 64 octets (Microchip® le désigne comme « rangée » (Row) - Pour chaque groupe de 8 octets :

- Vous écrivez 8 octets dans le buffer - Vous lancez une séquence d’écriture

Pas vraiment simple, pour écrire un seul octet, n’est-ce pas ? Si vous voulez savoir à quoi

ressemble une telle routine, Microchip® l’a écrite pour vous pages 72 et 73 du datasheet, exemple 6-3.

113

Page 114: Part5_r1

6.5.5 Ecriture avec vérification et effacement Ecrire une valeur en mémoire programme c’est bien, vérifier qu’elle est bien écrite c’est mieux. Voici une routine, tirée d’un de mes sources, qui procède à cette écriture/vérification. Si une erreur est survenue, le flag ERR_FLASH sera positionné en sortie de la routine. Les variables utilisées sont déclarées en access-bank. Cette routine prend également en charge l’effacement de la zone de 64 octets, mais pas la sauvegarde, elle est donc parfaitement adaptée pour être intégré dans un bootloader, dans lequel les instructions sont écrites séquentiellement et dans l’ordre. Du reste, elle est tirée d’un de mes bootloaders 18F. On appelle cette routine avec le registre d’adressage indirect FSR0 pointant sur les 8 octets à écrire. TBLPTRH et TBLPTRL sont préalablement initialisés sur l’adresse d’écriture en mémoire flash par la routine appelante. Si l’écriture concerne les 8 premiers octets d’un bloc de 64, le bloc est complètement effacé. ;============================================================================= ; ECRIRE 8 OCTETS EN FLASH = ;============================================================================= ;----------------------------------------------------------------------------- ; Ecrit 8 octets pointés par FSR0 en mémoire flash ; Le pointeur est déjà dans TBLPTRH/TBLPTRL ; on procède ensuite à une vérification de l'écriture, et l’erreur est ; retournée via le flag ERR_FLASH ; Si on pointe sur la première adresse d'un bloc de 64, on commence par procéder ; à l'effacement du bloc. ;----------------------------------------------------------------------------- writeflash bcf ERR_FLASH ; effacer code d'erreur ; vérifier si début d'un bloc de 64 ; --------------------------------- movf TBLPTRL,w ; charger poids faible pointeur andlw 0x3F ; garder 6 bits lsb bnz writeflash2 ; si adresse pas muliple de 64, ne pas effacer ; effacer le bloc de 64 octets ; ---------------------------- bsf EECON1,EEPGD ; pointer sur mémoire flash bcf EECON1,CFGS ; pointer sur zone programme bsf EECON1,WREN ; autoriser écriture bsf EECON1,FREE ; autoriser effacement bloc STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'effacement des 64 octets nop ; une instruction non exécutée STARTINT ; réautoriser interruptions ; Ecrire les 8 data en flash ; -------------------------- writeflash2

114

Page 115: Part5_r1

movff TBLPTRH,local04 ; sauver pointeur MSB movff TBLPTRL,local05 ; sauver pointeur LSB tblrd*- ; nécessaire, car on doit terminer ; les tblwt en restant dans la page de 8 ; octet dans laquelle on veut écrire clrwdt ; effacer watchdog movlw 0x08 ; 8 octets à transférer movwf local06 ; dans variable locale (access-bank) writeflash3 movff POSTINC0,TABLAT ; charger premier octet, pointer sur suivant tblwt+* ; placer dans buffer d'écriture, préincrémenté decfsz local06,f ; décrémenter compteur bra writeflash3 ; pas dernier, suivant bsf EECON1,EEPGD ; pointer sur mémoire flash bcf EECON1,CFGS ; pointer sur zone programme bsf EECON1,WREN ; autoriser écriture STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'écriture des 8 octets nop ; une instruction non exécutée STARTINT ; réautoriser interruptions ; restaurer les pointeurs ; ----------------------- movff local04,TBLPTRH ; restaurer pointeur MSB movff local05,TBLPTRL ; restaurer pointeur LSB clrwdt ; effacer watchdog ; vérifier les 8 octets écrits ; ---------------------------- movlw 0x08 ; pour 8 octets movwf local06 ; dans compteur de boucles subwf FSR0L,f ; repointer sur premier octet à écrire btfss STATUS,C ; tester si débordement decf FSR0H,f ; oui, décrémenter poids fort writeflash4 tblrd*+ ; lire un octet en flash movf TABLAT,w ; charger octet lu cpfseq POSTINC0 ; comparer avec celui qu'on devait écrire bsf FLAS_ERR ; pas identique, erreur decfsz local06,f ; décrémenter compteur d'octets bra writeflash4 ; pas dernier, suivant return ; fin 6.6 Déclarations de constantes en mémoire flash Il nous reste à savoir comment placer des valeurs en mémoire flash directement à partir de notre fichier source. Ceci s’effectue par la directive DB ORG 0x100 DB 0x23 , 0x35 DB 0x22 , 0x16

115

Page 116: Part5_r1

Si vous regardez ce que ceci donne dans la mémoire programme après assemblage, vous verrez le résultat suivant : 0x100 : 0x23 0x101 : 0x35 0x102 : 0x22 0x103 : 0x16 Vos octets sont donc bien rangés aux endroits que vous aviez prévu. Attention, MPASM® travaille par mots de 16 bits. Aussi, si vous écrivez ceci : DB 0x23 DB 0x35 DB 0x22 DB 0x16 Cela semble strictement identique, et pourtant un examen de la mémoire programme vous montrera l’organisation suivante : 0x100 : 0x23 0x101 : 0x00 0x102 : 0x35 0x103 : 0x00 0x104 : 0x22 0x105 : 0x00 0x106 : 0x16 0x107 : 0x00 MPASM® a donc complété votre ligne en considérant que vous vouliez mettre 2 octets par ligne (ou un multiple de 2), et a complété de lui-même par un « ,0x00 ». Méfiez-vous.

Ceci est dû au fait que chaque instruction étant codée en mémoire flash sur 16 octets, MPASM® est contraint de provoquer un alignement sur les adresses paires après chaque ligne écrite. En effet, si ceci n’était pas effectué de façon automatique, une instruction « ordinaire » placée après une directive DB risquerait bel et bien de se retrouver à une adresse impaire. 6.7 Particularité de l’organisation des mots Si vous avez la curiosité d’examiner l’organisation des données en mémoire programme, vous allez constater quelque chose qui vous semblera peut-être curieux. Prenons le datasheet et examinons la table 25-2. Regardons à la ligne « goto ». Intéressons-nous au premier mot de cette instruction (ignorons le second mot). Il est constitué de deux octets :

- Le poids fort contient : B’1110 1111’ - Le poids faible contient la première partie de l’adresse de saut.

Si vous assemblez votre programme, commençant par :

116

Page 117: Part5_r1

nop goto init

et que vous examinez le contenu de la mémoire programme (menu view-program memory), vous constatez que votre programme commence bien par :

- 0x0000 (nop) - 0xFExx (premier mot du goto )

Dans mon cas, le « xx » vaut « 05 ». Nous aurons donc : - 0x0000 (nop) - 0xFE05 (goto )

Autrement dit, strictement rien d’anormal. Chargez maintenant votre fichier hex dans votre bloc-notes ou dans un éditeur (le fichier hex est un simple fichier texte, je vais en reparler. Vous avez un début du genre :

:020000040000FA :06000000000005EF00F016

Je vais revenir juste après sur le format des fichiers hex. En attentant, acceptez juste que vos instructions se retrouvent dans la zone en gras. Votre « nop » se retrouve sous forme des 2 premiers octets (0000) alors que votre premier mot de l’instruction « goto » se retrouve sur les 2 suivants (05EF). Or, là, ça ne correspond plus avec les EF05 de notre instruction, les 2 octets sont inversés en mémoire programme. En fait, c’est parfaitement logique si vous lisez le datasheet : Dans le 18F, les octets des instructions sont dans l’ordre : Octet de poids fort suivi de l’octet de poids faible. Dit autrement, le mot « 0xFE05 » est représenté en mémoire programme par la succession de l’octet 0x05 suivi de l’octet 0xFE. C’est MPLAB® qui réinverse les octets dans la fenêtre d’affichage pour vous montrer le mot dans l’ordre correct. Si vos utilisez Ic-Prog, les anciennes versions montraient la mémoire par octet, et donc dans l’ordre inversé, alors que les versions plus récentes montrent la mémoire par mot, et donc dans l’ordre correct. Méfiez-vous donc lorsque vous visualisez le contenu de la mémoire programme par octet, ou lorsque vous y accédez par octet, les octets de poids fort et de poids faible des instructions sont inversés. Ce n’est pas particulier au PIC18F, on retrouve souvent cette organisation sur les microprocesseurs et microcontrôleurs qui organisent leur mémoire programme ou data en mots de plus de 8 bits.

117

Page 118: Part5_r1

Bien évidemment, ça ne concerne pas vos directives DB : puisque vous déclarez vos octets dans l’ordre, ils seront évidemment dans le même ordre en mémoire programme. Pas d’inversion donc à ce niveau. 6.9 Organisation d’un fichier hex Voici une question qui revient souvent. J’ai donc décidé d’intégrer dans ce cours les explications générales sur les fichiers au format « hex », ceux-là mêmes que vous générez en assemblant vos programmes. Tout d’abord, il faut savoir qu’il existe plusieurs sortes de formats de fichiers « hex », les plus courants nous concernant sont :

- Le format Hex Intel (ou Intel Hex Format) : Caractérisé par le fait que toute les informations sont données en octets. L’extension de ces fichiers est « .hex ».

- Le format Hex Intel Inversé (ou Intel Split Hex Format) : Les informations, qui

concernent des mots de 16 bits, sont sauvegardées dans deux fichiers différents, un avec l’extension « .hxl » contenant les octets de poids faible, et un autre avec l’extension « .hxh » contenant les octets de poids fort.

- Le format Hex Intel 32 bits (ou Intel Hex 32) qui est construit de façon identique au

format 8 bits, mais en autorisant des adresses codées sur plus de 16 bits.

Nous n’utiliserons pas le second mode, puisque nous créons normalement des fichiers hex uniques. Le troisième mode est simplement une extension du premier, ce qui nous réduit à n’étudier qu’un seul type, que nous appellerons le type « étendu », et qui peut selon les cas utiliser ou non les fonctionnalités du type 32 bits.

Notez que vous pouvez rencontrer d’autres formats « Hex », comme le format

« Motorola » etc. En général, soit ces formats sont identiques à un des formats précédents, soit ils ne diffèrent que par quelques options supplémentaires. A vous de voir si vous utilisez d’autres micros, cet ouvrage étant consacré aux PIC®.

La question que vous pouvez vous poser est de savoir à quoi peut bien vous servir de

connaître la structure d’un fichier hex, puisque MPASM® et le logiciel de votre programmateur s’occupent de tout ?

En fait, si par exemple vous voulez réaliser un bootloader, votre programme sur PC devra

être capable de lire et d’analyser votre fichier hex, puisque vous devrez l’envoyer au PIC®, et donc vous devrez savoir comment il est constitué. On est évidemment ici à la limite entre programmation de PIC® et programmation sur PC, mais il ressort que c’est un sujet qui vous préoccupe.

118

Page 119: Part5_r1

6.9.1 Organisation générale Un fichier hex (étendu) est un fichier texte. C’est une suite de lignes qu’on peut séparer en blocs. Il est construit en général de la façon suivante (certains informations peuvent ne pas être présentes, surtout pour les fichiers hex non 32 bits) :

- Début de bloc - Ligne - Ligne - Ligne - … - Début de bloc - Ligne - Ligne - Ligne - … - Début de bloc - … - …

Un fichier hex peut ne contenir qu’un seul bloc. En général un bloc identifie une zone

mémoire particulière. Vous trouverez donc des blocs pour vos données eeprom, pour vos configurations, un ou plusieurs blocs pour votre programme… 6.9.2 Constitution générale d’une ligne hex Tout d’abord, nous l’avons vu, un fichier hex est un simple fichier texte contenant les informations nécessaires au fonctionnement d’un programme de programmateur (genre Ic-Prog) ou d’un programme de bootloader (BigoPIC, Domogest etc. pour ne citer que les miens). Vous pouvez donc visualiser le contenu d’un tel fichier à l’aide d’un simple éditeur de texte (votre bloc-notes par exemple). Chaque ligne d’un fichier hex est construire de façon identique et contient des zones différentes que j’appellerai « champs ». Voici le format type d’une ligne (attention, dans la réalité, il n’y a pas d’espaces entre les champs) : : BB AAAA TT HHHH....HHHH CC Chaque lettre représente un chiffre hexadécimal, il faut donc deux chiffres pour définir un octet. Voyons maintenant le rôle de chaque champ : :

Toute ligne valide commence par le symbole « : » caractéristique du format hex. BB

119

Page 120: Part5_r1

Cet octet indique le nombre d’octets de donnée présents sur la ligne

AAAA

Contient les 2 octets de poids faible de l’adresse du premier octet de la ligne par rapport à l’adresse du bloc. Un bloc ne peut donc pas concerner plus de 65536 adresses (16 bits). L’adresse de base donnée dans la déclaration du bloc (format 32bits) permet d’étendre les adresses sur 32 bits. TT Contient une valeur type indiquant de quel genre de ligne il s’agit. On trouve typiquement les valeurs suivantes :

- 00 : indique que la ligne contient des octets utiles - 01 : indique la fin de fichier - 04 : indique le début d’un bloc (extension). Les 2 octets de données présents sur cette

ligne indiquent les 2 octets de poids fort de toutes les adresses qui suivent.

Dans les fichiers crées par MPASM® pour les 18F (et 16F) je n’ai pas trouvé d’autre type valide.

Pour bien comprendre le rôle du type 04, imaginons le cas suivant :

- Le bloc est identifié avec TT = 04 contenant deux octets « ABCD » - L’adresse d’une ligne avec TT=00 est identifiée par l’adresse « EFGH »

Le premier octet de la ligne concernera donc l’adresse 0xABCDEFGH » Sur nos PIC18F il n’y a aucune adresse de plus de 24 bits, le « A » de notre adresse vaudra donc toujours « 0 ».

HH Ce sont les données utiles. Chaque groupe de 2 chiffres représente un octet. Il y a donc dans la ligne « BB*2 » chiffres H, puisque « BB » indique le nombre d’octets de données présents dans la ligne. CC Il s’agit d’un cheksum simplifié travaillent sur 8 bits. Il est utilisé comme vérification de la validité de la ligne. Sa valeur se calcule comme suit :

1) Somme de toutes les autres octets de la ligne (adresse et nombres d’octets compris) 2) Complément à 2 du poids faible du nombre obtenu (en hexadécimal)

Dans vos programmes sur PC, prendre le poids faible du résultat équivaut à prendre le

reste de la division entière du nombre par 256 décimal (astuce en passant).

120

Page 121: Part5_r1

Le complément à 2 s’obtient en inversant tous les bits et en ajoutant 1 au nombre obtenu.

6.9.3 Analyse d’un fichier réel Voici un petit fichier hex que j’ai généré à partir d’un fichier source très court : :020000040000FA :06000000000005EF00F016 :080008001100060EC16E00EEAE :1000100000F0EE6AEAA0FDD7A96EA69EA69CA68077 :10002000A850A92A1200A86E016EA69EA69CA684BE :10003000F29E550EA76EAA0EA76EA682F28EA69409 :100040000400A6B2FDD70090E8DF01620080120034 :10005000A68EA69CA684A688F29E550EA76EAA0E12 :10006000A76EA6820000A694F28E1200F6503F0BF7 :100070000CE1A68EA69CA684A688F29E550EA76EBD :10008000AA0EA76EA6820000F28EF7CF01F0F6CF7F :1000900002F00A000400080E036EEECFF5FF0F0019 :1000A000032EFBD7A68EA69CA684F29E550EA76EA5 :1000B000AA0EA76EA6820000F28E009001C0F7FF84 :1000C00002C0F6FF0400080E036EE95ED8A0EA063F :0E00D0000900F550EE620080032EFAD71200F0 :020000040030CA :0300010022060FC5 :010006008178 :060008000FC00FE00F40E5 :0200000400F00A :10000000436563692065737420756E207465787428 :02001000650089 :00000001FF Je vais analyser ce fichier avec vous, pour vous montrer que tout ceci est très simple. C’est ce que vous ferez si vous analysez un tel fichier à l’aide d’un logiciel de votre conception, bootloader ou autre. C’est ce que fait également un logiciel comme Ic-Prog. Commençons par repérer les différents blocs constituant notre fichier. Un début de bloc se reconnaît par le fait que l’octet « TT » vaut 0x04. Comme cet octet se trouve (je ne compte pas les deux points) en position 7/8 sur la ligne, le répérage est assez simple. On en profite pour vérifier si la dernière ligne est bien une fin de fichier, repérée par un TT = 0x01 :020000040000FA ; bloc 1 :06000000000005EF00F016 :080008001100060EC16E00EEAE :1000100000F0EE6AEAA0FDD7A96EA69EA69CA68077 :10002000A850A92A1200A86E016EA69EA69CA684BE :10003000F29E550EA76EAA0EA76EA682F28EA69409 :100040000400A6B2FDD70090E8DF01620080120034 :10005000A68EA69CA684A688F29E550EA76EAA0E12 :10006000A76EA6820000A694F28E1200F6503F0BF7 :100070000CE1A68EA69CA684A688F29E550EA76EBD :10008000AA0EA76EA6820000F28EF7CF01F0F6CF7F :1000900002F00A000400080E036EEECFF5FF0F0019 :1000A000032EFBD7A68EA69CA684F29E550EA76EA5 :1000B000AA0EA76EA6820000F28E009001C0F7FF84 :1000C00002C0F6FF0400080E036EE95ED8A0EA063F

121

Page 122: Part5_r1

:0E00D0000900F550EE620080032EFAD71200F0 :020000040030CA ; bloc 2 :0300010022060FC5 :010006008178 :060008000FC00FE00F40E5 :0200000400F00A : bloc 3 :10000000436563692065737420756E207465787428 :02001000650089 :00000001FF ; fin de fichier Vous voyez que vous avez 3 blocs distincts, et que le fichier se termine bien par une fin de fichier. La dernière ligne (fin de fichier) est la ligne la plus courte possible dans un fichier hex, puisqu’elle ne contient aucune donnée (BB = 00). Commençons par cette fin de fichier : 00 : 0 octet de donnée 0000 : adresse 0x0000 (non utilisée) 01 : Fin de fichier FF : checsum = (not (00+0000+1))+1 Pour le checksum, vous ajoutez tous les octets précédents, ce qui donne 0x01. Vous inversez, ce qui donne 0x0FE, puis vous ajoutez « 1 », soit 0xFF. Le checksum est donc correct. Voyons maintenant le bloc 1. Il est repéré par la ligne : :020000040000FA ; bloc 1 00 : 2 octets de données (l’adresse du début du bloc) 0000 : pas d’adresse précisée (toujours pour le type 04) 04 : début de bloc 0000 : bits 31/16 de l’adresse = 0x0000 FA : checksum = (not(02+04))+1 Vous savez dès lors que les adresses dans ce bloc couvriront donc la zone « 0000 0000 » à « 0000 FFFF », ce sont donc en fait des instructions à ranger dans notre mémoire programme. Notez que pour le type 04 :

- L’adresse du bloc est contenue dans les 2 octets de data, et non dans la zone adresse - La zone adresse vaut toujours 0000 - Il y a 2 octets utiles sur la ligne

Voyons la ligne suivante : :06000000000005EF00F016 Nous pouvons décomposer en :

122

Page 123: Part5_r1

06 : 6 octets de data sur la ligne (donc 3 mots d’instructions) 0000 : on commence à l’adresse 0x0000 du bloc 00 : type = 00 = données ordinaires 0000 : 2 octets représentant le premier mot d’instruction 05EF : 2 octets représentant le second mot d’instruction 00F0 : 2 octets représentant la troisième mot d’instruction 16 : checksum = poids faible de : (not(0x06+0x05+0xEF+0xF0))+1 J’ai regroupé les octets de donnée par 2 car il s’agit d’instructions codées sur 16 bits sur notre 18F.

Sur un 16F, les instructions étant codées sur 14 bits, les instructions auraient toutes une valeur maximale de 0x3FF, mais seraient toujours représentées en 2 mots de 8 bits dans les fichiers hex (bien que représentées en mot de 14 bits en mémoire flash). Si vous examinez la première instruction, vous remarquez qu’il s’agit de 0x0000, soit un « nop » si vous regardez dans le tableau 25-2 du datasheet. L’instruction suivante « 05EF » correspond en fait, à cause de l’inversion, au premier mot d’une instruction « 0xEF05 » soit un « goto » dont le poids faible de l’adresse vaut 0x05 Les deux octets suivants représentent le reste de l’adresse, puisque « goto » est une instruction sur 32 bits. Après l’inversion, le mot vaut 0xF000. Les 12 derniers bits donnent le poids fort de l’adresse, soit 0x000. Remarquez en passant que le second mot d’instruction ne notre goto commence bien par B’1111’ comme vu dans les chapitres précédents. Le goto sauterait donc à l’adresse 0x000 05, avec une adresse codée sur 20 bits. En réalité ce serait oublier que pour être certain que l’adresse soit toujours un nombre pair, le PIC® effectue une multiplication par deux de l’adresse passée en paramètre. L’adresse de saut est donc 0x0A, codée sur 21 bits ce qui correspond à la plage d’accès de nos PIC®. Notez au passage que MPASM® n’a pas coupé les instructions au milieu de 2 mots, c’est une des raisons pour laquelle le nombre d’octets utiles par ligne peut varier d’une ligne à l’autre. La ligne du fichier hex analysée correspond donc à : nop goto 0x010 Mais bon, c’est juste pour information. Sauf si vous désirez faire un désassembleur (bel exercice à réaliser), l’analyse du fichier hex ne vous impose pas pour réaliser un bootloader ou un programmateur de désassembler le code. Il suffit simplement de comprendre les valeurs à placer dans le PIC®, et l’endroit où les placer. Prenons encore la ligne suivante de ce même bloc, afin de valider notre compréhension : :080008001100060EC16E00EEAE

123

Page 124: Part5_r1

08 : 8 octets sur la ligne (donc 4 mots d’instruction) 0008 : premier octet à l’adresse 0000 0008 00 : type = octets de données (en l’occurrence, instructions) 11 : octet d’instruction 00 : octet d’instruction 06 : etc. 0E C1 6E 00 EE AE : checksum = poids faible de (not (8+8+11+6+E+C1+6E+EE))+1 Je n’analyse plus le code désassemblé, c’est inutile pour l’utilisation classique et vous devriez avoir compris le principe si vous désirez réaliser un désassembleur. Je ne reviendrai plus non plus sur le checksum, vous devez avoir compris le principe. Vous constatez donc que la ligne contient 8 octets d’instruction (donc 4 mots d’instruction) commençant à l’adresse 0x08 (donc il s’agit de notre premier vecteur d’interruption). Remarquez qu’aucun octet ne figure dans nos adresse 0x06/0x07, ce qui signifie (pour information) que notre programme est structuré de cette façon : ORG 0x00 nop ; adresse 0x00 : instruction 0x00 00 goto init ; adresse 0x02 à 0x05 : goto 0x0A : 0xEF05 0xF000 Org 0x08 xxxx ; première instruction interrupt Vous remarquez donc que les zones « vides » ne font pas partie du fichier hex, ce qui vous permet de ne programmer votre PIC® qu’avec les valeurs réellement utilisées (gain de temps). Les autres lignes de ce bloc ont la même logique, inutile d’approfondir. Voyons maintenant le début du bloc 2 : :020000040030CA ; bloc 2 02 : 2 octets de donnée : l’adresse du bloc 0000 : adresse : inutilisée pour un type 04 04 : type = début de bloc (extension) 0030 : adresse de départ du bloc CA : checksum

Vous remarquez que les octets présents dans ce bloc se situeront donc dans la plage d’adresse 0x0030 0000 et 0x0030 FFFF En fait, cette zone concerne les registres de configuration du PIC®. C’est donc la présence de ce bloc qui indique au programme de votre programmateur de PIC® si les valeurs des registres de configuration sont intégrées ou non dans votre fichier hex (et donc dans votre

124

Page 125: Part5_r1

fichier source). Si vous faites un bootloader et que vous refusez que l’utilisateur ne modifie les bits de configuration, il vous suffira d’ignorer ce bloc. Ensuite, notre dernier bloc est repéré par : :0200000400F00A : bloc 3 Mis à part le type de bloc = 04, ce qui nous intéresse est la zone d’adresse sur laquelle porte le contenu de ce bloc. Il s’agit ici des adresses comprises entre 0x00F0 0000 et 0x00F0 FFFF. En fait, il s’agit du contenu de notre mémoire eeprom. Toute déclaration d’une ou plusieurs valeurs dans la zone eeprom se traduira par l’apparition de ce bloc. La mémoire eeprom est organisée en octets. Si vous avez la curiosité de regarder à quoi correspondent ces octets, vous verrez qu’il s’agit des codes ASCII de la phrase « Ceci est un texte ». 6.9.4 Traitement par un programme PC Que vous désiriez faire un désassembleur, un bootloader, ou un logiciel de programmateur, il vous faudra procéder à l’analyse d’un fichier hex à partir de votre PC. Voici une routine extraite de mon programme Domogest utilisé pour mon système domotique. Vous aurez ainsi un exemple simple de lecture d’un fichier hex. Le code est en VB6, ce qui permet à tout le monde ayant quelques notions de programmation de le comprendre. Certaines variables sont déclarées en mode global, ajoutez-les dans votre source si vous utilisez cette routine telle quelle. Ce n’est pas de la programmation de PIC®, mais je pense que ça peut vous rendre éventuellement service. '======================================================================== ' OUVERTURE DU FICHIER .HEX = '======================================================================== Public Sub OpenHex() '------------------------------------------------------------------------ 'FORMAT D'UN FICHIER .HEX INTEL STANDARD '--------------------------------------- ' Chaque ligne est constituée de ":BBAAAATTHHHH....HHHHCC" 'avec: ' : = toute ligne commence par ":" ' BB = nombre d'octets de données de la ligne en cours ' AAAA = Offset de l'adresse de départ du premier octet de la ligne ' TT = 00 pour une donnée, 01 pour la fin de fichier ' HH = 8 bits de données. ' CC = checksum = complément à 2 du reste de la division par 0x100 de la somme de tous les octets de la ligne ' Chaque portion est précédée d'une ligne avec BB = "02" et TT = 04 (extension) ' Les 2 octets précisent le poids fort des adresses qui suivent 'Exemple: ':020000040000FA La section qui suit commence à 0x0000 (zone flash)

125

Page 126: Part5_r1

':10002000EAA0FDD7020E98D9006E060EC16EFF0E33 16 octets de données qui commencent à 0x0000 0020 ':0200000400F00A La section qui suit commence à 0x00F0 (zone eeprom) ':100000004247000000000000444F4D4F43414E2046 16 octets de données qui commencent à 0x00F0 0000 '------------------------------------------------------------------------ Dim Ligne As String, Par As String, Nbre%, Numfich% Dim Add As Long, Chk%, i%, j%, Tt As String, Ext% ' INITIALISATIONS ET BOITE DE DIALOGUE ' ------------------------------------ FileOpen = False ' par défaut, fichier pas chargé With MdiMain.CD1 ' facilité d'écriture On Error GoTo NoLoad ' Si erreur, fin de procédure .CancelError = True ' l'option cancel provoque l'erreur .DialogTitle = "Ouvrir un fichier .hex" ' Titre (ouvrir fichier hex...) .Filter = "Hex (*.hex) | *.hex" ' filtres possibles .FilterIndex = 1 ' filtre par défaut = hex .InitDir = HexPath ' répertoire des fichiers hex .MaxFileSize = 1024 ' Longueur max du nom de chemin+fichier .Flags = cdlOFNExplorer Or cdlOFNFileMustExist Or _ cdlOFNHideReadOnly Or cdlOFNLongNames Or _ cdlOFNPathMustExist ' mettre les flags .InitDir = HexPath ' répertoire par défaut .ShowOpen ' ouvrir boîte de dialogue HexPath = Left(.FileName, Len(.FileName) - Len(.FileTitle))

' Nouveau répertoire Hex Numfich = FreeFile ' obtenir un numéro de fichier Open .FileName For Input As Numfich ' ouvrir le fichier ReDim VFlash(&H7FFF) ' dimensionner tableaux ReDim VEeprom(255) ReDim Flash(&H7FFF) ReDim Eeprom(255) Ext = 0 ' par défaut, extension adresse = 0x0000 ' VERIFICATION DE LA LIGNE LUE ' ---------------------------- On Error GoTo Incorrect ' si fichier incorrect, traiter Do ' autant de boucle que de lignes valides Line Input #Numfich, Ligne ' lire une ligne du fichier If Left(Ligne, 1) <> ":" Then Error 13 ' Si premier car <>":", pas bon Par = "&H" + Mid(Ligne, 2, 2) ' prendre longueur, préciser hexa (&H) Nbre = CInt(Par) ' nombre de données If (Nbre * 2) + 11 <> Len(Ligne) Then Error 13 ' si longueur de ligne incorrecte, erreur Par = "&H" + Mid(Ligne, 4, 4) ' récupérer adresse Add = CLng(Par) ' adresse en hexa Chk = 0 ' checksum = 0 For i = 2 To (Len(Ligne) - 3) Step 2 ' pour chaque paire de chiffres,excepté checksum Par = "&H" + Mid(Ligne, i, 2) ' lire un byte hexa Chk = Chk + CInt(Par) ' ajouter octet au checksum Next i Chk = Chk Mod 256 ' garder le lsb Chk = (256 - Chk) Mod 256 ' complément à 2 If Chk <> CInt("&H" + Right(Ligne, 2)) Then Error 13 ' Si bad checksum, erreur ' MEMORISATION DES DATA DE LA LIGNE ' ---------------------------------

126

Page 127: Part5_r1

Tt = Mid(Ligne, 8, 2) ' lire de quel type de ligne il s'agit If Tt = "04" Then ' si extension d'adresse Ext = CInt("&H" + Mid(Ligne, 10, 4)) ' prendre l'extension de l'adresse End If If (Tt = "00") Then ' si ligne de donnée For i = 1 To Nbre ' pour chaque octet de data de la ligne Par = "&H" + Mid(Ligne, (i * 2) + 8, 2) ' récupérer un octet de data If Ext = 0 Then ' si c'est un octet de flash VFlash(Add) = True ' signaler que la donnée existe Flash(Add) = CByte(Par) ' mémoriser la donnée End If If Ext = &HF0 Then ' si c'est un octet d'eeprom VEeprom(Add) = True ' signaler que la donnée existe Eeprom(Add) = CByte(Par) ' mémoriser la donnée End If Add = Add + 1 ' octet suivant = adresse suivante Next i ' traiter octet suivant End If Loop Until Tt = "01" ' répéter jusque la ligne de fin de fichier ' TERMINER LE TRAITEMENT ' ---------------------- Close #Numfich ' fermer le fichier HexPath = Left(.FileName, Len(.FileName) - Len(.FileTitle)) ' Nouveau répertoire Hex FileOpen = True ' chargé avec succès End With Exit Sub Incorrect: Close #Numfich ' Si données incorrectes, fermer le fichier Msg = MsgBox("Fichier Hex incorrect", vbExclamation, "Erreur") 'Message d'erreur FileOpen = False ' fichier pas ouvert avec succès NoLoad: End Sub 6.10 Zones concernées et registres de configurations

Tout d’abord, il convient de noter que la zone mémoire flash est divisée en plusieurs

zones. Chaque zone peut se voir attribuer des droits en lecture et en écriture par le biais des bits de configuration.

Les configurations sont assez évoluées au niveau des PIC18F, et de plus varient d’un

modèle de PIC® à l’autre. J’y consacrerai donc un chapitre spécifique, en vous expliquant comment déterminer vous-même les valeurs de configuration pour tout nouveau PIC® utilisé.

Vous verrez qu’il n’y a rien de magique, et que je ne « suce pas les informations de mon

pouce », si vous permettez l’expression. Bref, si vous n’arrivez pas à accéder à votre mémoire flash, commencez par vérifier vos

paramètres de configuration.

127

Page 128: Part5_r1

Ceci étant dit, vous n’avez sans doute pas manqué de remarquer le bit CFGS du registre

EECON1. Ce bit détermine si le bit EEPGD détermine le type de mémoire auquel vous voulez accéder (mémoire programme flash ou mémoire data eeprom) ou si vous désirez accéder à la zone de configuration du PIC®. La zone de configuration contient évidemment les fameux bits de configuration, et vous comprenez immédiatement que vous pouvez avec un 18F accéder aux registres de configuration directement à partir de votre programme.

ATTENTION : cette procédure est très dangereuse, car une erreur à ce niveau peut

stopper votre PIC® de façon définitive en vous obligeant à le reprogrammer, et peut même l’endommager en cas de paramétrage incorrect du mode d’oscillateur

Cette procédure est donc à réserver à des cas très particuliers, je vous déconseille d’en

abuser. Si par exemple vous mettez les bits de configuration à jour par bootloader et que vous faites une erreur, votre PIC® risque bel et bien d’être bloqué sans possibilité de revenir en arrière autrement que par reprogrammation complète du PIC® via un programmateur classique. C’est assez dérangeant si ça survient sur une application commerciale par exemple.

Pour récapituler le rôle des bits, et étant donné qu’il y en a deux concernés, il y a donc 4 possibilités : CFGS EEPGD Accède… 0 0 A la mémoire data EEPROM 0 1 A la mémoire programme FLASH 1 0 Aux registres de configuration 1 1 Aux registres de configuration Il vous faut maintenant savoir à partir de quelle adresse se localisent les registres de configuration. Et bien tout simplement à partir de 0x30 00 00. Le nombre réel d’adresses utilisées dépend du modèle du PIC®. Reste une question qui préoccupe maintenant les plus perspicaces des lecteurs (de nouveau, bravo si vous vous êtes posé la question) : Etant donné qu’une zone flash doit préalablement être effacée (remise à 0XFF) avant de pouvoir être écrite et que de plus l’effacement concerne l’intégralité d’un bloc de 64 adresses, comment éviter que le PIC® ne se retrouve dans une situation qui l’empêche de fonctionner après avoir procédé à l’effacement de la zone de configuration ? En effet, si vous effacez tous les registres de configuration avant de les réécrire, il y a toutes les chances pour que votre PIC® s’arrête dès l’effacement terminé, et ce de façon définitive. La réponse à cette question est très simple : c’est impossible !

C’est pourquoi Microchip® a utilisé pour cette partie de mémoire une technologie flash qui permet de forcer un bit à 1 ou à 0, et qui donc ne nécessite aucun effacement. Pour programmer un registre de configuration, vous procéderez comme pour tout emplacement flash ordinaire, sauf que :

- Vous positionnerez le bit CFGS lors de la séquence d’écriture imposée.

128

Page 129: Part5_r1

- Vous n’effacerez pas la zone concernée - Vous écrirez les registres un à la fois. - Vous utilisez TBLPTR en pointant sur l’adresse du registre >=0x30 00 00

Bref, dit de façon plus simple, vous écrivez octet par octet comme vous le faisiez sur les

premiers PIC16F, mais en passant tout de même via le buffer.

Notez que Microchip® utilise les syntaxes suivantes dans ses datasheets :

- Long write (écriture longue) : se réfère à l’écriture réelle en flash - Short write (écriture courte) : se réfère à l’écriture dans le buffer via TBLWT Selon la terminologie Microchip®, une écriture complète en mémoire flash se compose

donc d’un short write suivie d’un long write. Ne vous y trompez pas, il ne s’agit pas de deux méthodes différentes pour écrire en mémoire flash, mais bel et bien des deux étapes nécessaires à cette écriture.

Les routines peuvent être générées automatiquement par les macros présentes dans le

fichier macros18f.inc appelées automatiquement par le fichier maquette fourni avec le cours. Notez pour terminer que votre zone flash contient d’autres informations « spéciales ».

Pour résumer les différentes zones, voici une liste générale valable pour tous les 18F De 0x000000 à 0x01FFFF : mémoire programme pour votre logiciel (dépend du PIC®) De 0x020000 à 0x020007 : registres spéciaux (vecteurs debugger etc.) non documentés De 0x030000 à 0x03FFFD : registres de configuration (pas tous utilisés) De 0x03FFFE à 0x03FFFF : type (10 bits) et révision (6 bits) du PIC® : en lecture seule

Notez que la zone 0x020000 à .0x200007 est pour le moins obscure dans les datasheets. Il y a toutes les chances qu’on retrouve là notamment le vecteur de debuggage qui était à l’adresse 0x2004 sur nos 16F et qui avait fait parler d’elle dans le cours-part4. C’est un domaine d’exploration qui peut être intéressant. Je manque de temps pour explorer ces possibilités, si ça vous dit, n’hésitez pas à me rapporter vos expérimentations.

129

Page 130: Part5_r1

Notes :

130

Page 131: Part5_r1

7. Les accès en mémoire eeprom 7.1 Généralités Quoi de plus logique que d’étudier les accès en mémoire data « eeprom » directement après l’étude de nos écritures en mémoire programme. Votre esprit étant bien concentré sur la procédure, ce chapitre ne devrait pas poser de problème, la seconde étape de l’écriture (le long write) est pratiquement identique pour les deux types de mémoire.

Les accès à la mémoire eeprom se font en gros comme sur les 16F, il y a seulement quelques particularités. Ces accès se font par l’intermédiaire de 4 registre : EECON1, EECON2, EEDATA, EEADR, ce qui devrait raviver vos souvenirs.

Le registre EECON1 a été décrit dans le chapitre consacré, je n’y reviendrai pas. Idem

pour EECON2 qui, je vous le rappelle, n’est pas un « vrai » registre. EEDATA est simplement le registre qui contiendra notre valeur lue ou à écrire

(l’équivalent de TABLAT de la mémoire flash), tandis que EEADR est le pointeur sur l’adresse eeprom relative concernée (l’équivalant de TBLPTR de la mémore flash).

L’analogie s’arrête là puisque notre mémoire eeprom est prévue plus pour faciliter les

accès que la vitesse d’écriture, et donc vous pouvez y écrire octet par octet et sans nécessiter d’effacement.

Notre PIC18F258 dispose, comme beaucoup d’autres membres de cette famille, de 256

octets en mémoire EEPROM. Il semble bien que Microchip® n’ait pas fait évoluer cette capacité sur cette nouvelle famille, ce qui est pour le moins dommage. Il faudra donc nous contenter de cette capacité limitée, et compenser en plaçant nos constantes en mémoire flash.

Tout d’abord, il faut comprendre que bien que la zone eeprom soit située dans notre PIC®

à l’adresse 0xF00000, vous y accéderez via leur adresse relative. De façon plus claire, le premier emplacement eeprom situé à l’adresse absolue 0xF00000

sera accédé par l’adresse relative 0x00, la seconde (0xF00001) par l’adresse 0x01, etc. Le registre EEADR permet donc de coder 256 emplacements différents, ce qui est justement la taille eeprom de notre 18F258. Des PIC® utilisant plus de mémoire eeprom devront donc recourir à au moins un registre supplémentaire. 7.2 Lecture en EEPROM La méthode pour accéder en lecture à une valeur située en eeprom est très simple.

1) Vous placez l’adresse de lecture dans EEADR 2) Vous configurez les bits EEPGD et CFGS de EECON1 sur 0 3) Vous lancez la lecture en forçant le bit RD de EECON1 4) Vous récupérez la donnée lue dans EEDATA.

131

Page 132: Part5_r1

Voici le code correspondant : movlw adresse ; charger adresse relative en eeprom

movwf EEADR ; sauver adresse dans pointeur bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS ; pas zone de configuration bsf EECON1,RD ; ordre de lecture movf EEDATA,w ; charger valeur lue Vous constatez que c’est très simple. Vous pouvez mettre ceci sous forme d’une macro ou d’une sous-routine. Attention, beaucoup confondent le « movf addresse,w » avec le « movlw adresse ». Si l’adresse relative de votre donnée en mémoire eeprom se trouve dans une variable « adresse », utilisez la première méthode. Par contre, si « adresse » EST l’adresse en question et non une variable, utilisez la seconde. Vu le courrier reçu à ce sujet, il ne me semble pas inutile de le rappeler.

Du reste, voici une routine qui exécute ces opérations, il y a deux points d’entrée à cette

sous-routine, selon que l’adresse soit déjà ou non dans EEADR ou simplement dans le registre w (WREG).

Cette routine incrémente de plus le registre EEADR en sortie, ce qui permet de pointer

directement vers l’élément suivant. Dès lors, si vous voulez lire une seule valeur, vous chargez l’adresse dans W et vous appelez « readeepw ». Par contre, si vous utilisez une boucle pour lire plusieurs valeurs, vous placez la première adresse dans EEADR et vous appelez en boucle « readeep ». C’est comme ça que je procède dans mes propres programmes.

;============================================================================= ; LIRE EEPROM = ;============================================================================= ;----------------------------------------------------------------------------- ; si on entre par readeepw, Wreg contient l'adresse mémoire eeprom ; si on entre par readeep, l'adresse est contenue dans EEADR ; la donnée est retournée dans WREG et dans EEDATA ; en sortie, EEADR est incrémenté ;----------------------------------------------------------------------------- readeepw movwf EEADR ; sauver adresse dans pointeur readeep bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS ; pas en zone configuration bsf EECON1,RD ; ordre de lecture movf EEDATA,w ; charger valeur lue incf EEADR,f ; pointer sur suivant return ; et retour 7.3 Déclarations en zone eeprom Avant de passer à l’écriture, voyons comment insérer des données eeprom directement dans notre fichier source. Il est important de comprendre qu’il faut deux choses différentes :

1) Initialiser les données dans la zone 0xF00000.

132

Page 133: Part5_r1

2) Faire correspondre chaque emplacement à sa position relative (je vais y revenir)

Commençons par initialiser des valeurs eeprom présentes dès la programmation du PIC®. Pour ce faire, il suffit d’utiliser notre directive ORG pointant sur la zone eeprom. Pour bien visualiser la méthode, voici un exemple fictif : ;============================================================================= ; ZONE EEPROM = ;============================================================================= ORG 0xF00000 eeprom01 DB ADRESSE,.75 ; Adresse (constante),Délai eeprom02 DB "Ceci est un texte" ; petit texte en eeprom eeprom03 DB 0x20,0x30 ; encore quelques valeurs Si nous assemblons ces données dans un fichier source valide, en imaginant que nous avons défini « ADRESSE » avec la valeur 0x10, et que nous examinons le contenu de la mémoire eeprom à l’aide de la fenêtre « eeprom » du menu « view » de MPLAB®, nous remarquons l’organisation suivante :

- A partir de l’adresse 0xF00000 nous avons les valeurs 0x10 et 0x4B (.75) - A partir de l’adresse 0xF00002 nous avons les valeurs 0x43, 0x65 … qui représentent

les caractères ASCII de chaque lettre de notre texte « Ceci est un texte ». - A l’adresse 0xF00013 nous avons la valeur 0x00 - A partir de l’adresse 0x14 nous avons les valeurs 0x20 et 0x30

La première chose à constater est que tout semble logique excepté la présence de notre

valeur 0x00 à l’adresse 0xF00013. Si nous nous amusons à compter les caractères de «Ceci est un texte , vous constatez qu’il y en a 17.

La conclusion est que, tout comme pour la mémoire flash : MPASM® complète chaque ligne contenant un nombre impair de données par la

valeur 0x00. Moralité, n’oubliez pas, si vous ne voulez pas faire d’erreurs, de compléter chaque ligne

DB pour qu’elle présente un nombre pair d’octets. De toutes façons MPASM® le fera pour vous, car il veut toujours que chaque adresse

indiquée dans le fichier source corresponde à une adresse paire. Pour lui, l’étiquette « eeprom03 » vaudra donc 0xF00014 , ce qui explique le « 0 » présent en 0xF00013.

Evidemment si vous ne placez pas d’étiquette « eeprom03 » ça ne changera strictement

rien, car pour MPASM® : nouvelle ligne = nouvelle adresse. Maintenant, il nous faut utiliser les adresses définies pour lire nos valeurs. Imaginons que

nous voulions lire notre valeur 0x20 présente à l’adresse « eeprom03 ». Si nous écrivons :

133

Page 134: Part5_r1

movlw eeprom03 ; charger adresse de la donnée en eeprom movwf EEADR ; placer adresse dans le pointeur. xxx ; etc.

Ca fonctionnera, MAIS vous serez gratifié d’un beau « warning 202» inélégant, genre : Warning[202] C:\FICHIERS\INTERRUPTS-HIERAR.ASM 252 : Argument out of range. Least significant bits used. En effet, l’adresse de « eeprom03 » est 0xF00014, qui est une adresse sur 24 bits. Or, tant notre registre W (movlw) que notre registre EEADR sont sur 8 bits. Heureusement par défaut lorsque MPLASM voit une valeur trop grande pour entrer dans un registre il ne charge que les 8 bits les moins significatifs. Il chargera donc « 0x14 », ce qui est bien l’adresse relative de notre valeur en eeprom. Mais bon, vous imaginez bien que ce n’est pas élégant du tout, il nous faut donc résoudre ce problème. Pour ce faire, vous avez plusieurs solutions. La première est de simplement préciser explicitement à MPASM® que vous ne chargez que les 8 bits de poids faibles à l’aide de la directive « LOW » :

movlw LOW(eeprom03) ; charger adresse relative de la donnée en eeprom movwf EEADR ; placer adresse dans le pointeur. xxx ; etc.

Dans ce cas, plus de warning, mais vous devez traîner des « low » partout dans votre source à chaque utilisation d’une adresse EEPROM. Et, évidemment, si vous voulez accéder à la valeur 0x30 située à l’adresse suivante, vous devrez utiliser : Movlw LOW(eeprom03+1) La seconde méthode est d’ajouter en bas de votre zone eeprom les adresses relatives d’accès sous forme de DEFINE ou de EQU. Voici un petit exemple d’une zone eeprom utilisant cette méthode : ;============================================================================= ; ZONE EEPROM = ;============================================================================= ORG 0xF00000 eeprom01 DB ADRESSE,.75 ; Adresse (constante) , Délai eeprom02 DB "Ceci est un texte" ; petit texte eeprom03 DB 0x20,0x30 ; Valeur1 , valeur2 ; offsets ; ------- ADD_EEP EQU LOW(eeprom01) ; adresse de la carte DEL_EEP EQU LOW(eeprom01)+1 ; offset délai TEXT_EEP EQU LOW(eeprom02) ; début du texte

134

Page 135: Part5_r1

VAL1_EEP EQU LOW(eeprom03) ; valeur 1 VAL2_EEP EQU LOW(eeprom03)+1 ; valeur 2 Remarquez en passant que « LOW(eeprom03)+1 » est identique à « LOW(eeprom03+1).

Par contre, « HIGH(eeprom03+1) » ne serait PAS identique à « HIGH(eeprom03)+1 ». Je vous laisse y réfléchir au besoin,…à vos petits papiers ! Vous auriez évidemment pu calculer vous-même les adresses relatives et écrire : ADD_EEP EQU 0x00 DEL_EEP EQU 0x01 TEXT_EEP EQU 0x02 VAL1_EEP EQU 0x14 VAL2_EEP EQU 0x15 Mais les risques de se tromper sont évidemment supérieurs, surtout avec le remplissage automatique des adresses impaires avec des « 0 » . A vous cependant de choisir votre méthode, moi je mets en général une étiquette par ligne. Pour les offsets, j’ai l’habitude de toujours écrire les constantes en majuscules (on les voit mieux dans un source et on ne les confond pas avec une variable) et je fais suivre chaque offset eeprom par le suffixe « _EEP », comme ça je vois directement de quoi je parle. Je vous conseille d’adopter des conventions pour chaque usage, ainsi vous relirez vos sources beaucoup plus rapidement. Notez bien que les valeurs déclarées dans la zone 0xF00000 seront générées dans votre fichier hex, et donc seront inscrites dans votre PIC® au moment de sa programmation (et uniquement à ce moment). Au premier démarrage du PIC® ces données seront déjà valides, mais si vous les modifiez dans le déroulement du programme elles ne seront pas réécrites après une remise sous tension du PIC®, il faudra pour ça reprogrammer le PIC®. Je le précise parce qu’il semble que ce ne soit pas clair pour tout le monde. Nous savons maintenant déclarer des valeurs en eeprom à intégrer au fichier hex, et relire tout emplacement mémoire eeprom. Voyons maintenant l’écriture des données en eeprom durant l’exécution du programme. 7.4 Ecriture en EEPROM

Pour écrire dans notre mémoire eeprom nous devrons, tout comme pour l’écriture en

mémoire flash, utiliser une séquence imposée par Microchip® mettant en œuvre les mêmes registres et les mêmes bits.

La méthode est donc la suivante :

1) Vous placez la valeur à écrire dans le registre EEDATA 2) Vous placez l’adresse d’écriture dans EEADR 3) Vous configurez les bits EEPGD et CFGS de EECOP1 sur 0 4) Vous autorisez les écritures en forçant le bit WREN de EECON1 5) Vous coupez les interruptions (si elles étaient en service)

135

Page 136: Part5_r1

6) Vous exécutez la séquence imposée 7) Vous donnez l’ordre d’écriture en forçant le bit WR de EECON1 8) Vous remettez les interruptions (si nécessaire)

Attention, vous ne pouvez pas commencer une séquence d’écriture en eeprom avant que la

précédente ne soit terminée, ce qui se fait en testant si le bit WR de EECON1 est revenu à 0. En effet, contrairement aux écritures en mémoire flash, le PIC® ne s’arrête pas et continue l’exécution de votre programme pendant que l’écriture en mémoire eeprom se termine. Vous avez donc deux possibilités :

1) Soit vous testez WR en début de chaque écriture pour vous assurer que le cycle précédent est terminé

2) Soit vous testez WR avant de sortir de la routine pour vous assurer que l’écriture est

terminée.

La première méthode présente l’avantage de ne pas perdre du temps inutile surtout si vous n’avez qu’une seule valeur à écrire.

La seconde permet de s’assurer que la valeur a bien été écrite (et éventuellement vérifiée)

avant de poursuivre, ce qui garanti une meilleure fiabilité. Pour ma part, sauf rares exceptions, j’utilise cette seconde méthode.

Voici une routine qui exécute ces opérations. Le registre EEADR doit être préalablement initialisé avec l’adresse de la variable à écrire. La valeur à écrire est passée dans W. Selon mon habitude, EEADR est incrémenté avant de sortir, ce qui facilite les traitements en boucle. ;============================================================================= ; ECRIRE EEPROM = ;============================================================================= ;----------------------------------------------------------------------------- ; EEADR contient l'adresse mémoire eeprom ; la donnée est passée dans WREG ; en sortie, EEADR est incrémenté ;----------------------------------------------------------------------------- writeeep movwf EEDATA ; sauver data bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS ; pas en zone de config bsf EECON1,WREN ; autoriser écritures STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'ordre d'écriture STARTINT ; réautoriser interruptions (si nécessaire) bcf EECON1,WREN ; réinterdire écritures clrwdt ; effacer watchdog btfsc EECON1,WR ; tester si écriture terminée bra $-(2*2) ; non, attendre

136

Page 137: Part5_r1

return ; et fin Ecrire une valeur en eeprom, c’est bien, s’assurer qu’elle est correctement écrite, c’est

mieux. Voici une routine qui écrit et vérifie une donnée en mémoire eeprom. Le flag EEP_ERR est placé à 1 si une erreur a été rencontrée (erreur de vérification). EEP_ERR est déclaré comme étant un bit d’un emplacement RAM en access-bank.

;============================================================================= ; ECRIRE EEPROM = ;============================================================================= ;----------------------------------------------------------------------------- ; EEADR contient l'adresse mémoire eeprom ; la donnée est passée dans WREG ; en sortie, EEADR est incrémenté ; La valeur relue est renvoyée dans W ; si vérif est fausse, EEP_ERR est positionné à 1 ;----------------------------------------------------------------------------- writeeep movwf EEDATA ; sauver data movwf eeptemp ; sauver seconde fois (access-bank) bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS bsf EECON1,WREN ; autoriser écritures STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'ordre d'écriture STARTINT ; réautoriser interruptions bcf EECON1,WREN ; interdire écritures clrwdt ; effacer watchdog btfsc EECON1,WR ; tester si écriture terminée bra $-(2*2) ; non, attendre (-2 instructions) bcf EEP_ERR ; effacer flag d’erreur rcall readeep ; relire la valeur écrite (chapitre précédent) cpfseq eeptemp ; comparer avec celui qu'on devait écrire bsf EEP_ERR ; différents, positionner flag d’erreur return ; et fin

N’oubliez pas d’avertir l’utilisateur en cas d’erreur d’écriture eeprom (led ou autre mécanisme). Attention, n’oubliez pas que la boucle de test de fin d’écriture va prendre environ 2ms pour un 18F258, ce qui correspond à 20.000 temps de cycle à 40Mhz, c’est énorme, c’est pourquoi le temps de vérification est dérisoire relativement et son utilité est de fait évidente. Contrairement à l’écriture en flash, le PIC® n’est pas «figé » durant le déroulement de l’écriture. Le watchdog continue donc de compter, il faut donc le reseter dans la boucle de test de fin d’écriture. Avec un 18F258 vous disposez de la possibilité d’écrire 1.000.000 de fois environ à un emplacement déterminé, ce qui vous laisse de la marge.

137

Page 138: Part5_r1

Les routines peuvent être générées automatiquement par les macros présentes dans le

fichier macros18f.inc appelées automatiquement par le fichier maquette fourni avec le cours.

138

Page 139: Part5_r1

8. Les interruptions 8.1 Introduction Comme sur les PIC16F, la gamme high-end dispose de la possibilité d’interrompre votre programme sur base d’événements asynchrones à son déroulement : les interruptions. Nous allons retrouver ici le même type de mécanisme, mais évidemment avec des améliorations. Comme sur les 16F, le déclenchement d’une interruption conduit le PIC® à sauter à une adresse fixe de la mémoire programme. Cette adresse est figée par hardware. Pour les spécialistes d’autres processeurs, on peut dire que le vecteur d’interruption est non éditable. En fait, nous verrons qu’il y en a deux. Souvenez-vous que pour qu’un événement déclenche une interruption, il faut que les éléments suivants soient réunis :

1) Les interruptions générales doivent être validées 2) Le bit de validation de cet événement doit être validé 3) Si nécessaire pour ce type d’interruption ou pour le mode choisi, les interruptions

périphériques doivent être validées 4) Le flag d’interruption doit avoir été forcé par l’événement lui-même.

N’oubliez pas non plus que pour sortir d’une interruption, il faut d’abord effacer le flag

qui a déclenché l’interruption, sauf dans le cas où ce flag est en lecture seule car dans ce cas vous avez une action à effectuer qui l’effacera automatiquement (lecture d’un registre par exemple).

Enfin, souvenez-vous qu’un flag sera positionné lorsque survient l’événement prévu,

même si le bit de validation n’est pas positionné. Dans ce cas, évidemment, le flag ne déclenchera pas l’interruption correspondante.

Corollaire : si vous positionnez un bit d’interruption en cours de programme, posez-vous

la question de savoir si vous voulez que l’interruption se déclenche immédiatement sur base d’un événement déjà réalisé par le passé, dans le cas contraire, pensez à effacer le flag avant de positionner le bit de validation. 8.2 Les registres concernés Comme les données sont un peu dispersées dans le datasheet, je vous fournis ici un tableau récapitulatif des registres utilisés pour les interruptions. Vous allez voir qu’il y en a beaucoup, c’est pourquoi ce tableau peut vous être utile. Les noms de bits se terminant par « IF » sont les flags (Interrupt Flag), ceux se terminant pas « IE » sont les bits de validation (Interrupt Enable), et ceux se terminant par IP déterminent la priorité (Interrupt Priority). Rassurez-vous, nous allons voir ces concepts plus en détails, mais d’abord le tableau :

139

Page 140: Part5_r1

Registre B7 B6 B5 B4 B3 B2 B1 B0 RCON IPEN - - RI TO PD POR BOR

INTCON GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF INTCON2 RBPU INTEDG0 INTEDG1 INTEDG2 - TMR0IP - RBIP INTCON3 INT2IP INT1IP - INT2IE INT1IE - INT2IF INT1IF

PIR1 PSPIF ADIF RCIF TXIF SSPIF CCP1IF TMR2IF TMR1IF PIR2 - CMIF - EEIF BCLIF LVDIF TMR3IF ECCP1IF PIR3 IRXIF WAKIF ERRIF TXB2IF TXB1IF TXB0IF RXB1IF RXB0IF PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE PIE2 - CMIE - EEIE BCLIE LVDIE TMR3IE ECCP1IE PIE3 IRXIE WAKIE ERRIE TXB2IE TXB1IE TXB0IE RXB1IE RXB0IE IPR1 PSPIP ADIP RCIP TXIP SSPIP CCP1IP TMR2IP TMR1IP IPR2 - CMPIP - EEIP BCLIP LVDIP TMR3IP ECCP1IP IPR3 IRXIP WAKIP ERRIP TXB2IP TXB1IP TXB0IP RXB1IP RXB0IP

Les noms des bits en grisé représentent des bits qui n’ont rien à voir avec les interruptions. Le bit RBPU permet de mettre en service les résistances de pullup sur le PORTB (comme pour le 16F), alors que les bits grisés de RCON (RI,TO,PD,POR,BOR) sont liés aux mécanismes de détection de la source d’un reset dont nous parlerons par la suite. Sur certains PIC®, le module ECCP1 (ECCP1IF, IE, et IP) pourrait être remplacé par un module CCP2 (CCP1IF, IE, IP). Il n’existe pas cependant pour certains PIC® (le 18F458 en est pourvu mais pas le 18F258). De façon identique les bits PSPxx n’existent que pour les PIC® pourvus d’un port parallèle, comme le 18F458. ATTENTION : il est interdit d’utiliser une instruction movff pour modifier n’importe lequel de ces registres si au moins une interruption est en service. Donc, pour être très clair, si vous avez autorisé au moins une interruption dans votre programme, vous ne pouvez pas accéder en écriture à un des registres repris dans le tableau ci-dessus à l’aide d’une instruction movff. La raison en est que le PIC® pourrait être interrompu au milieu de l’instruction d’écriture et que ça pourrait induire des fonctionnements erratiques. Bien évidemment, vous pouvez continuer cependant d’utiliser l’instruction movff pour n’importe quel autre emplacement, cette limite n’est donc en aucun cas dérangeante, car il est rare de transférer une variable dans un registre d’interruption (et même si ça devait être fait, il suffit d’utiliser un movf suivi d’un movwf, puisque les registres SFR se trouvent tous en access-bank. Pour être bien clair, si les interruptions sont en service : movff variable,INTCON ; INTERDIT movff INTCON,variable ; AUTORISE movf variable,w ; movwf INTCON ; AUTORISE

140

Page 141: Part5_r1

Nous sommes maintenant prêts à étudier les interruptions plus en détails 8.3 Interruptions simples Les interruptions peuvent fonctionner dans un PIC18F exactement comme pour les PIC16F. Si vous n’agissez pas sur les bits spécifiques prévus, par défaut le PIC® est prêt pour fonctionner avec les interruptions dans ce mode. Les interruptions se passent dans ce cas sur un seul niveau, et une interruption ne peut toujours pas être interrompue par une autre. Les interruptions sur un seul niveau sont en service dès lancement des dites interruptions si le bit IPEN du registre RCON a la valeur « 0 », ce qui est le cas automatiquement à la mise sous tension du PIC®. Dans ce cas, le vecteur d’interruption pointe sur la valeur 0x08 (attention, pas 0x04 comme sur les 16F), ce qui signifie que toute interruption connectera le compteur ordinal (PC) sur cette adresse. Microchip® a déplacé l’adresse de 0x04 à 0x08 pour laisser suffisamment de place à l’adresse 0x00 pour exécuter les premières opérations utiles (saut). En effet, n’oubliez pas qu’il faut 2 adresses (ou 4) par instruction de 16 bits par rapport au PIC16F où il n’en fallait qu’une seule (mais avec des instructions sur 14 bits). A vous de faire alors les sauvegardes nécessaires et de traiter ces interruptions. Mais contrairement aux 16F, vous n’avez plus besoin de ruser pour effectuer les dites sauvegardes. En effet, vous disposez de plusieurs atouts supplémentaires non négligeables :

- L’instruction movff permet de copier un registre dans un autre SANS modifier les bits de STATUS, et donc vous pouvez sauver tous vos registres (même STATUS) à l’aide de cette simple instruction

- L’instruction movff permet de copier un registre de n’importe quelle banque vers un

registre (variable) de n’importe quelle autre banque. Vous n’avez donc plus à vous préoccuper de placer vos registres de sauvegarde dans une banque commune.

- Le mode FAST vous permet d’avoir automatiquement une sauvegarde des registres les

plus importants, c'est-à-dire : WREG (le registre W de travail), BSR (le pointeur de banques), et STATUS (contenant les flags). Ceci vous permet d’éviter la plupart des sauvegardes de registres. Notez que le fait que BSR soit sauvé vous permet par exemple d’utiliser une banque spécifique pour les variables utilisées en mode interruption (peut être utile pour les très gros programmes).

Pour mettre les interruptions en service, il vous suffit de forcer à 1 le bit GIE (General

Interrupt Enable) du registre INTCON. Bien évidemment, il vous faudra également valider une interruption (sans quoi l’opération n’a pas de sens). Les bits concernant la priorité sont ignorés dans ce mode de fonctionnement, inutile de vous en préoccuper.

141

Page 142: Part5_r1

Attention, si le bit « IE » concernant l’interruption se trouve dans un registre PIE1, PIE2 ou PIE3, vous devez forcer à 1 également le bit PEIE du registre INTCON Ensuite, il vous suffit de placer votre routine d’interruption à l’adresse 0x08, en vous souvenant que les registres WREG, BSR, et STATUS sont déjà sauvegardés. Dans ce cas, vous sortirez de l’interruption sans oublier la mention « FAST » afin de restaurer automatiquement ces registres. Pour récapituler le rôle des bits : - IPEN du registre RCON doit être à « 0 » (mis par défaut) - GIE du registre INTCON coupe ou valide toute interruption - PEIE du registre INTCON coupe ou valide toute interruption liée à un registre PIEx - Chaque bit xxxIE coupe ou valide chaque interruption particulière.

Ces opérations nécessitent un « ET » logique. Pour pouvoir donc valider une interruption,

il faut que toutes les conditions soient remplies. Le bit PEIE n’a d’influence que pour les interruptions dont les bits de contrôle se trouvent dans un des registres PIE1, PIE2, ou PIE3.

Tout d’abord une petite remarque : Comme indiqué sur mon site, pour que les sources des

exemples soient alignés chez vous comme chez moi, paramétrez MPLAB® comme suit : Edit -> properties -> ASM files -> Tab = 4 Ensuite, onglet « Text » -> select font -> courrier new , taille = 9 Je sais que ce ne sont pas les valeurs standard, mais bon, j’ai démarré les PIC® avec

MPLAB® V5, et les mises en page étaient différentes. Or, j’ai beaucoup de sources. De plus, ça me permet de les exporter facilement dans mon logiciel de création de documents en restant plus ou moins aligné (et donc en minimisant les retouches).

Voici maintenant un exemple de structure de programme utilisant les interruptions

simples. Le fichier source est fourni dans le répertoire « fichiers » obtenu au décompactage du cours, sous le nom : Interrupt_simple.asm :

;***************************************************************************** ; EXEMPLE DE STRUCTURE DE PROGRAMME AVEC LES INTERRUPTIONS SIMPLES * ; POUR UN 18F258 (OU TOUT AUTRE 18F DU MEME TYPE, 18F252 ETC) * ;***************************************************************************** ; * ; NOM : Exemple d'interrupt simple * ; Date création : * ; Date modification : * ; Version : * ; Circuit : * ; Auteur : Bigonoff * ; * ;***************************************************************************** ; Fichier requis: P186F258.inc * ; * ; Fréquence de travail : * ; Fréquence du quartz : * ; *

142

Page 143: Part5_r1

;***************************************************************************** ; * ; Pins utilisées : * ; ---------------- * ; * ;***************************************************************************** LIST p=18F258 ; Définition de processeur (mettez le vôtre) #include <p18F258.inc> ; fichier include (en rapport avec le type) Comme d’habitude, un petit commentaire en début de programme aide à définir en quoi consiste le projet. Ce n’est JAMAIS inutile, et ce n’est JAMAIS une perte de temps.

Songez-y, surtout lorsque je vois des programmes qu’on m’envoie pour me poser une question, dépourvus de tout commentaire et particulièrement illisibles. Chez moi, c’est poubelle directement, je ne perds pas mon temps à lire des sources pour lesquels l’auteur a voulu « gagner du temps ». Mon temps vaut bien celui de ces utilisateurs, pas vrai? ;============================================================================= ; CONFIGURATIONS = ;============================================================================= ; Compléter les configurations : voir cours-part5 et/ou fichier maquette Les configurations seront traitées dans un chapitre suivant, je les ai donc retirées de cet exemple afin de ne pas vous embrouiller. ;============================================================================= ; ZONE EEPROM = ;============================================================================= ORG 0xF00000 Sur un PIC18F, vous placerez à partir de cette adresse vos valeurs placées en eeprom. Nous trouvons ensuite les différentes banques RAM. (ici, pour un 18F258, ou 18F252 etc.) . Notez que j’ai séparé la banque 0 en deux parties, la première qui est accessible via l’access-bank, et la suivante uniquement via le mode banked (relisez au besoin le chapitre sur les modes d’adressage). ;============================================================================= ; VARIABLES ACCESS RAM = ;============================================================================= ; zone de 96 octets ; ----------------- CBLOCK 0x00 ; zone access ram de la banque 0 ENDC ;============================================================================= ; VARIABLES BANQUE 0 = ;============================================================================= ; zone de 160 octets, suite de l'access RAM ; ----------------------------------------- CBLOCK 0x60 fsr0_temp : 2 ; sauvegarde de FSR0 (2 registres) ENDC

143

Page 144: Part5_r1

;============================================================================= ; VARIABLES BANQUE 1 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x100 ENDC ;============================================================================= ; VARIABLES BANQUE 2 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x200 ENDC ;============================================================================= ; VARIABLES BANQUE 3 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x300 ENDC ;============================================================================= ; MACROS = ;============================================================================= ;============================================================================= ; PROGRAMME = ;============================================================================= ; vecteur de reset ; ---------------- ORG 0x00 nop ; pour debugger goto init ; sauter initialisation Vous retrouvez ici la méthode habituelle déjà mise en place sur les PIC16F. On commence par un saut vers la routine d’initialisation, ce qui permet de sauter par-dessus la routine d’interruption. Le « nop » du départ sert si vous utilisez un ICD2® pour debugger vos programmes. Ce n’est pas indispensable, mais recommandé par Microchip® pour avoir un démarrage correct dès la première instruction utile rencontrée. Vous pouvez franchement vous en passer mais l’intérêt de le faire est franchement limité. ;============================================================================= ; INTERRUPTION SIMPLE = ;============================================================================= ORG 0x08 ; point de départ des interruptions simples ; sauvegardes supplémentaires ; --------------------------- movff FSR0L,fsr0_temp ; sauver FSR0L (exemple)

144

Page 145: Part5_r1

movff FSR0H,fsr0_temp+1 ; et FSR0H ;traitement de l'interruption ;----------------------------- btfss INTCON,INT0IE ; tester si interrupts INT0 en service bra inter2 ; non, test suivant btfss INTCON,INT0IF ; tester si flag INT0IF positionné bra inter2 ; non, test suivant rcall traitinit0 ; soit on traite interrupt dans une routine séparée ; xxxx ; soit on traite directement l'interruption ici ; dans ce cas, supprimer la ligne précédente bcf INTCON,INT0IF ; effacer le flag (ou le faire dans traitinit0) bra interrest ; et terminer par restauration retfie FAST ; si pas de restauration, on peut sortir ici ; dans ce cas, effacer la ligne précédente inter2 ; test suivant ; xxxxx ; et ainsi de suite ; restaurations ; ------------- interrest movff fsr0_temp,FSR0L ; restaurer FSR0L movff fsr0_temp+1,FSR0H ; et FSR0H retfie FAST ; retour avec restauration de ; WREG, BSR, et STATUS Notez donc qu’il y a plusieurs façons de procéder, mais qui reviennent en gros au même. Vous pouvez traiter l’interruption directement dans la routine, ou appeler une sous-routine. Tout est question d’organisation, de clarté de relecture, et éventuellement de temps critique ou de place sur la pile.

Sauf cas exceptionnel, je vous conseille de toujours privilégier la relecture du code, c’est gage d’un meilleur suivi, et donc d’une diminution du nombre de bugs. Notez que BSR, STATUS, et WREG (votre registre W) sont automatiquement sauvés dans des registres spécifiques internes quoi que vous fassiez, et donc il est parfaitement inutile d’effectuer une sauvegarde supplémentaire de ces registres par software. Bien entendu, la restauration ne se fait que si vous précisez l’option « FAST » lors du retfie, n’oubliez donc surtout pas de le faire. J’ai ajouté la sauvegarde d’un registre FSR0, constitué de deux registres, FSR0L et FSR0H uniquement à titre d’exemple. Si votre routine d’interruption modifie des registres qui sont utilisés dans votre programme principal, n’oubliez pas de les sauvegarder et de les restaurer. A ce propos, n’oubliez pas que, l’instruction movff permettant d’accéder à l’intégralité de la mémoire RAM sans nécessiter de changer de banque, vous pouvez placer vos variables de sauvegarde où bon vous semble. ;============================================================================= ; ROUTINE D'INTERRUPTION INT0 = ;============================================================================= ;----------------------------------------------------------------------------- ; Peut être traitée directement dans la routine d'interruption ;----------------------------------------------------------------------------- traitinit0 ; xx ; traitement de la routine

145

Page 146: Part5_r1

bcf INTCON,INT0IF ; effacement du flag (si pas fait dans la ; routine d'interruption principale) return ; fin de traitement (attention, pas retfie) ; ni de FAST. Il ne s’agit que d’un exemple. N’oubliez pas que vous devez reseter la cause de l’interruption (ici, le flag INT0IF) quelque part avant le retour d’interruption, sous peine de voir votre programme tourner en rond dans l’interruption. ;============================================================================= ; INITIALISATIONS = ;============================================================================= ;----------------------------------------------------------------------------- ; contient les initialisations exécutées lors d'un reset ; ---------------------------------------------------------------------------- init ; initialiser les interruptions ; ------------------------------ bcf INTCON,T0IF ; par sécurité bsf INTCON,T0IE ; mettre interrupts INT0 en service bsf INTCON2,INTEDG0 ; interrupt sur flanc montant (exemple) ; xxxx ; initialiser les autres interruptions bsf INTCON,GIE ; mettre interruptions en service. J’ai lancé ici à titre d’exemple une interruption sur un flanc montant sur INT0, ceci afin de vous montrer comment ça fonctionne. Vu que vous connaissez déjà les PIC16F, ceci devrait vous sembler familier. La seule chose qui change, c’est que vous n’avez plus aucun changement de banque. La mise en service de toutes les interruptions prévues se fait en forçant le bit GIE à « 1 ». C’est tout ce qu’il y a à faire. ;***************************************************************************** ;***************************************************************************** ; PROGRAMME PRINCIPAL = ;***************************************************************************** ;***************************************************************************** main ; xxxx ; votre programme principal ici bra main ; par exemple ; n'oubliez pas de terminer votre programme par une ; boucle END ; directive de fin de programme ; ATTENTION, ne signifie pas que le PIC s'arrête, ; indique seulement à MPASM que l'assemblage ; s'arrête ici (voir cours-part1) Votre programme principal s’exécute après la phase d’initialisation. N’oubliez pas que vous devez toujours terminer votre programme par une boucle car la directive END est uniquement destinée à votre assembleur, et ne stoppe pas le PIC®. Si votre programme ne doit s’exécuter qu’une fois à la mise sous tension, écrivez simplement en fin de programme : loop bra loop

146

Page 147: Part5_r1

ou passez le PIC® en mode sleep (attention aux réveils éventuels, de nouveau : bouclez). 8.4 Les interruptions hiérarchisées En fait, il s’agit d’un grand mot, sur les 18F la hiérarchisation se limite à deux niveaux de priorité des interruptions. Mais ce mécanisme de priorité est déjà un grand pas en avant pour des applications aux timings précis. Si vous mettez ce mécanisme en service, vous disposez alors de deux niveaux d’interruptions :

- Les interruptions basse priorité - Les interruptions haute priorité.

Notez que Lapalisse vous aurait dit la même chose.

L’intérêt est que les interruptions basse priorité peuvent être elles-mêmes interrompues

par les interruptions de type haute priorité.

Un exemple simple d’utilisation serait que vous avez une réception d’octets sur une liaison USART. Vous avez également la réception d’un pulse très court sur une pin du PIC®, dont vous désirez mesurer la durée.

Avec les 16F, si vous étiez en train de traiter la réception d’un octet durant l’apparition du

pulse, le traitement de celui-ci aurait été différé jusqu’à la fin du traitement de l’interruption en cours.

Avec les 18F, il vous suffit de programmer la réception USART en interruption basse

priorité et la détection du pulse en mode haute priorité. Dans ce cas, le traitement par interruption de l’octet reçu sera immédiatement interrompu par l’arrivée du pulse, traité dans la routine d’interruption haute priorité.

Dit autrement : une interruption haute priorité est une interruption qui peut interrompre

une interruption basse priorité (et évidemment également le programme en lui-même).

A l’inverse, une interruption haute priorité ne peut être interrompue par rien d’autre, ni par une interruption basse priorité, ni par une autre interruption haute priorité.

Ceci vous permet de toujours réagir aux événements les plus critiques, même si une

interruption « classique » est déjà en cours d’exécution.

ATTENTION, ne tombez pas dans la paranoïa en utilisant systématiquement des interruptions haute priorité. En effet, souvenez-vous de toujours vous mettre à l’échelle de temps du PIC®. Un 18F258 cadencé à 40Mhz exécute 10 instructions par µs. Vouloir recevoir un octet transmis en RS232 à 19200 bauds, par exemple, via une interruption haute priorité n’a pas le moindre intérêt, le PIC® ayant le temps d’exécuter 5200 instructions entre la réception de deux octets consécutifs. Constatez que ça vous laisse le temps de terminer une autre interruption (ou alors il y a un problème de conception de vos interruptions).

147

Page 148: Part5_r1

Remarque : Si vous obtenez un programme dans lequel toutes vos interruptions sont en haute priorité (ou en basse priorité), alors c’est que vous n’aviez pas besoin du mécanisme de priorité. Pour mettre en service les interruptions hiérarchisées, ou encore les interruptions prioritaires, il vous suffit de placer à 1 le bit IPEN du registre RCON. Dès ce moment, les interruptions répondront à la logique des priorités. Vous devez donc, et ce pour chaque interruption, préciser si vous désirez que celle-ci soit considérée comme interruption haute ou basse priorité. Ceci est fait en positionnant ou non le bit « IP » correspondant à l’interruption concernée. A la mise sous tension, tous les bits « IP » sont forcés à « 1 », ce qui correspond à un fonctionnement en mode haute-priorité. Autrement dit : Une fois les interruptions prioritaires en service, toute interruption est considérée par défaut comme interruption haute priorité. Si vous souhaitez qu’une interruption soit considérée comme étant en basse priorité, n’oubliez pas de couper le bit concerné. Pour ne jamais oublier, autant préciser explicitement le mode quel qu’il soit. Le rôle de certains bits change en mode haute priorité, ce qui explique qu’ils possèdent un double nom. Ainsi, le bit GIE, qui coupait toutes les interruptions, devient le bit GIEH (General Interrupt Enable High priority), qui permet de mettre ou couper d’une seule fois toutes les interruptions haute priorité. Remarque : Si vous coupez GIEH, vous coupez TOUTES les priorités, haute et basse priorité. Par contre, si vous positionnez GIEH, vous n’autorisez que les interruptions hautes priorités, car la mise en service des interruptions basses priorités nécessite un bit supplémentaire. De même, le bit PEIE qui validait toutes les interruptions périphériques (liées aux registres PIEx) devient le bit GIEL (General Interrupt Enable Low priority), qui permet de couper d’une fois toutes les interruptions basse priorité. Pour bien comprendre :

- Pour permettre les interruptions en mode prioritaire, forcer IPEN à 1. - Pour mettre les interruptions haute priorité en service, il faut en plus forcer GIEH à 1 - Pour mettre les interruptions basse priorité en service, il faut en plus forcer GIEL à 1 - Pour chaque interruption en service, il faut préciser si elle sera haute ou basse priorité

AVANT de la valider.

Constatez de ce fait que les interruptions placées dans les registres PIE1 à PIE3 ne sont plus sous le contrôle du bit PEIE, puisque ce dernier n’existe plus. Elles seront donc en service indépendamment de ce bit.

148

Page 149: Part5_r1

Constatez également que le fait de couper les interruptions haute priorité coupe également les interruptions basse priorité, alors que la réciproque n’est pas vraie.

Si nous prenons par exemple deux interruptions, INT1 et INT2 (pour faire simple). Si nous décidons que INT1 sera en haute priorité et INT2 en basse priorité, voici ce qui en découle :

- Vous mettez à 1 IPEN de RCON - Vous placez INT1IP de INTCON3 à 1 : haute priorité pour INT1 (fait par défaut) - Vous choisissez le sens du flanc sur INT1 avec INTEDG1 de INTCON2 - Vous validez le bit INT1IE de INTCON3 à 1 : valider l’interruption INT1 - Vous placez INT2IP de INTCON3 à 0 : basse priorité pour INT2 - Vous choisissez le sens du flanc sur INT2 avec INTEDG2 de INTCON2 - Vous validez le bit INT2IE de INTCON3 : valider l’interruption INT2 - Vous mettez GIEH de INTCON à 1 : lancer interruptions hautes priorités - Vous mettez GIEL de INTCON à 1 : lancer interruptions basses priorités

ATTENTION : Suite à un bug constaté sur certains PIC®, vous devez toujours

forcer GIEH à 1 avant GIEL. De même, vous devez toujours couper GIEL avant de couper GIEH

Si vous utilisez les interruptions prioritaires, voici deux macros pour couper et remettre toutes les interruptions. STARTINT macro ; lancer les interruptions (suite à un bug dans le PIC) bsf INTCON,GIEH ; autoriser interruptions H.P. bsf INTCON,GIEL ; autoriser interruptions B.P. endm STOPINT macro ; stopper les interruptions (suite à un bug dans le PIC) bcf INTCON,GIEL ; couper interruptions B.P. bcf INTCON,GIEH ; couper interruptions H.P. endm Vous l’aurez compris, ce sont les deux macros utilisées dans nos chapitres consacrés aux accès en mémoire. Vous n’aurez qu’à remplacer le contenu de ces macros si vous ne souhaitez pas utiliser les interruptions hiérarchisées, mais en fait, même pas, car le fichier maquette que je vous propose le fait automatiquement. L’utilisation de ces macros vous permettra de contrer ce bug. Attention, il n’est pas dit que ce bug existe pour tous les PIC18F, mais alors à vous de vérifier au cas par cas. Même si le bug n’existe pas, l’utilisation de cette macro ne fera aucun tort. Elles sont de toutes façons présentes automatiquement dans le fichier « macro18f.inc » si vous utilisez le fichier maquette fourni avec ce cours. De toutes façons, une bonne pratique est d’aller toujours voir chez Microchip® si un problème n’est pas répertorié pour le PIC® que vous utilisez, en regardant dans les « errata ». Microchip® a le mérite d’annoncer clairement les bugs trouvés et de donner le cas échéant des méthodes pour le contourner ou éviter son apparition. Ce n’est pas le cas de tous les constructeurs, je peux vous l’assurer.

149

Page 150: Part5_r1

C’est pourquoi également il faut toujours éviter de se ruer sur tout ce qui est nouveau (famille de microcontrôleurs, logiciels, OS etc.), car les bugs sont inévitables et il vaut mieux laisser d’autres utilisateurs se casser le nez dessus avant vous ce qui permet au constructeur de faire les mises à jour (ce n’est pas très élégant, mais vu qu’il y a toujours des malades des nouveautés, autant leur en laisser la primeur).

Par exemple, j’ai attendu l’écriture de ce cours et l’arrivée de MPLAB® V7.52 avant d’abandonner ma vieille version 6.6. Bien m’en a pris vu le nombre énorme de bugs répertoriés dans les premières versions.

Durant ce temps, j’ai pu continuer à travailler tranquillement avec la version 6.6. Je vois également avec « amusement » les problèmes rencontrés par ceux qui se sont rués sur le nouvel OS Vista de Microcrotte. Mais bon, refermons cette parenthèse. Qu’en est-il maintenant de notre vecteur d’interruption pointant sur l’adresse 0x08 ? Et, question subsidiaire, comment reconnaître une interruption basse ou haute priorité ? La réponse à ces deux questions tient dans la même réponse. Avec les interruptions prioritaires, vous avez maintenant deux vecteurs d’interruptions au lieu d’un seul. Dès la mise en service :

Toute interruption haute priorité se connectera à l’adresse 0x08 Toute interruption basse priorité se connectera à l’adresse 0x18

C’est comme ça que vous détectez automatiquement le type d’interruption, et que vous pourrez traiter différemment chacun des deux types. Notez en passant que vu que seulement 10 emplacements mémoire programme séparent les deux interruptions, vous serez contraint à l’adresse 0x08 (haute priorité) de faire un saut plus loin pour y traiter vos interruptions haute priorité, alors que vos interruptions basse priorité pourront se traiter directement à l’adresse 0x18. C’est très mal pensé de la part de Microchip®, car il aurait été préférable de ne pas sauter pour les interruptions haute priorité, afin d’économiser des cycles inutiles. Une inversion des deux vecteurs aurait été préférable, et de loin. Une mauvaise note sur ce point. Comme quoi, même de grands spécialistes comme les développeurs de microcontrôleurs peuvent faire des erreurs aussi grossières. Notez que dans mes sources, et dans le reste de ce document, la dénomination « HP » se rapporte aux interruptions hautes priorités (High Priority) et la dénomination « LP » aux interruptions basses priorités (Low Priority).

Nous allons maintenant créer un petit programme d’exemple pour montrer comment mettre en service les interruptions haute priorité. Cet exemple est disponible dans le répertoire « fichiers » obtenu au décompactage du cours sous la dénomination : Interrupts-hierar.asm : ;***************************************************************************** ; EXEMPLE DE STRUCTURE DE PROGRAMME AVEC LES INTERRUPTIONS HIERARCHISEES * ; POUR UN 18F258 (OU TOUT AUTRE 18F DU MEME TYPE, 18F252 ETC) * ;***************************************************************************** ; *

150

Page 151: Part5_r1

; NOM : Exemple d'interrupt hiérarchisée * ; Date création : * ; Date modification : * ; Version : * ; Circuit : * ; Auteur : Bigonoff * ; * ;***************************************************************************** ; Fichier requis: P186F258.inc * ; * ; Fréquence de travail : * ; Fréquence du quartz : * ; * ;***************************************************************************** ; * ; Pins utilisées : * ; ---------------- * ; * ;***************************************************************************** LIST p=18F258 ; Définition de processeur (mettez le vôtre) #include <p18F258.inc> ; fichier include (en rapport avec le type) ;============================================================================= ; CONFIGURATIONS = ;============================================================================= ; Compléter les configurations : voir cours-part5 et/ou fichier maquette ;============================================================================= ; ZONE EEPROM = ;============================================================================= ORG 0xF00000 ;============================================================================= ; VARIABLES ACCESS RAM = ;============================================================================= ; zone de 96 octets ; ----------------- CBLOCK 0x00 ; zone access ram de la banque 0 ENDC Jusqu’ici, rien ne change par rapport à l’exemple précédent sur les interruptions simples. Voyons maintenant au niveau des déclarations des variables : ;============================================================================= ; VARIABLES BANQUE 0 = ;============================================================================= ; zone de 160 octets, suite de l'access RAM ; ----------------------------------------- CBLOCK 0x60 fsr0_lp : 2 ; sauvegarde de FSR0 (2 registres) pour LP fsr0_hp : 2 ; sauvegarde de FSR0 pour HP wreg_lp : 1 ; sauvegarde de WREG pour LP status_lp : 1 ; sauvegarde de STATUS pour LP ENDC

151

Page 152: Part5_r1

Remarquez que maintenant nous avons deux fois la sauvegarde de FSR0 (ce n’est qu’un exemple). C’est logique car si vous sauvegardiez FSR0 à la fois dans l’interruption HP et l’interruption LP au même endroit, une interruption HP survenant durant le traitement de l’interruption LP écraserait la sauvegarde faite par la routine d’interruption LP, ce qui est évidemment incorrect. N’oubliez donc pas lorsque vous sauvegardez de préciser des emplacements différents pour les registres sauvés en haute et en basse priorité. Pour m’y retrouver et ne pas me tromper, je précise « _lp » ou « _hp », ce qui est suffisamment explicite pour éviter tout risque d’erreur. Notez aussi que vous voyez apparaître deux sauvegardes supplémentaires, « wreg_lp » et « status_lp ». En effet, comme nous l’avons vu, la restauration automatique de type FAST ne fonctionne que pour les interruptions de haute priorité, si vous avez activé les interruptions hiérarchisées. Comme dans l’immense majorité des cas vous modifierez STATUS et WREG à la fois dans votre programme principal et dans votre routine d’interruption, vous devrez les sauvez « manuellement » dans la routine d’interruption basse priorité. Du reste, nous étions habitués à ces procédures dans le cas des PIC16F. Ici, cependant, la sauvegarde est très simple à effectuer. Nous aurions pu sauver également le registre BSR, mais en fait, dans la réalité, vous verrez que vous changerez très rarement de banque, et donc dans la majorité des cas la sauvegarde est inutile. A vous de voir cependant en fonction de votre utilisation. ;============================================================================= ; VARIABLES BANQUE 1 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x100 ENDC ;============================================================================= ; VARIABLES BANQUE 2 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x200 ENDC ;============================================================================= ; VARIABLES BANQUE 3 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x300 ENDC ;============================================================================= ; MACROS = ;=============================================================================

152

Page 153: Part5_r1

STARTINT macro ; lancer les interruptions (suite à un bug dans le PIC) bsf INTCON,GIEH ; autoriser interruptions H.P. bsf INTCON,GIEL ; autoriser interruptions B.P. endm STOPINT macro ; stopper les interruptions(suite à un bug dans le PIC) bcf INTCON,GIEL ; couper interruptions B.P. bcf INTCON,GIEH ; couper interruptions H.P. endm Nous retrouvons ici nos deux macros destinées à lancer et à couper les interruptions. Leur utilisation vous permet de ne pas oublier de tenir compte du bug constaté dans certains PIC® (dont le 18Fxx8). Lorsque les interruptions HP sont en service (GIEH = 1), vous pouvez couper sans précaution et remettre les interruptions LP. Par contre, si vous voulez agir sur les interruptions HP (donc sur toutes les interruptions), utilisez cette macro ou tapez le code correspondant dans votre programme. ;============================================================================= ;============================================================================= ; PROGRAMME = ;============================================================================= ;============================================================================= ; vecteur de reset ; ---------------- ORG 0x00 nop ; pour debugger goto init ; sauter initialisation ;============================================================================= ;============================================================================= ; VECTEUR HAUTE PRIORITE = ;============================================================================= ;============================================================================= ORG 0x08 bra interhp ; malheureusement, il n'y a pas la place, il faut ; sauter plus loin (éventuellement par goto) Nous retrouvons ici le problème dont je vous parlais. Nous sommes en 0x08 et l’interruption basse priorité commence en 0x18. Nous n’avons donc manifestement pas la place de placer ici le traitement de nos interruptions (10 adresses = 5 instructions maximum). ;============================================================================= ;============================================================================= ; INTERRUPTION BASSE PRIORITE = ;============================================================================= ;============================================================================= ORG 0x18 ; point de départ des interruptions LP ; sauvegardes indispensables ; -------------------------- movff STATUS,status_lp ; sauver STATUS movff WREG,wreg_lp ; sauver WREG ; on pourrait sauver BSR par exemple ; si l'interrupt modifie les banques ; sauvegardes supplémentaires ; ---------------------------

153

Page 154: Part5_r1

movff FSR0L,fsr0_lp ; sauver FSR0L (exemple) movff FSR0H,fsr0_lp ; et FSR0H ;traitement de l'interruption ;----------------------------- btfss INTCON3,INT2IE ; tester si interrupts INT2 en service bra interlp2 ; non, test suivant btfss INTCON3,INT2IF ; tester si flag INT2IF positionné bra interlp2 ; non, test suivant rcall traitint2 ; soit on traite interrupt dans une routine séparée ; xxxx ; soit on traite directement l'interruption ici ; dans ce cas, supprimer la ligne précédente bcf INTCON3,INT2IF ; effacer le flag (ou le faire dans traitint2) bra interlprest ; et terminer par restauration interlp2 ; test suivant ; xxxxx ; et ainsi de suite ; restaurations ; ------------- interlprest movff fsr0_lp,FSR0L ; restaurer FSR0L movff fsr0_lp+1,FSR0H ; et FSR0H movff status_lp,STATUS ; restaurer STATUS movff wreg_lp,WREG ; restaurer WREG retfie ; retour simple ; interdit d'utiliser FAST, puisque les ; interruptions HP sont en service. Comme nous l’avons déjà vu à plusieurs reprises, il n’est plus possible de revenir de l’interruption basse priorité avec l’option « FAST ». Dès lors, nous devons nous charger de la sauvegarde et de la restauration au minimum de WREG et STATUS.

Notez que si vous placez wreg_lp en access-bank, vous pouvez vous contentez d’un : movwf wreg_lp Pour sauver le registre WREG, et d’un : movf wreg_lp,w Pour le restaurer. N’oubliez cependant pas que si vous restaurez de cette façon vous devrez restaurer WREG AVANT de restaurer STATUS (car le movf affecte le registre STATUS). D’autres emplacements sont également possibles avec cette méthode si vous savez sur quelle banque pointe BSR ou si vous forcez BSR avec movlb sur la banque de wreg_lp avant de faire la sauvegarde. A vous de voir. Voyons maintenant le vrai traitement de l’interruption HP, lancée à partir du saut à l’adresse 0x08 : ;============================================================================= ;============================================================================= ; INTERRUPTION HAUTE PRIORITE = ;=============================================================================

154

Page 155: Part5_r1

;============================================================================= interhp ; sauvegardes supplémentaires ; --------------------------- movff FSR0L,fsr0_hp ; sauver FSR0L (exemple) movff FSR0H,fsr0_hp ; et FSR0H ;traitement de l'interruption ;----------------------------- btfss INTCON3,INT1IE ; tester si interrupts INT1 en service bra interhp2 ; non, test suivant btfss INTCON3,INT1IF ; tester si flag INT1IF positionné bra interhp2 ; non, test suivant rcall traitint1 ; soit on traite interrupt dans une routine séparée ; xxxx ; soit on traite directement l'interruption ici ; dans ce cas, supprimer la ligne précédente bcf INTCON3,INT1IF ; effacer le flag (ou le faire dans traitint1) bra interhprest ; et terminer par restauration retfie FAST ; si pas de restauration complémentaire, ; on peut sortir ici. ; Dans ce cas, effacer la ligne précédente. ; Ne pas oublier le FAST pour restaurer WREG,

; STATUS, BSR interhp2 ; test suivant ; xxxxx ; et ainsi de suite ; restaurations ; ------------- interhprest movff fsr0_hp,FSR0L ; restaurer FSR0L movff fsr0_hp+1,FSR0H ; et FSR0H retfie FAST ; retour avec restauration automatique Vous voyez que la routine fonctionne comme tout autre routine d’interruption. Vous sortirez cependant par l’option « FAST », ce qui vous évite de sauver et restaurer nos registres de base. Remarquez que dans la routine d’interruption HP vous ne devez tester que les interruptions initialisées comme étant HP, alors que dans la routine LP vous ne devez tester que les interruptions initialisées comme étant LP.

Inutile donc ici de tester le bit INT1IF, puisqu’une interruption INT1 se connectera à l’adresse 0x018 et non à l’adresse 0x08. Ca facilite donc le traitement de vos interruptions. Si vous avez deux interruptions, vous pouvez même vous servir de ce mécanisme pour supprimer tout test d’interruption, chacune se faisant à une adresse différente, il suffit de les programmer avec une priorité différente. ;============================================================================= ; ROUTINE D'INTERRUPTION INT2 = ;============================================================================= ;----------------------------------------------------------------------------- ; Peut être traitée directement dans la routine d'interruption ;----------------------------------------------------------------------------- traitint2 ; xx ; traitement de la routine bcf INTCON3,INT2IF ; effacement du flag (si pas fait dans la

155

Page 156: Part5_r1

; routine d'interruption principale) return ; fin de traitement (attention, pas retfie) ; ni de FAST. ;============================================================================= ; ROUTINE D'INTERRUPTION INT1 = ;============================================================================= ;----------------------------------------------------------------------------- ; Peut être traitée directement dans la routine d'interruption ;----------------------------------------------------------------------------- traitint1 ; xx ; traitement de la routine bcf INTCON3,INT1IF ; effacement du flag (si pas fait dans la ; routine d'interruption principale) return ; fin de traitement (attention, pas retfie) ; ni de FAST. Au niveau du traitement des interruptions en elles-mêmes, il n’y a strictement aucune différence quel que soit le niveau de priorité. ;============================================================================= ; INITIALISATIONS = ;============================================================================= ;----------------------------------------------------------------------------- ; contient les initialisations exécutées lors d'un reset ; ---------------------------------------------------------------------------- init ; initialiser interruption INT1 ; ----------------------------- bsf INTCON3,INT1IP ; interrupt INT1 en HP (était déjà positionné) bsf INTCON2,INTEDG1 ; pour l'exemple, détection d'un flanc montant bcf INTCON3,INT1IF ; par sécurité bsf INTCON3,INT1IE ; interrupts INT1 en service ; initialiser interruption INT2 ; ----------------------------- bcf INTCON3,INT2IP ; interrupt INT2 en LP (obligatoire) bcf INTCON2,INTEDG2 ; pour l'exemple, détection d'un flanc descendant bcf INTCON3,INT2IF ; par sécurité bsf INTCON3,INT2IE ; interrupts INT2 en service ; lancer les interruptions ; ------------------------ bsf RCON,IPEN ; mettre interruptions hiérarchisées en service STARTINT ; lancer interruptions via macro bsf INTCON,GIEH ; alternative, supprimer alors la macro bsf INTCON,GIEL Nous retrouvons ici la séquence précédemment expliquée. Vous voyez que ce n’est pas plus compliqué que de lancer des interruptions classiques, il y a juste un bit supplémentaire à configurer et le bit IPEN à forcer à 1. ;***************************************************************************** ;***************************************************************************** ; PROGRAMME PRINCIPAL = ;***************************************************************************** ;***************************************************************************** main ; xxxx ; votre programme principal ici

156

Page 157: Part5_r1

bra main ; par exemple ; n'oubliez pas de terminer votre programme par une ; boucle END ; directive de fin de programme ; ATTENTION, ne signifie pas que le PIC s'arrête, ; indique seulement à MPASM que l'assemblage ; s'arrête ici (voir cours-part1) Voici pour notre exemple. Les mécanismes d’interruption vous ont livré tous leurs secrets, nous allons voir maintenant les différentes interruptions disponibles un peu plus en détails. 8.5 Les sources d’interruption Par rapport aux 16F, le nombre d’événements susceptibles de déclencher une interruption a été revu à la hausse. Je vais reprendre les événements « standards » pouvant déclencher une interruption sur nos PIC®. Evidemment en fonction du PIC® choisi, certains pourraient ne pas exister (bus CAN par exemple), ou de façon tout aussi évidente, il pourrait y en avoir d’autres. Cette liste vous donne cependant au minimum toutes les interruptions basiques, ce qui vous donne une idée des possibilités. Attention, erreur courante, ne confondez pas le « 0 » (Zéro) avec le « O » (la lettre O). Pour faire la distinction, fiez-vous à la signification de l’abréviation. 8.5.1 Flanc détecté sur la pin INT0 (INT)

Bit de validation : INT0IE du registre INTCON Utilisation de PEIE (si IPEN=0) : Non Bit de flag : INT0IF du registre INTCON Bit de sélection du flanc : INTEDG0 du registre INTCON2 Bit de priorité (si IPEN=1) : Aucun

Cette interruption se déclenche si le niveau sur la pin s’est modifié dans un seul sens. L’interruption fonctionne de fait sur base d’un flanc. Si INTEDG0 est mis à 0, l’interruption se déclenche sur le front descendant du signal (5V->0V), alors que s’il est mis à 1, la détection se fait sur base du front montant (0V -> 5V). Il n’est pas possible de détecter simultanément les deux flancs, mais si c’est nécessaire une astuce simple permet de le faire. En effet, il suffit dans l’interruption déclenchée par le flanc de positionner INTEDG0, après avoir effacé le flag INT0IF, sur la valeur inverse du niveau de INT0, ou d’inverser à chaque interruption la valeur de INTEDG0.

Sur les PIC® dépourvus de INT1, la pin INT0 peut s’appeler INT. Généralement INT0 est présent sur la pin RB0 du PIC®. Remarquez qu’il n’y a pas de bit de sélection de priorité sur INT0. En effet, dès que vous mettez les interruptions prioritaires en service via IPEN, cette interruption sera automatiquement en haute priorité. Pensez-y au moment de concevoir votre circuit, le hardware est souvent lié au software, c’est le cas ici.

157

Page 158: Part5_r1

8.5.2 Flanc détecté sur la pin INT1

Bit de validation : INT1IE du registre INTCON3 Utilisation de PEIE (si IPEN =0) : Non Bit de flag : INT1IF du registre INTCON3 Bit de sélection du flanc : INTEDG1 du registre INTCON2 Bit de priorité (si IPEN=1) : INT1IP du registre INTCON3

Cette interruption fonctionne exactement comme la précédente. L’exception est que cette fois vous pouvez choisir par INT1IP la priorité de votre interruption. En général INT1 se trouve sur la pin RB1. 8.5.3 Flanc détecté sur la pin INT2

Bit de validation : INT2IE du registre INTCON3 Utilisation de PEIE (si IPEN =0) : Non Bit de flag : INT2IF du registre INTCON3 Bit de sélection du flanc : INTEDG2 du registre INTCON2 Bit de priorité (si IPEN=1) : INT2IP du registre INTCON3

Cette interruption fonctionne exactement comme la précédente. En général, INT2 se trouve sur la pin RB2. 8.5.4 Changement de niveau sur les pins RB4/RB7

Bit de validation : RBIE du registre INTCON Utilisation de PEIE (si IPEN =0) : Non Bit de flag : RBIF du registre INTCON Bit de priorité (si IPEN=1) : RBIP du registre INTCON2

Tout changement de niveau sur une des pins RB4 à RB7, dans un sens ou dans l’autre, déclenchera cette interruption. Vous ne pouvez donc pas choisir la pin concernée ni le sens de transition, vous devrez tester dans la routine d’interruption. ATTENTION : avant de reseter le flag RBIF dans votre routine d’interruption, vous devez impérativement avoir lu au moins une fois le PORTB. Sans cette précaution, le flag serait immédiatement repositionné et vous ne pourriez pas sortir de votre routine d’interruption. Vous retrouvez ce phénomène sur les 16F, mais vu le courrier reçu en ce sens, il ne me semble pas inutile de faire un petit rappel. Notez qu’en général vous lirez le PORT, puisque vous devrez tester quel pin a été modifiée, mais autant le savoir, vous ne seriez pas le première à vous faire prendre. ATTENTION : au démarrage, le flag RBIF prend une valeur aléatoire. N’oubliez donc pas de le reseter avant de lancer l’interruption RBIE.

158

Page 159: Part5_r1

Sans cette précaution, vous prenez le risque d’avoir une interruption immédiate alors même qu’il n’y a pas encore eu de changement sur RB4 à RB7. 8.5.5 Débordement du Timer0

Bit de validation : TMR0IE du registre INTCON Utilisation de PEIE (si IPEN =0) : Non Bit de flag : TMR0IF du registre INTCON Bit de priorité (si IPEN=1) : TMR0IP du registre INTCON2

Cette interruption est la dernière qui fasse appel aux registres INTCONx, et donc considérées comme interruptions non périphériques. Les autres interruptions feront référence aux PIRx, PIEx et IPRx. Notez dès lors que toutes ces autres interruptions seront tributaires de PEIE en mode d’interruption classique, car seront considérées comme interruptions périphériques. L’interruption survient lors du débordement du timer0, c’est-à-dire :

- Si le timer0 est configuré en mode 8 bits, lorsqu’il passe de 0xFF à 0x00 - Si le timer0 est configuré en mode 16 bits, lorsqu’il passe de 0xFFFF à 0x0000

8.5.6 Débordement du Timer1

Bit de validation : TMR1IE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TMR1IF du registre PIR1 Bit de priorité (si IPEN=1) : TMR1IP du registre IPR1

L’interruption survient lors du débordement du timer1. Comme il s’agit d’un timer 16 bits, le débordement s’effectuera lors du passage de 0xFFFF vers 0x0000. 8.5.7 Débordement du Timer2

Bit de validation : TMR2IE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TMR2IF du registre PIR1 Bit de priorité (si IPEN=1) : TMR2IP du registre IPR1

L’interruption survient lors du débordement du postdiviseur du timer2. Il faut bien comprendre que le timer2 compte comme les autres timers en comptant les événements passés au travers d’un prédiviseur. Bref, tous les x événements (x étant la valeur affectée au prédiviseur) le timer2 incrémente d’une unité. Il est cependant de plus équipé d’un postdiviseur, connecté à la sortie du timer. Lorsque la valeur du timer2 (sur 8 bits) dépasse la valeur indiquée dans le registre PR2, le timer2 repasse à 0x00 et incrémente le contenu du postdiviseur.

159

Page 160: Part5_r1

Lorsque le contenu du postdiviseur dépasse la valeur qu’on lui a programmée, alors le flag d’interruption est positionné et l’interruption peut avoir lieu. Bref, tous les « x » débordements (avec x = valeur du postdiviseur), le flag sera positionné. Ce n’est donc pas à chaque débordement comme pour les timers précédents. Sans compter que la valeur de débordement du timer2 est fixée librement via le registre PR2. 8.5.8 Débordement du Timer3

Bit de validation : TMR3IE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TMR3IF du registre PIR2 Bit de priorité (si IPEN=1) : TMR3IP du registre IPR2

Le timer3 est un timer 16 bits, l’activation du flag TMR3If s’effectue lorsque le timer3 déborde de la valeur 0xFFFF vers la valeur 0x0000. 8.5.9 Buffer de réception de l’USART plein

Bit de validation : RCIE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : RCIF du registre PIR1 Bit de priorité (si IPEN=1) : RCIP du registre IPR1

Le flag RCIF est positionné lorsqu’un octet a été chargé dans le registre de réception RCSTA de l’USART contient un octet reçu par la liaison série. ATTENTION : le flag RCIF est en lecture seule. Vous ne pouvez pas le reseter par un bcf, la seule façon de le faire est de lire le contenu de RCSTA, ce qui déclenchera son effacement automatique. 8.5.10 Buffer d’émission de l’USART vide

Bit de validation : TXIE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TXIF du registre PIR1 Bit de priorité (si IPEN=1) : TXIP du registre IPR1

Le flag TXIF est positionné lorsque le buffer d’émission USART (TXSTA) ne contient plus aucun octet. De fait, dès que vous mettez l’émission USART en service, et étant donné qu’à ce moment là le buffer d’émission est vide, le flag sera immédiatement positionné et une interruption éventuellement déclenchée (si validée). Le buffer d’émission est transféré directement dans le registre à décalage de sortie. Ceci vous assure d’avoir un octet préparé d’avance pendant qu’un autre est en cours d’émission. Dès lors, lors du lancement des interruptions d’émission USART, vous aurez deux interruptions consécutives car le buffer d’émission sera directement vide.

160

Page 161: Part5_r1

ATTENTION : le flag TXIF est en lecture seule. Vous ne pouvez pas le reseter par un bcf, la seule façon de le faire est de charger le registre TXSTA, ce qui déclenchera son effacement automatique. 8.5.11 Emission ou réception SSP terminée

Bit de validation : SSPIE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : SSPIF du registre PIR1 Bit de priorité (si IPEN=1) : SSPIP du registre IPR1

Le flag est positionné lorsqu’une opération de transfert synchrone via le module SSP est terminée. Le reset du flag se fait de façon classique à l’aide d’une instruction bcf. 8.5.12 Interruption du module compare et capture 1

Bit de validation : CCP1IE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : CCP1IF du registre PIR1 Bit de priorité (si IPEN=1) : CCP1IP du registre IPR1

Le comportement du flag dépend du mode de fonctionnement actuel du module compare et capture 1 : En mode capture : une capture du timer1 a été réalisée. En mode compare : L’égalité avec le timer1 a été constatée. Le flag doit être reseté par software, et le flag n’est pas affecté si le module travaille en mode PWM. 8.5.13 Interruption du module enhanced compare and capture 1

Bit de validation : ECCP1IE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : ECCP1IF du registre PIR2 Bit de priorité (si IPEN=1) : ECCP1IP du registre IPR2

Le comportement du flag dépend du mode de fonctionnement actuel du module enhanced compare et capture 1 : En mode capture : une capture du timer1 ou du timer3 a été réalisée. En mode compare : L’égalité avec le timer1 ou le timer3 a été constatée. Le flag doit être reseté par software, et le flag n’est pas affecté si le module travaille en mode PWM.

161

Page 162: Part5_r1

Ce module (et donc les bits dont nous parlons) n’existe pas sur le modèle 18F258 mais bien sur le modèle 18F458. Sur certains PIC®, le module enhanced compare and capture 1 peut être remplacé par un module compare and capture 2 « ordinaire ». Vérifiez dans le datsheet. 8.5.14 Fin de numérisation

Bit de validation : ADIE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : ADIF du registre PIR1 Bit de priorité (si IPEN=1) : ADIP du registre IPR1

Le flag est positionné lorsque la conversion analogique/numérique via le convertisseur intégré est terminée. 8.5.15 Fin de transfert de donnée en mode parallèle

Bit de validation : PSPIE du registre PIE1 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : PSPIF du registre PIR1 Bit de priorité (si IPEN=1) : PSPIP du registre IPR1

Le flag est positionné lorsque l’écriture ou la lecture via le port parallèle a été exécutée. Cette interruption n’existe évidemment que pour les PIC® pourvus d’un port parallèle, comme le 18F458, mais pas comme le 18F258. 8.5.16 Chute de tension détectée sur le PIC®

Bit de validation : LVDIE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : LVDIF du registre PIR2 Bit de priorité (si IPEN=1) : LVDIP du registre IPR2

Le flag est positionné lorsque la tension d’alimentation du PIC® chute sous la limite programmée dans le registre LVDCON. Ceci permet à l’utilisateur de générer une interruption destinée à placer le PIC® en mode de fonctionnement sécurisé ou de prendre toute action nécessaire en cas de risque de dysfonctionnement (sauvegarde des paramètres, arrêt de périphériques etc.). Cette interruption est particulièrement utile si votre PIC® est alimenté par des piles, accumulateurs, ou batterie. Le flag devra être, comme d’habitude, reseté par software dans la routine d’interruption.

162

Page 163: Part5_r1

8.5.17 Collision détectée sur le bus I²C

Bit de validation : BCLIE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : BCLIF du registre PIR2 Bit de priorité (si IPEN=1) : BCLIP du registre IPR2

Le flag est positionné si une condition de collision est détectée dans la gestion du bus I²C. Il y a plusieurs causes possibles, référez-vous au cours-part2 ou au datasheet pour plus d’explications sur le bus I²C. 8.5.18 Fin d’écriture en eeprom interne

Bit de validation : EEIE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : EEIF du registre PIR2 Bit de priorité (si IPEN=1) : EEIP du registre IPR2

Le flag est positionné lorsque l’opération d’écriture en mémoire eeprom est terminée. Il doit comme d’habitude être reseté par software. 8.5.19 Détection d’un changement de la sortie d’un comparateur

Bit de validation : CMIE du registre PIE2 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : CMIF du registre PIR2 Bit de priorité (si IPEN=1) : CMIP du registre IPR2

Le flag est positionné si un basculement de la sortie d’un comparateur (ou du comparateur) est intervenu. Attention, la procédure de reset du flag nécessite l’exécution de deux opérations (exactement comme pour le bit RBIF)

- Lecture du registre CMCON, ce qui permet de mettre fin à la condition d’inégalité - Effacement du flag par un bcf

Si vous vous contentez d’effacer le flag, il sera immédiatement repositionné et donc vous resterez bloqué dans votre routine d’interruption.

8.5.20 Réception d’une trame CAN sur le buffer 0

Bit de validation : RXB0IE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : RXB0IF du registre PIR3 Bit de priorité (si IPEN=1) : RXB0IP du registre IPR3

163

Page 164: Part5_r1

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Le flag indique qu’une trame complète (ID + data) a été reçue et se trouve dans le buffer de réception 0. Le reset du flag s’effectue de façon simple en resetant le flag. 8.5.21 Réception d’une trame CAN sur le buffer 1

Bit de validation : RXB1IE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : RXB1IF du registre PIR3 Bit de priorité (si IPEN=1) : RXB1IP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Le flag indique qu’une trame complète (ID + data) a été reçue et se trouve dans le buffer de réception 1. Le reset du flag s’effectue de façon simple en resetant le flag. 8.5.22 Fin d’émission d’une trame CAN sur le buffer 0

Bit de validation : TXB0IE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TXB0IF du registre PIR3 Bit de priorité (si IPEN=1) : TXB0IP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Le flag indique qu’une trame complète (ID + data) a été émise sur le bus et que le buffer d’émission est à nouveau libre. Le reset du flag s’effectue de façon simple en resetant le flag. 8.5.23 Fin d’émission d’une trame CAN sur le buffer 1

Bit de validation : TXB1IE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TXB1IF du registre PIR3 Bit de priorité (si IPEN=1) : TXB1IP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8.

164

Page 165: Part5_r1

Le flag indique qu’une trame complète (ID + data) a été émise sur le bus et que le buffer d’émission est à nouveau libre. Le reset du flag s’effectue de façon simple en resetant le flag. 8.5.24 Fin d’émission d’une trame CAN sur le buffer 2

Bit de validation : TXB2IE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : TXB2IF du registre PIR3 Bit de priorité (si IPEN=1) : TXB2IP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Le flag indique qu’une trame complète (ID + data) a été émise sur le bus et que le buffer d’émission est à nouveau libre. Le reset du flag s’effectue de façon simple en resetant le flag. 8.5.25 Erreur sur le bus CAN

Bit de validation : ERRIE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : ERRIF du registre PIR3 Bit de priorité (si IPEN=1) : ERRIP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Le flag indique qu’une condition d’erreur a été rencontrée sur le bus CAN. Les causes possibles sont multiples, j’en parlerai dans un ouvrage consacré au bus CAN sur PIC®. Dans l’attente, référez-vous au datasheet ou à mon projet « Domocan » présent sur mon site. 8.5.26 Activité détectée sur le bus CAN

Bit de validation : WAKIE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : WAKIF du registre PIR3 Bit de priorité (si IPEN=1) : WAKIP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Ce flag indique qu’une activité a été détectée sur le bus CAN. Il est positionné même si le module CAN est mis en stand-by et même si le PIC® se trouve en veille (sleep). Le nom « WAKxxx » n’a pas été choisi au hasard car il est explicitement prévu pour réveiller (wake-up) le PIC® lorsqu’une activité CAN a été détectée.

165

Page 166: Part5_r1

8.5.27 Message invalide sur le bus CAN

Bit de validation : IRXIE du registre PIE3 Utilisation de PEIE (si IPEN =0) : Oui Bit de flag : IRXIF du registre PIR3 Bit de priorité (si IPEN=1) : IRXIP du registre IPR3

Cette interruption n’existe évidemment que sur les PIC® munis d’une gestion du bus CAN, comme les 18Fxx8. Ce flag indique qu’un message invalide a été détecté sur le bus CAN. Il s’agit d’une condition d’erreur relative au format des données CAN. De nouveau, plus d’explication dans le dataheet, dans les documents de mon application Domocan, ou dans le futur cours destiné au bus CAN. Ceci termine l’analyse de nos causes possibles d’interruption. Vous voyez qu’avec ces 18F vous êtes gâtés.

166

Page 167: Part5_r1

9. Les registres de configuration 9.1 Introduction Nous avons déjà effleuré le sujet lorsque nous avons parlé des écritures en mémoire programme. De nouveau, comme sur nos bons vieux 16F, les 18F disposent d’une série de registres de configuration, en bien plus grand nombre que sur les mid-ranges. Les registres se situent en mémoire flash à partir de l’adresse 0x300000, et sont accessibles dans votre programme à l’aide d’une directive spécifique. Plus que jamais il devient important d’intégrer les programmations des registres de configuration dans vos fichiers sources. Pour un utilisateur de 16F84 il était encore possible de « deviner » vos valeurs de configuration en les positionnant manuellement via le logiciel de programmation. Avec un 18F ça relève de la mission impossible. Par conséquent : Intégrez toujours les valeurs de vos configurations dans vos fichiers source en utilisant la directive appropriée. 9.2 MPLAB® V7 et directive CONFIG Grande nouveauté si vous utilisez conjointement MPLAB® V7 et les PIC18F. Bien que vous puissiez toujours utiliser la syntaxe habituelle recourant à la directive « __CONFIG », vous disposez maintenant d’une directive bien plus pratique : « CONFIG » (sans les tirets). Au lieu de devoir jouer bit par bit sur des noms de registres, il vous suffit maintenant de préciser directement la valeur de l’option choisie, en clair, et sans devoir vous référer à un nom de registre particulier. Un petit exemple vaut mieux qu’un long discours. Imaginons que nous voulions configurer notre PIC® pour que le watchdog soit en service avec un postdiviseur de 128. Avec la syntaxe reconnue jusqu’à présent, il fallait commencer par savoir que ce paramètre se trouve dans le registre _CONFIG2H. Ensuite, vous deviez écrire la configuration à l’aide de la directive __CONFIG de la façon suivante : __CONFIG _CONFIG2H, _WDT_OFF_2H & _WDTPS_128_2H C’est vrai que ce n’est pas très joli à regarder ni pratique à manipuler. La nouvelle directive CONFIG vous permet maintenant d’écrire tout simplement :

CONFIG WDT = ON ; Mise en service watchdog (ON/OFF) CONFIG WDTPS = 128 ; Postdiviseur du watchdog = 128 Qu’en pensez-vous ? N’est-ce pas plus clair et agréable à relire ? De plus, inutile de vous préoccuper de savoir dans quel registre se situent ces bits de configuration, c’est pris en charge par MPASM®.

167

Page 168: Part5_r1

Dès la version 7 de MPLAB® vous serez gratifié d’un WARNING si vous utilisez l’ancienne syntaxe, qui sera cependant toujours reconnue pour vos anciens PIC18F. 9.3 Où trouver les informations ? Eh non, je n’ai pas la science infuse et je ne suis pas né (quoi qu’en pensent certains) en sachant comment configurer un PIC®. Alors, lorsqu’on programme un nouveau PIC® et qu’on n’a pas encore de fichier maquette, comment faire pour connaître les différentes configurations ? (Qui a dit : je vais voir sur le site de Bigonoff ?). C’est très simple, il suffit de lire le fichier include du PIC® concerné, fourni par Microchip®, et présent dans le répertoire d’installation de MPLAB®. Pour le localiser, faites simplement une recherche avec l’outil de recherche de Windows. Recherche -> tous les fichiers -> P18F256.inc. Voyons donc ce que contient notre fichier « P18F258.inc ». Ouvrez-le donc dans un éditeur, ou dans le bloc-notes. Tout le début est consacré aux déclarations des registres et noms de bits. Notez en passant que certains « s’amusent » à redéclarer manuellement tous les noms de registres dans leurs sources (je reçois des sources écrits comme ça). Personnellement c’est le genre de passe-temps qui ne m’inspire guère, à chacun son truc. Microchip® vous fourni l’include, autant l’utiliser. En fait, c’est plus loin que les choses deviennent intéressantes : ;========================================================================== ; ; IMPORTANT: For the PIC18 devices, the __CONFIG directive has been ; superseded by the CONFIG directive. The following settings ; are available for this device. Vous retrouvez ici le message signalant que l’ancienne directive __CONFIG a été remplacée. Pour ceux qui se demandent encore « mais comment fait-il pour savoir tout ça ? » (dans mon courrier, j’ai parfois l’impression que certains me considèrent comme un extra-terrestre), la réponse est toute bête : je fais comme vous. En l’occurrence, en passant sur MPLAB® V7, j’ai réassemblé d’anciens fichiers. J’ai obtenu des warnings me signalant l’utilisation d’une nouvelle directive « CONFIG ». Dès lors, je me suis rendu dans l’aide de MPLAB -> MPASM et j’ai recherché « CONFIG ». J’ai obtenu la syntaxe, ne restait plus qu’à connaître le nom des options, qui avaient toutes les chances de se trouver dans le fichier d’include, avec les autres informations. C’était le cas. Voyez donc qu’il n’y a jamais rien de magique, tout le monde peut s’en sortir avec un nouvel outil, il suffit de faire un peu fonctionner sa matière grise.

168

Page 169: Part5_r1

Voici quelques unes des valeurs possibles à assigner aux noms des options de configuration. Je ne liste pas tout, c’est inutile. ; Oscillator Selection: ; OSC = LP LP ; OSC = XT XT ; OSC = HS HS ; OSC = RC RC ; OSC = EC EC-OSC2 as Clock Out ; OSC = ECIO EC-OSC2 as RA6 ; OSC = HSPLL HS-PLL Enabled ; OSC = RCIO RC-OSC2 as RA6 ; ; Osc. Switch Enable: ; OSCS = ON Enabled ; OSCS = OFF Disabled ; ; Power-up Timer: ; PWRT = ON Enabled ; PWRT = OFF Disabled ; ; Brown-out Reset: ; BOR = OFF Disabled ; BOR = ON Enabled ; Constatez que Microchip® vous donne toutes les infos nécessaires, c’est du reste son intérêt. Comme vous l’avez vu si vous avez recherché dans l’aide de MPASM®, la syntaxe est simplement : CONFIG nom_de_l’option = valeur_possible Par exemple, pour mettre le brown-out reset en service, vous écrirez : CONFIG BOR = ON Difficile de faire plus simple. N’oubliez pas que tout bit dont vous ne précisez pas la valeur sera initialisé à 1 par défaut, à vous de voir à quoi ça correspond. Je vous conseille cependant de forcer toutes les options possibles à la valeur de votre choix, ça vous évite d’en oublier. Si vous descendez plus bas dans le fichier vous trouverez tout d’abord les noms et adresses des registres utilisés anciennement dans la directive __CONFIG.

Notez que ces informations vous seront utiles si vous désirez modifier le contenu de vos configurations à partir de votre programme (écriture en mémoire flash-zone configuration) ainsi qu’expliqué dans le chapitre consacré aux écritures en mémoire programme : ; Configuration Bits ; ; Data Sheet Include File Address

169

Page 170: Part5_r1

; CONFIG1H = Configuration Byte 1H 300001h ; CONFIG2L = Configuration Byte 2L 300002h ; CONFIG2H = Configuration Byte 2H 300003h ; CONFIG4L = Configuration Byte 4L 300006h ; CONFIG5L = Configuration Byte 5L 300008h ; CONFIG5H = Configuration Byte 5H 300009h ; CONFIG6L = Configuration Byte 6L 30000Ah ; CONFIG6H = Configuration Byte 6H 30000Bh ; CONFIG7L = Configuration Byte 7L 30000Ch ; CONFIG7H = Configuration Byte 7H 30000Dh Remarquez que pour notre 18F258 nous avons pas moins de 10 registres de configuration, ce qui justifie la quasi-obligation d’insérer les configurations dans votre code source.

L’organisation en mots de 16 bits est rappelée par les dénominations « H » pour tout registre situé à une adresse impaire et « L » pour tout registre situé à une adresse paire. N’oubliez pas en effet qu’en mémoire programme, l’octet de poids fort se trouve toujours avant l’octet de poids faible, l’ordre est donc inversé par rapport à ce qui vous semblerait logique. Ensuite, nous trouvons les valeurs possibles à placer dans chacun des registres pour valider les bonnes options : ;Configuration Byte 1H Options _OSCS_ON_1H EQU H'DF' ; Oscillator Switch enable _OSCS_OFF_1H EQU H'FF' _LP_OSC_1H EQU H'F8' ; Oscillator type _XT_OSC_1H EQU H'F9' _HS_OSC_1H EQU H'FA' _RC_OSC_1H EQU H'FB' _EC_OSC_1H EQU H'FC' ; External Clock w/OSC2 output divide by 4 _ECIO_OSC_1H EQU H'FD' ; w/OSC2 as an IO pin (RA6) _HSPLL_OSC_1H EQU H'FE' ; HS PLL _RCIO_OSC_1H EQU H'FF' ; RC w/OSC2 as an IO pin (RA6) ;Configuration Byte 2L Options _BOR_ON_2L EQU H'FF' ; Brown-out Reset enable _BOR_OFF_2L EQU H'FD' _PWRT_OFF_2L EQU H'FF' ; Power-up Timer enable _PWRT_ON_2L EQU H'FE' _BORV_25_2L EQU H'FF' ; BOR Voltage - 2.5v _BORV_27_2L EQU H'FB' ; 2.7v _BORV_42_2L EQU H'F7' ; 4.2v _BORV_45_2L EQU H'F3' ; 4.5v Ce sont les syntaxes que nous utilisions avec les versions de MPLAB® V6. Dorénavant, l’utilisation de la directive CONFIG évite cette utilisation fastidieuse, mais toujours reconnue pour des raisons évidentes de rétro-compatibilité. Vous avez maintenant toutes les informations utiles pour réaliser vos propres configurations et comprendre celles des autres internautes, quelle que soit la version de MPLAB®. Ces configurations sont reprises dans le fichier maquette fourni.

170

Page 171: Part5_r1

10. Mise en circuit 10.1 Types d’oscillateur Comme tous les autres PIC®, notre 18F a besoin d’une horloge pour fonctionner. Tout comme les 16F, il dispose d’un oscillateur intégré qui permet d’utiliser un simple quartz externe comme référence de fréquence, mais il permet également d’autres modes. Le choix de l’oscillateur est déterminé par le positionnement correct des bits de configuration, via la directive CONFIG. Voici les choix possibles :

CONFIG OSC = LP ; Mode faible consommation CONFIG OSC = XT ; Mode faible fréquence CONFIG OSC = HS ; Mode haute fréquence CONFIG OSC = RC ; Avec réseau RC externe CONFIG OSC = RCIO ; Réseau RC avec OSC2 = RA6 CONFIG OSC = EC ; Horloge externe avec OSC = sortie horloge CONFIG OSC = ECIO ; Horloge externe avec OSC = RA6 CONFIG OSC = HSPLL ; Quartz avec PLL activée

Vous trouverez d’autres modes sur certains 18F, référez-vous dans ce cas au datasheet. 10.2 Utilisation d’un quartz ou d’un résonateur Dans les modes LP, XT, HS, et HS_PLL (parfois appelé HS4 dans les datasheets), le PIC® utilise son oscillateur interne complété par un quartz externe accompagné de 2 condensateurs, ou par un résonateur externe. La figure 2-1 du datasheet indique la façon de connecter. La résistance n’est nécessaire que pour certains types de quartz (voir datasheet du quartz), c’est inutile pour l’immense majorité des quartz que vous trouverez chez les revendeurs pour vos PIC®. Le mode HSPLL met en service une boucle à verrouillage de phase (PLL) interne qui fourni au PIC® une horloge de fréquence quadruple à la fréquence de l’oscillateur. Ceci permet d’utiliser un quartz de 10Mhz pour faire tourner votre PIC® à 40Mhz. Notez que le quartz oscillera bien à 10Mhz et non à 40Mhz, la multiplication se fait en interne.

171

Page 172: Part5_r1

Remarquez que votre 18F258 (ou votre 18F252) est compatible broche à broche avec un 16F876. De la même façon, un 18F458 ou un 18F452 seront compatibles avec un 16F877. Les deux quartz sur le condensateur ne sont en général pas nécessaires si vous utilisez un résonateur Dans ce cas, cependant, la précision de l’horloge sera moins bonne. Avec un quartz trouvé couramment dans le commerce, vous pouvez espérer une précision comprise entre 30 et 100 ppM (parts par Million), soit 0,00003%. Si vous désirez une précision spécifique vous devez étudier les datasheets des quartz proposés. Pour répondre aux interrogations de bon nombre d’internautes, une précision de 30ppM sur un PIC® utilisé comme horloge donnera une erreur typique de l’ordre de (3600 * 24*30)/1000000 = 2.5 secondes par jour.

Vous pouvez espérer arriver à une précision de l’ordre de 5ppM en recherchant les quartz les meilleurs, et encore mieux si vous compensez les dérives thermiques. Ceci sort cependant du cadre de cet ouvrage. Les condensateurs pourront se situer en général dans une fourchette comprise entre 15 et 56pF, fonction de la fréquence utilisée et des caractéristiques du quartz. Si le besoin s’en fait sentir, consultez le datasheet de votre quartz pour connaître la valeur de charge préconisée. En général, avec un quartz de 10Mhz, j’utilise 15 ou 27 pF. En remplaçant un des condensateurs par un condensateur ajustable, on peut très légèrement influer sur la fréquence de fonctionnement. Vous pouvez faire l’économie de la résistance de 10K et du bouton de reset si vous le désirez. Conservez cependant les condensateurs de découplage de 100nF.

172

Page 173: Part5_r1

Prenez la peine de relier toutes les pins Vss (2 sur ce modèle) et toutes les pins Vdd (une seule dans ce cas). Bien que toutes les pins Vdd soient reliées ensemble en interne, de même que toutes les pins Vss sont reliées ensemble, il faut les relier toutes aux alimentations correspondantes pour limiter les courants internes dans le PIC® et permettre une mise en œuvre correcte de la compatibilité électromagnétique (CEM). Pour un petit montage d’essai vous pouvez vous en passer, mais pas pour une application finale. Avec l’utilisation d’un quartz, vous pouvez tabler sur les paramètres suivants : Fréquence Quartz Config. Oscillateur Fréquence PIC® Cond. recommandé

32 KHz OSC = LP 32 KHz 33 pF 200 KHz OSC = LP 200 KHz 15 pF 200 KHz OSC = XT 200 KHz 47 à 68 pF 1 MHz OSC = XT 1 MHz 15 pF 4 MHz OSC = XT 4 MHz 15 pF 4 MHz OSC = HS 4 MHz 15 pF 8 MHz OSC = HS 8 MHz 15 à 33 pF 20 MHz OSC = HS 20 MHz 15 à 33 pF 25 MHz OSC = HS 25 MHz 33 pF 4 MHz OSC = HSPLL 16 MHz 15 pF 8 MHz OSC = HSPLL 32 MHz 15 à 33 pF 10 MHz OSC = HSPLL 40 MHz 33 pF

Vous avez souvent plusieurs possibilités pour la même fréquence, faites alors votre propre choix. Notez qu’en général dans ce cas le mode le plus au-dessus du tableau consomme le moins mais met le plus de temps pour démarrer. Remarquez qu’au dessus de 25 MHz vous êtes obligé d’utiliser le mode HSPLL avec un quartz du quart de la fréquence de travail du PIC® dont vous avez besoin. Les valeurs recommandées pour les condensateurs sont tout à fait indicatives. La valeur exacte dépend du datasheet du quartz utilisé et de plus cette valeur est très peu critique à l’usage. Une astuce pour accélérer le démarrage de l’oscillateur (et donc rendre votre PIC® plus rapidement opérationnel) est d’utiliser une valeur de condensateur de condensateur sur OSC2 plus grande que celle du condensateur sur OSC1. 10.3 Utilisation d’un réseau RC Si vous avez une application peu critique du point de vue des timings, et si vous voulez économiser (pour raison de coût ou de place) le quartz accompagné de ces condensateurs, vous pouvez vous contenter d’un simple réseau RC externe. Voici le montage correspondant :

173

Page 174: Part5_r1

Dans ce mode, la fréquence de fonctionnement est déterminée par :

- La valeur de la tension d’alimentation Vdd - La valeur de la résistance externe RExt - La valeur du condensateur externe CExt - Les dispersions des paramètres du PIC® - La température

La valeur de RExt doit être comprise entre 3KOhms et 100 KOhms et le condensateur doit

avoir une capacité au moins égale à 20pF. Le courant consommé par l’oscillateur en mode RC est approximativement de

Vdd/2RExt, que vous prendrez en compte si vous voulez calculer la consommation du montage au plus juste (l’idéal étant quand même de le mesurer). Trouver les courbes donnant la fréquence en fonction de ces paramètres n’est pas chose aisée. Vous ne les trouverez pas par exemple dans ce datasheet. Voici à titre d’exemple une courbe donnant les fréquences à partir des valeurs de résistance dans les conditions suivantes :

- T° de 25°C - Valeur de condensateur de 300pF

174

Page 175: Part5_r1

Remarquez que vous avez autant de courbes de ce type que de valeurs différentes de Cext et de valeurs différentes de t°. Tout afficher relève de la gageure, ce qui explique le peu de documentations à ce sujet. Avec une tension et une température parfaitement stables et des valeurs de composants sous la barre des 1%, vous pouvez espérer (c’est le cas de le dire) une précision de l’ordre de 1,5 à 2%. Pour la plupart des applications, cette précision se révèle souvent largement suffisante. Vous pouvez utiliser cette technique pour des fréquences comprises entre approximativement 25KHz et 4 MHZ. En dehors de ces limites, à vous d’expérimenter. Comme il est impossible de donner toutes les courbes possibles, voici quelques valeurs typiques à utiliser avec une alimentation de 5V sous une t° de 25°C.

Fréquence Valeur de Rext Valeur de Cext 28.3 KHz 100 KOhms 300 pF 77.2 KHz 100 KOhms 100 pF 268 KHz 100 KOhms 22 pF 269 KHz 10 KOhms 300 pF 501 KHz 5 KOhms 300 pF 688 KHz 10 KOhms 100 pF 707 KHz 3.3 KOhms 300 pF 1.27 MHz 5 KOhms 100 pF 1.80 MHz 3.3 KOhms 100 pF 2.35 MHz 10 KOhms 22 pF 4.12 MHz 5 Khz 22 pF

175

Page 176: Part5_r1

Pour d’autres valeurs de t° ou de tension, il vous faudra vous renseigner directement chez Microchip® ou effectuer des expérimentations. Pour terminer, voyons l’utilisation de la pin RA6/OSC dans le cas de l’utilisation d’un réseau RC. Elle peut en fait dans ce mode avoir 2 fonctions différentes en fonction du choix de la définition de vos paramètres de configuration : Si OSC = RC, la pin est considérée comme fonctionnant en « Osc » et fournit à sa sortie une fréquence égale à la fréquence de fonctionnement du PIC® divisée par 4. Si OSC = RCIO, la pin est considérée comme la pin standard RA6 et est utilisable en tant que pin « ordinaire ». Le mode RCIO vous permet donc de récupérer une pin supplémentaire par rapport au fonctionnement avec un quartz, ça peut être un avantage déterminant dans votre application pour justifier ce choix. 10.4 Utilisation d’une horloge externe Dans ce cas de figure, un oscillateur externe fournit le signal d’horloge directement au PIC® via sa pin OSC1. Si vous choisissez le mode « OSC = EC », le PIC® renverra le signal d’horloge divisé par 4 sur sa sortie OSC2. Si par contre vous choisissez le mode « OSC = ECIO », la pin OSC2 devient RA6 et peut être utilisée comme pin d’entrée/sortie classique. Ce mode est un moyen de récupérer une pin supplémentaire sur votre PIC®. Avec un oscillateur externe il n’y a pas de délai de retard au reset du PIC® pour le démarrage de l’horloge. Pour les applications qui doivent démarrer au reset dans un temps très court, l’utilisation de l’oscillateur externe peut être une solution (à condition qu’il soit alimenté avant que le PIC® ne soit mis sous tension). Certaines applications particulières, comme les cartes ISO7816 peuvent aussi utiliser ce mode, je vous renvoie aux cours part1 et part2 pour plus d’explications sur la norme ISO7816. Je ne vous donne pas de schéma, les explications suffisent largement. 10.5 Utilisation de 2 oscillateurs Le 18F258 (et bien d’autres de cette famille) dispose de la possibilité d’utiliser un oscillateur principal et un oscillateur secondaire permettant de travailler à deux fréquences distinctes. Ceci est particulièrement pratique pour économiser de l’énergie tout en laissant le PIC® fonctionner à allure réduite, ou pour n’enclencher la vitesse maximale qu’en cas d’utilisation de fonctions particulières.

176

Page 177: Part5_r1

La réduction des vitesses d’oscillateur peut être également pratique lors de communications à très basses vitesses. Voici un schéma d’utilisation typique du double oscillateur :

L’horloge primaire (connectée sur OSC1/OSC2) peut être de n’importe lequel des types vus précédemment. Sur le circuit secondaire, utilisant T1OSO/T1OSI, vous connecterez un quartz muni de ses condensateurs (ou éventuellement un résonateur, à tester). Le second oscillateur a été explicitement étudié par Microchip® pour travailler à des fréquences utilisées en horlogerie, aux alentours de 32 Khz. Apparemment rien ne semble déconseiller l’utilisation d’autres fréquences, mais cet oscillateur est étudié pour travailler à des fréquences beaucoup plus basses que l’oscillateur principal. Notez que l’utilisation des fréquences centrées sur 32 KHz ne doit rien au hasard, puisque 32,768 Khz permet, en utilisant un timer 16 bits sans prédiviseur, d’obtenir un temps exact de 2 secondes. Un timer 8 bits prédivisé par 128 donnera une base de temps exactement de 1 seconde. Cette valeur est donc particulièrement efficace pour des applications genre horloge. Voyons maintenant comment utiliser cette possibilité.

177

Page 178: Part5_r1

La première chose pour utiliser l’oscillateur secondaire, c’est que vous l’ayez explicitement permis dans vos registres de configuration, via le bit OSCS du registre _CONFIG1H. Avec l’ancienne syntaxe MPLAB® V6, ça donnait : __CONFIG _CONFIG1H, _OSCS_ON et avec la syntaxe MPLAB® V7 (que nous adopterons désormais), ce sera : CONFIG OSCS = ON Si ce bit de configuration n’est pas activé, vous serez dans l’impossibilité d’utiliser l’oscillateur secondaire. C’est évidemment voulu, car il est parfaitement inutile de basculer sur un oscillateur qui n’existe pas, et, de plus, l’utilisation de l’oscillateur secondaire vous fait évidemment perdre les pins RC0 et RC1. La seconde chose est que vous ne pouvez enclencher dans votre programme l’oscillateur secondaire que si celui-ci est en cours de fonctionnement. Ceci se valide en mettant le timer1 en service via le bit T1OSCEN du registre T1CON. N’oubliez pas qu’entre le moment où le timer est mis en service et le moment où le quartz oscille correctement, il peut s’écouler un certain temps, dépendant du quartz et de ses condensateurs, ainsi que de la tension d’alimentation. Si la commutation doit être rapide, lancez votre oscillateur secondaire dès le début de votre programme/ Ensuite, si les deux conditions précédentes sont réunies, vous pouvez alors durant votre programme passer d’un oscillateur à l’autre en agissant sur le bit SCS du registre OSCCON. C’est du reste le seul bit utilisé de ce registre pour notre 18F258. Pour résumer, imaginons que notre oscillateur principal soit équipé d’un quartz à 40Mhz et notre oscillateur secondaire d’un quartz à32Khz : Zone de configuration

CONFIG OSCS = ON ; à indiquer dans les configurations Zone de programme Instruction ; déroulement du programme Instruction ; le PIC tourne à 40Mhz Instruction .. bsf T1CON,T1OSCEN ; démarrage de l’oscillateur secondaire instruction ; le PIC tourne à 40 Mhz instruction ; l’oscillateur secondaire n’est pas stable instruction ; le PIC tourne à 40Mhz .. .. instruction ; l’oscillateur secondaire est stable instruction ; le PIC tourne à 40Mhz .. .. bsf OSCCON,SCS ; passage sur l’oscillateur secondaire instruction ; le PIC tourne à 32Khz instruction instruction .. bcf OSCCON,SCS ; passage sur l’oscillateur principal

178

Page 179: Part5_r1

instruction ; le PIC tourne à 40Mhz instruction .. bsf OSCCON,SCS ; passage sur l’oscillateur secondaire instruction ; le PIC tourne à 32Khz .. Vous avez maintenant une vue sur la méthode à utiliser pour commuter. N’oubliez pas que vos interruptions s’exécuteront à la vitesse actuelle du PIC®. Inutile de commuter la vitesse sur l’oscillateur principal durant l’interruption pour espérer gagner du temps, vous en perdriez énormément (voir les remarques ci-dessous). Notez ici quelques remarques importantes sur l’utilisation du mécanisme de double oscillateur :

Le passage sur l’oscillateur principal n’arrête pas l’oscillateur secondaire, son fonctionnement dépend du bit T1OSCEN. Le passage sur l’oscillateur secondaire stoppe par contre l’oscillateur primaire, car en général on travaille à basse vitesse pour des raisons d’économie d’énergie. Lorsqu’on commute de l’oscillateur principal vers l’oscillateur secondaire (supposé actif), le PIC® s’arrête de fonctionner durant un peu plus de 8 cycles d’oscillateur secondaire. Lorsqu’on commute de l’oscillateur secondaire vers l’oscillateur principal, le PIC® s’arrête durant le temps nécessaire à l’oscillateur principal pour redémarrer, ce qui peut prendre plusieurs ms. Si le bit de configuration OSCS et le bit T1OSCEN ne sont pas positionnés tous les deux, le bit SCS reste bloqué à 0 quelles que soient vos tentatives, vous ne pourrez donc pas forcer le PIC® à travailler sur l’oscillateur secondaire Lors de n’importe quel type de reset ainsi qu’à la mise sous tension, le bit SCS est toujours forcé à 0. Votre PIC® démarre donc toujours sur l’oscillateur principal après un reset.

179

Page 180: Part5_r1

Notes :

180

Page 181: Part5_r1

11. Le reset 11.1 Types de reset Notre PIC® peut être reseté pour plusieurs raisons. La première chose à savoir est qu’un reset branche le compteur de programme de notre PIC® à l’adresse 0x00. Il reprend donc votre programme depuis le début. La seconde chose importante est que selon le type de reset, certains registres peuvent prendre une valeur « imposée » ou ne pas être modifié par le reset en question. Il est donc important si vous voulez agir de façon différente selon le type de reset d’examiner les tableaux des registres dans les datasheets et de regarder comment sont affectés les bits en fonction du type de reset. Pour notre 18F258, les causes possibles de reset sont :

- La mise sous tension, ou POR (pour Power On Reset) - L’action sur la pin MCLR durant le déroulement normal du programme - L’action sur la pin MCLR pendant que le PIC® est en mode sleep - Débordement du watchdog durant le déroulement normal du programme - La chute de la tension d’alimentation, ou BOR (pour Brown Out Reset) - L’exécution de l’instruction « reset » - Le débordement de la pile (Stack full) - L’extraction d’une adresse de la pile déjà vide (Stack underflow) Vous voyez que ça nous fait déjà quelques possibilités. Notez que certaines des

possibilités ne peuvent survenir que si vous les avez autorisées dans les bits de configuration. A ces possibilités nous ajouterons deux événements qui ne sont pas des resets (puisque ne connectent pas le PIC® sur l’adresse 0x00, mais qui modifient certains bits que nous allons utiliser :

- Le réveil du PIC® qui était en mode sleep par le débordement du watchdog - Le réveil du PIC® qui était en mode sleep par une source d’interruption

11.2 Utilité de la détection du type de reset Commençons par la façon de détecter au démarrage le type de reset. Ceci s’effectue principalement (excepté les reset dûs à la pile) en examinant les différents bits du registre RCON. Il peut être nécessaire de déterminer le type de reset dans la séquence d’initialisation pour savoir par exemple comment agir au niveau des variables. Un exemple simple serait par exemple un robot circulant sur un terrain de jeu. Il démarre d’un endroit connu et mémorise sa position actuelle en incrémentant des variables en RAM. A la mise sous tension du robot, il faut donc initialiser les variables en fonction du point de départ du robot (mettons par exemple toutes les variables à 0).

181

Page 182: Part5_r1

Imaginons qu’une perturbation CEM importante vienne à « planter » notre programme, il serait alors reseté par le watchdog. Sans précaution particulière le programme réinitialiserait les variables à 0 et le robot « penserait » qu’il se trouve au point de départ, ce qui serait faux.

Le remède de l’utilisation du watchdog dans ce cas risquerait bien d’être pire que le mal. Dans le cas du reset par watchdog, il ne faudrait pas dans ce cas effacer les variables

contenant les positions du robot, afin qu’il puisse poursuivre son parcours. Nous aurions donc une routine d’initialisation du style : Init Si reset = power on, alors initialiser les variables Sinon, ne pas les initialiser

Un autre cas typique est le reset sur chute de tension d’un montage alimenté par piles ou accus. Dans ce cas, inutile de redémarrer tant que l’utilisateur n’a pas correctement rechargé les accus, ou remplacé les piles. On restera donc dans un état sécurisé tant que l’alimentation n’est pas redevenue optimale. Il nous faut donc une méthode pour connaître dès la phase d’initialisation à cause de quel événement nous nous trouvons en train d’initialiser notre PIC®. C’est de cela dont nous allons maintenant parler. 11.3 Mécanismes de détection Le secret de la détection du type de reset se trouve dans le registre RCON. Voici sa structure : RCON b7 b6 b5 b4 b3 b2 b1 b0 IPEN - - RI TO PD POR BOR Nous ferons abstraction du bit IPEN, qui concerne les interruptions et qui n’a rien à voir avec le type de reset. Nous en avons déjà parlé dans le chapitre consacré aux interruptions, je n’y reviens plus. Les resets provoqués par la pile font également intervenir un autre registre, STKPTR dont voici la structure : STKPTR b7 b6 b5 b4 b3 b2 b1 b0 STKFUL STKUNF - SP4 SP3 SP2 SP1 SP0 Seuls les bits STKFUL et STKUNF nous intéressent ici. Les bits SPx indiquent en fait l’emplacement de la pile actuellement pointé (comme la pile comporte 32 emplacements il nous faut 5 bits). Nous avons déjà parlé de STKPTR dans le chapitre consacré aux manipulations de la pile.

182

Page 183: Part5_r1

Notez au passage que le pointeur de pile est accessible en lecture et en écriture sur les 18F. Ceci ouvre la porte à différentes astuces dont nous avons parlé, mais je vous conseille tout de même d’être prudents et de conserver une structure correcte de vos programmes. Toute l’astuce de la détection du type de reset vient du fait que les différents bits qui composent ce registre ne sont pas affectés de la même façon selon le type de reset qui est intervenu. Voici le tableau qui donne l’état des différents bits en fonction du type d’événement susceptible de les modifier :

Evénement RI TO PD POR BOR FUL UNFMise sous tension 1 1 1 0 0 0 0

Action sur MCLR en mode normal U U U U U U U Action sur MCLR en mode sleep U 1 0 U U U U

Instruction reset 0 U U U U U U Débordement de pile U U U 1 1 U 1

Dépilement sur pile vide U U U 1 1 1 U Débordement watchdog en mode normal U 0 1 U U U U Débordement watchdog en mode sleep U 0 0 U U U U

Chute de tension d’alimentation 1 1 1 U 0 0 0 Réveil du mode sleep par interruption U 0 0 U U U U

FUL représente le bit STKFUL et UNF le bit STKUNF «U » représente un bit qui n’est pas modifié par l’événement (unchanged) Nous avons donc une série de bits, qui se retrouvent soit à un niveau forcé, soit inchangés à la suite d’un événement. Certains sont en lecture seule et d’autres peuvent être forcés par software. Nous devons nous débrouiller avec tout ça pour être capable de détecter lors de notre séquence d’initialisation quelle est la cause du reset. Tout d’abord, faites attention. Dans les datasheets j’ai trouvé beaucoup de contradictions (erreurs) au niveau de l’état des bits à la mise sous tension. On trouve également des contradictions d’un datasheet à l’autre. Par exemple, si vous prenez la figure « register 3-1 » page 27 du datasheet que je vous fourni, vous constatez qu’on renseigne POR et BOR comme ayant la valeur « 1 » à la mise sous tension, alors que c’est contradictoire avec le tableau 3-2 qui suit directement. Idem avec le datasheet général des 18F, qui renseigne BOR comme étant indéterminé à la mise sous tension. Pire, si vous regardez la figure 4-3 page 58, TO et PD sont renseignés en lecture seule alors qu’ils sont renseignés en lecture/écriture figure 3-1 page 27. Vous trouverez encore plein d’inepties, comme le fait que STKFUL et STKUNF soient repris en « unchanged » sur les tableaux lors de la mise sous tension (il faudra m’expliquer comment c’est possible).

183

Page 184: Part5_r1

Bref, nous allons tenter de faire des détections dont nous sommes certains qu’elles fonctionnent, quitte à forcer des bits qui n’avaient pas besoin de l’être. Pas toujours simple de s’y retrouver, surtout si les documents sensés servir de référence contiennent eux-mêmes des erreurs. 11.4 Détection de la mise sous tension Le premier événement à détecter est la mise sous tension. En effet, la mise sous tension nous impose souvent d’initialiser toutes nos variables utilisées, tous nos registres etc. Notez bien que vous devez lire l’état des registres utilisés dans vos programmes lors des différents événements dans les tableaux fournis dans le datasheet pour savoir lesquels réinitialiser et comment. Si vous regardez le tableau précédent, vous remarquez qu’à la mise sous tension le bit POR (Power On Reset) est forcé à 0. C’est le seul événement qui force ce bit à 0, les autres soit le laissent inchangé, soit le forcent à 1. Bref, si on teste le bit POR en premier, et que dès que testé on le force par logiciel à 1, on est certain que s’il est détecté comme « 0 » c’est qu’il s’agit d’une mise sous tension du PIC®. Bref, on peut écrire un test du genre : test btfsc RCON,POR ; reset = mise sous tension ? bra suite ; non, traiter autre cas possible bsf RCON,POR ; oui, forcer directement POR à 1 xxx ; et traiter mise sous tension Nous avons donc déjà la possibilité de détecter une mise sous tension. Comme nous forçons POR à 1 une fois testé, afin de ne pas confondre la mise sous tension avec un événement ne modifiant pas POR, et étant donné qu’aucun autre événement ne force POR à 0, ce bit ne nous sera plus d’aucune utilité pour différencier les autres resets possibles. 11.5 Reset sur chute d’alimentation Tout d’abord, il faut se souvenir que la détection ou non de la chute d’alimentation dépend des registres de configuration : CONFIG BOR = ON ; Reset si chute de tension (ON/OFF) CONFIG BORV = 42 ; Tension de reset en 1/10ème Volts (20,27,42,45) Le bit de configuration BOR sera donc mis en service (ne pas confondre avec le bit BOR de RCON), et la valeur de détection de la tension minimale de fonctionnement sera introduite dans BORV. Pour les petits malins, inutile d’essayer une autre valeur que les valeurs prévues, il s’agit de commutateurs internes pas d’une valeur programmable. Si tout ceci est fait, examinons notre tableau. Nous constatons que la chute de tension influence 4 bits différents :

184

Page 185: Part5_r1

RI : Reset Instruction : est forcé à 1 lors d’un reset par chute de tension ou d’une mise sous tension, et est forcé à 0 lors d’un reset par instruction « reset » TO : watch-dog Timer Out : est forcé à 1 lors d’un reset par chute de tension, d’une action sur MCLR en mode sleep, et lors de la mise sous tension PD : Power Down detection : est forcé à 1 lors de la mise sous tension, d’un reset par watchdog, et lors d’un reset par chute de tension BOR : Brown Out Reset : est forcé à 0 lors d’une mise sous tension et lors d’une chute de tension d’alimentation. Il est forcé à 1 lors des resets sur problèmes de pile. Nous allons donc devoir choisir parmi ces bits lequel ou lesquels sont les meilleurs choix. Tout d’abord, à ce stade, nous devons nous souvenir que nous avons déjà détecté par POR la condition de mise sous tension, ce bit est donc définitivement inutilisable pour la suite. Si nous examinons PD, nous constatons qu’il ne permet pas de différencier à ce stade le reset qui nous intéresse d’un reset par débordement de watchdog. Donc, on l’élimine. Idem pour TO qui ne permet pas de différencier le reset par chute de tension de celui par l’action sur MCRL. Donc, on élimine également. Voyons le cas de RI, plus subtil. Le cas de la mise sous tension est déjà traité à ce stade, il ne nous préoccupe donc plus. Reste donc deux possibilités :

- RI est forcé à 0 par un reset sur instruction « reset » - RI est forcé à 1 par un reset sur chute d’alimentation.

La première réaction est de se dire : C’est bon, on teste si RI vaut 1. S’il vaut 1, c’est

qu’on a bien un reset sur chute d’alimentation. Effectivement MAIS ce serait oublier qu’il y a une série de reset qui ne modifient pas RI.

Moralité, pour être certain que ce test fonctionne, il faudrait forcer dès la mise sous tension le bit RI à 0. Or, si on fait ça, pour tous les autres resets RI vaudra 0, et vu que c’est l’unique bit forcé (justement à 0) par le reset sur instruction « reset », il nous sera impossible par la suite de différencier le reset par instruction « reset » des autres. Nous ne pouvons donc pas nous permettre de forcer ce bit à 0, sous peine d’être mené à une impasse par la suite. A éliminer, donc.

Reste le bit BOR, dont le nom précise de plus qu’il est fait pour cet usage. On remarque

qu’il est mis à 0 lors de la mise sous tension et mis à 1 lors des resets sur problèmes de piles. Or, pour détecter les resets sur les piles nous disposons de deux bits spécifiques, STKFUL

et STKUNF, pas de problème de ce côté. Pas de problème non plus pour différencier la mise sous tension, c’est déjà fait. Bref, il nous suffit de forcer BOR à chaque reset pour être certain que si on lit 0 c’est

qu’on a bien un reset par chute de tension. Je donnerai le code complet un peu plus loin.

185

Page 186: Part5_r1

11.6 Reset software Puisque nous en avons déjà parlé, nous allons maintenant nous attaquer au reset via l’instruction « reset ». Je ne reprends plus tout le raisonnement précédent, vous avez compris le principe. Nous constatons qu’après avoir déjà détecté les cas précédents, le seul cas où RI est forcé à 0 est le reset par software. Il nous suffit donc de forcer ce bit à 1 après l’avoir testé dans notre routine de reset pour nous assurer qu’une lecture de ce bit à 0 indique un reset par software, via l’instruction « reset ». 11.7 Reset par débordement de pile Nous allons nous attaquer aux resets causés par la pile, car ils permettront de ne laisser que deux événements possibles pour terminer nos tests, ce qui sera nécessaire pour pouvoir tout différencier. Tout d’abord, le reset sur débordement de pile n’intervient que si vous l’avez autorisé dans vos registres de configuration à l’aide de la directive CONFIG : CONFIG STVR = ON Pour détecter un reset par débordement, il nous suffit de tester si le bit STKFUL du registre STKPTR est bien passé à 1. Notez qu’il n’est pas possible de forcer en écriture ce bit à 1, on peut seulement le reseter. Après avoir testé ce bit, il ne faudra évidemment pas oublier de le reseter 11.8 Reset par dépilement excessif La seconde cause de reset provoquée par la pile est due à la tentative de dépiler une adresse de plus qu’elle ne contient (par exemple un return sans call). Il n’y a pas besoin de l’autoriser par configuration, puisque toute tentative de retrait sur la pile vide se solde par le dépilement de l’adresse 0x000000, ce qui correspond à un « reset » sans en être véritablement un (on ne modifie aucun registre). Le test est strictement identique que pour le reset par débordement, il suffit de tester si STKUNF est bien passé à 1. Comme pour le bit précédent, il n’est pas possible de le forcer à 1 par software.

186

Page 187: Part5_r1

11.9 Reset par débordement du watchdog La situation se corse, il nous reste 5 cas et 2 bits. Puisque nous testons les conditions dans notre routine d’initialisation, ça veut dire qu’il y a eu reset du PIC®. On peut donc éliminer à ce stade les deux cas ne provoquant pas le reset. Il nous reste donc 3 cas et 2 bits : Cas TO PD MCLR en mode normal u u MCLR en mode sleep 1 0 Débordement watchdog en mode normal 0 1 La détection d’un « 0 » sur TO et d’un « 1 » sur PD nous permettrait à coup sûr de détecter à ce niveau le cas du reset par débordement du watchdog. MAIS encore faudrait-il que cette combinaison ne se rencontre pas si le reset est provoqué par le MCLR en mode normal, qui lui ne modifie aucun bit. Il nous faut donc examiner toutes les combinaisons possibles d’un reset par MCLR pour voir si cette combinaison est possible. Si on regarde tous les événements qui forcent les deux bits de cette façon, le seul cas est justement le cas du reset par watchdog. On rencontrera donc cette combinaison dans les deux cas suivants :

- Reset par débordement du watchdog en mode normal - Reset par MCLR en mode normal après un reset par débordement du watchdog

Ca nous donne donc deux cas pour la même combinaison. Il nous faut donc en éliminer

un. Le plus évident serait de forcer TO à 1 ou PD à 0 après avoir traité le reset par watchdog, mais malheureusement TO et PD sont en lecture seule (bien que certains endroits du datasheet indiquent le contraire).

Alors, bloqués ? En fait non, car en examinant le datasheet on s’aperçoit que

l’instruction CLRWDT a comme effet secondaire de forcer à 1 les bits TO et PD. Bref, si on ajoute une instruction clrwdt après avoir traité le reset par débordement du

watchdog, nous pourrons distinguer un futur reset par MCLR sans problème. CQFD. Vous pourriez objecter qu’il suffirait en fait de tester uniquement le bit TO, mais ça ne

permettrait pas de distinguer le cas qui nous intéresse d’un reset par MCLR survenu après un réveil du PIC® via le watchdog ou une cause d’interruption (regardez le tableau). Sauf évidemment :

- Si vous ajoutez une instruction clrwdt après chaque réveil du PIC®, après le nop

suivant l’instruction sleep et dans la routine d’interruption - Ou si votre programme ne place jamais le PIC® en mode sleep

187

Page 188: Part5_r1

Pour le premier cas, il est plus simple de tester un bit de plus dans la routine d’initialisation que de placer des clrwdt supplémentaires, et de plus à un endroit déconseillé (les interruptions).

Pour le second cas, ça se passe d’explications, évidemment.

11.10 Reset par MCLR La détection du reset par MCLR est très simple : si ce n’est aucun des resets précédents, alors c’est forcément un reset par MCLR. Nous avons donc traité tous les cas. Nous pouvons si besoin est différencier un reset par MCLR alors que le PIC® était en mode sommeil (sleep) d’un reset en mode de fonctionnement normal (run) en testant le bit PD. Si PD vaut 0, alors le MCLR a eu lieu durant la phase de sommeil du PIC®. MAIS, faites très attention, pour que ce test supplémentaire fonctionne, il faut être certain que PD sera à 1 dans tous les autres cas possibles. C’est-à-dire que dans ce cas vous êtes obligé d’ajouter une instruction « clrwdt » après chaque réveil du PIC®, afin de remettre le bit PD à son état haut. Je n’ai pas inclus la différenciation entre les deux resets par MCLR dans la routine que je vous présente maintenant. 11.11 Réalisation de la routine de test complète Voici le code à introduire dans votre routine d’initialisation si vous désirez tester quel genre de reset vous a amené à l’adresse 0x00. ;============================================================================= ; INITIALISATIONS = ;============================================================================= ;----------------------------------------------------------------------------- ; contient les initialisations exécutées lors d'un reset ; ---------------------------------------------------------------------------- init movlw 0x06 ; port A en mode numérique movwf ADCON1 ; dans registre de contrôle ; initialisation à toujours faire ; -------------------------------- ; PLACER VOS INITIALISATIONS INITIALES ICI ; détection du type de reset ; -------------------------- ; n'est utile que dans certaines applications. ; attention, l'ordre des tests est important inittest btfss RCON,POR ; reset = mise sous tension ? bra initpor ; oui, traiter mise sous tension btfss RCON,BOR ; reset = chute de tension ?

188

Page 189: Part5_r1

bra initbor ; oui, traiter chute de tension btfss RCON,RI ; reset = instruction "reset" ? bra initri ; oui, traiter reset software btfsc STKPTR,STKFUL ; reset = débordement de pile ? bra initful ; oui, traiter débordement btfsc STKPTR,STKUNF ; reset = dépilement excessif ? bra initunf ; oui, traiter retrait sur pile vide btfss RCON,TO ; tester si TO = 0 btfss RCON,PD ; oui, tester si PD = 1 bra initmclr ; PD = 0 ou TO = 1 -> reset sur MCLR ; traiter reset par débordement watchdog ; -------------------------------------- initwdt ; PLACER VOS INITIALISATIONS SUR WATCHDOG ICI bra initsuite ; poursuivre traitement ; traiter mise sous tension du PIC ; -------------------------------- initpor ; par exemple on efface la RAM banque 0 lfsr FSR0,0x00 ; pointer sur l'adresse 0 initpor1 clrf POSTINC0 ; effacer emplacement pointé, et incrément pointeur btfss FSR0H,0 ; banque 0 terminée? ; AUTRES INITIALISATIONS SUR MISE SOUS TENSION ICI bra initsuite ; poursuivre traitement ; traiter reset sur chute de tension ; ---------------------------------- initbor ; PLACER VOS INITIALISATIONS SUR CHUTE DE TENSION (ATTENTE RECHARGE ACCUS ETC.) bra initsuite ; poursuivre traitement ; traiter reset sur instruction reset ; ----------------------------------- initri ; PLACER VOS INITIALISATIONS SUR INSTRUCTION RESET ICI bra initsuite ; poursuivre traitement ; traiter reset sur débordement de pile ; ------------------------------------- initful ; PLACER VOS INITIALISATIONS SUR DEBORDEMENT ICI bra initsuite ; poursuivre traitement ; traiter reset sur débordement de pile ; ------------------------------------- initunf ; PLACER VOS INITIALISATIONS SUR DEPILEMENT EXCESSIF ICI bra initsuite ; poursuivre traitement ; traiter reset sur action MCLR ; ----------------------------- initmclr ; PLACER VOS INITIALISATIONS SUR RESET PAR MCLR ICI bra initsuite ; poursuivre traitement ; forcer tous les bits utiles pour resets futurs ; ----------------------------------------------

189

Page 190: Part5_r1

initsuite bsf RCON,POR ; forcer POR à 1 pour tester futurs resets bsf RCON,BOR ; forcer BOR à 1 pour tester futurs resets bsf RCON,RI ; forcer BOR à 1 pour tester futurs resets bcf STKPTR,STKFUL ; forcer STKFUL à 0 pour tester futurs resets bcf STKPTR,STKUNF ; forcer STKUNF à 0 pour tester futurs resets clrwdt ; forcer TO et PD à 1 pour tester futurs resets ; terminer avec autres initialisations générales ; ---------------------------------------------- ; TERMINER LES DIFFERENTES AUTRES INITIALISATIONS (INTERRUPTS ETC) J’ai intégré ce code dans le fichier maquette que je vous fournis avec le cours (m18F258.inc). 11.12 Reset sur alimentations lentes La montée en tension des alimentations doit se faire de façon suffisamment rapide pour que le circuit de reset interne du PIC® agissant à la mise sous tension fonctionne correctement. Si votre alimentation monte trop doucement en tension vous êtes contraints d’ajouter un circuit RC externe pour retarder le démarrage du PIC®. Le temps de montée de l’alimentation est donné comme paramètre D004 du datasheet, dans les pages concernant les caractéristiques électriques. Pour un 18F258, la vitesse de montée de l’alimentation doit être au moins de 0.05V+ms. Autrement dit, votre tension de 5v doit être présente en moins de 100ms. Voici le circuit à adopter dans le cas où ce ne serait pas le cas :

Les valeurs de R1 et C1 doivent être calculées pour que le temps de charge du condensateur permette à la tension d’alimentation du PIC® d’être stable avant que la tension sur MCLR soit suffisante pour démarrer le PIC®.

190

Page 191: Part5_r1

La constante réelle est assez compliquée à calculer, car elle dépend évidemment aussi de la montée de la tension d’alimentation (le temps de charge ne sera jamais plus court que le temps de montée de l’alimentation). Bref, la formule empirique : T = 0.7RC ne donnera pas un résultat fiable, mais un résultat dans le pire des cas. Vous pouvez cependant vous en servir pour déterminer le temps minimum de mise en service du PIC®, ce qui vous assure de pouvoir démarrer dans toutes les situations. Le temps trouvé doit être supérieur au temps que met l’alim pour arriver à sa tension nominale, avec C1 en farads et R1 en Ohms. Une bonne approche est également de procéder de façon expérimentale avec l’alimentation qui sera utilisée au final. Essayez de garder C le plus petit possible pour éviter des décharges brutales avec le bouton de reset (ou bien passez-vous du bouton reset). La résistance R1 doit avoir une valeur maximale de 40KOhms. Plus vous augmentez, plus votre PIC® est sensible aux perturbations CEM, mais plus vous descendez plus votre temps RC est court à condensateur égal. Notez donc que tout est question de compromis.

R2 doit se situer entre 100 ohms et 1Kohms, elle sert à limiter le courant circulant entre le condensateur et MCLR lorsque la tension du PIC® diminue.

La diode sera de préférence un modèle à faible tension de seuil, genre BAT41. Son rôle

est d’aider à la décharge du condensateur lorsque la tension d’alimentation diminue. 11.13 Reset et programmation ICSP® Lorsque vous utilisez la programmation ICSP® (In-Circuit Serial Programming), c'est-à-dire lorsque vous voulez pouvoir programmer votre PIC® directement sur votre carte d’application, sans utiliser de bootloader mais sans non plus retirer le PIC® de la carte, vous devez prendre certaines précautions au niveau de la pin de reset MCLR. La principale est que la tension de programmation (13V) envoyée par le programmateur sur MCLR/VPP ne doit pas se transmettre au VDD du montage. Or, dans une carte montée, c’est Vdd qui est appliqué sur MCLR, il y a donc une liaison physique entre MCLR et Vdd. Ensuite, le Vpp envoyé par le programmateur sur MCLR/VPP ne peut pas « voir » dans son circuit de condensateur. Il faut donc « isoler » C1 lors de la programmation. Ceci nous amène au circuit suivant :

191

Page 192: Part5_r1

Le connecteur ICSP® est un connecteur à votre choix (j’utilise de simples barrettes sécables), et vous utiliserez un câble (court) reliant ce connecteur à votre programmateur. La diode D1 est une diode à faible tension de seuil, de façon à ne pas trop faire chuter Vdd appliqué sur MCLR en mode de fonctionnement normal. Elle évite de laisser VPP envoyé par le programmateur remonter sur Vdd (indiqué ici +5V). Elle permet également d’isoler C1 lors de la phase de programmation. Si RB6 et RB7 sont utilisés sur la carte, l’idéal est de prévoir un jumper pour isoler ces pins lors de la phase de programmation, ou toute autre protection de type électronique. Attention que Vdd doit être appliqué au PIC® (carte sous tension) pour que la programmation fonctionne. J’utilise cette méthode avec succès sur toutes les cartes utilisant des PIC® en CMS, car c’est pratiquement la seule façon de les (re)programmer. Vous trouverez dans le datasheet les autres renseignements sur les caractéristiques du démarrage des PIC® (temps de démarrage de l’oscillateur etc.) pour peu que cela vous soit nécessaire. J’en ai terminé avec le reset des PIC®, je pense qu’avec ces renseignements et ceux déjà donnés dans les cours précédents, ainsi qu’avec le datasheet, vous devriez avoir tous les renseignements utiles.

192

Page 193: Part5_r1

12. L’étude du fichier maquette

Nous allons maintenant aborder l’étude de notre fichier maquette et de son include. Tout a

déjà en fait été expliqué dans les chapitres précédents, je ne reprends que les points particuliers

12.1 le fichier maquette Commençons comme d’habitude avec notre descriptif d’application ;***************************************************************************** ; Description sommaire * ; --------------------- * ; Fichier maquette pour les PICS 18F248 et 18F258 * ; * ;***************************************************************************** ; * ; NOM : * ; Date création : * ; Date modification : * ; Version : * ; Circuit : * ; Auteur : * ; * ;***************************************************************************** ; * ; Fichier requis: P186F258.inc * ; macros18f.inc (facultatif) * ; * ; Fréquence de travail : MHz * ; Fréquence du quartz : MHz * ; * ;***************************************************************************** ; * ; Pins utilisées : * ; ---------------- * ; * ; RA0/AN0/VRef : * ; RA1/AN1 : * ; RA2/AN2/Vref- : * ; RA3/AN3/Vref+ : * ; RA4/T0CKI : * ; RA5/AN4/SS/LVDIN : * ; * ; OSC1/CLKI : * ; OSC2/CLKO/RA6 : * ; * ; RC0/T1OSO/T1CKI : * ; RC1/T1OSI : * ; RC2/CCP1 : * ; RC3/SCK/SCL : * ; RC4/SDI/SDA : * ; RC5/SDO : * ; RC6/TX/CK : * ; RC7/RX/DT : * ; * ; RB0/INT0 : * ; RB1/INT1 : *

193

Page 194: Part5_r1

; RB2/CANTX/INT2 : * ; RB3/CANRX : * ; RB4 : * ; RB5 : * ; RB6 : * ; RB7 : * ; * ;***************************************************************************** ; * ; Notes: * ; * ;***************************************************************************** Ensuite, la déclaration du type de processeur (utilisé par MPASM® comme vérificateur) et l’include du fichier correspondant : LIST p=18F258 ; Définition de processeur #include <p18F258.inc> ; fichier include Viennent ensuite des declarations de constantes qui servent à déterminer des choix d’assemblage dans le fichier construit sur base de ce fichier maquette : ;============================================================================= ; SELECTION DES FONCTIONS UTILISEES = ;============================================================================= ;----------------------------------------------------------------------------- ; Définissez vos choix ici ; Les commandes seront traitées par assemblage conditionnel ;----------------------------------------------------------------------------- TYPEINT EQU 0 ; 0 pour interruptions classiques ; 1 pour interruptions avec priorité ;DEBUG = 1 ; placer un ";" pour la programmation normale ; enlever le ";" pour debug en ICD2 #include <macros18f.inc> ; dans le même répertoire, ou préciser le path ; inutile d'enlever, ça ne consomme pas de ; place si vous ne vous en servez pas. Le type d’interruption utilisée est précisé dans TYPEINT. Un simple changement de la valeur permet de construire votre fichier hex en tenant compte de l’utilisation ou non des interruptions sur priorités. Si vous travaillez avec un debugger sur circuit genre ICD2®, il vous suffit d’enlever le « ; » devant la ligne DEBUG. Ceci met en place des configurations particulières adaptées au debuggage sur circuit (pas de protection des zones, lectures autorisées, pas de watchdog etc). Lorsque vous avez terminé avec le debuggage, remettez le « ; » avant de réassembler votre source de façon définitive pour votre PIC® en mode opérationnel autonome. Vient directement dessous l’inclusion des macros (qui ne prennent aucune place en mémoire tant qu’elles ne sont pas utilisées). Comme les macros sont construites en fonction des choix précédents, c’est pour ça que je les place à cet endroit.

194

Page 195: Part5_r1

Puisque nous avons vu que les configurations étaient différentes en mode normal et en mode debuggage sur circuit, commençons par la partie commune aux deux : ;============================================================================= ; CONFIGURATIONS EN MODE NORMAL = ;============================================================================= ;----------------------------------------------------------------------------- ; Remplacer les paramètres proposés par une des alternatives possibles ; proposées entre parenthèses ; A partir de MPLAB7, la directive "CONFIG" remplace la directive "__CONFIG" ;----------------------------------------------------------------------------- CONFIG OSC = HS ; Oscillateur principal (LP,XT,HS,RC,EC,ECIO,HSPLL,RCIO) CONFIG OSCS = OFF ; Oscillateur secondaire refusé (ON/OFF) Le choix de l’oscillateur est évidemment un choix hardware et ne dépend pas si nous nous trouvons ou non en mode de debuggage. Par contre, dès la ligne suivante, vous paramétrez vos configurations concernant uniquement le mode finalisé de votre programme : IFNDEF DEBUG CONFIG PWRT = ON ; Délai au démarrage (ON/OFF) CONFIG BOR = ON ; Reset si chute de tension (ON/OFF) CONFIG BORV = 42 ; Tension de reset en 1/10ème Volts (20,27,42,45) CONFIG WDT = ON ; Mise en service watchdog (ON/OFF) CONFIG WDTPS = 128 ; Postdiviseur du watchdog (1,2,4,8,16,32,64,128) CONFIG STVR = ON ; Reset sur débordement de pile (ON/OFF) CONFIG LVP = OFF ; Programmation basse tension autorisée (ON/OFF) CONFIG DEBUG = OFF ; Debugger hors service (laisser sur OFF) CONFIG CP0 = OFF ; code protection sur block 0 (ON/OFF) CONFIG CP1 = OFF ; code protection sur block 1 (ON/OFF) CONFIG CP2 = OFF ; code protection sur block 2 (ON/OFF) CONFIG CP3 = OFF ; code protection sur block 3 (ON/OFF) CONFIG CPB = OFF ; code protection sur bootblock (ON/OFF) CONFIG CPD = OFF ; Code protection sur eeprom (ON/OFF) CONFIG WRT0 = OFF ; Protection écriture block 0 (ON/OFF) CONFIG WRT1 = OFF ; Protection écriture block 1 (ON/OFF) CONFIG WRT2 = OFF ; Protection écriture block 2 (ON/OFF) CONFIG WRT3 = OFF ; Protection écriture block 3 (ON/OFF) CONFIG WRTB = OFF ; Protection écriture bootblock (ON/OFF) CONFIG WRTC = OFF ; Protection écriture configurations (ON/OFF) CONFIG WRTD = OFF ; Protection écriture zone eeprom (ON/OFF) CONFIG EBTR0 = OFF ; protect. lecture de table block 0 (ON/OFF) CONFIG EBTR1 = OFF ; protect. lecture de table block 1 (ON/OFF) CONFIG EBTR2 = OFF ; protect. lecture de table block 2 (ON/OFF) CONFIG EBTR3 = OFF ; protect. lecture de table block 3 (ON/OFF) CONFIG EBTRB = OFF ; protect. lecture de table bootblock (ON/OFF) ENDIF

195

Page 196: Part5_r1

Remarquez que c’est de l’assemblage conditionnel (IFNDEF) et que le test n’est pas effectué par le PIC®, mais par MPASM® et uniquement durant l’assemblage du programme (il ne consomme aucune place en mémoire du PIC® et ne nécessite aucun temps de traitement). Ensuite nous trouvons les configurations utilisées uniquement en mode debugger sur circuit. De nouveau il s’agit d’un assemblage conditionnel, les informations ne seront présentes dans le PIC® qu’en phase de debuggage sur circuit. Je vous déconseille de modifier les valeurs présentes dans cette partie, sauf dans des cas particuliers et si vous savez ce que vous faites. Un paramètre incorrect à ce niveau peut empêcher le debugger de fonctionner correctement, même s’il ne semble pas y avoir un rapport direct. ;============================================================================= ; CONFIGURATIONS EN MODE DEBUGGER ICD2 = ;============================================================================= ;----------------------------------------------------------------------------- ; Remplacer les paramètres proposés par une des alternatives possibles ; proposées entre parenthèses. ; Ne pas modifier si le choix n'est pas proposé ; Facilite les commutations entre debugger et normal lorsqu'on debugge avec ; un debugger sur circuit genre ICD2 ;----------------------------------------------------------------------------- IFDEF DEBUG CONFIG PWRT = ON ; Délai au démarrage CONFIG BOR = ON ; Reset si chute de tension (ON/OFF) CONFIG BORV = 42 ; Tension de reset en 1/10ème Volts (20,27,42,45) CONFIG WDT = OFF ; watchdog interdit (non géré) CONFIG STVR = ON ; Reset sur débordement de pile (ON/OFF) CONFIG LVP = OFF ; Programmation basse tension OFF CONFIG DEBUG = ON ; Debugger en service CONFIG CP0 = OFF ; Aucun code protect (pose problème en debuggage) CONFIG CP1 = OFF CONFIG CP2 = OFF CONFIG CP3 = OFF CONFIG CPB = OFF CONFIG CPD = OFF CONFIG WRT0 = OFF ; Aucune protection en écriture CONFIG WRT1 = OFF CONFIG WRT2 = OFF CONFIG WRT3 = OFF CONFIG WRTB = OFF CONFIG WRTC = OFF CONFIG WRTD = OFF CONFIG EBTR0 = OFF ; Aucune protection en lecture de tables CONFIG EBTR1 = OFF CONFIG EBTR2 = OFF CONFIG EBTR3 = OFF CONFIG EBTRB = OFF ENDIF Viennent ensuite les déclarations en zone eeprom. J’y ai mis simplement des petits exemples (à supprimer dans votre application, évidemment) ;=============================================================================

196

Page 197: Part5_r1

; ZONE EEPROM = ;============================================================================= ORG 0xF00000 eep0 DB "Ceci est un texte" ; texte en eeprom eep1 DB 0x01, 0x05 ; valeurs en eeprom TEXTE_EE EQU LOW (eep0) ; adresse relative du texte VAL1_EEP EQU LOW (eep1) ; adresse relative de la première valeur VAL2_EEP EQU LOW (eep1+1) ; adresse relative de la seconde valeur Notez l’utilisation d’étiquettes et d’adresses relatives déclarées avec des EQU. Voyons ensuite notre zone RAM : ;============================================================================= ; VARIABLES ACCESS RAM = ;============================================================================= ; zone de 96 octets ; ----------------- CBLOCK 0x00 ; zone access ram de la banque 0 flags : 1 ; flags divers ; b0 : 1= erreur de vérification local01 : 1 ; variable locale 1 (multi-usage) local02 : 1 ; variable locale 2 (multi-usage) local03 : 1 ; variable locale 3 (multi-usage) ENDC #DEFINE ERR_EEP flags,0 ; flag d'erreur de vérification eeprom #DEFINE ERR_FLASH flags,0 ; flag d'erreur vérif flash (même) J’y ai déclaré un octet contenant des flags, dont un bit est utilisé dans les macros fournies (écriture en eeprom et en flash). Les « defines » en bas correspondent aux noms symboliques utilisés dans ces macros. Comme on n’écrit pas simultanément en flash et en eeprom, j’ai utilisé en fait le même bit du même octet pour reporter les conditions d’erreur. Si vous n’utilisez pas mes macros, vous pouvez supprimer les variables déclarées ici. Vous pouvez également les déplacer, à condition qu’elles soient en access-bank ou dans une banque pointée par BSR à chaque appel de la routine créée par la macro. Les variables locales sont utilisées par les macros, mais peuvent également être utilisée par toute routine qui a besoin d’une variable temporaire. De même, les flags sont réutilisables pour vos routines, à condition de ne pas oublier qu’un appel d’une écriture en flash ou en eeprom modifie le bit 0 de la variable «flags ». ;============================================================================= ; VARIABLES BANQUE 0 = ;============================================================================= ; zone de 160 octets, suite de l'access RAM ; ----------------------------------------- CBLOCK 0x60

197

Page 198: Part5_r1

ENDC IF TYPEINT > 0 ; si interrupts prioritaires en service CBLOCK 0xFD ; modifier en fonction du nombre de sauvegardes wreg_lp : 1 ; sauvegarde WREG pour int. LP bsr_lp : 1 ; idem BSR (facultatif) status_lp : 1 ; idem STATUS ENDC ENDIF En fin de banque 0, j’ai placé les déclarations des registres de sauvegarde nécessaires dans le cas des interruptions sur priorité. Si vous n’utilisez pas les priorités cette zone sera ignorée automatiquement. Evidemment, puisque les sauvegardes s’effectuent par des movff, vous pouvez placer ces variables où bon vous chante en RAM. Le reste de la RAM est inutilisé : ;============================================================================= ; VARIABLES BANQUE 1 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x100 ENDC ;============================================================================= ; VARIABLES BANQUE 2 = ;============================================================================= ; zone de 256 octets ; ------------------ CBLOCK 0x200 ; attention, pour le 18F248, les adresses 0x2F4 à 0x2FF sont utilisées ; par l'ICD2 ENDC ;============================================================================= ; VARIABLES BANQUE 3 = ;============================================================================= ; zone de 256 octets (uniquement 18F258) ; -------------------------------------- CBLOCK 0x300 ENDC ;============================================================================= ; VARIABLES BANQUE 4 = ;============================================================================= ; zone de 256 octets (uniquement 18F258) ; -------------------------------------- CBLOCK 0x400 ENDC ;=============================================================================

198

Page 199: Part5_r1

; VARIABLES BANQUE 5 = ;============================================================================= ; zone de 256 octets (uniquement 18F258) ; -------------------------------------- CBLOCK 0x500 ; attention, pour le 18F258, les adresses 0x5F4 à 0x5FF sont utilisées ; par l'ICD2 ENDC Notez que l’utilisation de l’ICD2® nécessite quelques emplacements RAM que vous devrez laisser libre si vous l’utilisez. Ces emplacements sont indiqués aussi bien pour les 18Fx4x que pour les 48Fx5x. Il s’agit en fait toujours des dernières adresses de la dernière banque existant pour le PIC® en question. Le début du programme est conforme à nos vieilles habitudes : ;============================================================================= ; PROGRAMME = ;============================================================================= ; vecteur de reset ; ---------------- ORG 0x00 nop ; pour debugger goto init ; sauter initialisation Ensuite, nous trouvons notre routine d’interruption qui sera utilisée si vous n’avez pas mis les priorités en service. Notez que seules les portions de code correspondant à vos choix seront écrites en flash, vous ne perdez donc aucune place en conservant la structure complète de cet fichier maquette. ;============================================================================= ; ROUTINE D'INTERRUPTION SANS PRIORITE = ;============================================================================= ;----------------------------------------------------------------------------- ; A utiliser si vous n'avez pas mis les priorités en service ;----------------------------------------------------------------------------- ORG 0x08 IF TYPEINT == 0 ; si interrupts prioritaires pas en service ; sauvegardes complémentaires ; --------------------------- ; placer ici les registres que vous voulez sauvegarder en plus de ; STATUS, BSR et WREG ; routine d'interruption ; ---------------------- ;PLACER LE CODE DE TEST DE VOS INTERRUPTIONS ICI ; restaurations complémentaires ; ----------------------------- intnprest ; placer ici les registres supplémentaires sauvegardés

199

Page 200: Part5_r1

retfie FAST ; retour avec restauration automatique ELSE ; si interruptions prioritaires bra traithp ; traiter interruption haute priorité ENDIF Nous avons déjà parlé de cette structure, je n’y reviens pas. De même pour les routines suivantes qui seront mises en service si vous avez choisi de valider les priorités : ;============================================================================= ; ROUTINE D'INTERRUPTION BASSE PRIORITE = ;============================================================================= ;----------------------------------------------------------------------------- ; A utiliser pour les interruptions basse priorité si vous avez mis les ; priorités en service ;----------------------------------------------------------------------------- IF (TYPEINT>0) ORG 0x18 ; sauvegardes indispensables ; -------------------------- movff STATUS,status_lp ; sauver STATUS movff WREG,wreg_lp ; sauvegardes supplémentaires ; --------------------------- movff BSR,bsr_lp ; si vous changez de banque ; placez ici vos sauvegardes supplémentaires ; routine d'interruption ; ---------------------- ;PLACER LE CODE DE TEST DE VOS INTERRUPTIONS ICI ; restaurations complémentaires ; ----------------------------- intlrest ; placer ici les registres supplémentaires sauvegardés movff bsr_lp,BSR ; si vous changez de banque ; restaurations obligatoires ; -------------------------- movff status_lp,STATUS ; restaurer STATUS movff wreg_lp,WREG ; restaurer WREG ENDIF ;============================================================================= ; ROUTINE D'INTERRUPTION HAUTE PRIORITE = ;============================================================================= ;----------------------------------------------------------------------------- ; A utiliser pour vos interruptions Haute-Priorité ;----------------------------------------------------------------------------- IF (TYPEINT >0) ; si interruptions prioritaires en service traithp ; sauvegardes complémentaires ; ---------------------------

200

Page 201: Part5_r1

; placer ici les registres que vous voulez sauvegarder en plus de ; STATUS, BSR et WREG ; routine d'interruption ; ---------------------- ;PLACER LE CODE DE TEST DE VOS INTERRUPTIONS ICI ; restaurations complémentaires ; ----------------------------- inthprest ; placer ici les registres supplémentaires sauvegardés retfie FAST ; retour avec restauration automatique ENDIF Les initialisations contiennent la mise en mode numérique du PORTA (car c’est quelque chose que les internautes oublient souvent), et la structure générale d’une initialisation. Toute la détection des types de reset est traitée, supprimez si vous n’en avez pas besoin (99% des cas) : ;============================================================================= ; INITIALISATIONS = ;============================================================================= ;----------------------------------------------------------------------------- ; contient les initialisations exécutées lors d'un reset ; ---------------------------------------------------------------------------- init movlw 0x06 ; port A en mode numérique movwf ADCON1 ; dans registre de contrôle ; initialisation à toujours faire ; -------------------------------- ; PLACER VOS INITIALISATIONS INITIALES ICI ; détection du type de reset ; -------------------------- ; n'est utile que dans certaines applications. ; attention, l'ordre des tests est important inittest btfss RCON,POR ; reset = mise sous tension ? bra initpor ; oui, traiter mise sous tension btfss RCON,BOR ; reset = chute de tension ? bra initbor ; oui, traiter chute de tension btfss RCON,RI ; reset = instruction "reset" ? bra initri ; oui, traiter reset software btfsc STKPTR,STKFUL ; reset = débordement de pile ? bra initful ; oui, traiter débordement btfsc STKPTR,STKUNF ; reset = dépilement excessif ? bra initunf ; oui, traiter retrait sur pile vide btfss RCON,TO ; tester si TO = 0 btfss RCON,PD ; oui, tester si PD = 1 bra initmclr ; PD = 0 ou TO = 1 -> reset sur MCLR ; traiter reset par débordement watchdog ; -------------------------------------- initwdt ; PLACER VOS INITIALISATIONS SUR WATCHDOG ICI bra initsuite ; poursuivre traitement

201

Page 202: Part5_r1

; traiter mise sous tension du PIC ; -------------------------------- initpor ; par exemple on efface la RAM banque 0 lfsr FSR0,0x00 ; pointer sur l'adresse 0 initpor1 clrf POSTINC0 ; effacer emplacement pointé, et incrément pointeur btfss FSR0H,0 ; banque 0 terminée? ; AUTRES INITIALISATIONS SUR MISE SOUS TENSION ICI bra initsuite ; poursuivre traitement ; traiter reset sur chute de tension ; ---------------------------------- initbor ; PLACER VOS INITIALISATIONS SUR CHUTE DE TENSION (ATTENTE RECHARGE ACCUS ETC.) bra initsuite ; poursuivre traitement ; traiter reset sur instruction reset ; ----------------------------------- initri ; PLACER VOS INITIALISATIONS SUR INSTRUCTION RESET ICI bra initsuite ; poursuivre traitement ; traiter reset sur débordement de pile ; ------------------------------------- initful ; PLACER VOS INITIALISATIONS SUR DEBORDEMENT ICI bra initsuite ; poursuivre traitement ; traiter reset sur débordement de pile ; ------------------------------------- initunf ; PLACER VOS INITIALISATIONS SUR DEPILEMENT EXCESSIF ICI bra initsuite ; poursuivre traitement ; traiter reset sur action MCLR ; ----------------------------- initmclr ; PLACER VOS INITIALISATIONS SUR RESET PAR MCLR ICI bra initsuite ; poursuivre traitement ; forcer tous les bits utiles pour resets futurs ; ---------------------------------------------- initsuite bsf RCON,POR ; forcer POR à 1 pour tester futurs resets bsf RCON,BOR ; forcer BOR à 1 pour tester futurs resets bsf RCON,RI ; forcer BOR à 1 pour tester futurs resets bcf STKPTR,STKFUL ; forcer STKFUL à 0 pour tester futurs resets bcf STKPTR,STKUNF ; forcer STKUNF à 0 pour tester futurs resets clrwdt ; forcer TO et PD à 1 ; terminer avec autres initialisations générales ; ---------------------------------------------- ; TERMINER LES DIFFERENTES AUTRES INITIALISATIONS (INTERRUPTS ETC) Le programme main ne contient rien qu’une boucle de fin, en guise de rappel. Toutes les variantes sont évidemment possibles, comme les bra main etc. :

202

Page 203: Part5_r1

;============================================================================= ; PROGRAMME PRINCIPAL = ;============================================================================= main fin bra fin ; fin du programme On termine en plaçant dans le source les routines génératrices de sous-routines contenues dans les macros et dont nous aurons besoin pour notre programme. Comprenez donc bien que :

- Si vous ne mettez pas le nom d’une des macros, et bien que les macros soient incluses au début du programme, AUCUN code ne sera généré concernant cette macro et donc aucune place ne sera occupée en mémoire programme.

- Vous ne mettez vos macros qu’une seule fois, car ce sont des macros qui génèrent des

sous-routines et non des macros à répéter à chaque emplacement.

- Regardez dans le fichier « macros18f.inc » pour savoir comment appeler les sous-routines créées.

Vous hésitez parfois entre utiliser une macro et utiliser un sous-programme. Ici, ce sont

des macros qui créent des sous-programmes. L’intérêt par rapport à un include contenant toutes les sous-routines directement est que seules les sous-routines dont vous avez besoin sont intégrées dans le code, et donc sont présentes en mémoire programme.

Le fichier macro contient également d’autres macros traditionnelles, à utiliser de façon

répétitive aux endroits voulus de votre programme (STARTINT par exemple). Voyez à ce niveau le chapitre suivant. ;============================================================================= ; SOUS-ROUTINES VIA MACROS = ;============================================================================= ;----------------------------------------------------------------------------- ; Sous-routines créées par les macros du fichier macros18f.inc ; Supprimez les macros inutilisées. ; Pour les détails sur l'utilisation, voir fichier macros18f.inc ;----------------------------------------------------------------------------- READ_EEPROM ; routines de lecture en mémoire eeprom WRITE_EEPROM ; routine d'écriture en mémoire eeprom ERASE_FLASH ; routine d'effacement d'un bloc en flash WRITE_FLASH ; écriture en mémoire flash END ; fin de programme Bien évidemment notre programme se termine avec la directive END.

203

Page 204: Part5_r1

12.2 Le fichier des macros Examinons maintenant notre include contenant nos macros : L’en-tête précise de quoi il s’agit : ;***************************************************************************** ; MACROS POUR VOS PROGRAMMES 18F * ;***************************************************************************** ; * ; NOM : macros18f.inc * ; Date création : 26/03/2007 * ; Date modification : 26/03/2007 * ; Version : 1.00 * ; Auteur : Bigonoff * ; * ;***************************************************************************** ; POUR LES MACROS ORDINAIRES * ; -------------------------- * ; Placer le nom de la macro à l'endroit où vous voulez l'utiliser * ; Les macros ordinaires peuvent être utilisées à plusieurs endroits * ; * ; POUR LES MACROS GENERATRICES DE SOUS-ROUTINE * ; -------------------------------------------- * ; 1) A un unique endroit de votre programme, placez le nom de la macro que * ; vous désirez intégrer (après votre programme principal) * ; * ; 2) Dans votre programme vous appelez la sous-routine créée par la macro * ; en faisant un call ou un rcall vers l'adresse de début de la routine * ; * ; 3) Lisez les en-tête de chaque macro pour avoir les explications * ; ainsi que les variables à déclarer * ; * ; REMARQUE : UNE MACRO NON UTILISEE NE CONSOMME AUCUN EMPLACEMENT EN MEMOIRE * ; INUTILE DONC D'EFFACER CELLES QUI NE VOUS SONT PAS UTILES * ; * ;***************************************************************************** Notez la distinction entre une macro à utiliser directement dans le code et les macros destinées à créer des sous-programme (et qui sont se terminent pas « return ») et qui, elles, ne seront présentes qu’une seule fois au maximum dans votre fichier source. Commençons par une macro « standard » à utiliser chaque fois que vous voulez couper toutes les interruptions dans votre programme : ;============================================================================= ; COUPER LES INTERRUPTIONS (MACRO ORDINAIRE) ;============================================================================= ;----------------------------------------------------------------------------- ; Utilise la déclaration TYPEINT du fichier maquette ; Coupe les interruptions générales de façon appropriée au type ;----------------------------------------------------------------------------- STOPINT macro IF TYPEINT == 0 ; si pas de priorité en service bcf INTCON,GIE ; couper simplement interruptions ELSE ; si priorités en service bcf INTCON,GIEL ; couper d'abord interrupts LP bcf INTCON,GIEH ; puis HP

204

Page 205: Part5_r1

ENDIF endm Remarquez que STOPINT, c’est son nom, coupe les interruptions de façon différente selon que vous avez ou non mis les interruptions prioritaires en service. L’utilisation systématique de cette macro vous évite de vous prendre la tête au moment de couper une interruption. Vous retrouvez la routine opposée, remettant toutes les interruptions en service, et qui s’appelle STARTINT : ;============================================================================= ; REMETTRE LES INTERRUPTIONS (MACRO ORDINAIRE) ;============================================================================= ;----------------------------------------------------------------------------- ; Utilise la déclaration TYPEINT du fichier maquette ; Relance les interruptions générales de façon appropriée au type ;----------------------------------------------------------------------------- STARTINT macro IF TYPEINT == 0 ; si pas de priorité en service bsf INTCON,GIE ; lancer simplement interruptions ELSE ; si priorités en service bsf INTCON,GIEH ; lancer d'abord interrupts HP bsf INTCON,GIEL ; puis LP ENDIF endm Ensuite, nous trouvons une macro qui va placer une sous-routine dans notre code si nous en avons besoin : READ_EEPROM. Si vous placez une seule fois dans votre source le nom de cette macro, vous pourrez appeler partout de votre programme via call ou rcall les sous-routines suivantes : readeepw Lit l’emplacement eeprom dont l’adresse relative est donnée par le contenu W, et retourne la valeur lue dans W et dans EEDATA. En sortie, EEADR pointe sur l’emplacement eeprom qui suit. readeep Lit l’emplacement eeprom dont l’adresse relative se trouve dans EEADR, et retourne la valeur lue dans W et dans EEDATA . En sortie, EEADR pointe sur l’emplacement eeprom suivant. ;============================================================================= ; LIRE EEPROM (GENERE SOUS-ROUTINE) = ;============================================================================= ;----------------------------------------------------------------------------- ; Nom de la macro à placer une seule fois en fin de programme ; ------------------------------------------------------------ ; READ_EEPROM ; ; Nom des variables à déclarer en access-bank ; --------------------------------------------

205

Page 206: Part5_r1

; Aucune ; ; Adresses d'appel possibles ; --------------------------- ; readeepw: lecture avec adresse mémoire dans Wreg ; readeep : lecture avec adresse déjà dans EEADR ; ; Valeur retournée ; ---------------- ; WREG contient la donnée lue ; ; Particularités ; --------------- ; En sortie, EEADR pointe sur la donnée suivante ;----------------------------------------------------------------------------- READ_EEPROM macro readeepw movwf EEADR ; sauver adresse dans pointeur readeep bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS ; pas en zone configuration bsf EECON1,RD ; ordre de lecture movf EEDATA,w ; charger valeur lue incf EEADR,f ; pointer sur suivant return ; et retour endm ; fin de macro Ensuite, nous trouvons la routine d’écriture en eeprom WRITE_EEP, qui génère un sous-programme. Si vous placez une seule fois le nom de cette macro dans votre source, vous disposerez de la sous-routine suivante, utilisable partout dans votre programme : writeeep Ecrit dans la mémoire eeprom la donnée contenue dans le registre W à l’adresse pointée par EEADR. Cette routine modifie la variable locale « local01 » située en access-bank et le bit 0 de la variable flags. En sortie, le flag ERR_EEP signale s’il y a eu une erreur de vérification (1), et le pointeur EEADR pointe sur l’emplacement suivant. Attention, l’utilisation de WRITE_EEPROM impose l’utilisation de READ_EEPROM. ;============================================================================= ; ECRIRE EEPROM (GENERE SOUS-ROUTINE) = ;============================================================================= ;----------------------------------------------------------------------------- ; Nom de la macro à placer une seule fois en fin de programme ; ------------------------------------------------------------ ; WRITE_EEPROM ; ; Nom des variables utilisées et modifiées ; ---------------------------------------- ; un flag ERR_EEP en access-bank ; variable locale : local01 en access-bank ; ; Adresse d'appel possible ; ------------------------ ; writeeep: écriture avec adresse dans EEADR et valeur dans WREG ;

206

Page 207: Part5_r1

; Valeur retournée ; ---------------- ; WREG contient la donnée relue ; ERR_EEP signale une erreur de vérification (1) ; ; Particularités ; --------------- ; En sortie, EEADR pointe sur la donnée suivante ;----------------------------------------------------------------------------- WRITE_EEPROM macro writeeep movwf EEDATA ; sauver data movwf local01 ; sauver seconde fois (access-bank) bcf EECON1,EEPGD ; pointer sur mémoire eeprom bcf EECON1,CFGS bsf EECON1,WREN ; autoriser écritures STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'ordre d'écriture STARTINT ; réautoriser interruptions bcf EECON1,WREN ; interdire écritures clrwdt ; effacer watchdog btfsc EECON1,WR ; tester si écriture terminée bra $-(2*2) ; non, attendre (-2 instructions) bcf ERR_EEP ; effacer flag d’erreur rcall readeep ; relire la valeur écrite cpfseq local01 ; comparer avec celui qu'on devait écrire bsf ERR_EEP ; différents, positionner flag d’erreur return ; et fin endm ; fin de macro De nouveau maintenant une macro qui crée une sous-routine. Si vous placez son nom : ERASE_FLASH dans votre source, vous disposerez d’une sous-routine supplémentaire : clrflash Efface les 64 octets en mémoire programme situé dans le bloc (row) pointé par TBLPTRx. La routine n’attend aucun paramètre et n’en retourne aucun. Elle ne modifie aucune variable en RAM : ;============================================================================= ; EFFACER UN BLOC EN MEMOIRE PROGRAMME(GENERE SOUS-ROUTINE) = ;============================================================================= ;----------------------------------------------------------------------------- ; Nom de la macro à placer une seule fois en fin de programme ; ------------------------------------------------------------ ; ERASE_FLASH ; ; Nom des variables utilisées et modifiées ; ---------------------------------------- ; Aucune ;

207

Page 208: Part5_r1

; Adresse d'appel possible ; ------------------------ ; clrflash: Effacement d'un bloc ; TBLPTRU,H,L pointent déjà sur le bon bloc ; ; Valeur retournée ; ---------------- ; Aucune ; ; Particularités ; --------------- ; Aucune ;----------------------------------------------------------------------------- ERASE_FLASH macro ; séquence d’effacement ; --------------------- bsf EECON1,EEPGD ; pointer sur la mémoire flash bcf EECON1,CFGS ; accès autorisé à la mémoire flash bsf EECON1,WREN ; écritures autorisées bsf EECON1,FREE ; indique une procédure d’effacement STOPINT ; interdire les interruptions (imposé) movlw 0x55 ; séquence imposée movwf EECON2 ; il n’y a rien à comprendre, uniquement movlw 0xAA ; recopier tel quel movwf EECON2 bsf EECON1,WR ; lancer l’effacement (arrêt du PIC) nop ; instruction ignorée à la remise en marche bcf EECON1,WREN ; réinterdire écriture STARTINT ; remettre les interruptions si nécessaire return ; et retour endm ; fin de macro Nous trouvons maintenant la macro WRITE_FLASH qui va créer la sous-routine d’écriture avec vérification de 8 octets en mémoire programme. Il y a deux points d’accès possibles : writeclflash Ecriture avec effacement du bloc de 64 emplacements si l’adresse pointée concerne une adresse multiple de 64. writeflash Ecriture sans effacement préalable. Pour les deux routines :

- TBLPTRx doit pointer sur la première adresse d’écriture multiple de 8 - FSR0 doit pointer sur le premier des 8 octets à écrire en mémoire flash - Si une erreur de vérification est survenue, elle est signalée par le passage à 1 du flag

ERR_FLASH. - Les variables locales local01,local02, et local03 sont modifiées par les routines - En sortie, FSR0 pointe sur l’adresse qui suit le dernier octet en RAM écrit

;============================================================================= ; ECRIRE 8 OCTETS EN MEMOIRE PROGRAMME =

208

Page 209: Part5_r1

;============================================================================= ;----------------------------------------------------------------------------- ; Nom de la macro à placer une seule fois en fin de programme ; ------------------------------------------------------------ ; WRITE_FLASH ; ; Nom des variables utilisées et modifiées ; ---------------------------------------- ; flag ERR_FLASH en acess-bank ; variables locales local01/03 en access-bank ; ; Adresse d'appel possible ; ------------------------ ; writeflash si écriture sans effacement ; writeclflash si écriture avec effacement ; conditions d'entrée: ; -------------------- ; TBLPTRU,H,L pointent déjà sur le bon bloc ; les 8 octets sont déjà pointés par FSR0 ; ; Valeur retournée ; ---------------- ; le flag ERR_FLASH (1 si erreur de vérification) ; ; Particularités ; --------------- ; En sortie, FSR0 pointe sur l'adresse qui suit le dernier octet ;----------------------------------------------------------------------------- WRITE_FLASH macro writeclflash ; vérifier si début d'un bloc de 64 ; --------------------------------- movf TBLPTRL,w ; charger poids faible pointeur andlw 0x3F ; garder 6 bits lsb bnz writeflash ; si adresse pas muliple de 64, ne pas effacer ; effacer le bloc de 64 octets ; ---------------------------- bsf EECON1,EEPGD ; pointer sur mémoire flash bcf EECON1,CFGS ; pointer sur zone programme bsf EECON1,WREN ; autoriser écriture bsf EECON1,FREE ; autoriser effacement bloc STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'effacement des 64 octets nop ; une instruction non exécutée STARTINT ; réautoriser interruptions ; Ecrire les 8 data en flash ; -------------------------- writeflash movff TBLPTRH,local01 ; sauver pointeur MSB movff TBLPTRL,local02 ; sauver pointeur LSB tblrd*- ; nécessaire, car on doit terminer ; les tblwt en restant dans la page de 8 ; octet dans laquelle on veut écrire clrwdt ; effacer watchdog

209

Page 210: Part5_r1

movlw 0x08 ; 8 octets à transférer movwf local03 ; dans variable locale (access-bank) writeflash3 movff POSTINC0,TABLAT ; charger premier octet, pointer sur suivant tblwt+* ; placer dans buffer d'écriture, préincrémenté decfsz local03,f ; décrémenter compteur bra writeflash3 ; pas dernier, suivant bsf EECON1,EEPGD ; pointer sur mémoire flash bcf EECON1,CFGS ; pointer sur zone programme bsf EECON1,WREN ; autoriser écriture STOPINT ; interdire interruptions movlw 0x55 ; séquence imposée movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR ; lancer l'écriture des 8 octets nop ; une instruction non exécutée STARTINT ; réautoriser interruptions ; restaurer les pointeurs ; ----------------------- bcf ERR_FLASH ; effacer code d'erreur movff local01,TBLPTRH ; restaurer pointeur MSB movff local02,TBLPTRL ; restaurer pointeur LSB clrwdt ; effacer watchdog ; vérifier les 8 octets écrits ; ---------------------------- movlw 0x08 ; pour 8 octets movwf local03 ; dans compteur de boucles subwf FSR0L,f ; repointer sur premier octet à écrire btfss STATUS,C ; tester si débordement decf FSR0H,f ; oui, décrémenter poids fort writeflash4 tblrd*+ ; lire un octet en flash movf TABLAT,w ; charger octet lu cpfseq POSTINC0 ; comparer avec celui qu'on devait écrire bsf ERR_FLASH ; pas identique, erreur decfsz local03,f ; décrémenter compteur d'octets bra writeflash4 ; pas dernier, suivant return ; fin endm ; fin de macro Ceci termine l’étude des macros actuellement présentes dans mon petit include. 12.2 Conclusions Nous avons maintenant fait le tour général de nos PIC18F. Comme vous êtes sensés avoir étudié auparavant les PIC16F, vous savez comment fonctionne une liaison série, un module compare et capture, un timer, ainsi que la plupart des autres modules intégrés. Je vous renvoie donc au datasheet pour avoir les particularités des modules des 18F par rapport aux 16F, étant donné que vous avez les clés pour tout comprendre. Vous comprendrez qu’écrire de nouveau tout un chapitre sur chaque module possible me prendrait une éternité, les datasheets sont de plus en plus gros au fur et à mesure qu’évoluent

210

Page 211: Part5_r1

les PIC®, et tout expliquer en détails avec des exemples concrets comme dans les deux premiers cours, nécessiterait un temps libre dont je peux difficilement disposer. Ceci bloquerait de plus tous mes autres projets, dont la finalisation de mon système domotique. J’ai donc décidé de clôturer ici le cours-part5, vous avez suffisamment de renseignements pour que, aidé du datasheet, vous puissiez démarrer immédiatement avec les 18F. Je vous fournis de plus un petit fichier maquette et un include pour vous aider à construire votre premier source. Je compléterai ce cours par des petits cours spécifiques sur des sujets bien ciblés et utilisables avec les 18F. Je pense par exemple à la gestion des bus CAN, à l’USB etc. Mais chaque chose en son temps… J’espère que ce cours vous aidera à démarrer avec les 18F, je vous rappelle encore une fois que ma démarche est que vous démarriez l’apprentissage à l’aide des 16F.

211

Page 212: Part5_r1

Notes :

212

Page 213: Part5_r1

Utilisation du présent document

Le présent ouvrage permet la migration des 16F vers les 18F. Il s’adresse aux personnes qui ont déjà lu les cours-part1 et part2, ou qui ont déjà une bonne connaissance des mécanismes appliqués dans les PIC® mid-range.

Communiquez à l’auteur (avec politesse) toute erreur constatée afin que la mise à jour

puisse être effectuée dans l’intérêt de tous. Pour des raisons de facilité de maintenance pour moi, j’ai décidé que ce cours serait

disponible uniquement sur mon site : http://www.abcelectronique.com/bigonoff ou www.bigonoff.org . Aussi, si vous trouvez mon cours ailleurs, merci de m’en avertir.

Bien entendu, j’autorise (et j’encourage) les webmasters à placer un lien sur le site,

inutile d’en faire la demande. Bien entendu, je ferai de même en retour si la requête m’en est faite. Ainsi, j’espère toucher le maximum d’utilisateurs.

Le présent ouvrage peut être utilisé par tous, et copié dans son intégralité, à condition de ne rien modifier. Il peut cependant être utilisé comme support de cours en tout ou en partie, à condition de préciser la référence originale et le lien vers mon site.

Tous les droits sur le contenu de ce cours et sur les programmes qui l’accompagnent

demeurent propriété de l’auteur, et ce, même si une notification contraire tend à démontrer le contraire (charte de l’hébergeur…).

L’auteur ne pourra être tenu pour responsable d’aucune conséquence directe ou indirecte

résultant de la lecture et/ou de l’application du cours ou des programmes. Toute utilisation commerciale est interdite sans le consentement écrit de l’auteur. Tout

extrait ou citation dans un but d’exemple doit être accompagné de la référence de l’ouvrage. L’auteur espère qu’il n’a enfreint aucun droit d’auteur en réalisant cet ouvrage et n’a

utilisé que les programmes mis gracieusement à la disposition du public par la société Microchip®. Les datasheets sont également disponibles gracieusement sur le site de cette société, à savoir : http://www.Microchip.com

Si vous avez aimé cet ouvrage, si vous l’utilisez, ou si vous avez des critiques, merci

de m’envoyer un petit mail. Ceci me permettra de savoir si je dois ou non continuer cette aventure avec les parties suivantes. Sachez que je réponds toujours au courrier reçu, mais notez que : - Je ne réalise pas les programmes de fin d’étude pour les étudiants (même en payant), c’est

une demande qui revient toutes les semaines dans mon courrier. Tout d’abord je n’ai pas le temps, et ensuite je ne pense pas que ce soit un bon service. Enfin, pour faire un peu d’humour, si je donnais mes tarifs, ces étudiants risqueraient un infarctus.

- Je n’ai malheureusement pas le temps de debugger des programmes complets. Inutile donc

de m’envoyer vos programmes avec un message du style « Ca ne fonctionne pas, vous

213

Page 214: Part5_r1

214

pouvez me dire pourquoi ? ». En effet, je passe plus de 2 heures par jour pour répondre au courrier, si, en plus, je devais debugger, j’y passerais la journée. Vous comprenez bien que c’est impossible, pensez que vous n’êtes pas seul à poser des questions. Posez plutôt une question précise sur la partie qui vous semble inexacte ou que vous n’avez pas comprise.

- Si vous avez des applications personnelles, n’hésitez pas à les faire partager par tous. Pour

ce faire, vous pouvez me les envoyer par mail. L’entraide ne doit pas se faire à sens unique.

- Avec cette version, j’essaye de répondre aux demandes légitimes des personnes qui

travaillent sur différentes plates-formes (Mac, Linux, Windows, etc.). Si, cependant, la version fournie est inexploitable sur votre machine, merci de me le faire savoir. Notez cependant que ce cours utilise MPLAB® pour les exercices, il faudra donc éventuellement adapter ces exercices en fonction du logiciel qu’il vous sera possible d’utiliser.

Merci au webmaster de HTUwww.abcelectronique.com UTH, pour son hébergement gratuit. Merci à Byte, pour sa proposition d’hébergement gratuit. Merci à Grosvince pour sa proposition d’hébergement gratuit. Merci à Thierry pour m’avoir offert mon nom de domaine Merci à Bruno d’en avoir repris le payement depuis 2007. Merci à tous ceux qui m’ont envoyé des mails d’encouragement, ça motive plus que vous ne le pensez. Dernière remarque : il est impossible que vous trouviez trace d’un plagiat ici, tout comme dans les précédents cours, étant donné que je n’ai lu UaucunU ouvrage sur le sujet, autre que les datasheets de Microchip®. Tout est donc issu de mes propres expériences. Donc, en cas de copie manifeste (j’en ai vu), ce sont les autres qui ont copié, les dates des documents vous le démontrera (ceci vaut également pour les ouvrages édités de façon classique, cette remarque n’est pas innocente).

Communiquez à l’auteur (avec politesse) toute erreur constatée afin que la mise à jour

puisse être effectuée dans l’intérêt de tous, si possible par email.

- Edition terminée en révision bêta le 05/04/2007. - Révision 1 le 25/11/2007 : Ajout de remarques pages 7, 9, 26, inversion de INT1 et INT2

dans l’exemple d’interruption hiérarchisée. Correctif page 157. Ajoute de la mention ® à la demande de Microchip®.

URéalisation : Bigonoff

Email : [email protected] (Attention BIGOCOURS, PAS BIGONOFF)