Vous êtes sur la page 1sur 535

LA PROGRAMMATION DES PICS PAR BIGONOFF

SECONDE PARTIE Rvision 13 LA GAMME MID-RANGE PAR LETUDE DES 16F87X (16F876-16F877)

1. INTRODUCTION............................................................................................................................................. 9 2. LES CARACTRISTIQUES DES 16F87X.................................................................................................. 11 2.1 CARACTRISTIQUES GNRALES DE LA FAMILLE 16F87X ............................................................................ 11 2.2 ORGANISATION DE LA RAM ........................................................................................................................ 12 2.3 STRUCTURE DU 16F876 ............................................................................................................................... 12 3. LES PARTICULARITS DE LA PROGRAMMATION DU 16F87X...................................................... 15 3.1 LA DIRECTIVE _CONFIG ....................................................................................................................... 15 3.2 UTILISATION DE LA MMOIRE RAM ............................................................................................................ 16 3.2.1 Ladressage direct................................................................................................................................ 16 3.2.2 Ladressage indirect............................................................................................................................. 18 3.3 LUTILISATION DU REGISTRE PCLATH ....................................................................................................... 18 3.3.1 PCLATH et les calculs dadresse......................................................................................................... 19 3.3.2 PCLATH et les sauts directs................................................................................................................. 19 4. SAUTS DE PAGES ET PREMIER PROJET SUR MPLAB ...................................................................... 23 4.1 STRUCTURE ET UTILISATION DU FICHIER MAQUETTE ............................................................................. 24 4.2 CRATION DUN PROGRAMME SANS SAUT DE PAGE...................................................................................... 28 4.3 PREMIRES TENTATIVES DE SAUT INTER-PAGES ........................................................................................... 28 4.4 EVITONS LE PIGE ........................................................................................................................................ 31 4.5 PREMIRE CORRECTION DE NOTRE EXERCICE............................................................................................... 32 4.6 EVITONS LES WARNINGS INUTILES ............................................................................................................... 33 4.7 DMONSTRATION PRATIQUE DE LUTILIT DES MACROS .............................................................................. 34 4.8 LES SOUS-PROGRAMMES INTER-PAGES ........................................................................................................ 38 5. LES SOURCES DINTERRUPTIONS ......................................................................................................... 43 5.1 ENUMRATION ............................................................................................................................................. 43 5.2 LE REGISTRE INTCON ET LES INTERRUPTIONS PRIPHRIQUES .................................................................. 44 5.2.1 Mise en service des interruptions primaires ........................................................................................ 44 5.2.2 Mise en service des interruptions priphriques.................................................................................. 45 5.3 LES REGISTRES PIE1, PIE2, PIR1 ET PIR2 .................................................................................................. 45 5.3 ETUDE DE LA ROUTINE DINTERRUPTION DU FICHIER MAQUETTE ........................................................... 47 5.4 CAS PARTICULIER DES 873 ET 874 ............................................................................................................... 54 6. MISE EN UVRE ET CONFIGURATION MINIMALE ......................................................................... 57 6.1 LE MATRIEL NCESSAIRE ........................................................................................................................... 57 6.2 LE SCHMA MINIMUM .................................................................................................................................. 58 6.3 LES PARTICULARITS LECTRIQUES ............................................................................................................. 59 7. MIGRATION DU 16F84 VERS LE 16F876................................................................................................. 63 7.1 SIMILITUDES ET DIFFRENCES AVEC LE 16F84............................................................................................. 63 7.2 CONVERSION DUN PROGRAMME CRIT POUR LE 16F84 VERS LE 16F876.................................................... 63 7.3 CAUSES DE NON FONCTIONNEMENT ............................................................................................................. 64 7.3 CONVERSION DUN EXEMPLE PRATIQUE....................................................................................................... 65 7.3.1 Ralisation du montage........................................................................................................................ 65 7.3.2 Cration du projet ................................................................................................................................ 66 7.3.3 La mthode conseille .......................................................................................................................... 67 8. OPTIMISONS UN PEU ................................................................................................................................. 79 8.1 LES DEUX GRANDS TYPES DOPTIMISATION ................................................................................................. 79 8.2 TRAVAIL SUR UN EXEMPLE CONCRET ........................................................................................................... 79 8.3 LE CHOIX DU TYPE DOPTIMISATION ............................................................................................................ 82 8.4 APPLICATION PRATIQUE............................................................................................................................... 82 8.4 OPTIMISATIONS PARTICULIRES .................................................................................................................. 92 8.4.1 Optimisation des niveaux de sous-programmes ................................................................................... 93 8.4.2 Lorganisation des ressources ........................................................................................................... 100

9. LES DIFFRENTS TYPES DE RESET .................................................................................................... 105 9.1 LES 6 TYPES DE RESET ............................................................................................................................... 105 9.2 LE RESET ET LES REGISTRES STATUS ET PCON....................................................................................... 105 9.3 DTERMINATION DE LVNEMENT ........................................................................................................... 106 10. LES PORTS ENTRE/SORTIE ............................................................................................................... 111 10.1 LE PORTA .............................................................................................................................................. 111 10.2 LE PORTB............................................................................................................................................... 112 10.3 LE PORTC............................................................................................................................................... 112 10.4 LE PORTD .............................................................................................................................................. 112 10.5 LE PORTE............................................................................................................................................... 113 10.5.1 Le registre TRISE............................................................................................................................. 113 11. LE PORTD EN MODE PSP ...................................................................................................................... 117 11.1 A QUOI SERT LE MODE PSP ? ................................................................................................................... 117 11.1 COMMENT PASSER EN MODE PSP ? ....................................................................................................... 117 11.2 CONNEXION DES PORTD ET PORTE EN MODE PSP ............................................................................... 118 11.3 LE FONCTIONNEMENT LOGICIEL ............................................................................................................... 118 11.4 LE FONCTIONNEMENT MATRIEL ............................................................................................................. 120 12. LES ACCS LA MMOIRE EEPROM .............................................................................................. 123 12.1 LE REGISTRE EECON1 ............................................................................................................................ 123 12.2 LACCS EN LECTURE .............................................................................................................................. 124 12.3 LACCS EN CRITURE ............................................................................................................................. 125 12.4 INITIALISATION DUNE ZONE EEPROM ...................................................................................................... 126 13. LES ACCS LA MMOIRE PROGRAMME..................................................................................... 127 13.1 GNRALITS .......................................................................................................................................... 127 13.2 LES ACCS EN LECTURE ........................................................................................................................... 127 13.3 LES ACCS EN CRITURE .......................................................................................................................... 129 13.4 PARTICULARITS ET MISE EN GARDE ........................................................................................................ 130 13.5 INITIALISATION DUNE ZONE EN MMOIRE PROGRAMME .......................................................................... 131 13.6 LA TECHNIQUE DU BOOTLOADER . ...................................................................................................... 131 13.6 APPLICATION PRATIQUE : UN CHENILLARD .............................................................................................. 131 14. LE TIMER 0................................................................................................................................................ 149 14.1 GNRALITS .......................................................................................................................................... 149 14.2 LCRITURE DANS TMR0 ........................................................................................................................ 149 14.3 LE TIMING EN MODE COMPTEUR ............................................................................................................... 150 14.4 MODIFICATION AU VOL DE LASSIGNATION DU PRDIVISEUR ............................................................ 152 14.4.1 Prdiviseur du timer 0 vers le watchdog.......................................................................................... 152 14.4.2 Prdiviseur du watchdog vers le timer 0.......................................................................................... 153 15. LE TIMER 1................................................................................................................................................ 155 15.1 CARACTRISTIQUES DU TIMER 1 .............................................................................................................. 155 15.2 LE TIMER 1 ET LES INTERRUPTIONS .......................................................................................................... 156 15.3 LES DIFFRENTS MODES DE FONCTIONNEMENT DU TIMER1...................................................................... 156 15.4 LE REGISTRE T1CON............................................................................................................................... 157 15.5 LE TIMER 1 EN MODE TIMER ............................................................................................................... 159 15.6 LE TIMER 1 EN MODE COMPTEUR SYNCHRONE ......................................................................................... 159 15.7 LE TIMER 1 EN MODE COMPTEUR ASYNCHRONE ....................................................................................... 161 15.8 LE TIMER 1 ET TOSCEN.......................................................................................................................... 162 15.9 UTILISATION DU DBORDEMENT .............................................................................................................. 164 15.10 UTILISATION DUNE LECTURE ................................................................................................................ 166 15.11 ECRITURE DU TIMER 1............................................................................................................................ 170 15.12 EXERCICE PRATIQUE .............................................................................................................................. 172 15.12.1 Un peu de maths............................................................................................................................. 173 15.12.2 Le programme ................................................................................................................................ 176 15.13 ERRATA : FONCTIONNEMENT NON CONFORME ...................................................................................... 181

16. LE DEBUGGAGE PIN-STIMULUS .................................................................................................. 185 17. LE TIMER 2................................................................................................................................................ 189 17.1 CARACTRISTIQUES DU TIMER 2 .............................................................................................................. 189 17.2 LE TIMER 2 ET LES INTERRUPTIONS .......................................................................................................... 190 17.2 LE TIMER 2 ET LES REGISTRES PR2 ET T2CON........................................................................................ 190 17.3 UTILISATION PRATIQUE DE NOTRE TIMER 2.............................................................................................. 193 18. RCAPITULATIF SUR LES TIMERS .................................................................................................. 199 18.1 LE CHOIX DU TIMER ................................................................................................................................. 199 18.1.1 Vous dsirez mesurer un temps compris entre 256 et 65536 cycles................................................. 199 18.1.2 Vous dsirez mesurer des temps allant jusque 524288 .................................................................... 199 18.1.3 Vous dsirez mesurer des temps quelconques avec une grande prcision ....................................... 199 18.1.4 Vous dsirez compter des vnements.............................................................................................. 200 18.2 LES BASES DE TEMPS MULTIPLES ............................................................................................................. 200 18.3 POSSIBILITS NON ENCORE ABORDES ..................................................................................................... 201 19. LE CONVERTISSEUR ANALOGIQUE/NUMRIQUE ....................................................................... 203 19.1 PRAMBULE ............................................................................................................................................. 203 19.2 NOMBRES NUMRIQUES, ANALOGIQUES ET CONVERSIONS....................................................................... 203 19.3 PRINCIPES DE CONVERSION SUR LES 16F87X ........................................................................................... 208 19.4 LE TEMPS DACQUISITION ........................................................................................................................ 209 19.5 LA CONVERSION....................................................................................................................................... 212 19.6 COMPROMIS VITESSE/PRCISION .............................................................................................................. 215 19.7 LES VALEURS REPRSENTES ................................................................................................................... 217 19.8 CONCLUSIONS POUR LA PARTIE THORIQUE............................................................................................. 218 19.9 LA THORIE APPLIQUE AUX PICS : PINS ET CANAUX UTILISS ............................................................... 219 19.10 LES TENSIONS DE RFRENCE ................................................................................................................ 220 19.11 MESURE DUNE TENSION ALTERNATIVE ................................................................................................. 224 19.12 LES REGISTRES ADRESL ET ADRESH................................................................................................. 227 19.13 LE REGISTRE ADCON1 ......................................................................................................................... 227 19.14 LE REGISTRE ADCON0 ......................................................................................................................... 230 19.15 LA CONVERSION ANALOGIQUE/NUMRIQUE ET LES INTERRUPTIONS ...................................................... 232 19.16 LUTILISATION PRATIQUE DU CONVERTISSEUR ...................................................................................... 232 19.17 EXERCICE PRATIQUE SUR LE CONVERTISSEUR A/D ................................................................................ 234 19.18 CONCLUSION.......................................................................................................................................... 248 20. LES MODULES CCP1 ET CCP2 ............................................................................................................. 251 20.1 GNRALITS .......................................................................................................................................... 251 20.2 RESSOURCES UTILISES ET INTERACTIONS ............................................................................................... 251 20.3 LES REGISTRES CCP1CON ET CCP2CON .............................................................................................. 252 20.4 LE MODE CAPTURE ............................................................................................................................. 253 20.4.1 Principe de fonctionnement.............................................................................................................. 253 20.4.2 Champs dapplication ...................................................................................................................... 255 20.4.3 Remarques et limites dutilisation.................................................................................................... 255 20.4.4 Mode sleep et astuce dutilisation .............................................................................................. 257 20.5 LE MODE COMPARE ............................................................................................................................ 257 20.5.1 Principe de fonctionnement.............................................................................................................. 258 20.5.2 Champs dapplication ...................................................................................................................... 262 20.5.3 Remarques et limites dutilisation.................................................................................................... 263 20.5.4 Le mode sleep ............................................................................................................................. 263 20.5.5 Fonctionnement non conforme......................................................................................................... 264 20.6 LE MODE PWM .................................................................................................................................. 265 20.6.1 La thorie du PWM .................................................................................................................... 265 20.6.2 La thorie applique aux PICs......................................................................................................... 267 20.6.3 Les registres utiliss ......................................................................................................................... 273 20.6.4 Champs dapplication ...................................................................................................................... 275 20.6.5 Remarques et limites dutilisation.................................................................................................... 275 20.6.6 Le mode sleep ............................................................................................................................. 275 20.7 EXERCICE PRATIQUE : COMMANDE DUN SERVOMOTEUR PAR LE PWM................................................... 276

20.8 EXERCICE 2 : UNE MTHODE PLUS ADAPTE ............................................................................................ 291 20.9 CONCLUSION............................................................................................................................................ 306 21. LE MODULE MSSP EN MODE SPI........................................................................................................ 309 21.1 INTRODUCTION SUR LE MODULE MSSP ................................................................................................... 309 21.2 LES LIAISONS SRIE DE TYPE SYNCHRONE................................................................................................ 309 21.3 LE MODE SPI ........................................................................................................................................... 311 21.4 LES REGISTRES UTILISS .......................................................................................................................... 312 21.5 LE MODE SPI MASTER ............................................................................................................................. 316 21.5.1 Mise en uvre .................................................................................................................................. 316 21.5.2 Le registre SSPSTAT ........................................................................................................................ 319 21.5.3 Le registre SSPCON......................................................................................................................... 320 21.5.4 Choix et chronogrammes ................................................................................................................. 321 21.5.5 Vitesses de transmission................................................................................................................... 325 21.5.6 Initialisation du mode SPI Master.................................................................................................... 326 21.5.7 Le mode sleep................................................................................................................................... 327 21.6 LE MODE SPI SLAVE.............................................................................................................................. 327 21.6.1 Mise en uvre .................................................................................................................................. 327 21.6.2 Le registre SSPSTAT ........................................................................................................................ 328 21.6.3 Le registre SSPCON......................................................................................................................... 329 21.6.4 Les autres registres concerns ......................................................................................................... 330 21.6.5 Choix et chronogrammes ................................................................................................................. 331 21.6.6 Vitesses de transmission................................................................................................................... 332 21.6.7 Initialisation du mode SPI SLAVE ................................................................................................... 332 21.6.8 Le mode sleep................................................................................................................................... 333 21.6.9 Scurit de la transmission .............................................................................................................. 333 21.7 EXERCICE PRATIQUE : COMMUNICATION SYNCHRONE ENTRE 2 PICS........................................................ 334 21.8 EXERCICE 2 : ALTERNATIVE DE FONCTIONNEMENT.................................................................................. 348 21.9 CONCLUSIONS .......................................................................................................................................... 354 22. LE BUS I2C.................................................................................................................................................. 357 22.1 INTRODUCTION ........................................................................................................................................ 357 22.1 CARACTRISTIQUES FONDAMENTALES .................................................................................................... 357 22.2 LES DIFFRENTS TYPES DE SIGNAUX ........................................................................................................ 361 22.2.1 Le bit ordinaire .......................................................................................................................... 361 22.2.2 Le start-condition ............................................................................................................................. 362 22.2.3 Le stop-condition.............................................................................................................................. 362 22.2.4 Lacknowledge ................................................................................................................................. 363 22.2.5 Le bit read/write............................................................................................................................... 363 22.3 La notion dadresse............................................................................................................................. 364 22.3 Structure dune trame IC ................................................................................................................... 364 22.4 EVNEMENTS SPCIAUX........................................................................................................................... 369 22.4.1 La libration du bus ......................................................................................................................... 369 22.4.2 Le repeated start-condition .............................................................................................................. 369 22.4.3 La pause ........................................................................................................................................... 369 22.5 ARBITRAGE DU BUS ................................................................................................................................. 370 22.6 LES EEPROM IC....................................................................................................................................... 373 22.6.1 Caractristiques gnrales............................................................................................................... 374 22.6.2 Octet de contrle et adresse esclave ................................................................................................ 375 22.6.3 Slection dune adresse interne........................................................................................................ 378 22.6.4 Ecriture dun octet ........................................................................................................................... 379 22.6.5 Ecriture par page ............................................................................................................................. 380 22.6.6 La lecture alatoire .......................................................................................................................... 381 22.6.7 La lecture de ladresse courante ...................................................................................................... 383 22.6.8 La lecture squentielle ..................................................................................................................... 383 22.6.9 Le cas de la 24C16........................................................................................................................... 384 22.6.10 Conclusions .................................................................................................................................... 385 23. LE MODULE MSSP EN MODE IC ........................................................................................................ 387 23.1 INTRODUCTION ........................................................................................................................................ 387

23.2 LE REGISTRE SSPSTAT........................................................................................................................... 388 23.3 LE REGISTRE SSPCON ............................................................................................................................ 389 23.4 LE REGISTRE SSPADD ............................................................................................................................ 390 23.5 LE REGISTRE SSPCON2 .......................................................................................................................... 393 23.6 LES COLLISIONS ....................................................................................................................................... 394 23.7 LE MODULE MSSP EN MODE IC MATRE ................................................................................................ 394 23.7.1 La configuration du module ............................................................................................................. 394 23.7.2 La vrification de la fin des oprations............................................................................................ 395 23.7.3 La gnration du start-condition...................................................................................................... 397 23.7.4 Lenvoi de ladresse de lesclave ..................................................................................................... 397 23.7.5 Le test de lACK ............................................................................................................................... 398 23.7.6 Lcriture dun octet ........................................................................................................................ 399 23.7.7 L envoi du repeated start-condition ................................................................................................ 399 23.7.8 Lenvoi de ladresse de lesclave ..................................................................................................... 401 23.7.9 La lecture de loctet ......................................................................................................................... 402 23.7.10 Lenvoi du NOACK ........................................................................................................................ 402 23.7.11 Lenvoi du stop-condition............................................................................................................... 403 23.8 LUTILISATION DES INTERRUPTIONS ........................................................................................................ 404 23.8 LE MODULE MSSP EN MODE IC MULTI-MATRE ..................................................................................... 405 23.8.1 Larbitrage ....................................................................................................................................... 405 23.8.2 La prise de contrle du bus .............................................................................................................. 405 23.8.3 La dtection de la perte de contle .................................................................................................. 406 23.9 LE MODULE MSSP EN MODE IC ESCLAVE 7 BITS.................................................................................... 406 23.9.1 Ladressage et linitialisation .......................................................................................................... 407 23.9.2 la rception du start-condition ......................................................................................................... 407 23.9.3 La rception de ladresse en mode criture ..................................................................................... 408 23.9.4 La gnration du ACK ............................................................................................................... 409 23.9.5 La rception dun octet de donne ................................................................................................... 409 23.9.6 La rception de ladresse en mode lecture....................................................................................... 410 23.9.6 Lmission dun octet ....................................................................................................................... 410 23.9.7 Le mode esclave 7 bits par les interruptions .................................................................................... 411 23.10 Le module MSSP en mode IC esclave 10 bits .................................................................................. 415 23.11 SYNTHSE .............................................................................................................................................. 417 23.12 EXERCICE PRATIQUE : PILOTAGE DUNE 24C64...................................................................................... 418 23.13 LE MODULE IC EN MODE SLEEP ............................................................................................................ 432 23.14 CONCLUSIONS ........................................................................................................................................ 432 23.15 ANNEXE : ERRATA SUR LE IC................................................................................................................ 432 23.16 BUG EN MODE IC ESCLAVE ................................................................................................................... 433 24. LE MODULE USART EN MODE SRIE SYNCHRONE..................................................................... 435 24.1 INTRODUCTION ........................................................................................................................................ 435 24.2 MISE EN UVRE ET PROTOCOLES ............................................................................................................. 435 24.3 LE REGISTRE TXSTA ............................................................................................................................... 437 24.4 LE REGISTRE RCSTA............................................................................................................................... 438 24.5 LE REGISTRE SPBRG............................................................................................................................... 439 24.6 LINITIALISATION .................................................................................................................................... 440 24.7 LMISSION EN MODE MATRE .................................................................................................................. 441 24.8 LMISSION EN MODE ESCLAVE................................................................................................................ 443 24.9 LA RCEPTION EN MODE MATRE ............................................................................................................. 444 24.9.1 La file FIFO de RCREG................................................................................................................... 445 24.9.2 Lerreur doverflow.......................................................................................................................... 446 24.10 LA RCEPTION EN MODE ESCLAVE ......................................................................................................... 447 24.11 LE MODE SLEEP ...................................................................................................................................... 447 24.12 DIFFRENCES ENTRE MSSP ET USART ................................................................................................ 447 24.13 EXERCICE PRATIQUE .............................................................................................................................. 448 24.14 REMARQUE SUR LE MODE SLEEP ............................................................................................................ 464 24.15 CONCLUSIONS ........................................................................................................................................ 465 25. LE MODULE USART EN MODE ASYNCHRONE............................................................................... 467 25.1 LE MODE SRIE ASYNCHRONE .................................................................................................................. 467

25.1.1 Le start-bit........................................................................................................................................ 467 25.1.2 Les bits de donne ............................................................................................................................ 468 25.1.3 La parit........................................................................................................................................... 468 25.1.4 Le stop-bit ........................................................................................................................................ 469 25.1.5 LES MODES COMPATIBLES .................................................................................................................... 470 25.1.6 Les erreurs de synchronisation ........................................................................................................ 473 25.2 MISE EN UVRE ....................................................................................................................................... 475 25.3 LE REGISTRE TXSTA ............................................................................................................................... 477 25.4 LE REGISTRE RCSTA............................................................................................................................... 478 25.5 LE REGISTRE SPBRG............................................................................................................................... 479 25.6 LINITIALISATION .................................................................................................................................... 480 25.7 EMISSION, RCEPTION, ET ERREUR DOVERFLOW .................................................................................... 480 25.8 LERREUR DE FRAME ............................................................................................................................... 483 25.9 UTILISATION DU 9ME BIT COMME BIT DE PARIT ...................................................................................... 483 25.10 LA GESTION AUTOMATIQUE DES ADRESSES ............................................................................................ 483 25.11 LE MODE SLEEP ...................................................................................................................................... 486 25.12 EXEMPLE DE COMMUNICATION SRIE ASYNCHRONE .............................................................................. 487 25.12.1 Utilisation de BSCP. ...................................................................................................................... 505 25.13 CONCLUSIONS ........................................................................................................................................ 509 26. NORME ISO7816 ET PIC16F876 ............................................................................................................. 511 26.1 AVANT-PROPOS ....................................................................................................................................... 511 26.2 LANCTRE WAFER ............................................................................................................................ 511 26.3 LA SUCCESSION PICCARD2 . ................................................................................................................ 513 26.4 LA BIGCARD 1 ......................................................................................................................................... 514 26.5 LA BIGCARD 2 .......................................................................................................................................... 515 26.6 CONCLUSIONS .......................................................................................................................................... 517 27. LE MODE SLEEP ...................................................................................................................................... 519 27.1 GNRALITS .......................................................................................................................................... 519 27.2 LES MCANISMES ..................................................................................................................................... 519 27.3 LES INTERRUPTIONS CONCERNES ........................................................................................................... 520 27.4 CONCLUSION.......................................................................................................................................... 521 28. LE RESTE DU DATASHEET ................................................................................................................... 523 28.1 INTRODUCTION ........................................................................................................................................ 523 28.2 LE MODE LVP.......................................................................................................................................... 523 28.3 PWRTE ET BODEN................................................................................................................................ 524 28.4 LES ADRESSES RSERVES 0X01 0X03............................................................................................ 525 28.5 LOVERCLOCKAGE ................................................................................................................................... 527 28.6 CE DONT JE NAI PAS PARL ..................................................................................................................... 528 ANNEXE1 : QUESTIONS FRQUEMMENT POSES (F.A.Q.)............................................................... 529 A1.1 JE NARRIVE PAS UTILISER MON PORTA ............................................................................................. 529 A1.2 LE CHOIX DU PROGRAMMATEUR ............................................................................................................. 529 CONTRIBUTION SUR BASE VOLONTAIRE ............................................................................................ 531 B. UTILISATION DU PRSENT DOCUMENT........................................................................................... 533

1. Introduction
Tout dabord, un grand remerciement tous ceux qui ont permis que cette aventure existe. Je parle ici de toutes les personnes qui mont envoy leurs commentaires et corrections, les diffrents hbergeurs de la premire partie de ce cours, ainsi que tous ceux qui mont envoy remerciements et encouragements. Je remercie davance tous ceux qui feront de mme pour cette seconde partie. Merci galement mon pouse pour sa patience durant toutes ces soires passes plant derrire mon ordi . Cette seconde partie sadresse aux personnes qui ont dj lu et compris la premire partie, ddicace la programmation du 16F84. Donc, je ne reviendrai pas sur les explications de base concernant la programmation, et je nexpliquerai pas non plus les diffrentes fonctions avec autant de dtail. Cet ouvrage est donc un complment, une volution, et non un ouvrage destin tre utilis de manire compltement indpendante. Le document indispensable pour aborder la programmation du processeur 16F87x est le datasheet 16F87x disponible sur le site http://www.microchip.com. Afin que tout le monde dispose du mme document que lauteur, jai intgr dans le rpertoire fichiers , la version du datasheet qui a servi pour laborer ce cours. Ceux qui veulent comprendre tous les dtails du fonctionnement des PICs concerns peuvent galement charger le Pic Micro Mid-Range MCU Family Reference Manual , disponible la mme adresse. Attention, limpression de cet ouvrage, disponible dailleurs par chapitres spars, requiert plusieurs centaines de feuilles. Cet ouvrage est plus technique que le prcdent. Cette dmarche ma t impose, dune part par ltendue des possibilits, et dautre part par la ncessit dune certaine part de thorie qui, seule, permettra de sortir le lecteur de toutes les situations non abordes de faon concrte. En effet, ces composants possdent une multitude de fonctions, elles-mmes parfois scindes en plusieurs modes de fonctionnement. Dcrire un exemple dtaill pour chacune de ses fonctions aurait ncessit un ouvrage trop consquent pour que je puisse en venir bout dans des dlais raisonnables. Vous remarquerez cependant que je nai pas t avare dexercices pratiques. Jai donc conserv le maximum dexemples, mais jai opt principalement pour une dmarche qui vise vous fournir tous les outils et toutes les explications qui vous permettront dutiliser toutes les fonctions de cette srie dans vos applications particulires. Ce faisant, jai d aborder beaucoup de thorie, ce qui rend cet ouvrage un peu plus dur lire que le prcdent. Ce nest pas une dmarche litiste, au contraire, mais une tentative pour vous ouvrir le maximum de portes pour le futur. De plus, vous tes senss, lors de la lecture de cet ouvrage, matriser parfaitement le 16F84, on ne peut donc plus parler douvrage pour dbutants. Jespre quen procdant de la sorte, je ne me suis pas montr trop rbarbatif.

Je suis toujours lcoute de tous, je rponds toujours au courrier reu, et si la demande se fait sentir concernant certains chapitres, il me sera toujours possible dajouter explications complmentaires et exemples supplmentaires. Nhsitez donc pas me faire part de vos remarques et suggestions, et jetez de temps en temps un il sur mon site (http://www.abcelectronique.com/bigonoff ou www.bigonoff.org ) pour voir si une nouvelle rvision nest pas disponible. A tout ceux qui mont demand ou qui se demandent pourquoi jai mis cet ouvrage gratuitement la disposition de tous, je rpondrai ceci : En ces temps o tout se marchande, o la mondialisation du commerce seffectue au dtriment du bien-tre le lHomme et de lHumanit, il est primordial de se dresser contre cette mentalit du tout marchand. Je ne suis pas le prcurseur de cette dmarche dans le domaine de la technique et de linformatique. Bien dautres ont compris ceci avant moi, comme par exemple les crateurs de logiciels freeware. Dautres suivront ces exemples, afin que le monde en gnral, et Internet en particulier, devienne ce quil aurait toujours d tre : un lieu dchange de la connaissance et un partage des dons et des richesses de chacun . Internet est un formidable outil de communication qui peut nous aider atteindre ce but. Veillons attentivement ce quil ne devienne pas un gigantesque lieu de e-business rcupr par les multinationales. Je vais cependant peut-tre chercher un diteur qui me permettra de distribuer en plus ces diffrents ouvrages par la voie classique. Ceci permettrait au plus grand nombre davoir accs aux informations, et, de plus, pourrait se rvler rentable pour lutilisateur, le prix de limpression dun tel ouvrage sur une imprimante jet dencre pouvant se rvler aussi onreux que lacquisition dun livre. Mais le cours restera disposition sur internet aussi longtemps que jen aurai la possibilit matrielle et financire. La demande de contribution sur base volontaire nest pas une entorse cette philosophie. Le cours reste gratuit, maide qui le veut, et comme il le veut. Jai construit ce livre en pensant son format papier. Il est prvu pour tre imprim en recto-verso. Comme il est amen tre corrig, et afin de ne pas vous imposer la rimpression complte en cas de modifications, jai laiss un peu de place libre entre chaque chapitre. Ainsi, une insertion ne ncessitera pas la renumrotation des pages. Je vous souhaite beaucoup de plaisir la lecture de ce petit ouvrage sans prtention, et je vous le conseille : exprimentez, exprimentez, et exprimentez encore ! Et vive lInternet libre ! BIGONOFF

10

2. Les caractristiques des 16F87x


2.1 Caractristiques gnrales de la famille 16F87x Le 16F876 et le 16F877 (et dautres) font partie de la sous-famille des 16F87x. Cette branche fait partie intgrante de la grande famille des PICs Mid-Range, au mme titre que le 16F84 dont je parle dans le prcdent ouvrage. On peut considrer que le 16F84 constitue le circuit dentre de gamme de cette famille, alors que le 16F877 reprsente la couche suprieure. De nouveaux circuits ne devraient probablement pas tarder amliorer encore les performances. De ce fait, beaucoup de similitudes sont prvoir entre ces 2 composants. Nous verrons dans le chapitre suivant quels sont ces points communs. La famille 16F87x comprend toute une srie de composants, voici les diffrents types existant au moment de lcriture de cet ouvrage. Notez que les composants sont en constante volution. Il vous appartiendra donc de vous tenir au courant de ces volutions. PIC FLASH RAM 16F870 2K 128 16F871 2K 128 16F872 2K 128 16F873 4K 192 16F874 4K 192 16F876 8K 368 16F877 8K 368 EEPROM 64 64 64 128 128 256 256 I/O 22 33 22 22 33 22 33 A/D 5 8 5 5 8 5 8 Port // NON PSP NON NON PSP NON PSP Port srie USART USART MSSP USART/MSSP USART/MSSP USART/MSSP USART/MSSP

Tous ces composants sont identiques, aux exceptions cites dans le tableau prcdent. Les diffrences fondamentales entre ces PICs sont donc les quantits de mmoires disponibles, le nombre dentres/sorties, le nombre de convertisseurs de type analogique/digital (dire analogique/numrique , et le nombre et le type des ports intgrs. A lheure actuelle, ces composants existent dans divers types de botiers. Ces types sont reprsents page 1 et 2 du datasheet. Les botiers 40 broches sont utiliss par les composants qui disposent dun port //, comme le 16F877. Le botier qui nous intresse plus particulirement est celui dont le brochage est disponible en haut et gauche de la page 2. Ce botier est disponible en version standard (PDIP 28 broches) ou en version composant de surface , ou CMS (SOIC). Comme pour le 16F84, le numro peut tre suivi dun A , et dun -xx qui donne la frquence dhorloge maximum du composant. A lheure o jcris cet ouvrage, la version la plus courante est la version 20. Donc, la frquence que nous utiliserons en standard dans cet ouvrage sera de 20MHz. De mme, les exercices seront raliss sur un PIC16F876, moins cher que le 16F877. Nous nutiliserons ce dernier composant que lorsque ses spcificits seront indispensables la ralisation de lexercice.

11

Nous nous intresserons donc dans cet ouvrage principalement au 16F876, qui est le plus utilis lheure actuelle. La transposition vers un autre type ne demande quun minimum dadaptation. Nous parlerons galement du 16F877 lorsque nous aborderons le port parallle. Ainsi, nous aurons vu lintgralit des fonctions de cette famille. 2.2 Organisation de la RAM Nous voyons donc que la mmoire RAM disponible du 16F876 est de 368 octets. Elle est rpartie de la manire suivante et donne page 13 du datasheet : 1) 2) 3) 4) 5) 80 octets en banque 0, adresses 0x20 0x6F 80 octets en banque 1, adresses 0xA0 0XEF 96 octets en banque 2, adresses 0x110 0x16F 96 octets en banque 3, adresses 0x190 0x1EF 16 octets communs aux 4 banques, soit 0x70 0x7F = 0xF0 0xFF = 0x170 0x17F = 0x1F0 0x1FF

Que signifient ces octets communs ? Tout simplement que si vous accdez au registre (adresse mmoire RAM) 0x70 ou au registre 0XF0, et bien vous accdez en ralit au mme emplacement. Ceci lavantage de permettre dutiliser ces emplacements sans devoir connatre ltat de RP0,RP1, et IRP. 2.3 Structure du 16F876 Allons ensemble regarder la structure interne du 16F876, schmatise figure 1.1 page 5 du datasheet. Que constatons-nous au premier abord ? En premier lieu, les largeurs de bus internes sont les mmes que pour le 16F84, cest dire que nous devrons faire face aux mmes contraintes pour les accs aux diffrentes banques et registres. Ensuite, nous constatons la prsence de plus de ports, ce qui augmente dautant le nombre dentres/sorties disponibles. Viennent ensuite les timers, au nombre de 3 au lieu dun seul pour le 16F84. A ct de ces timers on remarquera la prsence dun convertisseur analogique de 10 bits. Au vu de ce schma-bloc et des indications prcdentes, on peut donc dire, pour dgrossir le sujet de manire approximative, quun 16F876, cest un 16F84 dot en supplment : De plus de mmoire RAM (rpartie sur 4 banques), Flash, et eeprom De plus de ports dentre/sortie De plus de timers De nouvelles fonctionnalits, comme les gestions de ports srie Dun convertisseur A/D (analogique/numrique) plusieurs canaux dune rsolution de 10 bits.

12

Nous voyons donc que ltude du 16F876 peut se rsumer lapprentissage des diffrences par rapport au 16F84. Ces diffrences introduisent cependant une part non ngligeable de thorie, ce qui ma convaincu dcrire cette seconde partie. Il faut dire qucrire un livre de plus de 500 pages rien que sur les diffrences avec la prcdente version, propos de laquelle louvrage comportait 200 pages, indique que les dites diffrences justifiaient quon sy attarde. Il existe de plus des fonctions qui ncessitent une tude plus pousse (debuggage sur circuit, techniques du bootloader), mais jaborderai ceci dans le livre suivant (3me partie : Les secrets des 16F87x). En effet, afin de limiter la taille de cet ouvrage, et dacclrer sa sortie, jai scind la version initialement prvue en plusieurs volumes. En bonne logique, commenons donc par voir ce qui caractrise le 16F87x

13

Notes : .

14

3. Les particularits de la programmation du 16F87x


3.1 La directive _CONFIG Tout comme nous lavons vu dans la premire partie, cette directive dtermine le fonctionnement du PIC. La valeur est inscrite au moment de la programmation dans un registre spcial, situ en mmoire programme ladresse 0x2007, et ne peut plus tre modifi en cours dexcution du programme. Notez que puisque la mmoire programme des PICs vierges contient des 1 , les niveaux actifs de ces bits sera le niveau 0 . Ce registre de 14 bits (puisquen mmoire programme) dispose dune organisation diffrente par rapport celui du 16F84. Voici donc ses fonctions, reprises page 122 du datasheet : CP1/CP0 : bits 13/12 et 5/4 : Dtermine quelle zone du 16F876 sera protge contre la lecture. Vous pouvez donc choisir de protger la totalit du PIC, ou seulement une partie. Les diffrentes zones pouvant tre protges sont les suivantes : CP1 CP0 1 1 Aucune protection (_CP_OFF) 1 0 Protection de la zone 0x1F00 0x1FFF (_CP_UPPER_256) 0 1 Protection de la zone 0x1000 0x1FFF (_CP_HALF) 0 0 Protection de lintgralit de la mmoire (_CP_ALL) DEBUG : bit 11 : Debuggage sur circuit. Permet de ddicacer RB7 et RB6 la communication avec un debugger. 1 : RB6 et RB7 sont des I/O ordinaires (_DEBUG_OFF) 0 : RB6 et RB7 sont utiliss pour le debuggage sur circuit (_DEBUG_ON) Bit 10 : non utilis WRT : bit 9 : Autorisation dcriture en flash 1 : Le programme peut crire dans les zones non protges par les bits CP1/CP0 (_WRT_ENABLE_ON) 0 : Le programme ne peut pas crire en mmoire flash (_WRT_ENABLE_OFF) CPD : bit 8 : Protection en lecture de la mmoire eeprom. 1 : mmoire eeprom non protge (_CPD_OFF) 0 : mmoire eeprom protge (_CPD_ON) LVP : bit 7 : Utilisation de la pin RB3/PGM comme broche de programmation 1 : La pin RB3 permet la programmation du circuit sous tension de 5V (_LVP_ON) 0 : La pin RB3 est utilise comme I/O standard (_LVP_OFF) BODE N : bit 6 : provoque le reset du PIC en cas de chute de tension (surveillance de la tension dalimentation) 1 : En service (_BODEN_ON)

15

0 : hors service (_BODEN_OFF) PWRTE : bit 3 : Dlai de dmarrage la mise en service. Attention, est automatiquement mis en service si le bit BODEN est positionn. 1 : dlai hors service (sauf si BODEN = 1) (_PWRTE_OFF) 0 : dlai en service (_PWRTE_ON) WDTE : bit 2 : Watchdog timer 1 : WDT en service (_WDT_ON) 0 : WDT hors service (_WDT_OFF) FOSC1/FOSC0 : bits 1/0 : slection du type doscillateur 11 : Oscillateur de type RC (_RC_OSC) 10 : Oscillateur haute vitesse (_HS_OSC) 01 : Oscillateur basse vitesse (_XT_OSC) 00 : Oscillateur faible consommation (_LP_OSC)

Noubliez pas dutiliser ces valeurs dans la directive _CONFIG. Les diffrentes valeurs, prdfinies dans le fichier P16F876.INC doivent tre spares par des directives & . Voici un exemple dutilisation : __CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC Je fournis dans les fichiers joints ce cours, une maquette pour le 16F876 et une pour le 16F877, dans lesquelles le corps de programme et les explications des directives ont dj t labors par mes soins. Ces fichiers m16f876.asm et m16F877.asm vous pargneront sans aucun doute bien du travail peu valorisant. 3.2 Utilisation de la mmoire RAM Sur ce processeur, nous avons donc 4 banques de mmoire RAM. Nous voyons que les adresses schelonnent entre 0x00 et 0x1FF. Si vous convertissez 0x1FF en binaire, laide de la calculette de Windows, vous constaterez quil faut 9 bits pour coder cette valeur. La largeur du bus de donnes tant de 8 bits, et la largeur du bus dadressage direct de 7 (voir la premire partie), il va nous falloir trouver des bits supplmentaires. 3.2.1 Ladressage direct Pour ce type dadressage, il nous manque donc 2 bits. Tout comme dans le cas du 16F84, nous allons trouver ces 2 bits dans le registre STATUS. La combinaison des bits RP0 et RP1 de ce registre, permet donc daccder en adressage direct lintgralit de la mmoire RAM. Ladresse finale est donc compose de RP1/RP0 comme bits 8 et 7, complts des 7 bits de ladresse directe utilise dans lexpression.

16

Les 4 possibilits concernant RP1/RP0 donnent donc : 00 : 01 : 10 : 11 : Accs la RAM 0x00 0x7F Accs la RAM 0x80 0xFF Accs la RAM 0x100 0x17F Accs la RAM 0x180 0x1FF

Notez que la RAM situe de 0x70 0x7F est accessible quel que soit ltat de RP0 et RP1. En effet, les zones correspondantes dans les autres banques sont en fait des images de cette zone Voici des emplacements de mmoire RAM. Pour rappel, les ( ) dans les commentaires signifient le contenu de :
variable1 variable2 variable3 movlw movwf movlw bcf bcf movwf movwf bsf movf movwf bsf movwf EQU EQU EQU 0x50 variable2 0x30 STATUS , RP0 STATUS , RP1 variable1 variable3 STATUS , RP1 variable2 , W variable1 STATUS , RP0 variable1 0x25 0x7E 0xA1 ; adresse de la variable 1 ; adresse de la variable 2 ; adresse de la variable 3 ; ; ; ; ; ; ; ; ; ; ; ; ; charger dans la RPx charger passage 0x50 dans W case mmoire 0x7E, car indpendant de 0x30 dans W en banque 0

placer dans la case mmoire 0x25 placer dans 0x21, car seuls 7 bits sont pris en compte. Pige. passer en banque 2 (0x17E) dans W, donc (0x7E), donc 0x50 0x50 dans la case mmoire 0x125 passage en banque 3 0x50 dans la case mmoire 0x1A5

Vous pouvez facilement voir les correspondances des adresses en utilisant le tableau 2.3 page 13 du datasheet, ou avec la calculatrice de Windows. Si vous ne comprenez pas bien ou que vous doutez, procdez la mthode suivante pour obtenir ladresse concerne : 1) Convertissez ladresse en binaire avec la calculatrice Windows 2) Entrez en binaire les 2 bits RP0 et RP1 3) Compltez avec les 7 bits gauche de ladresse binaire. Par exemple, pour la dernire ligne du prcdent exemple : 1) RP0/RP1 = 11 2) 25 en hexadcimal donne 100101 en binaire (attention, il ny a que 6 chiffres, je complte donc par un 0 gauche : 0100101 3) Ceci donne B110100101, soit 0x1A5

17

3.2.2 Ladressage indirect Ladressage indirect utilise le registre FSR/INDF . Hors, ce registre une largeur de 8 bits. Donc, tel quel, il lui est impossible dadresser plus de 2 banques. Il va donc falloir trouver une fois de plus un bit supplmentaire. Ce bit est le bit IRP du registre STATUS. Ce bit est donc utilis comme bit de poids fort (bit 8) complt par les 8 bits contenus dans le registre FSR. En fonction de la valeur de IRP, nous accderons donc : IRP = 0 : Banques 0 et 1, soit registres de 0x00 0xFF IRP = 1 : Banques 2 et 3, soit registres de 0x100 0x1FF Donc, faites attention : les bits RP0 et RP1 ninfluencent en aucune faon ladressage indirect. De mme , IRP ninfluence en aucune faon ladressage direct. Voici un exemple dutilisation de ladressage indirect. On prsume que cet exemple est la suite du prcdent :
bcf movlw movwf movf bsf movwf movlw movwf bcf bcf movf movwf STATUS,IRP variable1 FSR INDF,w STATUS,IRP INDF variable3 FSR STATUS,RP0 STATUS,RP1 variable3,w INDF ; ; ; ; ; ; ; ; ; pointer banques 0 et 1 indirect charger 0x25, donc adresse de variable1 mettre 0x25 dans le pointeur charger (0x25), donc 0x30 pointer banque 2 et 3 indirect mettre 0x30 dans le registre 0x125 charger adresse variable3 dans pointeur pointer banque0 en adressage direct

; charger (0x21) car 0xA1 en 7 bits , donc 0x30 ; placer 0x30 en 0x1A1. En effet, 8 bits de ; ladresse 0xA1 + bit8 1 (IRP)

Pour rsumer : Vous devez toujours vrifier RP0 et RP1 avant toute utilisation de ladressage direct. Vous devez toujours vrifier IRP avant toute utilisation de ladressage indirect.

Sachez dj que, suivant mes observations, plus de la moiti des erreurs de programmation persistantes sont dues des erreurs de slection de banques. 3.3 Lutilisation du registre PCLATH Nous avons dj vu ce registre dans ltude du 16F84 : en effet, lors dune opration mathmatique sur le registre PCL (dans le cadre de lutilisation dun tableau), le rsultat donnait une valeur sur 8 bits. Notre 16F84 permettant un adressage de plus de 8 bits, les bits manquants taient galement emprunts PCLATH.
18

3.3.1 PCLATH et les calculs dadresse Cette limitation existe ici aussi de la mme manire. Donc, nous devrons donc utiliser notre PCLATH en suivant les recommandations suivantes : En cas dopration sur PCL, la valeur sur 8 bits obtenue est complte par PCLATH pour former ladresse de destination effective. Supposons donc que nous fassions une addition de w par rapport PCL. O va pointer alors le pointeur de programme (PC) ?

Voici le contenu de PC aprs laddition : b7 b0 seront en fait le contenu effectif de PCL b12 b8 seront les bits b4 b0 de PCLATH. Exemple dopration qui sapplique dans ce cas :
movlw movwf movlw addwf B10010 PCLATH 0x22 PCL , f ; ; ; ; charger valeur initialiser PCLATH charger valeur sauter plus loin

PCLATH (5 bits) PCL (8 bits) B4 B3 B2 B1 B0 B7 B6 B5 B4 B3 B2 PC(13 bits) = adresse de saut B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2

B1 B1

B0 B0

Ce fonctionnement est strictement identique celui expliqu dans le cadre du 16F84. Si on prend comme exemple concret le rsultat suivant : PCL vaut B11011010 aprs laddition PCLATH a t initialis B00010010 par lutilisateur. Notez que b7 b5 sont inutiliss. Le saut final seffectuera donc ladresse B1001011011010 aprs laddition, soit 0x12DA. PCLATH (5 bits) PCL (8 bits) 0 0 1 0 1 1 0 1 1 0 PC(13 bits) = adresse de saut 0 0 1 0 1 1 0 1 1 0 1 1 0 0

1 1

3.3.2 PCLATH et les sauts directs Et oui, avec ce processeur survient un autre problme, problme que nous navions pas rencontr cette fois avec le 16F84. Si vous examinez le jeu dinstructions du 16F876 dans le dtail, vous allez vous apercevoir quil est strictement identique celui du 16F84, et, de ce fait, prsente les mmes limitations.

19

Une de celle-ci, la plus gnante, est le nombre de bits qui accompagne les instructions de saut, comme le goto . Rappelez-vous que les instructions de saut sont de la forme : Op-code + 11 bits de destination. Or, les sauts sur 11 bits ne permettent que des sauts lintrieur dune page de 2K Mots. Notre processeur utilise un maximum de 8Kmots de programme, et ncessite donc 2 bits supplmentaires pour accder lintgralit de la mmoire programme. Le schma-bloc donnant bien une largeur de bus dadressage de 13 bits. Ces bits, nous allons donc aller les chercher galement dans le registre PCLATH. Comme il ne nous manque que 2 bits, nous nutiliserons donc que les 2 bits b4 et b3 de PCLATH. Pour rsumer, le saut seffectuera ladresse donne en argument complte en poids fort par les 2 bits b4 et b3 de PCLATH. Les bits 2 0 de PCLATH sont inutiliss ici. PCLATH B4 B3 B10 B9 ARGUMENT DU SAUT (11 bits) B8 B7 B6 B5 B4 B3 B2 PC(13 bits) = adresse de saut B12 B11 B10 B9 B8 B7 B6 B5 B4 B3 B2 Petit exemple pratique : soit les instructions suivantes :
movlw movwf goto 0x13 PCLATH 0x112 ; charger 0x13, soit B10011, donc b4=1, b3=0 ; dans PCLATH ; sauter thoriquement en 0x112 (B00100010010)

B1 B1

B0 B0

Voici ce que a donne : PCLATH 1 0 0 1 0 0 0 0 ARGUMENT DU SAUT (11 bits) 1 0 0 0 1 0 0 PC(13 bits) = adresse de saut 1 0 0 0 1 0 0 1 1 0 0

Donc, vous constaterez que le programme saute en ralit ladresse 0x1112, et non ladresse 0x112. Mais ne vous inquitez pas trop : tout dabord nous allons voir tout a avec un exemple pratique, et ensuite, les sauts dans les espaces suprieurs 2K ne devraient pas vous perturber avant un certain temps. Noubliez pas en effet que vous ne rencontrerez ce problme que si votre programme dpasse les 2Kmots de code. Donc, il faudra que vous ayez crit plus de 2000 lignes de code dans votre programme. Nul doute que quand vous en serez l, vous matriserez parfaitement ce type de limitation. De plus, je vais vous venir en aide, en vous expliquant et en crant des macros pour vous faciliter le traitement de ce problme le jour o vous le rencontrerez.

20

Notez quil est rare dcrire un ligne du type


goto 0x112

car, en gnral, vous utiliserez plutt un saut vers une tiquette, du style :
goto etiquette

Dans ce cas, vous devrez raisonner de faon inverse. Il vous faudra savoir dans quelle page de 2Kmots se trouve etiquette et ensuite vous positionnerez PCLATH en consquence. Rassurez-vous cependant, MPASM ne manquera pas de vous avertir, par un warning, quil y a un risque ce niveau. Mais, pour tre certain que vous compreniez bien tout, nous allons mettre tout ceci en pratique dans le chapitre suivant.

21

NOTES : ...

22

4. Sauts de pages et premier projet sur MPLAB


Pour clarifier la situation, nous allons donc mettre ceci en pratique. Commencez donc par faire une copie de votre fichier m16f876.asm et renommez cette copie en pclath.asm . Lancez MPLAB et crez un nouveau projet pclath.pjt . Je nentrerai plus dans les dtails, vu que jai tout expliqu dans la premire partie du cours. Dans la fentre edit project qui souvre alors, slectionnez < change > ct de la fentre development mode , puis choisissez la mise en service du simulateur pour le 16F876. Ceci est donn pour lancienne version 5.x de MPLAB. Pour la 6.x, voyez le courspart1

Cliquez ensuite sur la ligne pclath [.hex] , le bouton < Node Properties > devient actif. Cliquez dessus et configurez la fentre qui souvre de manire identique celle de la figure suivante :

23

Cliquez OK dans la fentre Node Properties , puis revenez la fentre Edit project . Crez un nouveau nud avec <Add Node> et slectionnez votre fichier pclath.asm . Cliquez ensuite <OK> dans la fentre Edit project . Dans le menu file , ouvrez enfin votre fichier pclath .asm . Vous tes enfin prts travailler sur votre premier programme en 16F876. Si vous navez pas tout suivi, retournez jeter un il dans le cours sur le 16F84. 4.1 Structure et utilisation du fichier maquette Commencez donc, comme toujours, par remplir votre cadre de commentaires, pour expliquer ce que contiendra votre nouveau projet. Tout comme dans la premire partie du cours, jinsiste de nouveau pour que vous documentiez au maximum vos programmes. Cest le seul et unique moyen de vous permettre une maintenance aise de vos ralisations dans le temps. Ce qui vous semble vident aujourdhui ne le sera peut-tre plus demain. Sachez que moi-mme je documente mes programmes exactement de la faon que je dcris dans ce cours, car ce qui est bon pour vous lest forcment pour moi aussi. Jouvre ici une parenthse, pour me fcher un peu (une fois nest pas coutume, vous ne my reprendrez plus). Malgr tous ces avertissements, je reois rgulirement des programmes excuts par certains dentre-vous, et vierges de tout commentaire. Dune part, je ne lirai plus les programmes sans commentaire, et dautre part, si ces personnes me les envoient, cest quils ne fonctionnent pas. Comment alors admettre que ces personnes nont pas besoin de conseils pour raliser leur programme, mais en ont besoin pour les debugger lorsquils ne fonctionnent pas ? De mme, je reois des programmes de personnes qui ont dcid de nutiliser aucun symbole. Essayez donc de lire des programmes du style :
bsf movlw movwf movf 03,7 0x3C 04 0,0

Et bien, moi, jy renonce dfinitivement. Et dire que leurs auteurs stonnent quils sont difficiles debugger. Cest quand mme plus clair comme ceci, non ? (cest le mme programme) :
bsf movlw movwf movf STATUS,IRP mavariable FSR INDF,w ; ; ; ; pointer sur banques 2 et 3 charger adresse de ma variable dans pointeur indirect charger mavariable dans w

Autrement dit, si vous vous obstinez utiliser la premire mthode (qui vous refera trs vite perdre le temps gagn), alors cest que vous avez dcid de vous passer de mes conseils. Gardez donc dans ce cas vos problmes de debuggage pour vous.

24

Ben oui, cette fois je rle un peu, mais il faut me comprendre, non ? Jestime donc que si vous poursuivez, cest que vous tes pleins de bonne volont, et que vous comprenez que cest dans votre propre intrt dutiliser les commentaires et adressages symboliques. Je referme la parenthse. Voici donc un exemple de zone de commentaires pour cet exercice :
;************************************************************************ ; Exercice sur les sauts inter-pages pour montrer l'utilisation de * ; PCLATH. * ; * ;************************************************************************ ; * ; NOM: PCLATH * ; Date: 04/07/2001 * ; * ; Version: 1.0 * ; * ; Circuit: aucun * ; Auteur: Bigonoff * ; * ; * ;************************************************************************ ; * ; Fichier requis: P16F876.inc * ; * ; * ;************************************************************************

Viennent ensuite, comme toujours, la dclaration de processeur destine MPASM, et la rfrence au fichier dinclusion, qui devra tre prsent dans votre rpertoire des fichiers.
LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

Ensuite, vous trouvez la fameuse directive _CONFIG qui dfinit le fonctionnement de base du processeur. Rien de spcial ici, vu que ce nest quun exercice statique:
__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

Plus bas se trouvent les assignations systme. Ceci dans le but de vous viter de chercher dans le programme aprs les registres concerns. Il suffit de modifier ici les valeurs de certains registres. Ces valeurs seront charges lors de linitialisation par la routine init .
;************************************************************************** ; ASSIGNATIONS SYSTEME * ;************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'00000000' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; INTEDG b6 : 1= Interrupt sur flanc montant de RB0 ; 0= Interrupt sur flanc descend. de RB0 ; TOCS b5 : 1= source clock = transition sur RA4

25

; ; TOSE ; ; PSA ; ; PS2/PS0 ; ; ; ; ; ; ; ;

0= horloge interne 1= Slection flanc montant RA4(si B5=1) 0= Slection flanc descendant RA4 b3 : 1= Assignation prdiviseur sur Watchdog 0= Assignation prdiviseur sur Tmr0 b2/b0 : valeur du prdiviseur 000 = 1/1 (watchdog) ou 1/2 (tmr0) 001 = 1/2 1/4 010 = 1/4 1/8 011 = 1/8 1/16 100 = 1/16 1/32 101 = 1/32 1/64 110 = 1/64 1/128 111 = 1/128 1/256 b4 :

; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQU B'00000000' ; GIE b7 : masque autorisation gnrale interrupt ; ne pas mettre ce bit 1 ici ; sera mis en temps utile ; PEIE b6 : masque autorisation gnrale priphriques ; T0IE b5 : masque interruption tmr0 ; INTE b4 : masque interruption RB0/Int ; RBIE b3 : masque interruption RB4/RB7 ; T0IF b2 : flag tmr0 ; INTF b1 : flag RB0/Int ; RBIF b0 : flag interruption RB4/RB7 ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQU B'00000000' ; PSPIE b7 : Toujours 0 sur PIC 16F786 ; ADIE b6 : masque interrupt convertisseur A/D ; RCIE b5 : masque interrupt rception USART ; TXIE b4 : masque interrupt transmission USART ; SSPIE b3 : masque interrupt port srie synchrone ; CCP1IE b2 : masque interrupt CCP1 ; TMR2IE b1 : masque interrupt TMR2 = PR2 ; TMR1IE b0 : masque interrupt dbordement tmr1 ; REGISTRE PIE2 (contrle interruptions particulires) ; ---------------------------------------------------PIE2VAL EQU B'00000000' ; UNUSED b7 : inutilis, laisser 0 ; RESERVED b6 : rserv, laisser 0 ; UNUSED b5 : inutilis, laisser 0 ; EEIE b4 : masque interrupt criture EEPROM ; BCLIE b3 : masque interrupt collision bus ; UNUSED b2 : inutilis, laisser 0 ; UNUSED b1 : inutilis, laisser 0 ; CCP2IE b0 : masque interrupt CCP2

Ne vous occupez pas de ces registres ici. Nous verrons ceux que vous ne connaissez pas encore plus tard. Vous trouvez galement deux zones prvues pour placer vos propres dfinitions et assignations, le tout avec chaque fois un exemple.

26

Il va de soi que lorsque vous matriserez parfaitement les PICs, vous pourrez vous passer des assignations dont vous navez pas usage. Mais ceci vous permet dtre certains de ne rien oublier. Maintenant, vous vous trouvez dans la zone des macros, o quelques macros existent dj. Par exemple, les macros de passage de banques. Nous naurons pas besoin des autres dans le cadre de cet exercice
;************************************************************************** ; MACRO ;************************************************************************** BANK0 bcf bcf endm BANK1 bsf bcf endm BANK2 bcf bsf endm BANK3 bsf bsf endm macro STATUS,RP0 STATUS,RP1 macro STATUS,RP0 STATUS,RP1 macro STATUS,RP0 STATUS,RP1 macro STATUS,RP0 STATUS,RP1 ; passer en banque0

; passer en banque1

; passer en banque2

; passer en banque3

Ces macros permettent de slectionner une banque particulire en positionnant les 2 bits concerns. Il est vident que si vous savez dans quelle banque vous vous trouvez, il nest pas toujours ncessaire de repositionner les 2 bits. Il se peut en effet que lun des 2 soit dj dans le bon tat. Nanmoins, lutilisation de cette macro ne vous cotera quune ligne supplmentaire, et limitera fortement les risques derreurs. Vous verrez dautres macros dans cette maquette, nen tenez pas compte pour linstant. De plus, certaines macros que je dveloppe dans mes exercices et dans les fichiers maquettes ont leur quivalent (ou presque) dj intgr dans MPASM. Cependant, dans ce cas, leur contenu nest pas visible, cest donc une dmarche moins didactique (noubliez pas quil sagit dun cours). De toute faon, je vous parlerai de ces macros particulires. Ensuite vous trouvez les diffrentes zones demplacement RAM pour chacune des banques, ainsi que lemplacement des 16 octets de la zone commune. Je rappelle que ces 16 octets sont accessibles indpendamment de la valeur de RP0 et RP1.
;************************************************************************ ; VARIABLES ZONE COMMUNE ;************************************************************************ ; Zone de 16 bytes ; ---------------CBLOCK 0x70 ; Dbut de la zone (0x70 0x7F)

27

w_temp : 1 status_temp : 1 FSR_temp : 1 PCLATH_temp : 1 ENDC

; ; ; ;

Sauvegarde sauvegarde sauvegarde sauvegarde

registre W registre STATUS FSR (si indirect en interrupt) PCLATH (si prog>2K)

Dans cette zone, vous trouvez les variables de sauvegarde temporaire ncessaires lorsque vous utilisez les interruptions. Les explications concernant ces variables sont donnes dans le chapitre 5 sur les interruptions. Pour notre exemple, supprimez ces variables, nous nutiliserons pas les interruptions. Supprimez enfin tout ce qui suit les lignes suivantes, except la directive END :
;*********************************************************************** ; DEMARRAGE SUR RESET ;*********************************************************************** org 0x000 ; Adresse de dpart aprs reset

4.2 Cration dun programme sans saut de page Nous allons maintenant complter notre petit exercice. Noubliez pas de toujours laisser la directive END aprs la dernire ligne de votre programme. Ajoutez maintenant les lignes suivantes, puis compilez avec <F10> Tout devrait se passer sans problme.
saut1 nop nop goto END ; ne rien faire ; ne rien faire ; sauter

saut1

Tapez ensuite <F6>, le curseur du simulateur vient se placer sur la ligne ladresse 0x00. Pressez ensuite plusieurs fois <F7> pour constater que votre boucle fonctionne sans problme. 4.3 Premires tentatives de saut inter-pages Nous allons maintenant provoquer un saut entre 2 pages distinctes. Tout dabord, quelle est la longueur dune page ? Et bien, rappelez-vous : elle est de 2Kmots, qui est la limite des 11bits impose par larchitecture des instructions de saut. Donc, on dbordera de la page en passant de B11111111111 B100000000000, autrement dit quand on a modification du bit 12 ou du bit 13 (bit 12 dans ce cas). Cette nouvelle adresse est ladresse 0x800. Voici donc les zones des diffrentes pages : Page 0 : de 0x0000 0x07FF Page 1 : de 0x0800 0x0FFF

28

Page 2 : de 0x1000 0x17FF Page 3 : de 0x1800 0x1FFF Modifions donc notre petit programme de la faon suivante (noubliez pas la directive END , je ne lindiquerai plus) :
org 0x000 nop saut1 nop nop goto saut2 org 0x800 saut2 nop nop goto saut1 ; Adresse de dpart aprs reset ; ne rien faire ; ne rien faire ; ne rien faire ; sauter ; adresse de la suite du programme ; ne rien faire ; ne rien faire ; sauter

Lancez la compilation avec <F10>. Dans la fentre des rsultats, vous voyez apparatre 2 warnings . Les numros de lignes peuvent tre diffrents en fonction des espaces entre les lignes :
Message[306] D:\DOCUME~1\LESSONS\PICS-P~2\FICHIERS\PCLATH.ASM 214 : Crossing page boundary -- ensure page bits are set. Message[306] D:\DOCUME~1\LESSONS\PICS-P~2\FICHIERS\PCLATH.ASM 221 : Crossing page boundary -- ensure page bits are set.

Si vous vous reportez aux lignes incrimines (dans mon cas 214 et 221), vous constaterez que ces lignes correspondent aux deux instructions de saut. En fait MPASM attire votre attention sur le point suivant : les sauts incrimins traversent une limite de page, il vous demande de vrifier si les bits correspondants de PCLATH sont bien positionns. Ceci est logique, car la directive org0x800 place le saut2 en page 1, alors que le saut 1 est situ en page 0. Ne tenons pas compte de ces avertissements dans un premier temps. Tapez donc ensuite <F6> puis une succession de <F7> pour voir le droulement du programme. Que se passe-t-il ? Au lieu que linstruction goto saut2 nous envoie vers saut2 , nous nous retrouvons en fait la premire ligne du programme. Examinons ce qui sest pass : Linstruction goto saut2 est traduite par lassembleur en goto 0x800 Or, 0x800 comporte 12 bits, donc est tronque 11 bits, ce qui donne goto 0x00 . En effet 0x800 = B100000000000. Si vous enlevez le bit de poids fort, il reste B00000000000 La gestion interne du PIC ajoute ladresse les bits 3 et 4 de PCLATH, qui sont 0 . Ladresse finale est donc 0x00. En effet, ltude des 2 dernires colonnes du tableau 2-1

29

de la page 15 du datasheet nous apprend que ce registre est toujours remis 0 aprs un reset ou une mise sous tension. Que trouvons-nous ladresse 0x00 ? Tout simplement la ligne qui suit la directive org 0x00 . Modifions encore notre programme pour voir si vous avez tout compris : Remplaons pour cela la ligne
org 0x800 ; adresse de la suite du programme

Par
org 0x850 ; adresse de la suite du programme

Profitez-en pour ouvrir la fentre Special Fonction Registers du menu Window . Ceci vous permettre de suivre lvolution du registre PCL, titre dinformation. Lancez la compilation , puis faites avancer votre programme lentement en pas pas. Arriv la ligne du saut vers saut2 , pressez encore une fois <F7>. Que se passe-t-il ? Une nouvelle fentre souvre alors, intitule Program Memory Window . Cette fentre reprsente le contenu de votre mmoire programme. En effet, o donc avez-vous saut ? Appliquons de nouveau la mme mthode. Le saut a donc du avoir lieu vers ladresse 0x050, au lieu de 0x850 prvue. donc hors de notre programme. MPLAB ne peut donc pas pointer dans votre fichier source, il est contraint douvrir cette nouvelle fentre. Si vous ne pointez pas sur la bonne adresse, pas de panique, MPLAB semble ne pas apprcier ce type de manuvre. Faites dfiler la fentre jusqu ladresse 0x00. Vous verrez alors la premire partie de votre programme. Remarquez que lassembleur a bien traduit le saut en goto 0x050 . Dfilez maintenant jusque ladresse 0x850. vous voyez alors la seconde partie de notre programme.

30

4.4 Evitons le pige Nous allons encore une fois modifier notre programme en remplaant la ligne
org 0x850 ; adresse de la suite du programme

par
org 0x7FF ; adresse de la suite du programme

Nous allons donc sauter juste avant la limite de la page, ce qui fait que la premire instruction suivant ltiquette saut2 sera en page 0, tandis que la suite du programme sera en page 1. Nous nous trouvons donc au lieu de dbordement de page dun programme. Recompilons notre programme et analysons la fentre des rsultats. Nous constatons alors quil ny a plus quun seul warning, correspondant au second saut. Ceci est logique, car ltiquette saut2 est maintenant situe dans la mme page (0) que le premier goto . Faisons donc tourner notre programme en pas--pas. Il semble maintenant fonctionner tout fait correctement. Quel est donc ce mystre ? En fait, comme toujours, ce nest que pure logique. Il y a 3 phases distinguer dans le fonctionnement de ce petit bout de code. Le premier saut (goto saut1) seffectue sans problme, puisque ladresse de destination (0x7FF) est situe dans la page 0, les bits 3 et 4 de PCLATH tant 0, ladresse de destination obtenue est bien 0x7FF Ensuite, en avanant en pas pas, on passe la page2. Notez en passant 2 choses. PCL est remis 0, ce qui est logique, et PCLATH voit son contenu inchang. Cest ici quil ne faut pas tomber dans le pige :

31

LE REGISTRE PCLATH NEST JAMAIS MODIFIE AUTOMATIQUEMENT PAR LE PROGRAMME. Cest lutilisateur seul de procder sa modification. Mais PCLATH nest utilis que pour les sauts directs, PAS pour le droulement du programme. Si vous regardez en effet le schma-bloc du 16F876, vous constatez que le compteur de programme a une capacit de 13 bits, donc ne pose aucun problme pour le droulement squentiel du programme. Ensuite, vous trouvez le saut goto saut1 . De nouveau on sattend un problme, et il nen est rien. Pourquoi ? Et bien tout simplement, malgr le warning de MPASM qui demande de vrifier PCLATH, ce dernier est tout simplement bien configur.

En effet, nous navons pas jusqu prsent modifi PCLATH, donc il pointe toujours sur la page 0. Donc notre saut seffectuera bien en page0. Donc, CE NEST PAS PARCE QUE VOTRE PROGRAMME SEXECUTE DANS UNE PAGE QUE PCLATH POINTE VERS CETTE MEME PAGE. IL VOUS INCOMBE DE GERER VOUS-MEME PCLATH. Si vous avez compris ce qui prcde, vous allez tout comprendre dans le fonctionnement de PCLATH. Rassurez-vous, je vous le rappelle, ceci ne vous sera utile que pour des programmes dune taille suprieure 2Kmots, donc vous avez le temps de bien assimiler. Je commence cependant par cette partie, car je reois normment de courrier de personnes qui dsirent modifier un code-source crit pour un 16F876. Ces modifications entranent parfois un dbordement de page, ce qui empche donc le programme modifi de fonctionner. 4.5 Premire correction de notre exercice Remplaons encore une fois notre ligne
org 0x7FF ; adresse de la suite du programme ; adresse de la suite du programme

par
org 0x850

Mais, cette fois, nous allons tenter deffectuer correctement nos sauts. Pour ce faire, occupons-nous dabord du premier. Nous devrons donc sauter de la page 0 vers la page1. Mais avant tout, voici un petit tableau destin vous viter lusage de la calculatrice pour tablir les valeurs des bits 3 et 4 de PCLATH. Adresses de destination 0x0000 0x07FF 0x0800 0x0FFF 0x1000 0x17FF 0x1800 0x1FFF Page 0 1 2 3 B4 0 0 1 1 B3 0 1 0 1

Nous voyons daprs ce tableau que nous devrons positionner le bit3 de PCLATH 1 pour pouvoir sauter en page1, 0x850 tant bien situ dans cette page.

32

Donc, avant la ligne de saut, nous devrons ajouter la mise 1 de ce bit. Notez que lanalyse du tableau des registres du datasheet nous montre que PCLATH est prsent dans toutes les banques. Donc, inutile de positionner correctement RP0 et RP1. Et oui, jen vois qui avaient dj purement ignor ce dtail (rappelez-vous, plus de la moiti des erreurs de programmation)
bsf goto PCLATH , 3 saut2 ; prparer saut en page 1 ; sauter

Maintenant, occupons-nous de notre second saut. En effet, ladresse de destination va dpendre galement, COMME TOUS LES SAUTS, du contenu de PCLATH. Si nous laissons PCLATH dans cet tat, le saut2 seffectuera en ralit vers ladresse 0x800. Aussi, avant le saut, devrons-nous remettre PCLATH en configuration page 0 .
bcf goto PCLATH , 3 saut1 ; prparer saut en page 0 ; sauter

Compilons le programme : toujours les 2 warnings davertissement de franchissement de pages. Excutons le programme : il tourne maintenant parfaitement. Vous voyez, rien de sorcier, juste de la rflexion. 4.6 Evitons les warnings inutiles Nous avons vu que les warnings sont bien pratiques dans ce cas prcis pour nous indiquer les sauts inter-page. Le problme, cest quune fois la solution trouve, le warning persiste. Or, dans le cas dun tout gros programme, puisquil sagit ici de programmes dpassant 2Kmots, vous risquez de vous retrouver avec des dizaines de warnings de ce type. Une fois que vous aurez rgl chaque cas sparment, si vous modifiez votre programme, et que cette modification, par exemple une insertion, provoque des modifications dans les emplacements de sauts de pages, vous serez dans lincapacit de le vrifier sans tout reprendre depuis le dbut. Autre inconvnient, si votre correction est mauvaise, vous naurez pour le vrifier aucun autre choix que de tout contrler depuis le dbut. Jai rencontr galement ce problme, et jai trouv une astuce qui me met labri de ce type dinconvnient, car je transfre alors le contrle des sauts de page directement MPASM. Je vais vous montrer quelle a t ma dmarche et quelle solution pratique jai trouve (de nouveau, certaines macros intgres MPLAB effectuent le mme style doprations). MPASM gnre un warning de saut de page quand la page de destination est diffrente de la page dans laquelle linstruction de saut est excute. Lastuce consiste faire croire MPASM quil se trouve dans la mme page. Comment ? Tout simplement en modifiant les adresses de destination. Je commence par expliquer directement dans le source du programme. Voyons notre premier saut : nous nous trouvons en page0 et nous dsirons sauter ladresse saut2 situe en page1. MPASM remplace saut2 par sa valeur et voit que cette valeur nest pas en page0. Nous allons donc le bluffer.

33

Pour lui faire croire que le saut seffectue en page0, il suffit de supprimer le bit 11 de ladresse, afin de ne garder que les 11 bits utiliss en ralit. Cette opration naura aucun impact, puisque lassemblage ne conservera de toute faon que les 11 bits de poids faible. Par contre, MPASM naura plus aucune raison de gnrer un warning. Modifions donc simplement notre ligne
goto saut2 ; sauter

en
goto (saut2 & 0x7FF) ; sauter

Compilez le tout : le premier warning a disparu, occupons-nous du second. Ici, cest exactement le contraire. MPASM constate que nous sommes en page 1 et que nous sautons en page 0. Il suffit donc de lui faire croire que nous sautons en page1. Comment ? Tout simplement en forant le bit 11 de ladresse 1. Ceci naura aucune consquence, puisque lassemblage, seuls les 11 bits de poids faibles seront conservs. Modifions donc simplement notre ligne
goto (saut1 ) ; sauter

en
goto (saut1 | 0x800) ; sauter

Le symbole | signifie pour lassembleur OR (voir premire partie, oprations boolennes). Il est obtenu en maintenant la touche <alt> de droite enfonce et en tapant la touche <1> pour le clavier belge, et <6> pour le clavier franais (pas sur le pav numrique). Compilons tout a, plus de warning, excutons le programme : tout fonctionne bien. Mais tout ceci nest gure pratique, ni facile mettre en place. De plus le risque derreur devient trs grand au fur et mesure de laugmentation de taille de notre programme. En effet, si diffrentes insertions dplacent par exemple notre saut2 vers la page2, et bien linstruction de saut plac en page1 ne gnrera plus aucun warning. Ceci est logique, car on aura dfinitivement fait croire MPASM que notre appel seffectue lintrieur de la page0. Nous navons donc rsolu notre problme que de manire temporaire. Faites lessai si vous ntes pas convaincu. Modifiez org 0x850 en org 1000 , et vous constaterez que vous navez pas de warning pour linstruction goto saut2 , alors que le saut est erron. 4.7 Dmonstration pratique de lutilit des macros Pour que ce soit pratique, reste implmenter ceci dans des macros. Je vous ai donc mis au point une petit macro qui va faire tout le travail pour vous.
GOTOX macro ADRESSE ; saut inter-page local BIT4 = (ADRESSE & 0x1000) ; voir bit 12 local BIT3 = (ADRESSE & 0x0800) ; voir bit 11 local ICI ; adresse courante ICI local PICI = (ICI+2 & 0x1800) ; page du saut

34

IF BIT3 ; bsf PCLATH , 3 ; ELSE ; bcfPCLATH , 3 ; ENDIF IF BIT4 ; bsfPCLATH , 4 ; ELSE ; bcfPCLATH , 4 ; ENDIF goto (ADRESSE & 0x7FF | PICI); endm

si page 1 ou 3 b3 de PCLATH = 1 sinon b3 de PCLATH = 0 si page 2 ou 3 b4 de PCLATH = 1 sinon b4 de PCLATH = 0 adresse simule

Houl ! Jen vois qui paniquent. Restez cool, jexplique ligne par ligne, cest tout simple. Vous allez commencer voir le vritable intrt des macros. Plus quun simple traitement de texte, cest, cette fois, un vritable instrument anti-erreurs.
GOTOX macro ADRESSE ; saut inter-page

Ici, tout simplement on dclare la macro. Cette macro se nomme donc GOTOX, et elle reoit en argument ADRESSE, qui est ladresse de destination du saut. Donc, pour un saut inter-page vers ladresse saut2 , on utilisera donc : GOTOX saut2
local BIT4 = (ADRESSE & 0x1000) ; voir bit 12

Dans cette ligne, nous trouvons dabord la directive LOCAL , qui signifie que la valeur sera recalcule chaque appel de la macro. La valeur BIT4 sera donc diffrente chaque fois que vous appelerez GOTOX . Cest logique. Vient derrire le calcul de BIT4 . On pourra dire que BIT4 est gal ladresse de destination AND 0x1000, soit B1000000000000 . Donc, BIT4 vaudra 0 si ladresse de destination tient sur 12 bits, donc infrieure 0x1000, donc en page 0 ou 1. Cest dire si le bit 4 de PCLATH devra tre mis 0 pour sauter. Ladresse de destination a dans ce cas son bit 12 0 .
local BIT3 = (ADRESSE & 0x0800) ; voir bit 11

Strictement identique pour une adresse qui comporte son bit 11 0. Donc, ceci nous permet de savoir si ladresse de destination se situe ou non en page 1 et 3. Reportez-vous au tableau chapitre 4.5
ICI local ICI ; adresse courante

Ici, nous dclarons simplement une tiquette locale. Cette dclaration est suivie par ltiquette en elle-mme, en premire colonne, comme il se doit. Ceci nous permet de savoir o, et plus prcisment dans quelle page, la macro GOTOX a t appele. La directive local est indispensable, car si on appelle plusieurs fois la macro GOTOX , ICI sera chaque fois un endroit diffrent.
local PICI = (ICI+2 & 0x1800) ; page du saut

35

Bon, ici que va-t-on faire ? Et bien tout simplement dterminer la page dans laquelle se trouve notre macro. En effet, nous avons vu que pour berner les warnings de MPASM, il faut connatre la page dans laquelle on se trouve au moment de lappel. Donc, on commence par ajouter 2 ladresse courante. Pourquoi ? Tout simplement parce que ce qui importe, cest ladresse du goto . Or, notre macro va soccuper galement automatiquement des bits 3 et 4 de PCLATH, et ceci, bien videmment, avant le goto en question. Donc, ladresse de notre goto est ladresse actuelle incrmente de 2, puisque 2 instructions le prcderont. Ensuite, on trouve un AND avec 0x1800, ce qui a pour effet dliminer ladresse en cours et de conserver son adresse de page. Donc, lissue de ceci, PICI vaudra 0x000, 0x800, 0x1000, ou 0x1800.
IF BIT3 ; si page 1 ou 3

Ceci signifie tout simplement que la ou les lignes suivantes seront assembles SI la valeur de BIT3 existe, donc si BIT3 est diffrent de 0 , donc si on doit sauter en page 1 ou en page 3.
bsfPCLATH , 3 ; b3 de PCLATH = 1

Dans ce cas, MPASM crera automatiquement cette ligne, cest dire que le bit3 de PCLATH sera mis automatiquement 1.
ELSE bcf PCLATH , 3 ; sinon ; b3 de PCLATH = 0

Tout simplement : sinon (donc si BIT3 = 0), MPASM crira automatiquement cette dernire ligne, donc le bit3 de PCLATH sera mis automatiquement 0.
ENDIF

Ceci indique la fin du test prcdent.


IF BIT4 bsf PCLATH , 4 ELSE bcf PCLATH , 4 ENDIF ; ; ; ; si page 2 ou 3 b4 de PCLATH = 1 sinon b4 de PCLATH = 0

Et bien, ici, cest strictement identique, mais concernera le bit4 de PCLATH. Cette macro vous permet donc de sauter automatiquement de et vers nimporte laquelle des 4 pages sans plus vous soucier de rien. Avouez que cest rudement pratique.
goto (ADRESSE & 0x7FF | PICI) ; adresse simule

Ceci, cest la fameuse ligne, un peu amliore, que vous avez utilise dans lexemple prcdent. Cest cette ligne qui va berner MPASM en lui faisant croire que vous sautez dans la page actuelle.

36

La premire opration (ADRESSE & 0x7FF) permet de conserver ladresse code sur 11 bits, tandis que la seconde ( | PICI ) ajoute en fait ladresse de base de la page courante. Il ne reste plus qu modifier notre programme principal.
org 0x000 saut1 nop nop GOTOX saut2 org 0x850 saut2 nop nop GOTOX saut1 END ; Adresse de dpart aprs reset ; ne rien faire ; ne rien faire ; sauter ; adresse de la suite du programme ; ; ; ; ne rien faire ne rien faire sauter en page 0 directive fin de programme

Compilez le tout : plus de warning. Excutez : tout ce passe sans problme. Allez jeter un il sur le code obtenu laide de cette macro dans la fentre window-> program memory . Amusez-vous galement modifier les directives org pour essayer toutes sortes de combinaisons de pages. Vous verrez qu chaque fois MPASM compile correctement votre programme. Plus de warning, plus de soucis. Il ne vous reste plus quune chose retenir : en cas de warning concernant les sauts de page, remplacez linstruction goto par la macro GOTOX pour les lignes concernes, et le problme sera automatiquement rsolu. Nest-ce pas utile, les macros ? Attention, ne tombez cependant pas dans le pige grossier suivant. Par exemple, ne remplacez pas :
btfss STATUS , Z goto plusloin

par
btfss STATUS , Z GOTOX plusloin

Pourquoi ? Tout simplement parce que btfss permet de sauter linstruction suivante, et que GOTOX est une macro qui contient 3 instructions. Donc, le test ne fonctionnerait pas. En effet, ce ne serait pas goto qui suivrait btfss, mais bsf ou bcf PCLATH,3. Il vous faudra donc, soit excuter votre test dune autre faon, soit placer les modifications de PCLATH avant le test. Je vous ai cris 2 macros spares directement utilisables avec ce genre de configuration. : PCLAX (modifie PCLATH) et GOTSX (saut simple, sans modification de PCLATH). En fait, PCLAX suivi de GOTSX est lquivalent de GOTOX. Lavantage est de sparer le saut de linitialisation de PCLATH.
PCLAX macro ADRESSE ; ; local BIT4 = (ADRESSE & 0x1000) ; local BIT3 = (ADRESSE & 0x0800) ; positionne PCLATH pour les sauts sans le saut voir bit 12 voir bit 11

37

IF BIT3 bsf PCLATH , 3 ELSE bcf PCLATH , 3 ENDIF IF BIT4 bsf PCLATH , 4 ELSE bcf PCLATH , 4 ENDIF Endm GOTSX macro ADRESSE local ICI local PICI = (ICI & 0x1800) goto (ADRESSE & 0x7FF | PICI) endm

; ; ; ; ; ; ; ;

si page 1 ou 3 b3 de PCLATH = 1 sinon b3 de PCLATH = 0 si page 2 ou 3 b4 de PCLATH = 1 sinon b4 de PCLATH = 0

ICI

; ; ; ;

saut inter-page sans slection de PCLATH adresse courante page du saut

; adresse simule

En fait, il sagit tout simplement de la macro GOTOX scinde pour pouvoir tre utilise facilement avec les tests. Si on reprend notre exemple, on pourra remplacer
btfss STATUS , Z goto plusloin

par
PCLAX btfss GOTSX pluloin STATUS , Z plusloin ; ; ; ; configure le registre PCLATH pour le saut effectue le test (nimporte lequel) saute avec leurre de warning (une seule instruction)

Pour information, la macro intgre PAGESEL est quivalente notre macro PCLAX.
PAGESEL pluloin btfss STATUS , Z GOTSX plusloin ; ; ; ; configure le registre PCLATH pour le saut effectue le test (nimporte lequel) saute avec leurre de warning (une seule instruction)

4.8 Les sous-programmes inter-pages Maintenant, vous allez me dire. Les sauts de type goto , cest bien joli, mais que se passe-t-il pour les sous-routines ? Et bien, identiquement la mme chose, un dtail prs. Une sous-routine se termine par une instruction de retour (return, retlw). Si vous regardez le schma-bloc, vous verrez que la totalit du compteur de programme, cest dire les 13 bits, a t conserve dans la pile. Donc, vous devez positionner PCLATH pour lappel de sous-routine, exactement comme pour un saut, par contre, vous ne devez pas vous en proccuper concernant le retour. Ceci est heureux, car sinon il serait difficile dappeler la mme sous-routine depuis 2 pages diffrentes.

38

Par contre, comme votre programme revient sa page dappel aprs le return, vous devrez repositionner correctement PCLATH pour viter quun saut ordinaire ne prenne en compte votre modification de PCLATH. Le retour seffectuera donc toujours correctement quelles que soient les pages dappel et demplacement de la sous-routine. Reste seulement tablir pour les call , les macros correspondant aux goto . Les voici :
CALLX macro ADRESSE local BIT4 = (ADRESSE & 0x1000) local BIT3 = (ADRESSE & 0x0800) local ICI ICI local PICI = (ICI+2 & 0x1800) IF BIT3 bsf PCLATH , 3 ELSE bcf PCLATH , 3 ENDIF IF BIT4 bsf PCLATH , 4 ELSE bcf PCLATH , 4 ENDIF call (ADRESSE & 0x7FF | PICI) local BIT4 = ((ICI+5)& 0x1000) local BIT3 = ((ICI+5) & 0x0800) IF BIT3 bsf PCLATH , 3 ELSE bcf PCLATH , 3 ENDIF IF BIT4 bsf PCLATH , 4 ELSE bcf PCLATH , 4 ENDIF endm CALLSX macro ADRESSE local ICI local PICI = (ICI+2 & 0x1800) call (ADRESSE & 0x7FF | PICI) endm ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; call inter-page voir bit 12 voir bit 11 adresse courante page du saut si page 1 ou 3 b3 de PCLATH = 1 sinon b3 de PCLATH = 0 si page 2 ou 3 b4 de PCLATH = 1 sinon b4 de PCLATH = 0 adresse simule voir bit 12 voir bit 11 si page 1 ou 3 b3 de PCLATH = 1 sinon b3 de PCLATH = 0 si page 2 ou 3 b4 de PCLATH = 1 sinon b4 de PCLATH = 0

ICI

sous-routine inter-page sans slection de PCLATH adresse courante page du saut

; adresse simule

La macro PCLAX tant identique, inutile de lcrire deux fois. Le calcul (ICI+5) se justifie par le raisonnement suivant : Il importe de repositionner PCLATH pour quil pointe dans la page courante, afin dviter quun simple goto ou un call lintrieur de la page ne provoque en ralit un saut dans la page prcdemment pointe par PCLATH modifi par la macro. Tout ceci est ralis automatiquement par notre macro CALLX

39

La macro va gnrer 5 lignes de code, et donc aprs son excution, le programme continuera ladresse du dbut de la macro (ICI) incrmente de 5. Vous navez donc plus qu placer votre macro la place de votre call habituel.
Etiquette_pagex CALLX etiquette_pagey Suite du programme principal etiquette_pagey ici commence la sous-routine return ; ; ; ; on est en page x appel de sous-routine en page y poursuite, avec PCLATH pointant automatiquement sur la page x

; sous-routine en page y ; traitement normal ; retour normal de sous-routine

Vous pouvez galement pointer sur la bonne page en utilisant PCLAX, et en indiquant ladresse courante comme adresse de destination.
PCLAX CALLSX etiquette_pagey etiquette_pagey ; ; ; ; pointer sur page y appel de sous-routine en page y tiquette pour dterminer pagecourante repointer sur page courante

ici

PCLAX ici Suite du programme

Ou, enfin, en utilisant la macro intgre de MPASM.


PAGESEL CALLSX etiquette_pagey etiquette_pagey ; ; ; ; pointer sur page y appel de sous-routine en page y tiquette pour dterminer pagecourante repointer sur page courante

ici

PAGESEL ici Suite du programme

Vous voyez que CALLX est tout de mme plus pratique, avec bien moins de risques doubli. Jai t particulirement long dans ce chapitre, mais cette notion tait assez dlicate aborder, et il me semblait dommageable de vouloir utiliser un processeur plus puissant et se retrouver bloqu le jour on dsire crer un programme consquent. A la fin de ce chapitre, je place dans le rpertoire fichiers , le programme pclath.asm , qui contient lexercice termin.

40

Notes :

41

Notes :

42

5. Les sources dinterruptions


Jai choisi dnumrer les diffrentes sources dinterruption du 16F876, car nous allons en avoir besoin dans les chapitres suivants. De plus, nous allons pouvoir mettre en vidence quelques diffrences avec le 16F84, ce qui place ce chapitre dans la suite logique du prcdent. 5.1 Enumration Voici un tableau rcapitulatif des 13 interruptions disponibles sur le 16F876. Notez que le 16F877/4 dispose dun port parallle supplmentaire qui lui autorise une source dinterruption en sus. Sur ces composants, nous aurons donc 14 sources dinterruption. Dclencheur Timer 0 Pin RB0 / INT Ch. RB4/RB7 Convert. A/D Rx USART Tx USART Port srie SSP Module CCP1 Module CCP2 Timer 1 Timer 2 EEPROM SSP mode I2C Port parallle Flag T0IF INTF RBIF ADIF RCIF TXIF SSPIF CCP1IF CCP2IF TMR1IF TMR2IF EEIF BCLIF PSPIF Registre INTCON INTCON INTCON PIR1 PIR1 PIR1 PIR1 PIR1 PIR2 PIR1 PIR1 PIR2 PIR2 PIR1 Adr 0x0B 0x0B 0x0B 0x0C 0x0C 0x0C 0x0C 0x0C 0x0D 0x0C 0x0C 0x0D 0x0D 0x0C PEIE Enable NON T0IE NON INTE NON RBIE OUI ADIE OUI RCIE OUI TXIE OUI SSPIE OUI CCP1IE OUI CCP2IE OUI TMR1IE OUI TMR2IE OUI EEIE OUI BCLIE OUI PSPIE Registre INTCON INTCON INTCON PIE1 PIE1 PIE1 PIE1 PIE1 PIE2 PIE1 PIE1 PIE2 PIE2 PIE1 Adr 0x0B 0x0B 0x0B 0x8C 0x8C 0x8C 0x8C 0x8C 0x8D 0x8C 0x8C 0x8D 0x8D 0x8C

Quelques mots sur ce tableau. En jaune vous avez les 3 interruptions dont le traitement est identique celui du 16F84 et que nous avons vues dans la premire partie. En vert, vous voyez linterruption dcriture eeprom dont le traitement a t modifi. Cette interruption tait dj prsente sur le 16F84, mais son traitement est ici lgrement diffrent. En bleu ce sont les nouvelles interruptions utilisables sur le 16F876. La ligne grise se rfre linterruption supplmentaire du 16F877 ou 16F874. Lexplication des diffrentes colonnes, de gauche droite : Dclencheur : Evnement ou fonction qui est la source de linterruption Flag : Bit qui se trouve positionn lorsque lvnement survient

43

Registre : Registre auquel le flag appartient Adr : Adresse de ce registre (tous en banque0, avec INTCON prsent dans les 4 banques) PEIE : Indique si le positionnement de PEIE du registre INTCON joue un rle. Cest cause du OUI dans la ligne EEPROM que le traitement de cette interruption est ici lgrement diffrente du 16F84. Enable : nom du bit qui permet dautoriser ou non linterruption Registre : Registre qui contient ce bit Adr : adresse de ce registre (tous en banque1, avec INTCON dans les 4 banques)

5.2 Le registre INTCON et les interruptions priphriques Commenons donc par ce que nous connaissons, et pour faire simple, comparons le contenu de ce registre avec celui du 16F84, histoire davoir une ide des modifications. GIE GIE EEIE PEIE INTCON pour le 16F84 T0IE INTE RBIE T0IF INTCON pour le 16F87x T0IE INTE RBIE T0IF INTF INTF RBIF RBIF

En fait, nous constatons que tout est rest identique au 16F84, except EEIE, devenu PEIE. Nous allons expliquer pourquoi. En fait, Microchip a choisi une mthode un peu particulire, et a scind les interruptions entre ce que nous pourrions appeler les interruptions primaires et les interruptions secondaires, appeles ici interruptions priphriques. Pour raliser ceci, on a donc ajout au registre INTCON un second bit de validation gnrale des interruptions qui ne concerne que les interruptions priphriques. Comme il ny avait pas la place dans ce registre, le bit EEIE concernant lcriture en eeprom, et considr comme secondaire , a t dplac vers le registre PIR2. 5.2.1 Mise en service des interruptions primaires Des constatations prcdentes, nous pouvons dire que nous disposons de 3 interruptions primaires. Cest dire utilisables exactement de la mme manire que pour le 16F84. Pour mettre en service une de ces interruptions, vous devez donc : 1) Valider le bit concernant cette interruption 2) Valider le bit GIE (General interrupt enable) qui met toutes les interruptions choisies en service Donc, ce niveau vous ne devriez avoir aucun problme, rien de chang.

44

5.2.2 Mise en service des interruptions priphriques Nous avons vu que les interruptions priphriques sont tributaires du bit de validation gnrale des interruptions priphriques PEIE. Voici donc les 3 tapes ncessaires la mise en service dune telle interruption : 1) Validation du bit concernant linterruption dans le registre concern (PIE1 ou PIE2) 2) Validation du bit PEIE (PEripheral Interrupt Enable bit) du registre INTCON 3) Validation du bit GIE qui met toutes les interruptions en service. Notez donc que la mise en service de PEIE ne vous dispense pas linitialisation du bit GIE, qui reste de toute faon prioritaire. Cette architecture vous permet donc de couper toutes les interruptions dun coup (effacement de GIE) ou de couper toutes les interruptions priphriques en une seule opration (effacement de PEIE) tout en conservant les interruptions primaires. Ceci vous donne davantage de souplesse dans le traitement des interruptions, mais complique un petit peu le traitement. 5.3 Les registres PIE1, PIE2, PIR1 et PIR2 Vous avez vu que ces composants disposent de plus de sources dinterruptions que ne peut en grer le registre INTCON. Donc, les autorisations dinterruptions vont se trouver dans dautres registres. Ces registres sont PIE1 et PIE2. Les flags correspondants se trouvent quant eux dans les registres PIR1 et PIR2. Voici le nom de chaque bit de ces registres Registre Adresse PIE1 0x8C PIR1 0x0C PIE2 0x8D PIR2 0x0D B7
PSPIE PSPIF N.I. N.I.

B6
ADIE ADIF Rserv Rserv

B5
RCIE RCIF N.I. N.I.

B4
TXIE TXIF EEIE EEIF

B3
SSPIE SSPIF BCLIE BCLIF

B2
CCP1IE CCP1IF N.I. N.I.

B1

B0

TMR2IE TMR1IE TMR2IF TMR1IF N.I. CCP2IE N.I. CCP2IF

Vous remarquerez que les 2 registres dautorisations (PIE1 et PIE2) se trouvent en banque 1, tandis que les registres de flags (PIR1 et PIR2) se trouvent en banque 0. Remarquez aussi quil y a quelques bits non implments, qui donneront toujours 0 sils sont lus, mais aussi 2 bits rservs quil est impratif de maintenir 0 afin de conserver toute compatibilit future. Les bits des registres PIEx permettent dautoriser les interruptions correspondantes, mais rappelez-vous que ces bits ne sont oprationnels que si le bit PEIE du registre INTCON est mis 1. Dans le cas contraire, toutes ces interruptions sont invalides. Pour quune des interruptions dcrites ici soit oprationnelle, je rappelle quil faut donc la triple condition suivante : Le bit GIE du registre INTCON doit tre mis 1
45

Le bit PEIE du registre INTCON doit tre mis 1 Le bit correspondant linterruption concerne (registre PIE1 ou PIE2) doit tre mis 1.

Voici maintenant le rle de chacun des bits dautorisation dinterruption, sachant quun bit 1 autorise linterruption correspondante : PIE1 PSPIE ADIE RCIE TXIE SSPIE CCP1IE TMR2IE TMR1IE PIE2 EEIE BCLIE CCP2IE Lecture/criture sur le port PSP // (concerne uniquement les 16F877/16F874) Conversion analogique/digitale Rception dun caractre sur le port srie USART Emission dun caractre sur le port srie USART Communication sur le port srie synchrone SSP Evnement sur compare/capture registre 1 Correspondance de valeurs pour le timer TMR2 Dbordement du timer TMR1 Ecriture dans lEEPROM Collision de bus pour le port srie synchrone I2C Evnement sur compare/capture registre 2

Et maintenant, les flags correspondants. Attention, certains flags ncessitent une explication assez pousse, explications qui seront reprises dans les chapitres correspondant aux vnements concerns. PIR1 PSPIF ADIF RCIF TXIF SSPIF Lecture ou criture termine sur le port // du 16F877 Fin de la conversion analogique/digitale Le buffer de rception de lUSART est plein (lecture seule) Le buffer dmission de lUSART est vide (lecture seule) Fin de lvnement dpendant du mode de fonctionnement comme suit : Mode SPI Un caractre a t envoy ou reu Mode I2C esclave Un caractre a t envoy ou reu Mode I2C matre Un caractre a t envoy ou reu ou fin de la squence start ou fin de la squence stop ou fin de la squence restart ou fin de la squence acknowledge ou start dtect durant IDLE (multimatre) ou stop dtect durant IDLE (multimatre) CCP1IF Evnement compare/capture 1 dtect suivant mode : Mode capture : capture de la valeur TMR1 ralise Mode compare : La valeur de TMR1 atteint la valeur programme TMR2IF La valeur de TMR2 atteint la valeur programme TMR1IF Dbordement du timer TMR1 PIR2 EEIF BCLIF CCP2IF Fin dcriture de la valeur en EEPROM Collision de bus, quand le SSP est configur en matre I2C Evnement compare/capture 2 dtect suivant le mode : Mode capture : capture du TMR1 ralise

46

Mode compare : La valeur de TMR1 atteint la valeur programme 5.3 Etude de la routine dinterruption du fichier maquette . Voici le contenu de la routine dinterruption principale contenue dans le fichier m16f876.asm . Cette routine contient tous les tests qui permettent daiguiller les interruptions vers la bonne sous-routine. Dans la majorit des cas, vous naurez plus qu vous occuper de remplir correctement les sous-routines concernes. Vous pourrez galement supprimer les tests qui ne vous sont pas ncessaires, et, ventuellement inverser leur ordre afin de dfinir des priorits. Soyez dans ce cas attentifs au bon usage du bit PEIE.
;************************************************************************** ; ROUTINE INTERRUPTION ;************************************************************************** ;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS , w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp movf FSR , w ; charger FSR movwf FSR_temp ; sauvegarder FSR movf PCLATH , w ; charger PCLATH movwf PCLATH_temp ; le sauver clrf PCLATH ; on est en page 0 BANK0 ; passer en banque0

Cette premire partie effectue la sauvegarde des diffrents registres. Nous allons dcrire ici leur utilit et leurs impratifs. w_temp est la sauvegarde du registre de travail W . Ce registre va tre utilis pour la sauvegarde des autres registres. De fait, cest le premier qui doit tre sauv. Hors, si vous sauvez le registre W avant dtre autoris modifier STATUS , qui lui nest pas encore sauv, vous ne pouvez donc pas changer de banque. En effet, le changement de banque ncessite la modification de RP0 et RP1 du registre STATUS . Donc, si vous suivez toujours, au moment de linterruption, vous ne pouvez pas savoir comment sont configurs RP0 et RP1, donc vous ne savez pas vers quelle banque vous pointez. Il est donc de ce fait impratif de sauvegarder W dans la zone RAM commune. REMARQUE IMPORTANTE Les 16F873 et 16F874 ne disposent pas dune zone de banque commune. Comme vous ne pouvez pas prvoir dans quelle banque vous allez sauver W, vous devez rserver la mme adresse relative pour w_temp la fois dans la banque 0 et dans la banque 1. Ensuite, vous effacerez le registre STATUS pour sauver les autres registres dans la banque 0 (par exemple)
movwf w_temp swapf STATUS,W ; Sauver W dans la banque 0 ou dans la 1 ; charger STATUS swapp

47

clrf STATUS movwf status_temp

; pointer sur la banque 0 ; sauver STATUS en banque 0

La suite tant identique, except quil est inutile dcrire la macro BANK0 La dclaration sera du type :
CBLOCK 0x20 w_temp : 1 .. .. ENDC CBLOCK 0xA0 w_temp2 : 1 .. .. ENDC ; Dbut de la zone (0x20 0x7F) ; sauvegarde de W en banque 0

; ; ; ; ;

Dbut de la zone (0xA0 0xFF) sauvegarde de w en banque 1 (mme position imprative que pour w_temp. Dans ce cas, premire position ltiquette w_temp2 ne sera pas utilise, mais lemplacement est rserv en pratiquant de la sorte.

Revenons notre 16F876 : Pour status_temp vous pourriez par contre commencer par charger status dans w, puis procder un changement de banque avant de le sauver. Sa sauvegarde en banque 0 nest donc pas imprative. Vous pouvez donc placer status_temp dans nimporte quelle banque. Ceci est galement vrai pour les registres suivants FSR_temp sauvegarde le registre FSR dadressage indirect. Ce registre ne devra tre imprativement sauv que si la routine dinterruption risque de perturber ce niveau le programme principal. Il faut donc 2 conditions ceci : Il faut que le programme principal utilise ladressage indirect, sinon, aucune raison de sauvegarder FSR. ET il faut que la routine dinterruption utilise ladressage indirect, car sinon le registre FSR ne serait pas modifi et il ny aurait donc aucune raison de le sauvegarder.

Notez que rien ne vous empche de sauvegarder FSR dans tous les cas pour des raisons de facilit et de prcaution. Il ne vous en cotera que 2 mots de programme et quelques nanosecondes de temps dexcution. A vous de voir si ces surplus sont nuisibles ou non votre programme. La mme remarque est valable pour la sauvegarde de PCLATH. Pour terminer, voyons donc la sauvegarde du registre PCLATH dans PCLATH_temp : cette sauvegarde na de sens bien entendu que si la routine dinterruption modifie PCLATH. En gnral, cette modification risque dintervenir si votre programme est dune taille suprieure 2Kmots. Dans ce cas, en effet, le programme principal a de grandes chances de modifier PCLATH pour effectuer des sauts corrects, et vous serez alors contraint de remettre les bits 3 et 4 de PCLATH 0. Ceci pour viter quun saut dans votre routine dinterruption ne sorte de celle-ci. Notez que si vous supprimez la sauvegarde de PCLATH dans votre routine dinterruption et que vous utilisez PCLATH pour un autre usage (tableau), vous devrez alors galement supprimer la ligne clrf PCLATH qui modifie ce registre dans la routine dinterruption. Dans ce cas

48

particulier, vous ntes pas oblig de sauver PCLATH, mais vous ne pouvez pas le modifier non plus. On termine ce dbut par la macro BANK0, qui permet de pointer sur la banque 0 en cas dadressage direct. Voyons maintenant la suite de notre routine :
; Interruption TMR0 ; ----------------btfsc btfss goto call bcf goto INTCON , T0IE INTCON , T0IF intsw1 inttmr0 INTCON , T0IF restorereg ; ; ; ; ; ; tester si interrupt timer autorise oui, tester si interrupt timer en cours non test suivant oui, traiter interrupt tmr0 effacer flag interrupt tmr0 et fin d'interruption

Il sagit bien videmment ici de vrifier si cest le timer0 qui a dclench linterruption. On commence par tester si linterruption a t autorise, en vrifiant le positionnement de T0IE. Vous allez me dire : drle de procdure, quel est lintrt ? Et bien, vous ne devez jamais perdre de vue que les flags sont positionns quel que soit ltat du bit de validation correspondant. Vous avez donc 3 cas possibles : 1) Vous nutilisez pas les interruptions tmr0 dans votre programme : dans ce cas, vous pouvez tout simplement effacer ces 6 lignes de code. 2) Vous utilisez les interruptions tmr0 dans votre programme. Cependant, le bit T0IE reste en service tout au long de votre programme. Il est donc inutile de le tester. Vous pouvez donc effacer la premire ligne. 3) Votre programme utilise les interruptions du timer0, mais pas tout le temps, de plus vous utilisez dautres sources dinterruption. Donc, dans votre programme principal, vous coupez par moment T0IE afin de ne pas tre interrompu par le dbordement du timer0 pendant certaines priodes. Dans ce cas, si votre timer0 dborde, le flag T0IF sera positionn , mais linterruption correspondante ne sera pas gnre. Si par malheur se produit alors une interruption due un autre vnement que vous aviez autoris, et que vous ne testiez pas T0IE, alors vous seriez induit en erreur lors du test de T0IF. Celui-ci tant positionn, bien que linterruption ne soit pas due au dbordement de tmr0. Vous devez donc laisser le test de T0IE. La seconde ligne, qui vrifie si le bit T0IF est positionn, ne sera donc excut que si la ligne prcdente a dfini que T0IE est bien positionn. Donc, Si T0IF et T0IE sont tout deux positionns, on saute la ligne call inttmr0 . Dans le cas contraire, on saute, via goto intsw1 , au test suivant pour continuer la recherche de la source de linterruption. La ligne call inttmr0 permet dappeler la sous-routine inttmr0 , dans laquelle vous pourrez placer le code de traitement de votre routine dinterruption timer0.
49

IMPORTANT : la routine dinterruption, telle quelle est crite ici, utilise des sousroutines. Donc, si vous utilisez les interruptions, et sachant que vous disposez de 8 niveaux sur la pile (voir premire partie), il vous restera donc : 8 1 (pour les interruptions) 1 (pour lappel de sous-routine dans la routine dinterruption) = 6 imbrications de sous-routines possibles. Si vous avez besoin dun niveau supplmentaire, il vous suffira tout simplement de remplacer le call intttmr0 par un goto inttmr0 . Dans lancienne sous-routine inttmr0 , vous devrez donc remplacer le return par un goto restorereg , aprs avoir ajout la ligne bcf INTCON , T0IF juste avant. Vous devrez bien entendu procder de la mme faon pour chaque interruption utilise. Ceci vous conomise donc un niveau de pile, par labsence de sous-routines dans la routine dinterruption. Mais il est quant mme peu frquent dans un programme bien structur de dpasser cette limite. Ne vous inquitez donc pas de ceci pour linstant. La ligne jaune, bcf INTCON,T0IF permet deffacer le flag aprs avoir trait la routine. Noubliez pas que ces flags, pour la plupart (nous verrons les exceptions), ne seffacent pas automatiquement. Si vous oubliez de les effacer, ds que le programme sortira de linterruption, il y entrera de nouveau, et ce indfiniment ( moins que le watchdog ne soit en service). Mais gure besoin de vous inquiter, cette routine prend tout ceci en charge pour vous. Ce quil importe, cest davoir compris, nul besoin de rinventer la roue . La ligne verte, goto restorereg , quant elle, permet, une fois le traitement de votre interruption effectu, de sortir directement de la routine dinterruption, aprs avoir restaur les diffrents registres. Notez que la suppression de cette ligne permet de conserver les tests suivants, donc de traiter en une seule fois plusieurs interruptions simultanes . Cest vous que le choix appartient en fonction du cas particulier de votre programme. Il vous est galement possible dinverser lordre des diffrents tests. Ceci vous amne tablir une hirarchie de priorits dans le cas dinterruptions multiples simultanes . La premire teste sera dans ce cas la premire traite. Noubliez pas dans ce cas de bien prendre en compte le test de PEIE, ainsi que de grer correctement les tiquettes. Ensuite, nous trouvons :
; Interruption RB0/INT ; -------------------INTCON , INTE INTCON , INTF intsw2 intrb0 INTCON , INTF restorereg ; ; ; ; ; ; tester si interrupt RB0 autorise oui, tester si interrupt RB0 en cours non sauter au test suivant oui, traiter interrupt RB0 effacer flag interupt RB0 et fin d'interruption

intsw1 btfsc btfss goto call bcf goto

Ce test namne aucun commentaire particulier, et fonctionne de faon identique au premier cas cit.

50

intsw2 btfsc btfss goto call bcf goto

; interruption RB4/RB7 ; -------------------INTCON , RBIE INTCON , RBIF intsw3 intrb4 INTCON , RBIF restorereg ; ; ; ; ; ; tester si interrupt RB4/7 autorise oui, tester si interrupt RB4/7 en cours non sauter oui, traiter interrupt RB4/7 effacer flag interupt RB4/7 et fin d'interrupt

ATTENTION : noubliez pas (cours-part1) que dans la routine intrb4 de traitement de cette interruption, vous devrez imprativement lire le PORTB, afin dannuler la condition de positionnement de RBIF, et ainsi permettre son reset lors du retour. Vient ensuite :
intsw3 btfss INTCON , PEIE goto restorereg ; tester interruption priphrique autorise ; non, fin d'interruption

Ceci amne un petit commentaire. En effet, vous avez deux faons darriver ce point de votre routine dinterruption : 1) Vous avez conserv les lignes goto restorereg . Dans ce cas, puisque vous arrivez ce point du programme, cest que forcment vous ntes pas pass par une des interruptions prcdentes. En effet, aprs excution dune interruption prcdente, cette partie aurait t saute via la ligne en question. Donc, dans ce cas, les vnements prcdents nont pas t les dclencheurs de linterruption. Il sagit donc forcment dune interruption priphrique, donc, il est inutile de tester PEIE, car il est forcment 1 . 2) Vous avez supprim les lignes goto restorereg . Dans ce cas, il est possible quune interruption prcdente ait eu lieu. Dans ce cas, il vous reste deux sous-solutions : Si le bit PEIE reste inchang durant toute lexcution du programme, vous pouvez supprimer ce test. Si vous modifiez le bit PEIE durant lexcution de votre programme, vous devez laisser cette ligne, pour les mmes raisons que celles voques pour le test de T0IE.

Naturellement, si vous nutilisez dans votre programme aucune interruption priphrique, inutile de laisser ces lignes. Notez que si vous ne touchez rien, tout fonctionnera trs bien dans tous les cas (sauf certains programmes spcifiques). Je vous conseille donc, dans le doute, de laisser cette routine telle quelle, si vous ntes pas quelques microsecondes prs. Ensuite, nous trouvons les tests des interruptions priphriques

51

; Interruption convertisseur A/D ; -----------------------------bsf btfss goto bcf btfss goto call bcf goto STATUS , RP0 PIE1 , ADIE intsw4 STATUS , RP0 PIR1 , ADIF intsw4 intad PIR1 , ADIF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intsw4 bsf btfss goto bcf btfss goto call goto

; Interruption rception USART ; ---------------------------STATUS PIE1 , intsw5 STATUS PIR1 , intsw5 intrc , RP0 RCIE , RP0 RCIF ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt LE FLAG NE DOIT PAS ETRE REMIS A 0 et fin d'interrupt

restorereg

intsw5 bsf btfss goto bcf btfss goto call

; Interruption transmission USART ; ------------------------------STATUS PIE1 , intsw6 STATUS PIR1 , intsw6 inttx , RP0 TXIE , RP0 TXIF ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt LE FLAG NE DOIT PAS ETRE REMIS A 0

goto
intsw6 bsf btfss goto bcf btfss goto call bcf goto

restorereg

; et fin d'interrupt

; Interruption SSP ; ---------------STATUS , RP0 PIE1 , SSPIE intsw7 STATUS , RP0 PIR1 , SSPIF intsw7 intssp PIR1 , SSPIF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intsw7 bsf btfss goto bcf btfss goto call

; Interruption CCP1 ; ----------------STATUS , RP0 PIE1 , CCP1IE intsw8 STATUS , RP0 PIR1 , CCP1IF intsw8 intccp1 ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt

52

bcf goto

PIR1 , CCP1IF restorereg

; effacer flag interupt ; et fin d'interrupt

intsw8 bsf btfss goto bcf btfss goto call bcf goto

; Interruption TMR2 ; ----------------STATUS , RP0 PIE1 , TMR2IE intsw9 STATUS , RP0 PIR1 , TMR2IF intsw9 inttmr2 PIR1 , TMR2IF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intsw9 bsf btfss goto bcf btfss goto call bcf goto

; Interruption TMR1 ; ----------------STATUS , RP0 PIE1 , TMR1IE intswA STATUS , RP0 PIR1 , TMR1IF intswA inttmr1 PIR1 , TMR1IF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intswA bsf btfss goto bcf btfss goto call bcf goto

; Interruption EEPROM ; ------------------STATUS , RP0 PIE2 , EEIE intswB STATUS , RP0 PIR2 , EEIF intswB inteprom PIR2 , EEIF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intswB bsf btfss goto bcf btfss goto call bcf goto

; Interruption COLLISION ; ---------------------STATUS , RP0 PIE2 , BCLIE intswC STATUS , RP0 PIR2 , BCLIF intswC intbc PIR2 , BCLIF restorereg ; ; ; ; ; ; ; ; ; slectionner banque1 tester si interrupt autorise non sauter oui, slectionner banque0 oui, tester si interrupt en cours non sauter oui, traiter interrupt effacer flag interupt et fin d'interrupt

intswC bcf STATUS , RP0 call intccp2 bcfPIR2 , CCP2IF

; interruption CCP2 ; ----------------; slectionner banque0 ; traiter interrupt ; effacer flag interupt

53

Trois remarques : Comme cest prcis dans le fichier source, les flags RCIF et TXIF ne doivent pas tre effacs manuellement. Vous ne le pouvez dailleurs pas, il sont en lecture seule. Nous verrons plus tard que ces flags sont en fait remis 0 automatiquement lors de la lecture du contenu du registre de rception ou de lcriture du registre dmission. Si vous conservez les lignes goto restorereg dans votre routine dinterruption, alors vous narriverez votre dernier test dinterruption que si tous les autres ont chou. Dans ce cas, inutile donc de tester la dernire interruption, puisque cest forcment elle qui a provoqu lvnement. Par contre, si vous dcidez de supprimer ces lignes, il vous faudra alors ajouter un test supplmentaire sur la dernire interruption, sans quoi elle serait excute chaque fois. Il est inutile de conserver les tests des interruptions que vous nutilisez pas dans votre programme. Donc, de fait, inutile galement de conserver des interruptions qui ne concernent pas votre type de processeur. Cest pour a que cette routine nintgre pas linterruption du port parallle. Si vous utilisez un 16F877, par exemple, libre vous de lajouter. Je lai dailleurs fait pour vous dans le fichier m16F877.asm . Pour terminer, nous aurons :
;restaurer registres ;------------------restorereg movf PCLATH_temp , w ; movwf PCLATH ; movf FSR_temp , w ; movwf FSR ; swapf status_temp , w ; movwf STATUS ; swapf w_temp , f ; ; swapf w_temp , w ; ; retfie

recharger ancien PCLATH le restaurer charger FSR sauv restaurer FSR swap ancien status, rsultat dans w restaurer status Inversion L et H de l'ancien W sans modifier Z Rinversion de L et H dans W W restaur sans modifier status

; return from interrupt

Ce bout de code restaure les registres sauvegards prcdemment. Aussi, si vous avez supprim des sauvegardes, noubliez pas de supprimez galement les restaurations. La dernire ligne sort de la routine dinterruption, et replace le bit GIE 1. 5.4 Cas particulier des 873 et 874 Si vous tes attentif, vous aurez remarqu une diffrence fondamentale entre lorganisation mmoire dun 16F873 ou 16F874 et celle dun 16F876 ou 16F877. En effet, les deux premiers cits ne possdent que deux banques de mmoire RAM utilisateur et surtout ne disposent pas dune zone dite commune .

54

Ds lors, si vous utilisez la routine dinterruption prcdemment propose, vous ne pourrez jamais savoir dans quelle banque se trouveront sauvs les diffrents registres, puisque vous ignorerez dans quel tat se trouve le registre STATUS. La premire astuce consiste donc forcer la slection de banque sur la banque 0 avant de sauvegarder les principaux registres. Nous ajoutons donc simplement un clrf STATUS lendroit appropri, cest--dire APRES, videmment avoir lu lancienne valeur de STATUS afin de pouvoir la restaurer ultrieurement. Ca donne :
;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w clrf STATUS ; force la banque 0 (pas de commune sur 873/874) movwf status_temp ; sauver status swapp movf FSR , w ; charger FSR movwf FSR_temp ; sauvegarder FSR movf PCLATH , w ; charger PCLATH movwf PCLATH_temp ; le sauver clrf PCLATH ; on est en page 0

Moyennant ceci, les registres STATUS, FSR, et PCLATH seront maintenant sauvs en banque 0, ce qui ne ncessite plus lutilisation de la zone commune (vous pouvez videmment utiliser cette astuce sur le 16F876/877 pour conomiser de la zone commune. Subsiste cependant la sauvegarde de W, qui ne peut tre faite aprs la lecture de STATUS, puisque a modifierait videmment le contenu de W. Moralit, il est impossible de prvoir si w_temp se trouvera en banque 0 ou en banque 1. La solution est donc de rserver deux emplacements au mme offset en banque 0 et en banque 1 pour la sauvegarde de W. Ceci rsoud notre problme, moyennant le sacrifice dun octet en RAM.
;banque 0 CBLOCK 0x20 w_temp : 1 ; Dbut de la zone (0x20 0x6F) ; sauvegarde de W durant interrupt ; attention, offset identique en banque 1 status_temp : 1 ; sauvegarde de status durant interrupt FSR_temp : 1 ; sauvegarde de FSR durant interrupt PCLATH_temp : 1 ; sauvegarde de PCLATH durant interrupt ENDC ; Fin de la zone ; ; ; ; Dbut de la zone (0xA0 0xEF) second emplacement de sauvegarde de W attention, mme offset que w_temp Fin de la zone

; banque 1 CBLOCK 0xA0 w_temp_2 : 1 ENDC

Vous trouverez les fichiers maquettes correspondants dans votre rpertoire fichiers .

55

Notes :

56

6. Mise en uvre et configuration minimale


Et oui, la thorie, cest bien beau et indispensable, mais si vous dsirez programmer des 16F876, cest bien entendu pour raliser des montages pratiques. Nous allons donc commencer ici la partie pratique de lutilisation de ce nouveau PIC pour vous. Et tout dabord : 6.1 Le matriel ncessaire Jai tout dabord pens crer une platine dexprimentation universelle, avec programmateur intgr. Jai cependant renonc au dernier moment pour les raisons suivantes : Cette seconde partie sadresse ceux qui ont dj expriment le 16F84, et donc qui ne dsirent pas forcment utiliser toutes les possibilits du 16F876, mais dsirent utiliser une application particulire. Il mest apparu trs difficile de crer une carte qui permette dexploiter lintgralit des fonctions des 16F87x, sauf utiliser jumpers, switches mcaniques et lectroniques, qui auraient rendu la carte trs lourde mettre en uvre, et trs onreuse. Tous les magasines spcialiss proposent des platines dexprimentation dusage gnral. Chacun pourra donc trouver la solution qui lui convient le mieux. Tout le monde na pas envie de se lancer dans la ralisation dune carte assez complexe, ncessitant la ralisation dun circuit imprim

Pour toutes ces raisons, et tant donn que cette seconde partie peut tre utilise comme un manuel de rfrence, jai choisi dutiliser de petites platines dexprimentation simples pouvant tre ralises sur une platine dessais. Ceci permet de disposer de fonctions autonomes claires, et permet au lecteur de raliser une petite platine centre sur le sujet qui lintresse. Le matriel minimum pour raliser la carte de base est donc : 1 PIC 16F876 20 en botier DIP 1 alimentation de 5V continus. Une simple pile plate de 4.5V peut faire laffaire. 2 supports 28 broches, largeur 0.3 tulipe , ou 2 supports 14 pins tulipe 1 Quartz 20 MHz 2 condensateurs non polariss 15 pF 1 platine dexprimentation trous, comme pour la premire partie du cours A ceci viendront sajouter les diffrents composants ncessaires la ralisation des diffrentes applications. Au sujet des 2 supports, je vous conseille en effet dinsrer votre PIC dans un des supports, et de le laisser insrer tout au long de vos exprimentations. Il vous suffira

57

dinsrer ce support dans le support de la platine dexprimentation, comme si PIC + support se comportaient comme un composant unique. Ainsi, vous pargnerez les pins de votre PIC. 6.2 Le schma minimum Voici les schmas minimum permettant de faire fonctionner notre 16F876 ou notre 16F877

58

Vous pouvez constater que la mise en uvre du 16F876 (et du 16F877) est similaire celle du 16F84. 6.3 Les particularits lectriques Vous constatez que sur le schma concernant le 16F876, vous avez 2 connexions VSS qui sont relies la masse. En fait, en interne, ces pins sont interconnectes. La prsence de ces 2 pins sexplique pour une raison dquilibrage des courants et dquipotentialit. Les courants vhiculs dans le pic sont loin dtre ngligeables du fait des nombreuses entres/sorties disponibles. Le constructeur a donc dcid de rpartir les courants en plaant 2 pins pour lalimentation VSS, bien videmment, pour les mmes raisons, ces pins sont situes de part et dautre du PIC, et en positions relativement centrales. Sur le 16F877, cette procdure a t galement tendue VDD, car encore plus dentres/sorties sont disponibles. Notez que ces PICs fonctionneront si vous dcidez de ne connecter quune seule de chacune des pins, mais dans ce cas vous vous exposerez une destruction des composants lors des fortes charges, suite au non respect de la rpartition des courants internes et/ou des fonctionnements erratiques ds un dfaut dquipotentialit. Pour le reste nous noterons lhabituelle connexion de MCLR au +5V, cette pin tant utilise pour effectuer un reset du composant en cas de connexion la masse. Nous trouvons galement le quartz, qui pourra tre remplac par un rsonateur ou par un simple rseau RC, de la mme manire que pour le 16F84. Les condensateurs sur le quartz, du fait de la frquence plus importante du quartz utilis ici (20MHz) seront de valeur infrieure celle utilise pour le 16F84 ( 4Mhz), soit 15pF. La tolrance sur ces composants permet dutiliser dautres valeurs, mais mon exprience ma montr que cest cette valeur qui permet le fonctionnement le plus fiable cette frquence. Certains composants ont en effet refus de fonctionner avec des valeurs de 27pF avec les quartz qui taient en ma possession. Aucun na refuser de fonctionner avec la valeur de 15pF. Il se peut cependant que vous rencontriez un comportement diffrent (peu probable), du fait de la provenance de votre propre quartz. En cas de doute, demandez la documentation de celui-ci afin dadapter les valeurs, ou essayez dautres valeurs de faon exprimentale. Si vous avez besoin dun reset hardware , le montage suivant est le plus simple et le plus pratique. Le condensateur est facultatif, sauf en environnement perturb.

59

60

Notes :

61

Notes :

62

7. Migration du 16F84 vers le 16F876


7.1 Similitudes et diffrences avec le 16F84 Nous allons directement prciser quelles sont les similitudes entre le 16F84 et le 16F876, afin de vous montrer quels chapitres de la premire partie vous pouvez vous rapporter sans problme. En fait, cest trs simple : pratiquement tout ce que vous avez vu sur le 16F84 est identique pour le 16F876. Ceci inclus : le jeu dinstructions les modes dadressage Lutilisation des sous-programmes le timer 0 Le mcanisme gnral des interruptions Les niveaux dimbrication des sous-programmes Les limitations dues aux largeurs de bus internes (utilisation de RP0 par exemple) Lutilisation du watchdog

En contrepartie, et en sus des fonctions supplmentaires, vous noterez les diffrences suivantes : La sparation des registres en 4 banque implique la modification des adresses de ceux-ci Lutilisation de 4 banques implique lutilisation du bit RP1 en plus du bit RP0 Lutilisation de 4 banques implique lutilisation du bit IRP pour ladressage indirect Lutilisation dun programme de plus de 2K implique lutilisation de PCLATH Laugmentation des possibilits modifie lorganisation des registres dinterruption Linitialisation pralable du registre ADCON1 est ncessaire pour lutilisation du PORTA Les variables (zones RAM disponibles) ne se trouvent pas aux mmes emplacements

7.2 Conversion dun programme crit pour le 16F84 vers le 16F876 En consquence des points prcdents, il vous apparat donc que les programmes crits pour 16F84 peuvent tre assez facilement ports vers le 16F876. Mais attention, comme les adresses ne sont plus identiques, vous devrez imprativement recompiler le fichier source dans MPLAB. Je vais vous expliquer un peu plus loin comment procder en partant dun exemple pratique. La premire chose importante retenir est donc que : ON NE PEUT PAS PLACER TEL QUEL DANS UN 16F876 UN FICHIER .HEX ASSEMBLE POUR UN 16F84.

63

7.3 Causes de non fonctionnement Si le programme transfr ne fonctionne plus, il faudra lexaminer avec attention. En effet, il se peut que vous rencontriez un des cas suivants, surtout si le programme a t ralis sans tenir compte des recommandations que je fais dans la premire partie. 1) Vrifiez si des adresses ne sont pas utilises directement dans le programme Exemple :
movf 0x0C , w ; charger W avec la variable adresse 0x0C

Solution : il vous faut dclarer la variable dans la zone des variables, et utiliser son tiquette dans le programme
movf mavariable,w ; la variable mavariable est dclare en zone 0x20

2) Vrifiez les assignations et dfinitions dadresses Exemple :


mavariable EQU 0x0C

Solution : modifiez lassignation ou la dfinition de faon la rendre compatible avec le 16F876 Exemple :
mavariable EQU 0x020

Ou mieux, dclarez la variable de faon classique dans la zone des variables. 3) Vrifiez si des bits non utiliss dans le 16F84 nont pas t utiliss en tant que bits libres pour le programmeur Exemple :
bsf PCLATH , 4 ; mettre flag perso 1

Solution : dclarez une variable supplmentaire contenant le flag et utilisez celui-ci dans le programme Exemple :
bsf mesflags , 1 ; mettre flag perso 1

4) Vrifiez si le programme nutilise pas une astuce concernant les adresses .


64

Exemple :
movlw movwf movf bsf movwf 0x10 FSR INDF , w FSR , 2 INDF ; ; ; ; ; pointer sur 0x10 placer adresse dans pointeur charger une variable pointer sur 0x14 copier la valeur

Solution : ceci est un peu plus dlicat. La procdure utilise ci-dessus utilise une astuce en manipulant directement les bits du pointeur FSR. Il est vident que si vous changez les adresses des variables, ceci va entraner un dysfonctionnement du programme. Dans ce cas vous devrez rorganiser vos variables en consquence, ou modifier le corps du programme pour tenir compte des nouvelles dispositions. 5) Si les entres/sorties sur PORTA fonctionnent sur simulateur mais pas en pratique Vous avez probablement omis dinitialiser le registre ADCON1. Notez que le fichier maquette inclus cette gestion. Vous navez donc pas vous en inquiter pour l instant. Evidemment, on considre que les frquences de fonctionnement des 2 PICs sont identiques. 7.3 Conversion dun exemple pratique Afin de mettre ceci en pratique, nous allons migrer notre programme led_ Tmr1 pour le porter sur 16F876. Afin de vous faciliter la vie, le fichier original, cr pour le 16F84, est fourni sous la rfrence Led16f84.asm. Il existe plusieurs mthodes pour raliser cette opration. La premire qui vient lesprit est de crer un nouveau projet et de modifier le fichier original. Cette mthode prsente cependant un trs fort taux de probabilit derreur, surtout pour les gros projets. Je vais donc vous prsenter une mthode plus fiable. 7.3.1 Ralisation du montage Afin de pouvoir tester notre exercice, nous allons raliser la platine dexprimentation correspondante. Il suffit dans ce cas de raliser le schma de base prsent au chapitre 6 et dy connecter une LED sur RA2. Ceci ne devrait vous poser aucun problme.

65

7.3.2 Cration du projet Nous allons maintenant crer notre premier vrai projet 16F876. Pour ceci, commencez par dupliquer le fichier m16F876.asm , et renommez-le Led_tmr0.asm . Ensuite excutez les tapes suivantes : Lancez MPLAB Cliquez new project Choisir le nom Led_tmr0.pjt en pointant dans le rpertoire des fichiers dexemple. Dans la fentre qui souvre, cliquez change ct de development mode Choisissez PIC16F876 dans le menu droulant processor Cochez la case MPLAB SIM Simulator Cliquez OK Une fentre vous indique quil ny a pas de fichier hex . Cliquez OK Confirmez par OK les diffrentes fentres davertissement. Dans la fentre edit project reste ouverte, cliquez sur add node Choisissez votre fichier Led_tmr0.asm Cliquez OK , la fentre project edit se ferme, lcran est vide. Nous allons maintenant ouvrir le fichier assembler et lancien fichier. Cliquez file->open et ouvrez le fichier Led_tmr0.asm Cliquez file->open et ouvrez le fichier Led16F84.asm . Maintenant, vous disposez des 2 fichiers sur votre cran, mais rappelez-vous : Seul le ou les fichiers qui sont prciss dans les nuds ( node ) du projet sont pris en compte au moment de lassemblage. Egalement ceux prciss comme inclus dans ces mmes fichiers (directive include ). Par consquent, 2 fichiers sont bien affichs lcran, mais le fichier Led_16F84.asm na aucune influence sur lassemblage, car il ne fait pas partie du nud du projet, et nest pas non plus utilis par une directive dinclusion dans le seul fichier nud de notre projet (Led_tmr0.asm).
66

7.3.3 La mthode conseille Vous laurez dj compris, la mthode que je trouve la plus sre, est deffectuer des copier/coller partir du fichier source vers le fichier cible. Ceci prsente lavantage de ne rien oublier durant les modifications de code. Commenons donc par remplir nos zones de commentaires.
;************************************************************************** ; Ce fichier est la base de dpart pour une programmation avec * ; le PIC 16F876. Il contient les informations de base pour * ; dmarrer. * ; * ;************************************************************************** ; * ; NOM: Led_tmr0 * ; Date: 07/04/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ; * ; * ;************************************************************************** ; Exercice de migration d'un programme de 16F84 vers le 16F876. * ; Clignotement d'une LED une frquence de 1Hz en utilisant les * ; interruptions du timer0. * ; * ;**************************************************************************

Ensuite, intressons-nous notre fichier Led_16F84.asm , et regardons dans lordre : Les 2 premires lignes, savoir :
LIST p=16F84 #include <p16F84.inc> ; Dfinition de processeur ; Dfinitions des constantes

Ont dj leur quivalent dans le fichier Led-tmr0.asm sous la forme de :


LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

Nous navons donc pas nous en occuper. Pour rappel, il sagit de la dclaration du type de processeur, et du nom du fichier contenant les assignations correspondantes inclure. Ensuite, nous trouvons la directive _CONFIG qui dfinit le mode de fonctionnement du processeur. Nous devons transcrire cette ligne, et non la copier simplement, tant donn que leur structure est diffrente sur les 2 PICs. Au niveau du fichier pour le 16F84, nous trouvons :
__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

67

Nous notons alors le mode de fonctionnement choisi, savoir : Pas de protection programme en lecture Pas de watchdog Mise en service du reset retard la mise sous tension Oscillateur en mode HS.

Nous allons ensuite transposer ce mode de fonctionnement au fichier du 16F876, en compltant les nouveaux modes en fonction de nos besoins. Pour vous faciliter la vie, les commentaires des diffrents modes de fonctionnement font partie du fichier maquette, donc galement, par consquence, de votre fichier Led_tmr0.asm . Prenons la ligne par dfaut, et voyons ce que nous devons changer :
__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

Voyons donc les bits dans lordre : CP_OFF Absence de protection du programme en lecture. Cest ce que nous voulions. DEBUG_OFF Pas de mode debug en service : conservons ce paramtre, puisque nous nutilisons pas de fonction supplmentaire celles prvues dans le 16F84. WRT_ENABLE_OFF A conserver pour les mmes raisons CPD_OFF Idem LVP_OFF De mme BODEN_OFF Ne pas se tracasser de tout ceci pour linstant PWRTE_ON Laissons ce paramtre comme pour le 16F84, cest dire dans sa valeur actuelle WDT_OFF Pas de watchdog, tout comme pour lexercice sur le 16F84 HS_OSC Avec un quartz 20MHz, le tableau nous donne cette valeur pour loscillateur. Vous constatez donc que la mthode est simple : Vous appliquez les paramtres du source du 16F84 au fichier du 16F876 Vous laissez les autres bits dans leur configuration par dfaut Si vous vouliez ajouter des options ou des modifications, vous corrigez alors ventuellement la directive _CONFIG en consquence.

Donc, pour notre cas particulier, il ny a rien changer dans notre ligne de directive. Passons donc aux assignations systme. Du ct du 16F84, nous avions :

68

OPTIONVAL EQUH'0087'

; Valeur registre option ; Rsistance pull-up OFF ; Prscaler timer 256 ; Interruptions sur tmr0

INTERMASK EQUH'00A0'

Vous constaterez en regardant les datasheets, que le registre OPTION a strictement la mme configuration pour les 2 PICs, donc vous pouvez dans un premier temps recopier les mmes valeurs pour le fichier 16F876 de votre exercice. Par contre, INTERMASK initialisait les interruptions, au niveau du registre INTCON. Nous avons vu quil y a des changements au niveau des interruptions, donc, pour viter toute erreur, nous allons reprendre bit par bit les valeurs en question. Ici, nous avons donc, comme document et vrifi, une mise en service des interruptions du timer0. Dans ce cas-ci, vous constaterez que la valeur pour les 2 fichiers sera identique. Dans le fichier 16F876, jai opt pour une valeur sous forme binaire, pour faciliter la comprhension. Donc, pour mettre les interruptions timer0 en service, il suffit de positionner le bit 5 1 . Notez quil est inutile de soccuper de GIE, car il sera trait dans la routine dinitialisation. Les 2 lignes correspondantes pour notre fichier Led_tmr0.asm deviennent donc (pensez toujours adapter vos commentaires) :
; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000111' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; PSA b3 : 0= Assignation prdiviseur sur Tmr0 ; PS2/PS0 b2/b0 valeur du prdiviseur ; 111 = 1/128 1/256 ; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQUB'00100000' ; GIE b7 : masque autorisation gnrale interrupt ; ne pas mettre ce bit 1 ici ; T0IE b5 : masque interruption tmr0

Du ct du fichier 16f876, vous noterez galement les assignations pour les valeurs des registres PIE1 et PIE2. Comme aucune des interruptions utilises dans ces registres ne sont utilises ici, vous pouvez les laisser tels quels. Nous verrons galement plus loin la fin de cette zone. Ensuite, nous trouvons notre zone des dfinitions :
;********************************************************************* ; DEFINE * ;********************************************************************* #DEFINE LED PORTA,2 ; LED

Nous supprimons donc lexemple fourni dans le fichier 16F876 :


69

; exemple ; ------#DEFINE LED1 PORTB,1 ; LED de sortie 1

et nous le remplaons par la vritable dfinition de notre fichier source :


#DEFINE LED PORTA,2 ; LED

Nous accderons la led par une fonction xor , nous dfinirons donc la valeur qui va modifier la led :
#DEFINE LEDMASK B00000100 valeur pour le masque de la led

Nous arrivons aux zones de macros, zones quil ne faut pas modifier, les macros 16F84 et 16F876 tant diffrentes. Seules les macros personnelles peuvent tre ajoutes aprs avoir ventuellement t modifies. Nous navons pas de telles macros dans cet exercice. Nous voici la zone des variables. Le 16F876 contient plus de banques que le 16F84. Mais, comme le programme provient dun 16F84, ces banques sont forcment inutilises. Cependant, les adresses des zones sont diffrentes, il ne faudra donc recopier que le contenu des zones, et non leur dclaration. Du ct 16F84, nous avions :
;********************************************************************* ; DECLARATIONS DE VARIABLES * ;********************************************************************* CBLOCK 0x00C ; dbut de la zone variables w_temp :1 ; Sauvegarde du registre W status_temp : 1 ; Sauvegarde du registre STATUS cmpt : 1 ; compteur de passage ENDC ; Fin de la zone

Ici, il nous faut oprer avec une grande attention. En effet, nous disposons de plusieurs banques de variables dans le 16F876. On pourrait tre tent de tout mettre dans la banque0, mais, en cas de modifications ou dajouts ultrieurs, il faut prvoir que certaines variables, principalement celles utilises pour la sauvegarde des registres au niveau des interruptions, doivent tre sauves dans la zone commune. La premire tape consiste donc ne pas tenir compte de ces variables, car elles sont dj dclares dans le fichier 16F876. Les autres variables pourront en gnral tre copies dans la banque0. Dans ce cas, nous constatons que les variables w_temp et status_temp sont les variables de sauvegarde dj dclares. Nous ne nous en occupons donc pas. La seule variable propre au programme est la variable cmpt. Nous copions donc sa dclaration dans la banque0. Ceci nous donne donc, au niveau de la banque 0, et aprs avoir supprim les lignes dexemples :

70

;************************************************************************** ; VARIABLES BANQUE 0 * ;************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 ; Dbut de la zone (0x20 0x6F) cmpt : 1 ; compteur de passage ENDC ; Fin de la zone

Quant la banque commune, elle conserve les dclarations des variables de sauvegarde. Les autres banques tant inutilises dans notre exemple pourront donc tre supprimes des dclarations.
;************************************************************************** ; VARIABLES ZONE COMMUNE * ;************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 ; Dbut de la zone (0x70 0x7F) w_temp : 1 ; Sauvegarde registre W status_temp : 1 ; sauvegarde registre STATUS FSR_temp : 1 ; sauvegarde FSR (si indirect en interrupt) PCLATH_temp : 1 ; sauvegarde PCLATH (si prog>2K) ENDC

Nous trouvons maintenant la routine dinterruption principale. Cette routine dpend du type de PIC utilis, nous ny touchons donc pas pour linstant. Vous allez voir que cette maquette va donc une nouvelle fois vous simplifier la vie. Vous trouvez ensuite le code correspondant la routine dinterruption du timer0.
;********************************************************************** ; INTERRUPTION TIMER 0 * ;********************************************************************** inttimer decfsz cmpt , f ; dcrmenter compteur de passages return ; pas 0, on ne fait rien movlw LEDMASK ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw 7 ; pour 7 nouveaux passages movwf cmpt ; dans compteur de passages return ; fin d'interruption timer

Puisque la gestion principale et les switches vers les sous-routines dinterruption font partie de votre maquette, vous navez donc plus qu copier tout ce qui suit ltiquette inttimer de votre fichier 16F84 dans lemplacement prvu de votre fichier 16F876. Ceci nous donne :
;************************************************************************** ; INTERRUPTION TIMER 0 * ;************************************************************************** inttmr0 decfsz cmpt , f ; dcrmenter compteur de passages return ; pas 0, on ne fait rien movlw b'00000100' ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw 7 ; pour 7 nouveaux passages

71

movwf cmpt return

; dans compteur de passages ; fin d'interruption timer

Comme il ny a pas dautres interruptions traites, voici donc termine la partie interruption. Nest-ce pas simple ? Bien sr, il reste plein de code inutile, mais le but de cet exercice nest pas loptimisation, mais la comprhension des mcanismes de conversion. Une fois lexercice termin, il vous sera loisible, titre dexercice personnel, doptimiser le source obtenu. En attendant, voyons la suite. Nous en sommes maintenant la routine dinitialisation. Pour notre 16F84, nous avions :
;********************************************************************* ; INITIALISATIONS * ;********************************************************************* init clrf clrf BANK1 clrf movlw movwf PORTA PORTB EEADR OPTIONVAL OPTION_REG ; ; ; ; ; ; Sorties portA 0 sorties portB 0 passer banque1 permet de diminuer la consommation charger masque initialiser registre option

movlw movwf init1 clrf incf btfss goto btfss goto

0x0c FSR INDF FSR,f FSR,6 init1 FSR,4 init1

; Effacer RAM ; -----------; initialisation pointeur ; pointeur d'adressage indirect ; ; ; ; ; ; effacer ram pointer sur suivant tester si fin zone atteinte (>=40) non, boucler tester si fin zone atteinte (>=50) non, boucler

bcfLED BANK0

; initialiser ports ; ----------------; passer LED en sortie ; passer banque0 ; masque interruption ; charger interrupt control

movlw INTERMASK movwf INTCON

; initialisations variables ; ------------------------movlw 7 ; charger 7 movwf cmpt ; initialiser compteur de passages goto start ; sauter programme principal

Nous devons distinguer les diffrentes parties de cette tape. La premire partie est gre de nouveau par notre fichier maquette. Inutile de sen proccuper. Il en va de mme pour leffacement de la zone RAM. Notez que notre fichier maquette nefface que la zone RAM de la banque0. Si vous souhaitez effacer les autres banques, vous dajouter le code ncessaire. Ce ne sera cependant pas ncessaire ici. Les lignes non surlignes reprsentent les tapes prises en charge par la routine dinitialisation standard, celles en vert sont des lignes spcifiques au programme.

72

Nous voyons donc quil nous suffit dajouter la configuration des entres/sorties et linitialisation de notre compteur pour terminer notre routine dinitialisation. Vous constatez que notre routine dinitialisation 16F876 inclus les initialisations des entres/sorties, en faisant rfrence des assignations. En effet, nous trouvons :
init BANK0 clrf clrf clrf bsf movlw movwf movlw movwf movlw movwf movlw movwf ; initialisation PORTS (banque 0 et 1) ; -----------------------------------; slectionner banque0 PORTA ; Sorties PORTA 0 PORTB ; sorties PORTB 0 PORTC ; sorties PORTC 0 STATUS,RP0 ; passer en banque1 B'00000110' ; PORTA en mode digital ADCON1 ; criture dans contrle DIRPORTA ; Direction PORTA TRISA ; criture dans registre DIRPORTB ; Direction PORTB TRISB ; criture dans registre DIRPORTC ; Direction PORTC TRISC ; criture dans registre

A/D direction direction direction

Vous constatez que les directions des PORTS sont inclus dans les assignations DIRPORTx, assignations qui sont dclares dans la zone des assignations systme :
; DIRECTION DES PORTS I/O ; ----------------------DIRPORTA EQUB'00111111' ; Direction PORTA (1=entre) DIRPORTB EQUB'11111111' ; Direction PORTB DIRPORTC EQUB'11111111' ; Direction PORTC

Il nous suffit donc de transposer notre initialisation bsf LED qui place le RA2 en sortie directement dans la zone des assignations systme. Nous modifierons donc la ligne concerne en :
DIRPORTA EQUB'00111011' ; RA2 en sortie

Restent les lignes :


movlw 7 movwf cmpt ; charger 7 ; initialiser compteur de passages

Pour travailler proprement, nous dclarons la valeur 7 dans une assignation, au niveau de la zone des define par exemple :
;************************************************************************** ; DEFINE * ;************************************************************************** #DEFINE LED PORTA,2 ; LED de sortie #DEFINE CMPTVAL 7 ; valeur de recharge du compteur

Notez que nous pouvions galement crire :


CMPTVAL EQU 7

73

Qui revient strictement au mme dans ce cas. Nous placerons donc la ligne dinitialisation du compteur dans notre routine dinitialisation, en utilisant notre assignation (ou dfinition). La routine dinitialisation devient donc :
;************************************************************************** ; INITIALISATIONS * ;************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------; slectionner banque0 PORTA ; Sorties PORTA 0 PORTB ; sorties PORTB 0 PORTC ; sorties PORTC 0 STATUS,RP0 ; passer en banque1 ADCONVAL ; PORTA en mode digital/analogique ADCON1 ; criture dans contrle A/D DIRPORTA ; Direction PORTA TRISA ; criture dans registre direction DIRPORTB ; Direction PORTB TRISB ; criture dans registre direction DIRPORTC ; Direction PORTC TRISC ; criture dans registre direction

BANK0 clrf clrf clrf bsf movlw movwf movlw movwf movlw movwf movlw movwf

; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1 PIE2VAL ; initialiser registre PIE2 ; interruptions priphriques 2

movlw movwf movlw movwf movlw movwf

; Effacer RAM banque 0 ; --------------------bcf STATUS,RP0 ; slectionner banque 0 movlw 0x20 ; initialisation pointeur movwf FSR ; d'adressage indirect init1 clrf INDF ; effacer ram incf FSR,f ; pointer sur suivant btfss FSR,7 ; tester si fin zone atteinte (>7F) goto init1 ; non, boucler ; initialiser variable ; -------------------movlw CMPTVAL ; charger valeur d'initialisation movwf cmpt ; initialiser compteur de passages ; autoriser interruptions (banque 0) ; ---------------------------------PIR1 ; effacer flags 1 PIR2 ; effacer flags 2

clrf clrf

74

bsf goto

INTCON,GIE start

; valider interruptions ; programme principal

Les lignes surlignes en jaune introduisent correspondent notre initialisation du PORTA. Ces lignes sont inchanges, la modification intervenant dans la zone des assignations systme. Les lignes surlignes en vert correspondent linitialisation du compteur suivant le define dclar prcdemment. Cette mthode prsente lavantage de pouvoir changer de valeur sans devoir rechercher toutes les occurrences dans le programme, au risque den oublier une. Comme nous avons analys notre programme, nous savons que cette valeur est galement utilise dans la routine dinterruption du timer 0 :
movlw 7 ; pour 7 nouveaux passages

Donc, nous modifions galement cette ligne en utilisant notre dfinition :


movlw CMPTVAL ; pour CMPTVAL nouveaux passages

Ainsi, toute modification de la valeur de recharge du compteur ne ncessitera que ldition de lassignation. Ceci vite doublier des valeurs en chemin. Arrivs ce stade, il ne nous reste plus que notre programme principal :
;********************************************************************* ; PROGRAMME PRINCIPAL * ;********************************************************************* start goto start ; boucler

END Quil nous suffit de recopier tel quel vers notre fichier 16F876. Attention cependant ce que la directive END figure bien en fin de programme. Lancez lassemblage en pressant <F10>. Si vous ne vous tes pas tromps, lassemblage devrait se drouler sans problme. Le rapport contenant, comme dhabitude les warnings des registres situs dans des banques diffrentes de 0. A ce stade, vous pouvez alors fermer votre fichier Led_16F84 , vous nen aurez plus besoin. Reste dans la fentre de MPLAB uniquement votre fichier Led_tmr0.asm . Programmez votre PIC16F876 avec le fichier Led_tmr0.hex obtenu et testez le rsultat sur votre platine dexprimentation. Lancez lalimentation, vous constatez, si vous ne vous tes pas tromp que la LED clignote, mais une frquence bien plus leve quun clignotement par seconde. Si vous examinez le programme, vous constaterez que cest logique, puisque la base de temps est donne par le timer TMR0. Or ce timer compte en synchronisme avec la frquence du quartz du PIC. Le programme tait conu pour un quartz 4MHz, et maintenant nous utilisons un quartz 20MHz. La vitesse est donc 5 fois trop importante.
75

Si vous allez dans la routine dinterruption du timer0, vous verrez que cest la variable cmpt qui compte le nombre de passages dans tmr0. Une fois cmpt 0, on inverse la LED et on rinitialise cmpt avec la valeur CMPTVAL. Souvenez-vous que cette valeur tait fixe 7 dans la zones des dfinitions. Il suffit donc pour rsoudre notre problme, de multiplier CMPTVAL par 5. Remplacez donc :
#DEFINE CMPTVAL 7 ; valeur de recharge du compteur ; valeur de recharge du compteur

par
#DEFINE CMPTVAL D35

Attention, D35, et non 35, qui serait interprt comme 0x35, soit 53 dcimal. Rassemblez avec <F10>, reprogrammez le processeur et relancez lalimentation : La LED clignote une frquence de 1Hz. Vous voyez lintrt davoir utilis une assignation : plus besoin de rechercher les 2 endroits o on initialise cmpt. Ne courez pas montrer votre pouse. Non, a je vous lai dj dit dans la premire partie. Le fichier assembleur tel quil devrait tre la fin de cet exercice vous est fourni dans le rpertoire fichiers , comme dhabitude, et comme les exercices pratiques de la suite de cet ouvrage.

76

Notes :

77

Notes :

78

8. Optimisons un peu
Plusieurs dentre vous, ce niveau, vont trouver que le programme prcdent est un peu gros pour les fonctions quil excute. Si vous tes dans ce cas, vous navez pas tort. Cest pourquoi jintercale ce chapitre ici pour commencer vous parler doptimisation. Cependant, toutes les mthodes doptimisation ne peuvent tre dcrites ici, jen reparlerai donc tout au long de cette seconde partie. Et tout dabord : Optmiser , en voici un bien grand mot. Pourquoi faire ? Et bien, dans ce cas prcis, justement, pour rien du tout. Il ne sert strictement rien doptimiser ce programme. En effet, ce programme est suffisamment petit pour entrer dans le 16F876 (et mme le 16F873). De plus notre programme passe la plupart de son temps attendre une interruption en excutant sans fin la boucle du programme principal. Quil attende l ou ailleurs, quelle importance ? Je vous propose juste cette petite optimisation pour commencer vous habituer ces techniques pour le cas o elle serait ncessaire, mais tout dabord, un peu de thorie applique . 8.1 Les deux grands types doptimisation On peut dire quune optimisation est destine rendre plus efficace un programme. On peut distinguer, dans la plupart des cas, 2 grands type doptimisations : La diminution de la taille du programme Lamlioration de la vitesse de traitement ou de raction un vnement.

Il semble au premier regard que ces 2 techniques soient complmentaires. En effet, la diminution de la taille du programme induit une diminution du nombre de lignes, donc de la dure dexcution. En y regardant de plus prs, vous verrez quil nen est rien. 8.2 Travail sur un exemple concret Prenons un exemple concret sous la forme dune portion de programme, parfois appel code . Imaginons un programme qui place la valeur 5 dans 20 emplacement mmoires conscutifs :
Start movlw D20 movwf compteur movlw dest movwf FSR Loop movlw 0x05 movwf INDF incf FSR,f decfsz compteur,f ; ; ; ; ; ; ; ; pour 20 boucles dans variable de comptage adresse de destination dans pointeur indirect valeur mettre dans emplacements placer valeur 0x05 dans destination pointer sur emplacement suivant dcrmenter compteur de boucles

79

goto nop

loop

; pas fini, suivant ; instruction suivante dans le programme

Que pouvons-nous dire de ce bout de code ? En fait, nous voyons que nous avons initialis 20 variables (dest,dest+1..dest+D19) avec la valeur 5 tout en nutilisant que 9 instructions. Vous allez me dire quil est impossible doptimiser ce programme fictif ? Dtrompezvous. Il faut tout dabord vous rappeler quil y a 2 voies doptimisations, loptimisation en taille et loptimisation en vitesse dexcution. Si vous regardez ce programme, vous verrez quil est difficile de faire plus court. Par contre nous allons voir quon peut facilement faire plus rapide, preuve que les 2 voies ne sont pas toujours lies, et mme peuvent tre opposes. Supposons que cette portion de code intervienne un niveau o la vitesse de raction est critique. Si vous avez remarqu que le contenu de W ne changeait jamais dans la boucle, bravo, vous commencez alors raisonner en programmeur. Si ce nest pas encore le cas, aucun problme, cest en travaillant quon devient travailleur, cest donc en PICant quon devient PIColeur (ne me prenez surtout pas au mot, je ne veux pas dennuis avec votre pouse). Donc, pour rester srieux (a change), calculons la vitesse dexcution (en cycles dinstructions) de notre programme. Pour les 4 premires lignes, nous aurons donc 4 cycles. Ensuite, les 3 premires lignes de la boucle (3 cycles) seront excutes 20 fois, car 20 boucles. Ceci nous donne 60 cycles. Ensuite, plus compliqu, la condition du decfsz sera fausse (pas de saut) les 19 premires fois, et vraie (saut) la 20me fois. Soit un total de 19+2 = 21 cycles. Vient enfin le goto qui produira 19 fois un saut, la 20me fois il ne sera pas excut suite au saut du decfsz . Soit donc un total de 19*2 = 38 cycles.

Rappelez-vous en effet que les instructions qui amnent un saut ncessitent 2 cycles (voir premire partie). Donc, pour rsumer, ce programme prsente les caractristiques suivantes : Taille : 9 mots de programme Temps dexcution : 123 cycles

Revenons-en notre remarque sur la valeur inchange de W lintrieur de la boucle. Si W ne change pas, inutile de rpter son chargement chaque passage dans la boucle. Il faudra cependant linitialiser au moins une fois, donc avant lexcution de cette boucle. Ceci nous donne :
Start

80

movlw D20 movwf compteur movlw dest movwf FSR movlw 0x05 Loop movwf INDF incf FSR,f decfsz compteur,f goto loop nop

; ; ; ; ; ; ; ; ; ;

pour 20 boucles dans variable de comptage adresse de destination dans pointeur indirect valeur mettre dans emplacements placer valeur 0x05 dans destination pointer sur emplacement suivant dcrmenter compteur de boucles pas fini, suivant instruction suivante dans le programme

Vous voyez donc que la taille du programme est strictement inchange, puisquon na fait que dplacer linstruction movlw 0x05 en dehors de la boucle. Par contre, durant lexcution du programme, cette instruction ne sera plus excute quune seule fois, au lieu des 20 fois de lexemple prcdent. Nous avons donc un gain de 19 cycles, qui nous ramne le temps dexcution 104 cycles. Nous avons donc optimis notre programme au niveau temps dexcution. La taille restant inchange, ces 2 mthodes ne sont pas incompatibles. Maintenant, imaginons que ces 104 cycles soient inacceptables pour notre programme, qui ncessite de ragir beaucoup plus vite. Ecrivons donc tout btement :
Start movlw movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf movwf 0x05 dest dest+1 dest+2 dest+3 dest+4 dest+5 dest+6 dest+7 dest+8 dest+9 dest+A dest+B dest+C dest+D dest+E dest+F dest+10 dest+11 dest+12 dest+13 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; valeur placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer placer mettre dans les emplacements 0x05 dans emplacement 1 0x05 dans emplacement 2 0x05 dans emplacement 3 0x05 dans emplacement 4 0x05 dans emplacement 5 0x05 dans emplacement 6 0x05 dans emplacement 7 0x05 dans emplacement 8 0x05 dans emplacement 9 0x05 dans emplacement 10 0x05 dans emplacement 11 0x05 dans emplacement 12 0x05 dans emplacement 13 0x05 dans emplacement 14 0x05 dans emplacement 15 0x05 dans emplacement 16 0x05 dans emplacement 17 0x05 dans emplacement 18 0x05 dans emplacement 19 0x05 dans emplacement 20

Que dire de ce programme ? Et bien, calculons ses caractristiques comme prcdemment : Taille du programme : 21 mots de programme Temps dexcution : 21 cycles.

Nous constatons donc que nous avons augment la taille du programme (donc perdu en optimisation taille , mais, par contre, nous sommes pass de 104 21 cycles de temps dexcution. Nous avons donc gagn en optimisation temps .

81

Nous voyons donc de faon vidente que les 2 types doptimisation peuvent ncessiter des solutions opposes. En effet, si javais commenc par vous donner ce dernier exemple, la plupart dentre-vous aurait immdiatement dclar : Ce programme nest pas optimis . En fait, il lest, mais au niveau du temps, pas au niveau de la taille. Et il est tellement optimis au niveau temps quil est impossible de loptimiser encore davantage. Tout ceci pour vous dire : mfiez-vous des grands pros pour lesquels votre programme nest jamais assez bien optimis . Si votre programme ralise ce quil a faire dans les temps imparti, alors il est sufisamment optimis. Dans ce cas, mieux vaut privilgier la lisibilit du code, afin den faciliter la maintenance. Je ne suis pas un fan de loptimisation sans raison. 8.3 Le choix du type doptimisation A ce stade, vous allez vous demander comment vous devez procder en pratique, et quelles procdures utiliser . Il ny a pas de rponse passe-partout, il sagit dune question de bon sens. Voici ce que je vous conseille, et que je pratique par moi-mme. En premier lieu, donner priorit la clart et l ouverture du programme. Par ouverture , jentends la construction du programme de faon permettre sa modification ultrieure. Par exemple, le programme prcdent, dans sa premire version (et sa premire amlioration) est plus clair, plus agrable lire, et plus simple modifier que la dernire version. Pour changer le nombre de boucles, il suffit dans le premier cas, de modifier la valeur D20 . En second lieu, et si cest ncessaire, dterminer le type doptimisation. Si par exemple, vous manquez de place pour votre programme, ou si vous ne souhaitez pas utiliser plusieurs pages, vous optimiserez la taille de celui-ci. Par contre, si certaines squences sont critiques en temps dexcution (par exemple pour certaines interruptions), vous tenterez doptimiser au niveau temps dexcution. Dans tous les cas, placez un commentaire dans votre programme lendroit de loptimisation, pour vous rappeler en cas de modification ultrieure la mthode utilise et la raison de cette optimisation. En effet, cest clair que si vous reprenez un jour la dernire version du programme prcdent, vous allez vous dire mais pourquoi ai-je programm de cette faon ? Optimisons donc ceci en diminuant la taille . Et crac, vous allez allonger la dure dexcution, alors quil y avait probablement une bonne raison de lavoir raccourcie. Rappelez-vous quil ne sert rien de vouloir tout prix optimiser un programme si le besoin ne sen fait pas sentir. Prfrez de loin un programme clair et bien structur. 8.4 Application pratique Nous allons reprendre notre exercice Led_tmr0 et tenter de loptimiser au niveau de la clart et de la taille. Si vous avez compris ce qui prcde vous savez quil est inutile doptimiser ce programme, si ne nest pour en amliorer la clart.

82

Je vais effectuer un copier/coller du programme Led_tmr0 en Led_otpi.asm . Ceci afin de conserver dans les exemples les 2 versions du programme. Vous pouvez, en ce qui vous concerne, travailler directement dans votre fichier original. La premire chose quil vous vient lesprit, en parcourant le source, est quil y a plein de dfinitions, macros, et autres assignations inutiles. Vous pouvez en effet les supprimer, mais ceci ne diminuera en rien la taille du fichier excutable obtenu ni sa vitesse dexcution. Rappelez-vous en effet que ce ne sont que de simples facilits dcritures. Ces directives ne sont remplaces par du code que si elles sont utilises effectivement dans le programme. Nanmoins, nous allons les supprimer pour rendre le fichier source plus compact et plus rapide lire. Il ne sagit donc pas ici doptimisation, tout au plus peut-on parler de suppression de textes inutiles. La meilleure faon pour savoir si une assignation, dfinition ou autre directive de traitement de texte est utilise, est de placer un ; devant sa dclaration. On lance ensuite lassemblage. Sil ny a pas de message derreur, cest que cette directive nest pas utilise dans notre programme. Nous allons en profiter galement pour liminer les commentaires inutiles pour notre application. Prenons donc le dbut du programme :
;************************************************************************** ; * ; Exercice doptimisation en taille d'un programme simple * ; Optimisation sur base du fichier "Led_tmr0" * ; * ;************************************************************************** ; * ; NOM: Led_opti * ; Date: 13/04/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;************************************************************************** ; Permet d'liminer le code superflu * ; * ;************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

Ici, nous nous contentons de commenter len-tte, comme affich ci-dessous. Au niveau de la configuration, nous ne changeons rien.

83

Viennent ensuite tous les commentaires gnraux concernant les configurations possibles. Nous ne conservons que les commentaires lis aux choix que nous avons faits, inutile de conserver les autres. Ceci nous donne :
;_CP_OFF Pas de protection ;_DEBUG_OFF RB6 et RB7 en utilisation normale ;_WRT_ENABLE_OFF Le programme ne peut pas crire dans la flash ;_CPD_OFF Mmoire EEprom dprotge ;_LVP_OFF RB3 en utilisation normale ;_BODEN_OFF Reset sur chute de tension hors service ;_PWRTE_ON Dmarrage temporis ;_WDT_OFF Watchdog hors service ;_HS_OSC Oscillateur haute vitesse (4Mhz<F<20Mhz)

Notez quil ne sagit ici que de commentaires (prcds par ; ). Libre vous donc de tout supprimer, ce que je ne vous conseille cependant pas pour des raisons de facilit de maintenance. Ensuite, nous trouvons les assignations systme. Si vous tentez de les faire prcder dun ; , vous obtiendrez des erreurs au moment de lassemblage. Ceci est normal, car le fichier maquette qui nous a servi de base de travail utilise ces assignations. Nous verrons donc ceci plus tard. Nous arrivons dans la zone de macros. Si vous faites des essais ou des recherches, vous vous apercevrez que la seule macro utilise dans cet exercice est la macro BANK0 . Vous pouvez donc franchement supprimer les autres.
;************************************************************************** ; MACRO * ;************************************************************************** ; Changement de banques ; ---------------------BANK0 macro ; passer en banque0 bcf STATUS,RP0 bcf STATUS,RP1 endm

Notez que ceci ne constitue pas une optimisation, puisque le code de la macro ne se retrouve quaux endroits o elle est utilise. Venons-en maintenant la routine dinterruption principale. Ici nous commenons rellement loptimisation en taille (et en vitesse). La premire chose laquelle on pourrait penser est dliminer toutes les vrifications concernant les interruptions non utilises dans ce programme. Procdons donc cette puration, sachant que nous nutilisons que linterruption tmr0 :
;************************************************************************** ; ROUTINE INTERRUPTION * ;************************************************************************** ;sauvegarder registres ;--------------------0x004 ; adresse d'interruption

org

84

movwf swapf movwf movf movwf movf movwf clrf BANK0

w_temp STATUS,w status_temp FSR , w FSR_temp PCLATH , w PCLATH_temp PCLATH

; ; ; ; ; ; ; ; ;

sauver registre W swap status avec rsultat dans w sauver status swapp charger FSR sauvegarder FSR charger PCLATH le sauver on est en page 0 passer en banque0

; Interruption TMR0 ; ----------------btfsc btfss goto call bcf goto INTCON,T0IE INTCON,T0IF intsw1 inttmr0 INTCON,T0IF restorereg ; ; ; ; ; ; tester si interrupt timer autorise oui, tester si interrupt timer en cours non test suivant oui, traiter interrupt tmr0 effacer flag interrupt tmr0 et fin d'interruption

;restaurer registres ;------------------restorereg movf PCLATH_temp , w ; recharger ancien PCLATH movwf PCLATH ; le restaurer movf FSR_temp , w ; charger FSR sauv movwf FSR ; restaurer FSR swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W ; sans modifier Z swapf w_temp,w ; Rinversion de L et H dans W ; W restaur sans modifier status retfie ; return from interrupt

Si vous assemblez ceci, vous aurez un message derreur du type : Symbol not previously defined (intsw1) En fait, ceci est logique, car vous avez supprim la ligne o devait seffectuer le saut en cas dune interruption non provoque par tmr0 :
goto intsw1 ; non test suivant

Comme il ny a pas de test suivant, vu quil ny a quune seule interruption utilise, ce test devrait pointeur sur restorereg . Remplacez donc cette ligne par :
goto restorereg ; non test suivant

A ce moment, cela fonctionne, mais na plus gure de sens, vu que nous aurons un algorithme du type : Si interruption non provoque par tmr0, sauter en restorereg, sinon, traiter interruption timer0 puis sauter en restorereg. Cest logique, vu que la seule faon daccder cette partie de code, cest quil y ait eu interruption. Or, la seule interruption autorise est linterruption tmr0. Inutile donc deffectuer un test.

85

Nous aurons donc :


; Interruption TMR0 ; ----------------inttmr0 ; traiter interrupt tmr0 INTCON,T0IF ; effacer flag interrupt tmr0 restorereg ; et fin d'interruption

call bcf goto

Nous pouvons donc aller plus loin, en constatant quil devient inutile pour la lisibilit dinclure un appel vers une sous-routine. Autant crire dans ce cas le code directement dans la routine principale dinterruption. Dplaons donc le contenu de la routine inttmr0 la place de lappel de cette sous-routine :
; Interruption TMR0 ; ----------------decfsz cmpt , f ; dcrmenter compteur de passages return ; pas 0, on ne fait rien movlw b'00000100' ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw CMPTVAL ; pour CMPTVAL nouveaux passages movwf cmpt ; dans compteur de passages bcf INTCON,T0IF ; effacer flag interrupt tmr0 goto restorereg ; et fin d'interruption

Ne pas, bien videmment, dplacer le return (ni ltiquette inttmr0 ) . Nous pouvons maintenant supprimer toutes les structures des diffrentes interruptions, structures qui ne contiennent que des return . Elles ne seront jamais appeles, et, du coup, parfaitement inutiles et consommatrices despace (pas de temps). Continuons examiner notre routine dinterruption en lexaminant depuis le dbut. Nous voyons que plusieurs registres sont sauvegards. Rappelez-vous quune sauvegarde est indispensable si les 2 conditions suivantes sont simultanment remplies : Le registre sauvegard doit tre utilis dans le programme principal Le registre sauvegard doit tre modifi dans la routine dinterruption

Si nous examinons le programme partir du moment o les interruptions sont en service, cest dire partir de la ligne bsf INTCON,GIE , vous voyez que le programme comporte uniquement 2 sauts. Voyons maintenant les registres sauvegarder effectivement : Le premier registre est le registre w . Dans ce cas prcis, et extrmement rare, il nest pas utilis dans le programme principal, il ny a donc pas de raison de le sauvegarder. Ensuite, nous trouvons la sauvegarde du registre STATUS . Comme notre programme principal ne contient aucune utilisation de ce registre, il nest donc pas ncessaire de le sauvegarder. Ce cas prcis est galement extrmement rare, tant donn que STATUS est utilis pour les changements de banques (RP0,RP1), pour les tests (btfsx STATUS,x), pour les oprations de rotation (utilisation du carry) etc.

86

Nous pouvons dire que pour la presque totalit des programmes, les registres W et STATUS seront systmatiquement sauvegards. Mais, tant donn que le but de ce chapitre est loptimisation, nous optimiserons donc. Continuons avec la sauvegarde de FSR. Notre programme principal nutilise pas ladressage indirect, pas plus que notre routine dinterruption. Donc, 2 bonnes raisons de supprimer cette sauvegarde. Maintenant, voyons PCLATH dont la sauvegarde est galement inutile, vu que notre programme tient en page0, et nutilise pas de tableaux mettant en uvre PCLATH. A supprimer donc galement. Il est vident que la suppression de la sauvegarde inclus la suppression de la restauration de ces registres. Autre consquence, cette suppression inclus galement la suppression des variables utilises pour cette sauvegarde. Voyons ce quil reste de notre routine dinterruption :
org 0x004 clrf PCLATH BANK0 ; adresse d'interruption ; on est en page 0 ; passer en banque0

; Interruption TMR0 ; ----------------decfsz cmpt , f ; dcrmenter compteur de passages return ; pas 0, on ne fait rien movlw b'00000100' ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw CMPTVAL ; pour CMPTVAL nouveaux passages movwf cmpt ; dans compteur de passages bcfINTCON,T0IF goto restorereg ; effacer flag interrupt tmr0 ; et fin d'interruption

;restaurer registres ;------------------restorereg retfie ; return from interrupt

On peut dire quelle a sensiblement maigri. Nanmoins, regardons-la en dtail. Nous constatons que la premire ligne place PCLATH 0. Or, si on regarde le datasheet de Microchip, on constate que ce registre est mis 0 lors dun reset. Comme il reste inchang durant lexcution de notre programme, nous pouvons nous passer de cette ligne. Nous pouvons raisonner de faon identique pour RP0 et RP1, forcs 0 par la macro BANK0 , qui est de ce fait inutile, et peut tre supprime de la routine dinterruption. En consquence, ntant plus utilise nulle part, nous pouvons nous passer galement de sa dclaration. Regardons attentivement, et nous constatons la prsence de la ligne goto restorereg , qui na plus aucune utilit, tant donn que ladresse de saut suit ladresse de linstruction. Cest donc un saut qui nen est plus un. Supprimons donc cette ligne. Il nous reste :

87

;************************************************************************** ; ROUTINE INTERRUPTION * ;************************************************************************** org 0x004 ; adresse d'interruption

; Interruption TMR0 ; ----------------decfsz cmpt , f ; dcrmenter compteur de passages return ; pas 0, on ne fait rien movlw b'00000100' ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw CMPTVAL ; pour CMPTVAL nouveaux passages movwf cmpt ; dans compteur de passages bcf INTCON,T0IF retfie ; effacer flag interrupt tmr0 ; return from interrupt

Voici notre programme fortement simplifi. Lancez lassemblage, placez le code dans votre PIC, et vous constatez que a ne fonctionne plus. Pourquoi ? En fait, si vous simulez en pas pas, vous constaterez que linterruption na lieu quune seule fois. En effet, linstruction return effectue une sortie de la routine dinterruption SANS remettre le bit GIE 1. Il ny aura donc pas de nouvelle interruption. De plus, le flag T0IF ne sera pas resett lors de la sortie de la routine dinterruption. Cette instruction se trouvait lorigine dans la sous-routine inttmr0 appele depuis la routine dinterruption principale. Elle permettait de revenir dans cette routine, qui, elle, se terminait par retfie aprs un reset du flag T0IF. Ce nest plus le cas ici. Il vous faut donc remplacer linstruction return par un saut lendroit voulu, qui permet de rtablir la situation dorigine. Notre routine dinterruption finale est donc :
;************************************************************************** ; ROUTINE INTERRUPTION * ;************************************************************************** org 0x004 ; adresse d'interruption

fin

decfsz goto movlw xorwf movlw movwf bcf retfie

; Interruption TMR0 ; ----------------cmpt , f ; dcrmenter compteur de passages fin ; pas 0, sortir aprs reset du flag b'00000100' ; slectionner bit inverser PORTA , f ; inverser LED CMPTVAL ; pour CMPTVAL nouveaux passages cmpt ; dans compteur de passages INTCON,T0IF ; effacer flag interrupt tmr0 ; return from interrupt

Voici notre routine dinterruption fortement allge. Voyons maintenant notre routine dinitialisation.

88

La ligne suivante peut tre supprime, comme expliqu prcdemment :


BANK0 ; slectionner banque0

Ensuite, les effacements prventifs des PORTs peuvent galement tre supprims pour cette application :
clrf clrf clrf PORTA PORTB PORTC ; Sorties PORTA 0 ; sorties PORTB 0 ; sorties PORTC 0

Par contre, les lignes suivantes sont absolument indispensables, comme je lai dj expliqu, et comme nous le verrons en dtail dans le chapitre sur les conversions analogiques/numriques.
movlw ADCON1VAL ; PORTA en mode digital/analogique movwf ADCON1 ; criture dans contrle A/D

Ensuite les lignes dinitialisation des directions des registres peuvent tre modifies :
movlw movwf movlw movwf movlw movwf DIRPORTA TRISA DIRPORTB TRISB DIRPORTC TRISC ; ; ; ; ; ; Direction PORTA criture dans registre direction Direction PORTB criture dans registre direction Direction PORTC criture dans registre direction

En effet, les ports sont tous placs en entre lors dun reset, inutile donc de les y forcer, dautant que seul le PORTA est utilis. De plus, comme une seule sortie est utilise, il suffit de forcer le bit correspondant 0 :
bcf LED ; placer RA2 en sortie

En effet, LED va tre remplac par PORTA,2 . Comme on pointe sur la banque1, du fait de la premire ligne, ceci sera excut comme TRISA,2 . Si vous doutez, relisez vite la premire partie. Plus loin, nous trouvons les initialisations des registres dinterruption. une seule interruption est utilise ici, qui nutilise pas les interruptions priphriques. Comme INTCON est mis 0 galement lors dun reset, nous pourrons nous limiter placer le bit dautorisation correspondant 1. La suppression des valeurs inutilises permet galement de supprimer les dfinitions correspondantes :
; registres interruptions (banque 1) ; ---------------------------------INTCON,T0IE ; interruptions tmr0 en service

bsf

Vient leffacement de la RAM, qui nest pas ncessaire ici, vu que ces emplacements ne sont pas utiliss. Supprimons-le. Linitialisation de la variable utilise(cmpt) nest utile que pour la dure du premier allumage de la LED. Comme la valeur linitialisation est alatoire, on pourrait avoir un

89

premier allumage aprs une dure allant au maximum jusque 8 secondes. Bien que ce ne soit pas gnant, conservons cette initialisation. Les 2 reset de PIRx ne sont pas ncessaires non plus, donc on supprime. Quant au goto start il est de fait inutile, car ladresse de saut suit directement le saut. Voici donc le programme pur, pour ne pas dire optimis . Assemblez-le et vrifiez son fonctionnement correct.
LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ;_BODEN_OFF ;_PWRTE_ON ;_WDT_OFF ;_HS_OSC Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension hors service Dmarrage temporis Watchdog hors service Oscillateur haute vitesse (4Mhz<F<20Mhz)

;************************************************************************** ; ASSIGNATIONS SYSTEME * ;************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000111' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; PSA b3 : 0= Assignation prdiviseur sur Tmr0 ; PS2/PS0 b2/b0 valeur du prdiviseur ; 111 = 1/128 1/256 ; REGISTRE ADCON1 (ANALOGIQUE/DIGITAL) ; -----------------------------------ADCON1VAL EQUB'00000110' ; PORTA en mode digital ;************************************************************************** ; DEFINE * ;************************************************************************** #DEFINE LED PORTA,2 ; LED de sortie #DEFINE CMPTVAL D'35' ; valeur de recharge du compteur ;************************************************************************** ; VARIABLES BANQUE 0 * ;************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 ; Dbut de la zone (0x20 0x6F) cmpt : 1 ; compteur de passage ENDC ; Fin de la zone ;************************************************************************** ; VARIABLES ZONE COMMUNE * ;************************************************************************** ; Zone de 16 bytes 90

; ---------------CBLOCK 0x70 ENDC

; Dbut de la zone (0x70 0x7F)

;////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ;////////////////////////////////////////////////////////////////////////// ;************************************************************************** ; DEMARRAGE SUR RESET * ;************************************************************************** org 0x000 ; Adresse de dpart aprs reset goto init ; Initialiser ;************************************************************************** ; ROUTINE INTERRUPTION * ;************************************************************************** org 0x004 ; adresse d'interruption ; Interruption TMR0 ; ----------------decfsz cmpt , f ; dcrmenter compteur de passages goto fin ; pas 0, on sort de l'interruption movlw b'00000100' ; slectionner bit inverser xorwf PORTA , f ; inverser LED movlw CMPTVAL ; pour CMPTVAL nouveaux passages movwf cmpt ; dans compteur de passages bcf INTCON,T0IF ; effacer flag interrupt tmr0 retfie ; return from interrupt

fin

;////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ;////////////////////////////////////////////////////////////////////////// ;************************************************************************** ; INITIALISATIONS * ;************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------bsf STATUS,RP0 ; passer en banque1 movlw ADCON1VAL ; PORTA en mode digital/analogique movwf ADCON1 ; criture dans contrle A/D bcf LED ; placer RA2 en sortie ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 0) ; ---------------------------------INTCON,T0IE ; interruptions tmr0 en service

bsf

; initialiser variable ; -------------------movlw CMPTVAL ; charger valeur d'initialisation movwf cmpt ; initialiser compteur de passages ; autoriser interruptions (banque 0) ; ---------------------------------INTCON,GIE ; valider interruptions

bsf

91

;************************************************************************** ; PROGRAMME PRINCIPAL * ;************************************************************************** start goto start ; boucler END ; directive fin de programme

Si vous regardez attentivement, vous voyez que dans nos suppressions, nous avons effac la ligne qui permettait de repointer sur la banque 0. Replaons donc cette ligne lendroit voulu :
; initialisation PORTS (banque 1) ; ------------------------------bsf STATUS,RP0 ; passer en banque1 movlw ADCON1VAL ; PORTA en mode digital/analogique movwf ADCON1 ; criture dans contrle A/D bcf LED ; placer RA2 en sortie ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option bcf STATUS,RP0 ; repasser en banque 0 ; registres interruptions (banque 0) ; ---------------------------------INTCON,T0IE ; interruptions tmr0 en service

bsf

8.4 Optimisations particulires Nous venons de voir les 2 grands types doptimisation, mais il en existe dautres, lies des cas particuliers. Ces types doptimisation peuvent tre par exemple : Loptimisation des ressources matrielles externes (par exemple, comment diminuer le nombre dI/O ncessaires pour le fonctionnement du projet). Un cas typique est lutilisation du multiplexage des pins utilises. Loptimisation des ressources matrielles internes (par exemple, diminution du nombre de timers utiliss). Loptimisation des niveaux de sous-programme (pour ne pas dpasser les 8 niveaux maximum autoriss). Loptimisation permise par un arrangement tudi des ressources

Comme pour le cas prcdent, toutes ces optimisations ne sont pas forcment compatibles. Il est clair que, par exemple, utiliser le multiplexage des pins va ncessiter plus de code et plus de temps dexcution. Il est une fois de plus question de priorit. Retenez donc que :

92

Il ny a pas une seule optimisation, il y a toujours plusieurs optimisations parfois contradictoires. Parler doptimiser un programme est donc incorrect ou incomplet. Il faut prciser de quel type doptimisation on parle. Il y a donc probablement dautres types doptimisation, mais le but de ce chapitre est de vous faire comprendre le raisonnement appliquer, et non de tout dtailler de faon exhaustive. 8.4.1 Optimisation des niveaux de sous-programmes Je vais vous toucher un mot des mthodes pour diminuer le nombre de niveaux de sousroutines utiliss dans vos programmes. En effet, je reois pas mal de courrier suite la premire partie du cours concernant les sous-routines, principalement des erreurs de stack . Tout dabord, rappelez-vous quil ne faut pas confondre niveaux de sous-routines et nombre de sous-routines. Les premiers sont limits 8, le second nest limit que par la capacit mmoire programme de votre pic. Pour dterminer le niveau de sous-routine utilis dans votre programme, il suffit de le parcourir en ajoutant 1 chaque call rencontr, et en soustrayant 1 chaque return rencontr. Le nombre maximal atteint lors de ce comptage dtermine le niveau maximum de sousprogramme utilis par votre programme. Noubliez cependant pas dajouter 1 ce nombre maximum, si vous utilisez les interruptions, augment du niveau de sous-programme utilis dans la routine dinterruption. Pour rester concrets, imaginons un programme fictif. Ce programme est incomplet, nessayez donc pas de le faire tourner, ou alors ajoutez ce quil manque.
goto start ; programme ;************************************************************************** ; ROUTINE INTERRUPTION * ;************************************************************************** ;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp ; Interruption TMR0 ; ----------------btfsc INTCON,T0IE ; tester si interrupt timer autorise btfss INTCON,T0IF ; oui, tester si interrupt timer en cours goto intsw1 ; non test suivant call inttmr0 ; oui, traiter interrupt tmr0 bcf INTCON,T0IF ; effacer flag interrupt tmr0 goto restorereg ; et fin d'interruption ; Interruption RB0/INT ; -------------------intrb0 ; oui, traiter interrupt RB0

intsw1 call

93

bcf

INTCON,INTF

; effacer flag interupt RB0

;restaurer registres ;------------------restorereg swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W ; sans modifier Z swapf w_temp,w ; Rinversion de L et H dans W retfie ; return from interrupt ;************************************************************************** ; INTERRUPTION TIMER 0 * ;************************************************************************** inttmr0 incf compteur,f ; incrmenter compteur return ; fin d'interruption timer ;************************************************************************** ; INTERRUPTION RB0/INT * ;************************************************************************** intrb0 movf mavariable ; charger mavariable call divis ; diviser par 2 return ; fin d'interruption RB0/INT ;************************************************************************** ; TRAITER REGISTRE * ;************************************************************************** traitreg movwf mavariable ; mettre w dans mavariable call multipli ; multiplier par 2 return ; retour ;************************************************************************** ; Diviser par 2 * ;************************************************************************** divis bcf STATUS,C ; effacer carry rrf mavariable,f ; diviser mavariable par 2 return ; retour ;************************************************************************** ; Multiplier par 2 * ;************************************************************************** multipli bcf STATUS,C ; effacer carry rlf mavariable,f ; multiplier mavariable par 2 return ; retour ;************************************************************************** ; Ajouter 1 * ;************************************************************************** ajout incf mavariable,f ; ajouter 1 return ; retour ;************************************************************************** ; programme principal * ;************************************************************************** start movlw 0x05 ; 5 dans w

94

call traitreg call ajout goto start END

; ; ; ;

mettre le double dans mavariable incrmenter mavariable boucler directive fin de programme

Etudions donc les niveaux de sous-routines de ce petit programme, du reste parfaitement inutile. Commenons par le programme principal : On commence par le niveau 0. En premire ligne, on trouve goto start . Il sagit dun saut, non dun appel de sous-routine, donc on najoute rien On arrive ltiquette start . On avance dans le programme, et on tombe sur la ligne call traitreg . Il sagit dun call, donc on ajoute 1. On est donc 1 niveau. On poursuit ltiquette traitreg et on avance dans la sous-routine. On arrive la ligne call multipli . On ajoute donc 1, nous voici 2. Poursuivons donc ladresse multipli . On droule le programme, et on arrive return . On dcompte donc 1. Nous revoici 1. Retour donc dans la routine traitreg, et plus prcisment la ligne return . On dcompte de nouveau 1. Nous revoici 0, et dans le programme principal, preuve que tout se passe bien. On poursuit le programme principal et on arrive la ligne call ajout . On ajoute 1, ce qui nous fait 1. On passe ltiquette ajout . On avance dans le programme et on trouve la ligne return . On soustrait 1, ce qui nous fait 0, et retour au programme principal. On dmarre de 0, on termine 0, tout est correct.

Rappelez-vous que si, un moment donn, vous obtenez une valeur suprieure 8 ou infrieure 0, vous avez forcment fait une erreur dans la construction de votre programme. En gnral il sagit dune mprise entre goto et call , ou un oubli ou ajout dun return . Nous voyons donc que la valeur maximale atteinte lors de lexcution de notre programme est de 2. Donc, notre programme principal utilise un niveau de sous-programme de 2. Etant donn quune interruption peut avoir lieu nimporte quel moment de notre programme (et donc au moment o nous sommes dans la sous-routine multipli, de niveau 2), il convient dajouter ce niveau ceux utiliss dans les routines dinterruption. Imaginons donc quune interruption intervienne au moment o nous nous trouvons dans le cas le plus dfavorable, cest dire au niveau 2 : Linterruption a lieu, et ncessite une sauvegarde sur la pile (stack), comme expliqu dans la premire partie. Nous tions 2, nous voici 3. Nous voici dans la routine dinterruption, nous arrivons la ligne call inttmr0 . Nous ajoutons donc 1, nous en sommes 4.

95

Dans la sous-routine inttmr0 , nous trouvons return , donc nous revoici 3, et dans la routine dinterruption principale. Nous trouvons ensuite call intrb0), ce qui nous ramne 4, nous voici dans intrb0 . Nous trouvons ensuite call divis qui nous amne 5 dans la sous-routine divis . Dans cette routine, nous trouvons return , nous revoici 4 dans la suite de la routine intrb0 . Nous poursuivons et nous trouvons un return . Nous voici 3 dans la routine dinterruption principale, qui se termine par retfie , qui est un return particulier. Nous sortons donc de la routine dinterruption et nous faisons 1, ce qui nous ramne au niveau 2 du dpart. De nouveau, preuve que tout sest bien pass.

Nous voyons donc que notre programme complet ncessite une profondeur de niveau de sous-routines de 5 (valeur maximale rencontre). Nous sommes donc en dessous des 8 niveaux permis, tout est bon. Mais tout nest pas toujours si rose, aussi, si votre programme dpasse les 8 niveaux permis, je vais vous donner quelques astuces pour en diminuer la valeur. La premire chose faire, cest de regarder si toutes les sous-routines sont utiles. En effet, une sous-routine appele une seule fois dans un programme nest pas vraiment indispensable. Mais a, tout le monde peut le faire, il ne sagit pas doptimisation. Tout dabord un prambule : En gnral, loptimisation des niveaux de sous-routines diminue la clart du programme, et sont donc rserver aux applications pour lesquelles on na pas le choix. Principalement pour viter le dbordement de la pile, bien entendu (niveaux >8), mais galement parfois pour gagner du temps dexcution. Dans tous les autres cas, ne tentez pas doptimiser les niveaux de sous-programmes. Je ne sais pas si cest vraiment une bonne ide de parler de ce type doptimisation, mais comme je reois beaucoup de courrier ce sujet, je my suis finalement dcid. Examinons tout dabord la routine dinterruption principale. Si vous regardez attentivement, vous distinguez que les sous-routines inttmr0 et intrb0 ne sont appeles qu partir dun seul endroit dans le programme. Ce sera en gnral vrai pour tous les programmes utilisant la maquette que je vous fournis. Donc, partant de l, il est possible de gagner un niveau de sous-routine cet endroit. En effet, si on appelle la sous-routine depuis un seul endroit, on connat donc le point de retour de cette sous-routine, qui sera la ligne suivant son appel. Vous suivez ? Donc, on peut remplacer le call vers cette sous-routine par un goto , et le return de cette sous-routine par un autre goto . Jen vois qui sont en train de fumer des mninges, pour dautres cest clair. Vous allez voir quen pratique avec la routine inttmr0 , ce nest pas trs compliqu.

96

; Interruption TMR0 ; ----------------btfsc INTCON,T0IE ; tester si interrupt timer autorise btfss INTCON,T0IF ; oui, tester si interrupt timer en cours goto intsw1 ; non test suivant goto inttmr0 ; oui, traiter interrupt tmr0 retour bcfINTCON,T0IF ; effacer flag interrupt tmr0 goto restorereg ; et fin d'interruption ; suite du traitement interrupt ;************************************************************************** ; INTERRUPTION TIMER 0 * ;************************************************************************** inttmr0 incf compteur,f ; incrmenter compteur goto retour ; fin d'interruption timer

Vous voyez donc que vous avez limin un call avec le return correspondant. Sachez que vous pouvez faire de mme avec la sous-routine intrb0 , vous gagnez en fait un niveau de sous-routine au total de votre programme. Mais ceci au dpend de la structuration de votre programme, je vous le rappelle. Tant que nous y sommes, nous pouvons constater quil ny a quune seule instruction (bcf INTCON,T0IF) entre le point de retour et un nouveau saut. Nous en profitons donc pour optimiser en taille et en temps la routine obtenue en dplaant cette instruction et en supprimant un saut.
; Interruption TMR0 ; ----------------btfsc INTCON,T0IE ; tester si interrupt timer autorise btfss INTCON,T0IF ; oui, tester si interrupt timer en cours goto intsw1 ; non test suivant goto inttmr0 ; oui, traiter interrupt tmr0 ; suite du traitement interrupt ;************************************************************************** ; INTERRUPTION TIMER 0 * ;************************************************************************** inttmr0 incf compteur,f ; incrmenter compteur bcfINTCON,T0IF ; effacer flag interrupt tmr0 goto retorereg ; fin d'interruption timer

Voici donc un niveau de gagn. Voyons maintenant une autre astuce, pas trs recommandable, et donc, je le rappelle, nutiliser quen cas dabsolue ncessit. Je lexplique car vous risquez de la rencontrer en analysant des programmes crits par dautres. Bon, accrochez-vous et regardez les 2 lignes suivantes tires de la routine dinterruption :
call divis return ; diviser par 2 ; fin d'interruption RB0/INT

Vous ne remarquez rien ? Effectivement, cest loin de sauter aux yeux. En fait, il sagit dun call suivi directement par un return .

97

Bon, vous allez me dire que vous ntes pas plus avanc. Jy arrive. Je vais raisonner en franais plutt quun organigramme dans un premier temps. Au moment de lexcution de cette sous-routine, se trouve sur la pile ladresse de retour du programme appelant A lappel de la sous-routine divis jempile ladresse de retour sur la pile et je saute en divis Au retour de cette routine divis , je dpile ladresse de retour et je reviens sur la ligne return de ma sous-routine affiche. A lexcution du return , je dpile ladresse du programme appelant et je retourne celui-ci.

Je suppose que a sembrouille encore plus dans votre esprit, ce nest pas le but recherch, mais, en me relisant, a semble logique. Je continue donc mon explication. Vous voyez donc, quen fait jempile ladresse de retour au moment de lappel de la routine divis . Mais cette adresse de retour ne va me servir que pour permettre lexcution de linstruction return , qui elle-mme va dpiler ladresse du programme appelant. Bon, un petit ordinogramme pour montrer tout a ne sera pas superflu.

Que constatons-nous de spcial sur cet ordinogramme ? Et bien, si vous suivez le droulement chronologique illustr par les flches, vous voyez que vous rencontrez 2 return conscutifs.

98

Si vous analysez bien, vous remarquez que ceci est d au fait que lappel de la sousroutine2 est suivi directement par le return marquant la fin de la sous-routine1. Vous ne voyez toujours pas ? En fait, si on traduit ces 2 retours conscutifs en franais, on pourrait dire : On dpile ladresse de retour empile afin de pointer sur linstruction qui demande de dpiler ladresse de retour afin dobtenir ladresse de retour vers le programme. On peut donc se dire que si ladresse de retour dpile dans la sous-routine2 ne sert qu obtenir ladresse de retour qui sera dpile par la sous-routine1, on pourrait souhaiter retourner directement de la sous-routine2 vers le programme principal. Vous commencez comprendre ? En fait, comme on ne peut pas dpiler 2 adresses la fois, on pourrait tout aussi bien se dire quil suffit de ne pas empiler ladresse de retour vers la sous-routine1, ce qui fait que le retour de la sous-routine2 permettrait le retour direct vers le sous-programme. Comme on naurait pas empil, on gagnerait un niveau de sous-routine. Or, comment ne pas empiler ? Simple ! Il suffit de remplacer le call par un goto . Ceci vous donne la chronologie suivante :

Un petit mot dexplication. En fait, si vous suivez partir du programme, vous voyez que nous rencontrons un call vers la sous-routine1. On empile donc ladresse de retour vers le programme principal. Ensuite, on arrive dans la sous-routine1, dans laquelle un goto nous renvoie la sous-routine2. Dans celle-ci on trouve un return qui dpile ladresse prcdemment empile, soit celle du retour vers le programme principal, sans repasser par la sous-routine1. De cette faon, la sous-routine2 peut tre appele de faon classique par un call , ou appele par un goto , auquel cas le retour seffectue depuis le programme qui a appel la sous-routine qui a appel la sous-routine2

99

Si vous comptez les niveaux de sous-routine, vous avez gagn un niveau. Ceci se traduit en langage dassemblage par :
;************************************************************************** ; INTERRUPTION RB0/INT * ;************************************************************************** intrb0 movf mavariable ; charger mavariable goto divis ; diviser par 2 ;************************************************************************** ; Diviser par 2 * ;************************************************************************** divis bcf STATUS,C ; effacer carry rrfmavariable,f ; diviser mavariable par 2 return ; retour

ATTENTION : ne me faites pas crire ce que je nai pas crit. Je vous rappelle que je dconseille fortement ce type de pratique, ne rserver quaux cas dsesprs. Jexplique ceci afin de vous aider comprendre certaines astuces que vous risquez de rencontrer si vous examinez des programmes raliss par dautres. Il existe encore dautres mthodes. Vous verrez par exemple dans le chapitre consacr au debugger, comment utiliser une sous-routine sans utiliser un seul niveau sur la pile. Mais lisez dans lordre, quand vous en serez arrivs l, il sera temps de vous casser les mninges. 8.4.2 Lorganisation des ressources Je vais maintenant vous montrer une astuce trs simple concernant des zones de donnes copier, comparer, etc. Le problme est que vous ne disposez que dun seul pointeur indirect, FSR. La manipulation simultane de 2 zones implique une gymnastique permanente au niveau de ce registre. Imaginons le petit programme suivant (pour tester au simulateur) :
LIST p=16F876 ; Dfinition de processeur #include <p16F876.inc> ; fichier include ; Zone de 80 bytes ; ---------------CBLOCK 0x20 Zone1 : D'14' Zone2 : D'14' cmpt : 1 sav1 : 1 sav2 : 1 inter : 1 ENDC ; ; ; ; ; ; ; Dbut de la zone (0x20 0x6F) table de 14 octets table de 14 octets variable locale sauvegarde pointeur1 sauvegarde pointeur2 zone intermdiaire

; Fin de la zone

100

ORG 0x00 start movlw movwf movlw movwf movlw movwf loop movf movwf movf movwf

D'14' cmpt Zone1 sav1 Zone2 sav2 sav1,w FSR INDF,w inter

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

nombre d'octets transfrer dans compteur adresse zone1 dans pointeur1 adresse zone2 dans pointeur2 charger pointeur1 dans pointeur indirect charger valeur pointe dans valeur intermdiaire charger pointeur2 dans pointeur indirect charger valeur intermdiaire transfrer valeur incrmenter pointeur1 incrmenter pointeur2 dcrmenter compteur d'lments pas dernier, suivant

movf sav2,w movwf FSR movf inter,w movwf INDF incf sav1,f incf sav2,f decfsz cmpt,f goto loop END

Vous constatez que ce nest vraiment pas pratique de modifier et sauver sans arrt FSR. Ceci impose 2 variables de sauvegarde et lutilisation dune variable intermdiaire pour stocker la donne transfrer. La premire chose laquelle vous pouvez penser, cest dconomiser les variables de sauvegarde de FSR, puisque la distance entre Zone1 et Zone2 est fixe. Ceci nous donne :
start movlw movwf movlw movwf D'14' cmpt Zone1 FSR ; ; ; ; nombre d'octets transfrer dans compteur adresse zone1 dans pointeur indirect

loop movf INDF,w movwf inter movlw Zone2-Zone1 addwf FSR,f movf inter,w movwf INDF movlw Zone1-Zone2+1 addwf FSR,f decfsz cmpt,f goto loop END

; charger valeur pointe ; dans valeur intermdiaire ; ; ; ; ; ; ; ; cart entre les 2 zones pointer sur zone2 charger valeur intermdiaire transfrer valeur cart entre zone2 et zone1 suivant pointer sur suivant dcrmenter compteur d'lments pas dernier, suivant

Cest dj mieux, mais on a toujours besoin dune sauvegarde intermdiaire. La ligne comprenant Zone1-Zone2+1 sexplique par le fait que pour pointer sur la Zone1, on doit ajouter la valeur ngative Zone1-Zone2, et, quensuite, on doit incrmenter cette valeur pour passer la variable suivante, do le +1 qui permet dviter lincrmentation. On peut alors se dire quil serait judicieux de placer les tables pour quun seul bit diffrencie leur emplacement. Zone1 va de B0010 0000 B 0010 1101

101

La Zone2 commence ds lors en B0010 1110. On remarque que si on dcale tout simplement Zone2 de 2 emplacements, elle stendra de B0011 0000 B0011 1101. Entre Zone1 et Zone2, seul le bit 4 de lemplacement est modifi. Profitons de cette astuce :
; Zone de 80 bytes ; ---------------CBLOCK 0x20 Zone1 : D'14' cmpt : 1 libre : 1 Zone2 : D'14' ENDC ORG 0x00 start movlw D'14' movwf cmpt movlw Zone1 movwf FSR loop movf INDF,w bsf FSR,4 movwf INDF incf FSR,f bcf FSR,4 decfsz cmpt,f goto loop END ; ; ; ; ; ; Dbut de la zone (0x20 0x6F) table de 14 octets : NE PAS DEPLACER variable locale emplacement libre table de 14 octets : NE PAS DEPLACER Fin de la zone

; ; ; ; ; ; ; ; ; ; ;

nombre d'octets transfrer dans compteur adresse zone1 dans pointeur indirect charger valeur pointe pointer zone2 transfrer valeur pointer sur suivant repointer Zone1 dcrmenter compteur d'lments pas dernier, suivant

Dans ce cas, on peut parler doptimisation, aussi bien en place quen vitesse dexcution, et mme en nombre de variables RAM utilises. Notez que nous avons dplac Zone2 en y insrant le cmpt, plus une variable inutilise qui pourra servir un autre usage. Noubliez pas de prciser dans votre zone de variables les zones quil ne faudra plus dplacer. Vous pouvez aussi utilisez les assignations (Zone1 EQU 0x20), mais alors faites trs attention aux double-emplois, je vous dconseille cette mthode. Vous pouvez galement utiliser cette technique en plaant vos tables dans des banques diffrentes. Si vous placez, par exemple Zone1 en 0x20 (banque0) et Zone2 en 0xA0 (banque1), le passage de la source la destination se fera cette fois en modifiant le bit 7 de FSR. Et si vous placez Zone1 en 0x20 (banque0) et Zone2 en 0x120 (banque2), alors le passage de lune lautre ne ncessitera que la modification du bit IRP du registre STATUS. Vous voyez donc que simplement dfinir un emplacement judicieux pour vos variables peut vous permettre des optimisations intressantes.

102

Notes :

103

Notes :

104

9. Les diffrents types de reset


Nous passons maintenant ltude des diffrentes causes qui peuvent aboutir provoquer un reset de notre PIC. Cest dire un redmarrage depuis ladresse 0x00 et la rinitialisation ventuelle de certains registres. Il peut tre en effet parfois utile de pouvoir dtecter, au niveau de la procdure dinitialisation, le type de reset qui vient dtre excut. Ceci afin de permettre de raliser des oprations diffrentes. On pourrait avoir, par exemple, un procdure qui initialiserait le systme en cas de premier dmarrage, et une autre qui permettrait de rcuprer un processus plant aprs un dbordement de watchdog suite un violent parasite secteur. Nous allons donc voir comment distinguer et reconnatre la cause du reset en cours. 9.1 Les 6 types de reset Nous pouvons dire que si notre programme est contraint de redmarrer au dbut de sa squence, ladresse 0x00, cest quun des vnements suivant est survenu (on excepte naturellement un saut volontaire de la part de lutilisateur de type goto 0x00 ) : Apparition de la tension dalimentation aprs une coupure Application dun niveau bas sur la pin MCLR durant le droulement du programme Application dun niveau bas sur la pin MCLR durant le mode sleep Dbordement du watchdog durant le droulement du programme Dbordement du watchdog durant le mode sleep Remonte de la tension dalimentation aprs une chute partielle

Parmis ces 6 types, le dbordement du watchdog durant le mode de sommeil nest pas proprement parler un reset, puisquil provoque le rveil du PIC, sans provoquer un reset ladresse 0x00, ni mme la modification des registres affects par les resets classiques. Cependant, jintgre ce cas ici, tant donn quil met en uvre les mmes procdures de dtection. Il existe au moins une faon non documente pour provoquer volontairement un reset par le programme. Comme je dconseille vivement ce genre de procds, je nen parlerai pas. 9.2 Le reset et les registres STATUS et PCON Pour pouvoir dterminer quel vnement a provoqu le reset, il nous faudra plusieurs bits. Etant donn que nous avons 6 causes de reset possible, nous avons besoin dun minimum de 3 bits. En ralit, nous allons voir que 4 bits sont utiliss, 2 se trouvent dans le registre STATUS, et 2 dans un nouveau registre, le registre PCON. Voici la description du registre PCON

105

b0 : BOR : b1 : POR : b2-b7 : N.U.

Brown Out Reset : Reset sur chute de tension Power On Reset : Reset par mise sous tension Non utiliss : non implments, lus comme 0

Les bits concerns par le reset dans le registre STATUS sont les suivants : b3 b4 : PD : TO : : Power Down bit : passage en mode sleep Time Out bit : reset par dbordement du watchdog

Notez que ces bits, ainsi que les bits qui nous intressent sont actifs ltat bas, cest-dire, pour rappel, que cest lorsquils sont 0 quils indiquent que lvnement a eu lieu. Il vous faut noter que le bit BOR est dans un tat indtermin lors de la mise sous tension. Ce bit est forc 0 lorsquune chute de tension provoque le reset du PIC. Donc, si vous voulez dtecter ce passage 0, il vous faudra tout dabord placer ce bit 1 dans votre routine dinitialisation. Vous pourrez par exemple utiliser les lignes suivantes :
init bsf btfss bsf bcf STATUS,RP0 PCON,POR PCON,BOR STATUS,RP0 ; ; ; ; passer en banque 1 tester si mise sous tension oui, alors forcer BOR 1 repasser banque0, suite de linitialisation

9.3 Dtermination de lvnement Je vous donne le tableau qui vous permettra de dtecter quel type de reset vous tes confronts dans votre routine dinitialisation. Vous auriez pu trouver ce tableau uniquement par dduction des explications prcdentes. POR BOR TO PD Evnement qui a provoqu le reset 0 X 1 1 Mise sous tension du PIC 0 X 0 X Impossible, puisque TO est mis 1 la mise sous tension 0 X X 0 Impossible, puisque PD est mis 1 la mise sous tension 1 0 1 1 Retour de la tension dalimentation aprs une chute de tension 1 1 0 1 Reset par dbordement du watchdog 1 1 0 0 Sortie du mode sleep par dbordement du watchdog 1 1 U U Reset provoqu par la broche MCLR 1 1 1 0 Reset par MCLR durant le mode sleep Je vais vous commenter un peu ce tableau, afin de vous viter de vous casser les mninges. Vous allez voir que tout est logique : La premire ligne concerne la mise sous tension du PIC, cest dire le passage de ce que le PIC considre comme une absence dalimentation, vers ce quil considre comme une alimentation correcte. Les valeurs exactes de ces niveaux sont donns, pour ceux que a intresse, dans le datasheet, au niveau des caractristiques lectriques.

106

Dans votre programme, vous dtecterez cet vnement en examinant le bit POR. Sil vaut 0, alors on est dans le cas de la mise sous tension, encore appele Power On Reset. Comme la mise sous tension provoque automatiquement la mise 1 de TO et de PD, les lignes 2 et 3 du tableau sont donc impossibles. Inutile donc de tester ces configurations, elles ne se prsenteront jamais. La quatrime ligne du tableau vous montre que le simple test du bit BOR vous permet de dterminer que le reset a t provoqu par une chute de la tension dalimentation. La faon dont le PIC dtermine une chute de tension est galement spcifie dans les caractristiques lectriques. Notez quune perte de tension totale, provoquera, au moment de la rapparition de la tension, un P.O.R. plutt quun B.O.R. On peut donc estimer quun B.O.R. est conscutif une chute de lalimentation qui place le PIC en risque de plantage , alors quun P.O.R. est la consquence dune perte dalimentation qui impose larrt total de llectronique du PIC. Une chose importante vous rappeler, cest que le bit BOR est dans un tat indtermin lors dune mise sous tension. Il pourrait tout aussi bien tre 1 qu 0. Cest pour a que si vous dsirez utiliser ce bit, vous devrez le forcer 1 lors du Power On Reset, afin de pouvoir tester son ventuel passage 0. Une seconde chose noter est que le bit BOR, donc le reset de type Brown Out Reset nest utilis QUE si le bit BODEN a t valid au niveau de la directive _CONFIG. Suivant votre application, vous avez donc 2 possibilits : 1) Soit vous nutilisez pas la fonction BOR, donc vous estimez quil est prfrable que votre PIC fonctionne cote que cote, mme si une chute dalimentation risque de provoquer un plantage de son programme 2) Soit vous dcidez que le PIC ne peut fonctionner que si la tension permet un fonctionnement sans risque, et donc vous activez le bit BODEN Pour situer les esprits, vous pouvez considrer, par exemple quun petit gadget qui fait clignoter une LED avec une alimentation par pile doit fonctionner le plus longtemps possible, donc vous ne validez pas BODEN. Par contre, un montage qui pilote une scie circulaire automatique ne devra fonctionner que sil nexiste aucun risque de plantage du programme, donc vous activerez BODEN. Pour comprendre les lignes suivantes, il vous faut savoir que le bit PD peut tre remis 1 par un des 2 vnements suivants : Mise sous tension Excution de linstruction clrwdt . La mise 0 est, comme je lai dit plus haut, provoque par linstruction sleep Sachant ceci, si nous examinons la cinquime ligne du tableau, nous voyons que TO nous indique que le reset a t provoqu par le dbordement du watchdog. En examinant en surplus le bit PD 1, nous pouvons en dduire que le programme tait en phase dexcution normale

107

dune instruction. Donc, dans cette circonstance, nous savons que le watchdog a provoqu un reset de notre programme en cours dexcution. Passons alors la sixime ligne, qui ne se diffrencie que par le bit PD, cette fois 0. Comme je lai dit, ce bit est mis 0 par linstruction sleep . Nous pouvons donc en dduire que le watchdog a rveill le PIC qui tait plac en mode sleep . Il ny a donc pas de reset provoqu dans ce cas. La septime ligne devrait vous laisser un peu perplexe. En effet, nous voyons 2 fois la mention u pour unchanged ou inchang . Si les bits peuvent tre 1 ou 0, ils peuvent tre dans un des tats correspondant aux deux lignes prcdentes. Comment dans ce cas savoir quel est le type de reset traiter ? En fait, il suffit de rflchir. Nous voyons donc daprs cette ligne quune action sur MCLR provoque un reset qui ne modifie ni TO, ni PD. A partir de l, imaginons que TO = PD = 1. Dans ce cas, on peut dire quon a affaire un reset qui nest ni un P.O.R, ni un B.O.R., puisque les bits correspondants sont 1 . Ce nest pas non plus un dbordement de watchdog, puisque TO = 1. Ne reste donc que la possibilit que le reset soit provoqu par une action sur MCLR. Si, par contre, le bit PD tait 0, cela voudrait dire quon a agit sur MCLR pendant que le PIC tait en mode sleep . Vous allez me dire, et si TO valait 0 ? Et bien, dans ce cas, cela voudrait dire que nous venions davoir affaire un reset de type dbordement de watchdog, juste avant notre action sur MCLR. Donc, il nous suffisait de remettre TO 1 une fois notre reset de type watchdog intervenu, pour viter de nous trouver confront cette incertitude. Si vous examinez la huitime ligne de notre tableau, vous voyez quelle correspond un des cas que je viens dexpliquer, savoir un reset de type MCLR alors que le PIC tait en mode sleep Au terme de lanalyse de ce tableau, vous devez tre capable, le jour o vous en aurez besoin, de dterminer quel est le type de reset qui vous amne excuter votre routine dinitialisation. Notez que je nai pas intgr les routines de test de reset dans le fichier maquette, car il est assez peu frquent quon doive les utiliser. Et quand on doit les utiliser, il est rare quon doive diffrencier tous les cas. Il vous suffira donc, le cas chant, de revenir lire ce chapitre et de traiter votre cas particulier en fonction de ce que je vous ai expliqu. Je ne ferai pas dexercices sur ce chapitre, car cela me semble inutile et demanderait beaucoup de manipulations pour tester tous les cas possibles. Jestime que ce serait de la perte de temps, dautant que les programmes se rsumeraient tester les diffrents bits dans lordre donn par le prcdent tableau. Par contre, une donne intressante obtenir est ltat des diffrents registres aprs chaque type de reset. Je vous conseille donc de regarder le tableau 12-6 du datasheet qui vous donnera toutes les informations utile ce niveau.

108

Certains registres sont en effet affects de faon diffrente selon le type de reset intervenu.

109

Notes :

110

10. Les ports entre/sortie


Nous allons maintenant voir dans ce chapitre les diffrents ports du 16F87x dans leur utilisation en tant que PORT I/O, cest--dire sans utiliser les fonctions spciales des pins concernes. Nous tudierons ces fonctions spciales dans les chapitres correspondants au moment voulu. Les PICs 16F87x disposent en effet dinnombrables possibilits, ce qui ne rend pas simple la description exhaustive de toutes les utilisations possibles. Commenons donc par les vieilles connaissances, et en premier lieu par : 10.1 Le PORTA Nous en avons dj parl dans le chapitre sur la conversion des programmes. En fait, le PORTA, dans sa partie PORT I/O est fort semblable celui observ sur le 16F84. Nous trouvons donc ici 6 pins I/O numrotes de RA0 RA5. Nous avons donc 6 bits utiles dans le registre PORTA et 6 bits dans le registre TRISA. Les bits 6 et 7 de ces registres ne sont pas implments. Comme toujours, ils seront lus comme des 0 . La principale diffrence avec le 16F84 provient de ce que le PORTA, au moment du reset, est configur comme un ensemble dentres analogiques. Donc, nous devrons forcer une valeur dans le registre ADCON1 dans notre routine dinitialisation pour pouvoir utiliser ce port comme port dentre/sortie de type gnral. Je verrai le registre ADCON1 dans le chapitre consacr au convertisseur A/D. Limportant, ici, est de savoir que pour utiliser le PORTA de la mme faon que sur le 16F84, nous devrons placer la valeur Bx000011x, dans lequel x vaut 0 ou 1. Pour les plus curieux dentre-vous, cette valeur provient du tableau 11-2, mais nous y reviendrons. Donc, pour utiliser le PORTA en mode I/O, nous placerons par exemple la valeur B00000110 dans ADCON1, soit 0x06. Notez que le registre ADCON1 se trouve en banque1.
bsf STATUS,RP0 movlw 0x06 movwf ADCON1 xxxx ; ; ; ; passer en banque 1 valeur pour mode numrique dans ADCON1 suite des initialisations.

Si vous examinez la routine dinitialisation de votre fichier maquette, vous verrez que linitialisation de ADCON1 a t prvue. De sorte que ceci vous vite cet oubli trs frquent, en croire le courrier que je reois ce sujet. Attention ce que lutilisation de cette valeur influe galement sur le fonctionnement du PORTE, comme vous le verrez plus loin. Le reste du PORTA est strictement identique celui du 16F84, avec la mme remarque au niveau de RA4, qui est toujours en drain ouvert, cest--dire quil ne permet pas dimposer un
111

niveau +5V sur la pin correspondante. Cest galement une erreur que je constate frquemment, au niveau lectronique, cette fois. 10.2 Le PORTB Rien de particulier dire sur ce PORTB, qui est identique celui du 16F84, du moins pour la partie I/O classique. Notez la pin RB0 qui, en configuration dentre, est de type trigger de Schmitt quand elle est utilise en mode interruption INT . La lecture simple de RB0 se fait, elle, de faon tout fait classique, en entre de type TTL. Je vous renvoie donc la premire partie pour plus dinformations sur ce PORT. 10.3 Le PORTC Nous voici dans les nouveauts. Cest un PORT qui nexistait pas sur le 16F84. Voyons donc, toujours au niveau de son utilisation classique , quelles sont ses caractristiques. Tout dabord au niveau programmation, cest un PORT tout ce quil y a de plus classique. Nous trouvons donc un registre TRISC localis dans la banque 1, qui permet de dcider quelles sont les entres et quelles sont les sorties. Le fonctionnement est identique celui des autres TRIS, savoir que le positionnement dun bit 1 place la pin en entre, et que le positionnement de ce bit 0 place la dite pin en sortie. Notez, comme pour tous les autres ports, que la mise sous tension du PIC, et tout autre reset, force tous les bits utiles de TRISx 1, ce qui place toutes les pins en entre. Je ny reviendrai plus. Nous trouvons galement le registre PORTC, qui, comme les autres PORTx, se trouve dans la banque 0. Son fonctionnement est, de nouveau, identique celui des autres PORTx Ensuite, au niveau lectronique, nous noterons que toutes les pins, lorsquelles sont configures en entre, sont des entres de type trigger de Schmitt . Je vous rappelle que ceci permet dviter (en simplifiant) les incertitudes de niveau sur les niveaux qui ne sont ni des 0V, ni des +5V, donc, en gnral, sur les signaux qui varient lentement dun niveau lautre. A la vue de ceci, lutilisation pour vous du PORTC ne devrait poser aucun problme. 10.4 Le PORTD Tout dabord, il faut noter, mais vous laurez remarqu, que ce port nest prsent que sur les 16F877, et non sur les 16F876. Je vous rappelle quafin dviter de traner partout des quivalences, quand je parle 16F877, jinclus le 16F874 et autres PICs compatibles. Il en va de mme pour le 16F876 par rapport au 16F873. Quand il y a une diffrence (comme la taille mmoire), je fais la distinction explicitement.

112

Une fois de plus, ce PORT fonctionne de faon identique aux autres, dans son mode de fonctionnement gnral. Le registre TRISD comportera donc les 8 bits de direction, pendant que le registre PORTD correspond aux pins I/O concernes. Notez quici galement, les 8 pins I/O, en mode entre, sont du type trigger de Schmitt . Prenez garde que le fonctionnement de ce port dpend galement de la valeur que vous placerez dans TRISE, qui concerne pourtant, premire vue, le PORTE. Mais, au moment dune mise sous tension, la valeur qui est place automatiquement dans TRISE configure votre PORTD en port I/O de type gnral. Vous ne devez donc pas, priori, vous en occuper si vous voulez rester dans ce mode. 10.5 Le PORTE De nouveau, et fort logiquement, ce port nest prsent que sur les PICs de type 16F877. Il ne comporte que 3 pins, RE0 RE2, mais, contrairement aux autres ports, les bits non concerns de TRISE sont, cette fois, implments pour dautres fonctions. Vous remarquerez que les pins REx peuvent galement tre utilises comme pins dentres analogiques. Cest de nouveau le registre ADCON1 qui dtermine si ce port sera utilis comme port I/O ou comme port analogique. Par dfaut, le port est configur comme port analogique, et donc, comme pour le PORTA, vous devrez placer la valeur adquate dans ADCON1 pour pouvoir lutiliser comme port I/O numrique. Notez cependant que la valeur 0x06 utilise dans lexplication pour le PORTA, place du mme coup votre PORTE en mode numrique. Vous navez donc, une fois de plus, rien ajouter votre fichier maquette pour fonctionner dans ce mode. Au niveau lectronique, les pins REx utilises en entre seront, une fois de plus, du type trigger de Schmitt . 10.5.1 Le registre TRISE Le registre TRISE dispose de certains bits de configuration qui ne sont pas prsents sur les autres TRISx. Microchip a voulu probablement faire des conomies de registre en utilisant ces bits pour des fonctions qui ne relvent pas de la dtermination du sens des entres/sorties. Voyons donc le rle de chacun des bits de TRISE : b7 : b6 : b5 : IBF OBF IBOV : Input Buffer Full status bit : Output Buffer Full status bit : Input Buffer OVerflow detect bit

113

b4 b3 b2 b1 b0

: : : : :

PSPMODE 0

: Parallel Slave Port MODE select bit : Non implment (lu comme 0 ) : Direction I/O pour RE2 (1 = entre) : Direction I/O pour RE1 : Direction I/O pour RE0

Nous voyons que les bits 0 2 fonctionnent de faon identique ceux des autres TRISx. Un bit 1 positionne la pin en entre, un bit 0 la positionne en sortie. Les bits 7 5 seront tudis en temps utile, examinons plutt le bit 4 (PSPMODE). Cest ce bit qui dtermine si le PORTD (et non le PORTE) sera utilis en port I/O classique. Si vous placez 1 ici, le PORTD sera considr comme un port dinterfaage avec un microprocesseur. Nous y reviendrons dans le chapitre suivant. Par chance, si les bits 0 2 sont bien forcs 1 lors dune mise sous tension, plaant RE0 RE2 en entre, par contre, les autres bits sont forcs 0 lors de cette mise sous tension. Donc le PORTD fonctionnera alors comme un port I/O classique, vous ne devez rien faire de spcial.

114

Notes :

115

Notes :

116

11. Le PORTD en mode PSP


11.1 A quoi sert le mode PSP ? Le mode PSP (pour Parallel Slave Port) permet un microprocesseur, ou tout autre systme extrieur de prendre le contrle du PORTD de votre PIC. Ce systme pourra crire de lui-mme des valeurs sur le PORTD, et y lire des valeurs que votre programme PIC y aura prpare son intention. Le PORTD devra donc passer alternativement en entre et en sortie, et sous la seule dcision du systme extrieur. Cest donc celui-ci qui dcide quel moment une lecture ou une criture sopre. Les transferts sont donc raliss de faon asynchrones avec le programme de votre PIC. Voici un chapitre qui va intresser les lectroniciens pointus dsireux dinterfacer leur PIC avec un autre processeur, de faon , par exemple, obtenir un super UART. Pour les autres, ne vous inquitez pas si vous ne comprenez pas tout, car vous nutiliserez probablement jamais cette possibilit, et, du reste, cela ne vous empchera pas de profiter des autres possibilits de votre PIC. La premire chose est de bien se mettre daccord sur les termes. Nous parlons ici du PORD en mode parallle. Il ne sagit pas ici du port parallle de votre imprimante, mais de la mthode de communication entre 2 microprocesseurs. Ensuite, il faut comprendre que ce mode est un mode Slave ( esclave), cest dire sous le contrle du microprocesseur sur lequel vous connectez votre PIC. Cest ce processeur qui va prendre le contrle de votre PORTD. 11.1 Comment passer en MODE PSP ? Commencez par regarder la figure 3-9 du datasheet. Ceux qui sont attentifs et qui ont lu le chapitre prcdent ont dj un lment de rponse. La premire chose faire est de forcer le bit PSPMODE du registre TRISE 1 . Ensuite, vous voyez que les 3 pins RE0/RE2 sont utilises dans un de leur mode spcial et portent de fait les dnominations RD/CS/WR. Ces pins sont actives ltat bas. Ces pins sont sous contrle du microprocesseur matre, ce qui fait que vous devez les configurer en entre via les bits b0/b2 de TRISE. Mais vous remarquez galement, si vous tes toujours attentif, que cest la valeur impose par une mise sous tension, donc inutile de vous en occuper. Vous notez galement que le PORTD passe sous contrle de ces pins, le registre TRISD nest donc plus daucune utilit : inutile de le configurer.

117

Notez que dans la plupart des cas, vous utiliserez les interruptions pour grez ce mode de fonctionnement, vous devez donc mettre en service linterruption PSP comme indiqu dans le chapitre sur les interruptions. En effet, les changes dinformation se font sur dcision du microprocesseur matre, et donc de faon asynchrone avec le programme de votre PIC. Les interruptions restent la meilleure faon de traiter rapidement les vnements asynchrones. 11.2 Connexion des PORTD et PORTE en mode PSP Si nous reprenons nos 3 pins du PORTE, avec les noms utiliss pour cette fonction, cest dire RD, CS, et WR, nous pourrons dire : La pin CS est la pin Chip Select , que tous les lectroniciens spcialiss dans les microprocesseurs connaissent. Pour les autres, on peut dire que le Chip Select permet de valider la slection du circuit concern, en loccurrence notre PIC. Sur la carte microprocesseur, un dtecteur dadresse, plac sur le bus dadresse permet de slectionner ladresse laquelle va rpondre notre PIC. Comme dans la plupart des systmes, ce CS sera actif ltat bas. Je pourrais donner ici un cours complet sur les techniques dinterfaages, mais ce nest pas le but de cet ouvrage. Je part du principe que quelquun qui veut interfacer son PIC avec une carte microprocesseur possde suffisamment de connaissances ce niveau pour se passer de mes explications. Si ce ntait pas le cas, et si je recevais du courrier prcis concernant ce point, jajouterai une annexe pour en parler. Ensuite, nous trouvons les signaux RD pour ReaD et WR pour WRite . De nouveau ces signaux sont actifs ltat bas. Notez que sur certains microprocesseurs, il nexiste quune seule pin RD/WR. A vous donc dans ce cas de gnrer lectroniquement les 2 signaux, ce qui nest pas compliqu. Maintenant, concernant le PORD, il sera connect au bus de data de la carte microprocesseur. Notez, une fois de plus, que sur certains microprocesseurs, les bus de data et dadresse peuvent tre multiplexs, ou dune largeur diffrente de 8 bits. Il vous incombe une fois de plus de rendre les signaux et les messages compatibles lectroniquement. 11.3 Le fonctionnement logiciel Maintenant que tout est configur et que le PIC est interfac, voyons comment fonctionne lensemble au niveau software.

118

En fait, la procdure est simple, vous voyez donc quil existe 2 cas : soit le microprocesseur crit dans le pic, soit il lit. Considrons tout dabord une criture. Le microprocesseur excute une criture de DATA ladresse du PIC. La DATA est mmorise dans le tampon (latch) de lecture du PIC. Le flag dinterruption PSPIF est positionn, ainsi que le bit IBF du registre TRISE Si linterruption est autorise, une interruption est gnre Le programme lit le latch pour savoir quelle valeur a t crite par le microprocesseur, ce qui provoque galement le reset du bit IBF (Input Buffer Full = buffer dentre plein) Si on regarde ce qui se passe au niveau du PIC, on aura tout simplement : Le flag PSPIF est positionn, et une interruption a ventuellement lieu Le programme excute un movf PORTD,w pour rcuprer la donne crite par le microprocesseur. Le test de IBF et OBF permet de savoir si on a eu affaire une lecture ou une criture. Notez que lorsque vous lisez le PORTD, vous obtenez non pas le niveau prsent sur les pins RD0 RD7, mais la valeur que le microprocesseur a crite directement dans le tampon dentre. Il se peut bien entendu que le microprocesseur crive une seconde donne dans le PORTD du PIC, avant que celui-ci nait eu le temps de lire la donne prcdente. Dans ce cas, cette prcdente donne est perdue, et le bit IBOV est positionn pour signaler lutilisateur quun crasement a eu lieu et quau moins une des donnes transmises est perdue. Vous voyez donc que tout ceci est extrmement simple. Ce nest pas plus compliqu au niveau dune lecture de donne depuis le microprocesseur : Le PIC crit sa donne dans le latch dcriture, qui est galement PORTD. Ceci provoque le positionnement du bit OBF (Ouput Buffer Full = buffer de sortie plein) Quand le microprocesseur le dcide, il effectue une lecture du PORTD, et rcupre alors la valeur que vous y avez crite Le flag OBF passe 0 ds que le cycle de lecture est commenc Le flag PSPIF est positionn une fois la fin du cycle de lecture termin

119

Une interruption est alors ventuellement gnre si elle a t autorise Si on regarde au niveau du PIC, on aura tout simplement :

On crit la valeur envoyer au processeur dans PORTD On est prvenu que la valeur a t lue par le positionnement du flag PSPIF, et, ventuellement, par lapparition de linterruption correspondante. Le test de IBF et OBF permet de savoir si on a eu affaire une lecture ou une criture.

Notez que le tampon de lecture possde la mme adresse que le tampon dcriture. Cette adresse est tout simplement le registre PORTD. Attention, ces tampons sont physiquement diffrents, donc en crivant dans PORTD vous ne dtruisez pas ce que le microprocesseur a crit. Autrement dit, vous ne lisez pas ce que vous avez crit, mais ce que le microprocesseur a crit. Pour faire simple, quand vous crivez dans PORTD, vous crivez dans un registre diffrent de celui que vous accdez en lisant PORTD. Vous avez donc 2 PORTD . Remarquez donc que dans ce mode, lcriture dune valeur dans PORTD ne se traduit par lapparition de cette valeur sur les pins RDx quau moment o le microprocesseur effectue une requte de lecture du PORTD. Le PORTD est donc sous le contrle total du microprocesseur extrieur. 11.4 Le fonctionnement matriel Les chronogrammes des figures 3-10 et 3-11 du datasheet vous donnent les droulements chronologiques des diffrentes tapes. Un spcialiste des techniques microprocesseurs naura aucun mal les interprter, je vais cependant vous les rsumer de faon simple, et en clair . Voyons tout dabord la chronologie dune criture. Avant tout, souvenez-vous que dans un change, nous nous plaons du point de vue du matre, cest--dire du microprocesseur. Il sagit donc dune criture dans le PIC, donc dune lecture au niveau du programme du PIC. Souvenez-vous galement quun cycle dinstruction est compos de 4 clocks dhorloge, nomms Q1 Q4. La frquence de fonctionnement du PIC est en effet gale la frquence de lhorloge divise par 4. Voici donc les vnements hardware et leurs implications software du cycle dcriture : La donne est place sur le PORTD par le microprocesseur (en connexion avec le bus de donnes) et doit rester stable jusqu la fin de lordre dcriture

120

Au minimum 20 25 ns plus tard, les lignes de contrle CS et WR sont places ltat bas (peu importe lordre) La ligne WR ou CS ou les deux repasse(nt) 1, la data est capture dans le latch dentre du PIC Le microprocesseur doit maintenir la donne stable encore durant 20 35ns Au temps Q4 qui suit, IBF et PSPIF passent simultanment 1.

Quant au cycle de lecture (on suppose que la donne se trouve dj dans le latch de sortie du PIC). Les lignes de contrle RD et CS sont places ltat bas (peu importe lordre) A ce moment, le bit OBF passe 0 Le PIC place la donne sur les lignes RD0 RD7 Au minimum 80 90ns plus tard, le microprocesseur effectue la lecture de la donne Il remonte ensuite la ligne RD ou CS, ou les 2. La donne disparat des lignes RD0 RD7 entre 10 et 30ns plus tard Au cycle Q4 suivant du PIC, le bit PSPIF est positionn, ce qui provoquera ventuellement une interruption

Ce chapitre tait particulirement ardu comprendre, jen suis conscient. Les explications risquent cependant de vous tre utiles un jour, cest pourquoi jai abord le sujet de faon assez dtaille. Vous vous rendez compte quil mest particulirement difficile de vous donner un exercice pratique mettant en uvre ce mode particulier, car cela ncessiterait de construire une seconde carte gre en matre par un microprocesseur. Mais je suis convaincu quune fois cet ouvrage et le prcdent assimils, vous pourrez construire le cas chant votre programme de communication inter-processeurs.

121

Notes :

122

12. Les accs la mmoire eeprom


Les accs en mmoire eeprom ne sont pas plus compliqus que pour le 16F84. Il suffit en effet de respecter la procdure indique par Microchip. Le fichier maquette que je vous fournis contient 2 macros destines lcriture et la lecture en eeprom. Ces macros mettent en jeu 4 registres, savoir EEADR, EEDATA, EECON1, et EECON2. Ces registres ont dj t dcris dans la premire partie consacre au 16F84. Nanmoins, le registre EECON1 subit pour cette version quelques modifications que nous allons dcrire. Le registre EEADR est toujours utilis pour placer ladresse relative en EEPROM, tandis que EEDATA contiendra la donne lue ou crire. Comme pour le 16F84, le registre EECON2 nen est pas vritablement un. Microchip lutilise en tant que registre de commande. Lcriture de valeurs spcifiques dans EECON2 provoque lexcution dune commande spcifique dans llectronique interne du PIC. Vous disposez dune zone de 128 octets en mmoire EEPROM pour les modles 16F873/4, et dune zone de 256 octets pour les modles 16F876/7. Ladresse relative de laccs EEPROM est donc compris entre 0x00 et 0xFF, ce qui permet de nutiliser quun registre de 8 bits pour dfinir cette adresse, tout comme sur le 16F84. 12.1 Le registre EECON1 b7 b6 b5 b4 b3 b2 b1 b0 : : : : : : : : EEPGD N.U. N.U. N.U. WRERR WREN WR RD : : : : : : : : EEprom ProGram/Data select bit Non Utilis Non Utilis Non Utilis WRite ERRor flag bit WRite ENable WRite control bit ReaD control bit

Le bit EEPGD permet de slectionner si on dsire crire en mmoire EEPROM ou en mmoire FLASH. Et oui, cest possible au niveau du 16F87x, nous verrons comment dans le chapitre suivant. Il sagit donc dun nouveau bit, qui nexistait pas sur le EECON1 du 16F84. Par contre, nous constatons la disparition du bit EEIF (le flag dinterruption de fin dcriture EEPROM). Celui-ci a tout simplement migr vers le registre PIR2, vous vous en souvenez peut-tre. Les autres bits demeurent inchangs, avec pour rappel :

123

WRERR qui indique une interruption anormale du cycle dcriture (par exemple, un reset) WREN qui permet dautoriser les oprations dcriture (sorte de scurit pour viter les critures accidentelles) WR qui permet de lancer le cycle dcriture RD qui permet de lancer le cycle de lecture Passons maintenant aux applications concrtes :

12.2 Laccs en lecture Commenons par examiner la procdure de lecture, via la macro concerne :
REEPROM macro clrwdt bcf STATUS,RP0 bsf STATUS,RP1 movwf EEADR bsf STATUS,RP0 bcf EECON1,EEPGD bsf EECON1,RD bcf STATUS,RP0 movf EEDATA,w bcf STATUS,RP1 endm ; lire eeprom(adresse & rsultat en w) ; reset watchdog ; passer en banque2 ; ; ; ; ; ; ; pointer sur adresse eeprom passer en banque3 pointer sur eeprom ordre de lecture passer en banque2 charger valeur lue passer en banque0

La procdure est extrmement simple. Elle consiste tout simplement crire ladresse relative EEPROM concerne dans le registre EEADR, de positionner le bit RD, et de rcuprer la valeur lue qui se trouve alors dans le registre EEDATA. Pour cette macro, ladresse de lecture est passe dans le registre W , la valeur lue est galement retourne en W. Nous pouvons donc appeler cette macro par une portion de code comme ceci :
movlw REEPROM movwf adreseeprom ; charger ladresse de lecture dans W ; lire EEPROM madata ; sauver valeur lue

Rien donc de particulier ou de neuf dans cette procdure. La seule chose se souvenir, cest que lemplacement physique rel de lEEPROM commence ladresse 0x2100, alors que ladresse relative que nous devons employer dans notre programme commence, elle, la valeur 0x00. Autrement dit, lorsque vous accdez ladresse EEPROM 0x01 dans votre programme, la donne se trouvera physiquement ladresse 0x2101 de votre PIC. Notez que linstruction clrwdt nest pas vraiment ncessaire, mais est utile lors de lappel successif de la macro.

124

12.3 Laccs en criture Procdons comme pour la lecture, en examinons la macro de notre fichier maquette.
WEEPROM macro addwrite LOCAL loop bcf STATUS,RP0 bsf STATUS,RP1 movwf EEDATA movlw addwrite movwf EEADR bsf STATUS,RP0 bcf EECON1 , EEPGD bsf EECON1 , WREN bcf INTCON , GIE movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1 , WR bsf INTCON , GIE loop clrwdt btfsc EECON1 , WR goto loop bcf EECON1 , WREN bcf STATUS , RP0 bcf STATUS , RP1 endm ; la donne se trouve dans W ; passer en banque2 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; placer data dans registre charger adresse d'criture placer dans registre passer en banque3 pointer sur mmoire data autoriser accs criture interdire interruptions charger 0x55 envoyer commande charger 0xAA envoyer commande lancer cycle d'criture rautoriser interruptions effacer watchdog tester si criture termine non, attendre verrouiller prochaine criture passer en banque0

Ici, nous aurons 2 paramtres passer la macro, savoir la donne crire, et ladresse laquelle on doit crire cette donne. Comme nous navons quun seul registre W, nous choisirons donc dajouter un argument cette macro, argument appel addwrite pour adresse dcriture. La valeur est donc passe dans W, ladresse est donne en argument. La procdure nest gure complique : on commence par mettre la donne crire dans EEDATA et ladresse dcriture dans EEADR. Ensuite, on met en service les critures sur la mmoire eeprom. On interdit ensuite les interruptions (recommand par Microchip), et on excute btement la squence de lancement de lcriture. On peut alors rautoriser les interruptions. Il nous faut ensuite attendre la fin de lcriture, aprs quoi on peut de nouveau remettre le verrou en interdisant de nouveau les critures. Il faut cependant tre attentif et noter quelques particularits de notre macro. En premier lieu, vous constatez que notre macro replace le bit GIE 1 afin de rautoriser les interruptions prcdemment interdites. Il va de soi que sil nest pas souhaitable que ce bit soit positionn dans votre programme, il vous faudra alors supprimer les lignes concernant GIE dans la macro.

125

Ensuite, la boucle dattente deffacement de WR (qui repasse 0 une fois lcriture termine) peut tre remplace par une mise en sommeil du PIC. Vous savez en effet quil est possible de rveiller le PIC sur le positionnement dun flag dinterruption. Le flag concern (EEIF) permet donc, en fin dcriture, de rveiller le PIC. Vous pouvez galement vous dire quil est dommage dattendre la fin de lcriture avant de poursuivre votre programme. En effet, si vous navez quune seule valeur crire, rien ne vous interdit dexcuter des instructions durant que le PIC effectue lcriture EEPROM en interne. Ce que vous ne pouvez pas faire, cest procder une nouvelle criture avant que la prcdente ne soit termine. Vous pouvez alors imaginer, condition de laisser en permanence le bit WREN positionn, de tester WR AVANT de procder lcriture. Ainsi, la premire criture nattend pas, la seconde attendra que la premire soit termine avant dcrire, mais nattendra pas que la seconde soit acheve avant de sortir. Il suffit alors de dplacer la boucle avant la squence impose pour lcriture. Enfin, noubliez pas que vous pouvez utiliser les interruptions pour tre prvenu de la fin de lcriture, et donc de la possibilit dcrire loctet suivant. Je vous rappelle que la dure de vie minimale garantie de leeprom est de 100.000 cycles dcriture par adresse. A titre dinformation, lcriture dun octet en mmoire eeprom ncessite une dure de lordre de 4ms. Avec un 16F876 cadenc 20MHz, cela reprsente pas moins de 20.000 cycles dinstruction. Vous voyez donc que les critures en EEPROM ncessitent normment de temps lchelle du PIC. La petite boucle qui semble anodine, en place de lutilisation des interruptions, vous fait donc perdre 20.000 cycles (et tout a en 3 lignes). Notez que le clrwdt nest en ralit pas ncessaire, puisque lcriture ne prend quau maxium 4ms, mais est utile dans le cas dexcutions successives de cette mme macro. Ca ne change du reste rien au temps perdu dans la boucle dattente. 12.4 Initialisation dune zone eeprom Nous lavions dj vu, mais pour complter ce chapitre, je vais le rappeler. La directive pour dclarer des valeurs en EEPROM est DE . Notez que ces valeurs seront alors crites en EEPROM directement au moment de la programmation. Ne pas oubliez dans ce cas de prcder la directive DE de la directive ORG pointant sur un emplacement EEPROM valide (de 0x2100 0x21FF). Exemple :
ORG 0x2100 DE 2,4,8,2 DE A ; zone eeprom ; mettre 2 dans 0x2100, 4 dans 0x2101 etc. ; mettre A (0x41) en 0x2104

126

13. Les accs la mmoire programme


13.1 Gnralits Voici une possibilit intressante qui nexiste pas sur notre bon vieux 16F84 : la possibilit dcrire des donnes dans la mmoire de programme, cest--dire en mmoire FLASH. Nous allons voir que les procdures sont fort semblables aux accs en mmoire eeprom. Les registres utiliss pour ces premiers le sont galement ici. Seulement, il faut que vous vous rappeliez que la mmoire de programme est constitue, non pas doctets, mais de mots de 14 bits. Donc, lcriture et la lecture concerne des mots de 14 bits. Vous vous rendez bien compte quun mot de 14 bits ne peut pas tenir dans un registre de 8 bits. Donc les donnes devront tre passes par le biais de 2 registres. Si, maintenant, nous raisonnons sur ladresse concerne par lopration de lecture ou dcriture, nous voyons trs bien galement que cette adresse ne pourra tenir dans un seul registre. En effet, la zone mmoire se caractrise par une adresse construite sur 13 bits. Ceci nous impose donc de nouveau 2 registres pour la dfinition de ladresse. Forts de ce qui prcde, nous pouvons alors en conclure que nous aurons besoin de 2 registres supplmentaires, ajouts aux 4 registres utiliss pour les accs EEPROM. Nous aurons donc pour ladressage, les registres EEADR et EEADRH. Vous comprenez immdiatement (je lespre) que le registre EEADRH contient les bits de poids fort (Hight) de ladresse daccs. De mme, nous aurons pour les donnes, les registres EEDATA et EEDATH. Ce dernier, galement, contient les bits de poids fort de la donne lue ou crire. Attention de bien vous souvenir que les donnes en mmoire programme sont des donnes de 14 bits (forcment la mme longueur que les instructions), alors que les adresses sont codes sur 13 bits. EEADRH contiendra donc au maximum 5 bits utiles, alors que EEDATH en contiendra 6. J espre que jusqu prsent, cest clair. Si a ne ltait pas, relisez calmement ce qui prcde. 13.2 Les accs en lecture Commenons par le plus simple, une lecture de donne dans la mmoire programme. Voyons donc le code suivant :

127

bcf bsf movlw movwf movlw movwf bsf bsf bsf bcf nop movf xxxx movf xxxx bcf

STATUS,RP0 STATUS,RP1 high adresse EEADRH low adresse EEADR STATUS,RP0 EECON1,EEPGD EECON1,RD STATUS,RP0 EEDATA,w EEDATH,w STATUS,RP1

; passer en banque 2 ; ; ; ; ; ; ; ; ; ; ; ; ; ; charger octet fort adresse placer dans pointeur adresse haut charger octet faible adresse placer dans pointeur adresse bas passer banque 3 pointer sur mmoire flash lancer la lecture passer banque2 2 cycles ncessaires charger 8 lsb sauver ou utiliser 8 lsb charger 6 msb sauver ou utiliser 6 msb passer banque0

Si vous regardez attentivement, vous voyez, dans cette portion de code, 2 directives. Il sagit de high et de low . Leur utilisation est trs simple : high signale MPASM quil doit prendre loctet de poids fort de la donne sur 16 bits place derrire. low , fort logiquement, indique quil faut prendre loctet de poids faible. Prenons un exemple concret avec la portion de code suivante :
ORG 0x1F05 label movlw low label movlw high label

; on charge donc low 0x1F05, donc on charge 0x05 ; on charge donc high 0x1F05, donc on charge 0x1F

Ce nest pas plus compliqu que a. Examinons maintenant la structure de notre lecture en mmoire FLASH. On commence par initialiser les 2 pointeurs dadresse, en utilisant les 2 directives prcdentes. adresse reprsente dans ce cas une tiquette place dans notre zone de donnes (nous verrons un exemple concret). Ensuite, on signale quon travaille en mmoire FLASH, et non en mmoire EEPROM, en positionnant le bit EEPGD, puis on lance la lecture. Le PIC va mettre 2 cycles pour lire la donne en FLASH. Durant ces 2 cycles, Microchip suggre dutiliser 2 instructions nop . Jai cependant profit du fait que je devais changer de banque pour utiliser utilement un de ces 2 cycles. Ne reste donc quun nop . Notez que durant ces 2 cycles, certains registres, comme EEADR sont inaccessibles. Prudence donc si vous tentez dutiliser lautre cycle pour raliser une opration. A vous de tester, comme je lai fait. Donc, aprs ces 2 cycles, nous pouvons rcuprer les 14 bits de la donne, dans les registres EEDATA et EEDATH et en faire lusage que nous voulons. Notez que rien ne vous empche de nutiliser que le registre EEDATA si vous nutilisez que 8 bits. Une fois de plus, la routine ne prsente aucune difficult particulire. Voyons maintenant lcriture.

128

13.3 Les accs en criture La procdure dcriture ne prsente pas non plus de difficult particulire, il convient simplement dtre attentif certains points prcis. Voyons donc cette portion de code :
bcf bsf movlw movwf movlw movwf movlw movwf movlw movwf bsf bsf bsf bcf movlw movwf movlw movwf bsf nop nop bsf bcf STATUS,RP0 STATUS,RP1 high adresse EEADRH low adresse EEADR datahaute EEDATH databasse EEDATA STATUS,RP0 EECON1,EEPGD EECON1,WREN INTCON,GIE 0x55 EECON2 0xAA EECON2 EECON1 , WR INTCON,GIE EECON1,WREN ; passer en banque 2 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; charger octet fort adresse placer dans pointeur adresse haut charger octet faible adresse placer dans pointeur adresse bas 6 bits hauts de la donne crire placer dans registre data haute 8 bits bas de la donne crire dans registre data basse passer banque 3 pointer sur mmoire programme autoriser les critures (verrou) interdire les interruptions charger 0x55 envoyer commande charger 0xAA envoyer commande lancer cycle d'criture 2 cycles inutilisables

; rautoriser les interruptions ; rinterdire les critures (verrou).

Vous constatez que, tout comme la procdure de lecture en mmoire programme est fort semblable celle de lecture en EEPROM, celle dcriture en mmoire programme est fort semblable celle dcriture en EEPROM. Nous commenons, en effet, par placer ladresse dans les 2 pointeurs (souvenez-vous, 13 bits). Ensuite, nous plaons la donne dans les 2 registres prvus. Notez que rien ne vous oblige dutiliser les 14 bits, si vous souhaitez mmoriser des octets, il vous suffit de ne pas utiliser EEDATH. Il nous faut maintenant indiquer que nous travaillons en mmoire programme, en positionnant EEPGD. Vient, une fois de plus, la procdure standard dcriture impose par Microchip. Comme dans le cas prcdent, cette procdure utilise des valeurs placer dans le registre fictif EECON2. Le Pic va alors sarrter, le temps deffectuer lcriture dans la mmoire programme. Lorsquil se rveillera, entre 4 et 8 ms plus tard, les 2 instructions suivantes ne seront pas excuts. Celui-ci va continuer avancer dans le droulement du programme, mais ninterprtera pas les 2 commandes suivantes. Je vous conseille donc (tout comme Microchip) de mettre 2 nop cet endroit. Ce temps est ncessaire au PIC pour crire les donnes en mmoire programme.
129

Vous pouvez maintenant, si vous le voulez, rautoriser les interruptions, puis remettre le verrou de protection des critures. 13.4 Particularits et mise en garde Vous constatez donc que les mthodes daccs sont fort semblables pour les accs en EEPROM et en FLASH. Il importe toutefois de bien cerner quelques diffrences notables. 1) Les accs en EEPROM sont limits 256 emplacements, alors quen mmoire programme, vous avez accs lintgralit des 2Kmots de la mmoire. Vous pouvez donc crer, par exemple, de plus gros tableaux. 2) Les donnes stockes en EEPROM ont une taille limite 8 bits, alors que les donnes stockes en mmoire programme ont une taille maximale de 14 bits. 3) Vous ne pouvez crire dans la mmoire programme que si vous avez prcis _WRT_ENABLE_ON dans votre directive de configuration. Forts de tout ceci, vous allez me dire maintenant alors je vais utiliser toujours les critures en mmoire programme, puisquil ny a que des avantages . Malheureusement, il y a un inconvnient, et il est de taille : La dure de vie garantie de la mmoire FLASH est seulement de 1000 cycles dcriture, contre 100.000 pour la mmoire EEPROM. Donc, vous voyez quune utilisation trop intensive de ce type dcriture peut mener trs rapidement la destruction du PIC. Si vous vous trompez dans la ralisation de votre programme, par exemple, et que votre routine dcriture en mmoire programme tourne en boucle continue, ceci provoquera la destruction thorique de votre flash au bout de 1000 boucles. Sachant quune procdure complte prend de lordre de 4ms 8ms, ceci nous donne une dure de vie de lordre de 4000 8000ms . Votre PIC serait donc dtruit aprs 8s maximum. Vous voyez donc quil faut tre extrmement prudent dans lutilisation de cette routine. Vous voyez donc lintrt du bit de verrou (WREN). Notez que les toutes dernires versions de 16F87xx ont vu la dure de vie de la mmoire flash augmente dun facteur 100. A vous de vrifier le moment venu si ncessaire. Voici, pour ma part comment jutilise ces diffrentes zones : En gnral, jutilise les donnes en mmoire programme pour les donnes fixes, cest-dire crites au moment de la programmation. Jvite donc dutiliser la routine dcriture en mmoire FLASH. Jutilise par contre rgulirement celle de lecture, qui nest pas destructive. Je place les paramtres qui sont susceptibles de modifications ventuelles en mmoire EEPROM, dans la mesure du possible.

130

Ceci explique que je vais raliser un exercice avec vous qui nutilise que la lecture en mmoire programme. Mais une fois la lecture comprise, lcriture lest aussi, il suffit de recopier la procdure standardise. 13.5 Initialisation dune zone en mmoire programme Vous allez me dire quil suffit dutiliser la directive DE . Malheureusement, cette directive ne permet que dcrire des donnes de la taille dun octet. Mais, en cherchant un peu dans les directives autorises, nous trouvons la directive DA , qui est lorigine prvue pour crire des chanes de caractres en mmoire programme. Elle saccommode fort bien de donnes de la taille de 14 bits. Un petit exemple vaut mieux quun long discours :
ORG DA DA 0x0200 0x1FF, 0x155 0xFFFF ; ; ; ; place 1FF en mmoire programme 0x200 et 0x155 en 0x201 devrait placer 0xFFFF en mmoire 0x202, mais, la taille dune case de mmoire programme tant limite 14 bits, la valeur relle sera de 0x3FFF

13.6 La technique du bootloader . Je nentrerai pas ici dans les dtails, puisque jutiliserai cette possibilit dans la troisime partie du cours. Je vous explique simplement en quoi consiste le raisonnement. On part du principe que le PIC peut crire dans la mmoire programme. On place donc une portion de code en mmoire haute, qui va communiquer avec un priphrique extrieur afin de recevoir un programme excuter (ce peut tre un PC, une EPROM externe, etc.). Ce programme sera charg par la routine du PIC, et plac en zone dexcution. Ainsi, le PIC se sera programm tout seul, sans lintervention dun programmateur. La seule limite est quil faut pralablement charger le programme dcriture (Bootloader) laide dun programmateur classique, et lactiver au moment opportun. Jy reviendrai, car cela autorise de nouvelles perspectives. 13.6 Application pratique : un chenillard Voici qui clture la thorie sur ce sujet. Mais je ne vais pas vous laisser l. Nous allons donc, si vous voulez bien, raliser un exemple pratique. Et, pour faire quelque chose doriginal, qui diriez-vous dun petit jeu de lumire, style chenillard ? Je vous donne le schma utilis. Notez que ce chenillard ne ncessitera que 8 LEDs en plus de notre PIC. Je vous donnerai cependant un petit schma qui vous permettra, si vous le voulez, de piloter directement des spots 220V.

131

Voyons dabord le chenillard LEDs :

Vous voyez que cest on ne peut plus simple, et ne vous ruinera pas. Pour ceux qui dsirent utiliser des lampes 220V, voici le schma que je leur propose.

ATTENTION DANS CE CAS : LES TENSION MISES EN JEU PEUVENT ETRE MORTELLES. IL EST DE CE FAIT HORS DE QUESTION DE REALISER CE MONTAGE SUR UNE PLATINE DESSAIS. UN CIRCUIT IMPRIME DEVRA ETRE REALISE, MIS EN BOITIER, ET LES MESURES DE SECURITE CORRECTES (TERRES, FUSIBLES, CEM) DEVRONT ETRE PRISES. Donc, si vous ntes pas un habitu des montages lectroniques, ignorez ce schma, il y va de votre scurit. Ralisez la version LEDs.

132

Afin davoir une ide de ce que nous avons faire, nous allons crer un pseudo-code . Ceci prsente lavantage de pouvoir dfinir les diffrentes fonctions dont nous aurons besoin, et la faon donc elles vont sarticuler. Un pseudo-code nest jamais que la description en franais de ce que notre programme va devoir raliser. Jaurais pu utiliser un ordinogramme, mais il faut varier les plaisirs, et satisfaire tout le monde. Jai souvent remarqu que les partisans du pseudo-code taient de farouches adversaires des ordinogrammes, et inversement. Pour ma part, je ne vois pas en quoi lutilisation de lun empche lutilisation de lautre. Tout est histoire de felling et dpend galement de lapplication en elle-mme. Certains programmes se laissant mettre plus volontiers dans une forme que dans une autre. Nous allons devoir lire des valeurs successives dans la mmoire programme, et envoyer ces valeurs sur le PORTB, afin dallumer les LEDS concernes. Bien entendu, afin de rester une chelle de temps humaine , nous allons devoir temporiser entre 2 allumages successifs. Dans le cas contraire, nous naurions le temps de rien voir. Nous allons donc imaginer que nous allumerons les LEDs une vitesse de lordre de 5 allumages par seconde. Ceci nous donnera donc un temps dattente de lordre de 200ms. Voyons donc ce que nous donne notre pseudo-code :
Dbut Initialisation du systme Boucle Je lis 1 octet en FLASH Je lenvoie sur le PORTB Jattends 200ms Je recommence la boucle

Vous voyez, a prsente lavantage dtre clair. Nous voyons donc que nous aurons besoin dune routine pour lire en FLASH, dune routine de temporisation, quant lenvoi sur le PORTB, il ne ncessite quune instruction. Il nous faut cependant rflchir la routine de lecture dun octet en flash. Nous allons devoir pointer sur le dbut de notre zone de donnes, et, une fois toutes les donnes envoyes, nous devrons recommencer. Il nous faut donc dterminer quand nous arrivons au bout de la zone de donnes. On pourrait indiquer la longueur totale, et compter les octets, mais ceci ncessitera dutiliser un compteur de 16 bits, pour le cas o nous dciderions dentrer plus de 256 donnes. Il serait ingnieux de penser que nous avons 8 LEDs piloter, et que nos donnes comportent 14 bits. Il nous reste donc 6 bits qui ne sont pas utiliss. Pourquoi dans ce cas ne pas dcider quun des bits positionn 1 indique que la dernire valeur est atteinte ? Nous choisirons par exemple le premier bit libre, savoir le bit 8.

133

Notre sous-routine pourra alors tre du type :


Lire en Flash Lire un mot en flash Incrmenter pointeur FLASH pour prochaine lecture Bit 8 = 1 ? Si oui, pointer sur premire donne en flash Retour

Reste notre routine de temporisation, et, pour savoir si la temporisation est termine, je propose de se servir de linterruption timer0, qui positionnera un flag pour le signaler. Sachant que nous voulons une valeur approximative de 200ms, nous utiliserons un prdiviseur de 256, ce qui nous donne un temps maxi de (256*256)/5000000, soit 13,1 ms. Pour obtenir nos 200ms, nous devrons donc passer approximativement 15 fois dans notre routine dinterruption, avant de signaler que le temps est coul.
Interruption timer0 Dcrmenter compteur Compteur = 0 ? Oui, positionner flag tmp Et recharger compteur avec la valeur 15 Retour dinterruption

Notre sous-routine de temporisation devient alors tout simplement :


Tempo Flag tmp positionn ? Non, boucler sur tempo Oui, effacer flag tmp et retour

Vous voyez donc que nous avons de la sorte ralis notre programme. Il ne reste plus qu remplacer les phrases en franais par des instructions. Nous allons donc maintenant nous intresser au programme en langage dassemblage. Commencez, comme dhabitude, par effectuer un copier/coller de votre fichier m16F876.asm . Renommez la copie en lum1.asm . Crez un projet et chargez ce fichier. Crons tout dabord notre zone de commentaires, comme je vous le rpte sans arrt (oui, je sais, je suis casse-bonbons ce niveau).
;***************************************************************************** ; Ralisation d'un mini-chenillard 8 LEDs * ; * ;***************************************************************************** ; * ; NOM: Lum1 * ; Date: 23/04/2002 * ; Version: 1.0 * ; Circuit: Circuit maquette (ou C.I. si lampes 220V) * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc *

134

; lumdat.inc * ; * ;***************************************************************************** ; * ; Notes: les 8 sorties sont sur le PORTB. Un niveau haut allume la LED * ; correspondante. * ; Exercice sur les lectures en mmoire FLASH * ; La frquence du quartz est de 20MHz * ; * ;*****************************************************************************

Maintenant, intressons-nous la configuration, ainsi qu lhabituel include . Jai dcid de mettre le watchdog en service, le reset de la configuration tant des plus classiques :
LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_ON & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ; _BODEN_OFF ;_PWRTE_ON ;_WDT_ON ;_HS_OSC Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension hors service Dmarrage temporis Watchdog en service Oscillateur haute vitesse (20Mhz)

Ensuite, nous avons les valeurs qui seront places dans les diffrents registres linitialisation. Il suffit de se rappeler que nous allons travailler avec le PORTB en sortie (donc, inutile de mettre les rsistance internes en service), et que nous avons besoin dune interruption sur le timer0, avec un prdiviseur de 256. Nous neffacerons pas tous les registres inutiliss, car je compte rutiliser ce programme plus loin dans le cours, afin de lamliorer avec de nouvelles fonctions. Effacez donc seulement les assignations pour PIE2, et celle concernant le PORTC.
;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQU B'10000111' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; PSA b3 : 0= Assignation prdiviseur sur Tmr0 ; PS2/PS0 b2/b0 : valeur du prdiviseur ; 111 = 1/256 ; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQU B'00100000' ; GIE b7 : masque autorisation gnrale interrupt ; PEIE b6 : masque autorisation gnrale priphriques ; T0IE b5 : masque interruption tmr0 ; INTE b4 : masque interuption RB0/Int

135

; ; ; ;

RBIE T0IF INTF RBIF

b3 b2 b1 b0

: : : :

masque interruption RB4/RB7 flag tmr0 flag RB0/Int flag interruption RB4/RB7

; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQU B'00000000' ; PSPIE b7 : Toujours 0 sur PIC 16F786 ; ADIE b6 : masque interrupt convertisseur A/D ; RCIE b5 : masque interrupt rception USART ; TXIE b4 : masque interrupt transmission USART ; SSPIE b3 : masque interrupt port srie synchrone ; CCP1IE b2 : masque interrupt CCP1 ; TMR2IE b1 : masque interrupt TMR2 = PR2 ; TMR1IE b0 : masque interrupt dbordement tmr1 ; REGISTRE ADCON1 (ANALOGIQUE/DIGITAL) ; -----------------------------------ADCON1VAL EQU B'00000110' ; PORTA en mode digital ; DIRECTION DES PORTS I/O ; ----------------------DIRPORTA EQU DIRPORTB EQU B'00111111' ; Direction PORTA (1=entre) B'00000000' ; Direction PORTB

Ensuite, les assignations du programme. Pour linstant, nous savons dj que nous avons une constante, savoir les 15 passages dans linterruption du timer0 pour atteindre nos 200ms. Dfinissons donc cette constante qui va servir recharger le compteur (recharge en anglais se dit reload ) . Jutilise souvent des noms de variables ou de fonctions en anglais, car langlais est plus court et ne comporte pas daccents.
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** RELOAD EQU D'15' ; nombre de passages dans tmr0

Prenez garde, une fois de plus, de ne pas tomber dans le pige grossier qui consisterait oublier le D . Dans ce cas, vous auriez 0x15 passages, soit D21 passages. Je suis certain que certains taient prts tomber dans le panneau. Je vous rappelle que la meilleure mthode est de toujours indiquer le systme avant la valeur. Ainsi, on ne se trompe jamais (enfin, quand je dis jamais , joublie peut-tre de dire que a mest dj arriv moi aussi). Nous navons pas encore de define pour linstant, laissons la zone vide, quant aux macros, nous ne conserverons que celles concernant les banques.
;***************************************************************************** ; DEFINE * ;***************************************************************************** ;***************************************************************************** ; MACRO * ;*****************************************************************************

136

; Changement de banques ; ---------------------BANK0 macro bcf STATUS,RP0 bcf STATUS,RP1 endm BANK1 macro bsf STATUS,RP0 bcf STATUS,RP1 endm BANK2 macro bcf STATUS,RP0 bsf STATUS,RP1 endm BANK3 macro bsf STATUS,RP0 bsf STATUS,RP1 endm ; passer en banque0;

; passer en banque1

; passer en banque2

; passer en banque3

Ce que nous pouvons dire sur nos zones de variables, aprs avoir effac les exemples, cest que nous avons besoin, priori, dune variable pour compter les passages dans le timer0. Par contre, notre programme aura une taille de moins de 2K, inutile donc de sauvegarder PCLATH. Inutile galement de sauvegarder FSR, nous nutiliserons pas ladressage indirect dans notre routine dinterruption toute simple. Ceci nous donne :
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 cmpt : 1 ENDC ; Dbut de la zone (0x20 0x6F) ; compteur de passages dans tmr0 ; Fin de la zone

;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

Nous naurons nul besoin de la zone RAM en banques 1,2, et 3. Donc, nous effaons ce qui les concerne. Si, se stade, nous lanons lassemblage, nous aurons videmment des erreurs relatives aux assignations que nous avons supprimes. Il ne suffit pas deffacer les assignations (ce qui ne nous fait rien gagner en taille du programme), il faut surtout effacer les instructions qui y font rfrence.
137

Donc, tout dabord, dans la routine dinterruption principale, nous allons effacer la sauvegarde et la restauration de PCLATH et de FSR. Profitons-en pour supprimer les tests dinterruptions qui ne nous seront pas utiles. Nous navons besoin ici que de linterruption tmr0. Cependant, je compte dans un futur exercice, rutiliser ce fichier. Je vous demande donc de conserver galement linterruption concernant le convertisseur A/D. Aprs un minimum de rflexion, et en vous rappelant quil est inutile, comme notre routine est conue, de tester la dernire interruption (si ce nest aucune autre, cest donc celleci), vous devriez arriver au rsultat suivant :
;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp BANK0 ; passer en banque0 ; Interruption TMR0 ; ----------------btfsc INTCON,T0IE ; tester si interrupt timer autorise btfss INTCON,T0IF ; oui, tester si interrupt timer en cours goto intsw1 ; non test suivant call inttmr0 ; oui, traiter interrupt tmr0 bcf INTCON,T0IF ; effacer flag interrupt tmr0 goto restorereg ; et fin d'interruption ; Interruption convertisseur A/D ; -----------------------------intad PIR1,ADIF ; traiter interrupt ; effacer flag interupt

intsw1 call bcf

;restaurer registres ;------------------restorereg swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W ; sans modifier Z swapf w_temp,w ; Rinversion de L et H dans W ; W restaur sans modifier status retfie ; return from interrupt ;***************************************************************************** ; INTERRUPTION TIMER 0 * ;***************************************************************************** inttmr0 return ; fin d'interruption timer ;***************************************************************************** ; INTERRUPTION CONVERTISSEUR A/D * ;***************************************************************************** intad

138

return

; fin d'interruption

Passons linitialisation, et supprimons ce dont nous naurons nul besoin, comme linitialisation du PORTC (conservez le PORTA, nous en aurons besoin ultrieurement) ou encore linitialisation de PIE2. Nous pouvons galement supprimer leffacement de la RAM en banque0, nous nutilisons dailleurs que peu de variables. Profitons-en pour initialiser notre unique variable actuelle avec la valeur de rechargement. Voici ce que vous devriez obtenir :
;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------BANK0 ; slectionner banque0 clrf PORTA ; Sorties PORTA 0 clrf PORTB ; sorties PORTB 0 bsf STATUS,RP0 ; passer en banque1 movlw ADCON1VAL ; PORTA en mode digital/analogique movwf ADCON1 ; criture dans contrle A/D movlw DIRPORTA ; Direction PORTA movwf TRISA ; criture dans registre direction movlw DIRPORTB ; Direction PORTB movwf TRISB ; criture dans registre direction ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1

movlw movwf movlw movwf

; initialiser variables ; --------------------movlw RELOAD ; valeur de rechargement movwf cmpt ; dans compteur de passage tmr0 ; autoriser interruptions (banque 0) ; ---------------------------------PIR1 ; effacer flags 1 INTCON,GIE ; valider interruptions start ; programme principal

clrf bsf goto

Passons maintenant aux choses srieuses, en commenant par laborer notre programme. Reprenons donc notre pseudo-code, et tout dabord la routine principale. Tout vous semble correct ? Nous en reparlerons.
Boucle Je lis 1 octet en FLASH Je lenvoie sur le PORTB Jattends 200ms

139

Je retourne en Boucle

Il est trs simple de convertir ceci en langage dassemblage.


;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start clrwdt call readflash movwf PORTB call tempo goto start END ; ; ; ; ; ; effacer watch dog lire un octet en flash le mettre sur le PORTB (allumage LEDs) attendre 200ms boucler directive fin de programme

Voyons maintenant notre sous-routine de temporisation, pour cela, reprenons notre pseudo-code :
Tempo Flag tmp positionn ? Non, boucler sur tempo Oui, effacer flag et retour

Nous avons besoin dun flag, cest--dire dune variable boolenne, ou encore, dun bit dans un octet. Il nous faut donc dfinir cette variable. Nous choisirons donc un octet en tant que flags . Cet octet contient 8 bits, donc 8 variables boolennes potentielles. Nous dfinirons le bit 0 comme notre flag boolen, que nous appellerons flagtempo . Notre zone de variables en banque 0 devient donc :
CBLOCK 0x20 cmpt : 1 flags : 1 ENDC ; ; ; ; ; Dbut de la zone (0x20 0x6F) compteur de passages dans tmr0 flags divers b0 : flag de temporisation Fin de la zone

#DEFINE flagtempo flags,0 ; flag de temporisation

Le define , est, comme dhabitude, destin nous faciliter les critures. Quand nous ajoutons une variable, le premier rflexe doit consister soccuper de son ventuelle initialisation. Au dpart, nous choisissons donc deffacer lensemble des flags.
; initialiser variables ; --------------------movlw RELOAD ; valeur de rechargement movwf cmpt ; dans compteur de passage tmr0 clrf flags ; effacer les flags

Nous pouvons donc maintenant crire notre routine de temporisation, que nous plaons, suivant notre habitude, entre la routine dintialisation et le programme principal :
;***************************************************************************** ; TEMPORISATION * ;***************************************************************************** ;-----------------------------------------------------------------------------

140

; Attend que le flag soit positionn par la routine d'interruption tmr0 ;----------------------------------------------------------------------------tempo btfss flagtempo ; tester si flag tempo positionn goto tempo ; non, attendre bcf flagtempo ; oui, effacer le flag return ; et retour

Bien entendu, pour que le flag puisse tre positionn, il nous faut crire notre routine dinterruption tmr0 partir de notre pseudo-code :
Interruption timer0 Dcrmenter compteur Compteur = 0 ? Oui, positionner flag tmp Et recharger compteur avec la valeur 15 Retour dinterruption

De nouveau, rien de plus simple, toutes les variables utiles ont dj t dfinies.
;***************************************************************************** ; INTERRUPTION TIMER 0 * ;***************************************************************************** inttmr0 decfsz cmpt,f ; dcrmenter compteur de passage return ; pas 0, rien d'autre faire bsf flagtempo ; si 0, positionner flag de temporisation movlw RELOAD ; valeur de rechargement movwf cmpt ; recharger compteur return ; fin d'interruption timer

Voici donc notre temporisation oprationnelle. Ne reste plus que la lecture des donnes en mmoire programme. Donnes, vous avez dit ? Mais oui, bien sr, il faut dabord les y mettre, nos donnes. Souvenez-vous que linitialisation des donnes en mmoire programme peut se faire avec la directive DA . Mais je vais en profiter pour utiliser la technique des include , ainsi, nous ferons dune pierre deux coups . Afin dviter de traner toutes les valeurs dallumage de nos LEDs dans le programme principal, et galement afin de vous viter de devoir tout recopier, nous allons crer un fichier spar contenant nos donnes. Commencez donc par aller dans le menu file -> new . une fentre intitule untitled (a ne sinvente pas) souvre lcran. Allez dans le menu files -> save as et sauvez-la sous ne nom lumdat.inc dans le mme rpertoire que votre projet. Vous auriez pu galement choisir lextension .asm , ou encore .txt , mais prenez lhabitude dutiliser lextension .inc pour vos fichiers INClude , ceci vous permettra de mieux vous y retrouver. Ds que cest fait, le nouveau nom et le chemin apparaissent dans lintitul de la fentre.

141

Vous avez maintenant 2 fentres ouvertes dans lcran de MPLAB. Mais, souvenez-vous, ce nest pas parce que le fichier est ouvert lcran quil fait partie de notre projet. Nous allons donc linclure dans celui-ci en ajoutant tout simplement une directive include . dans notre fichier .asm .
LIST p=16F876 #include <p16F876.inc> #include <lumdat.inc> ; Dfinition de processeur ; fichier include ; donnes d'allumage

Mais bon, ce nest pas tout dinclure le fichier, encore faut-il savoir quelle adresse les donnes vont aller se loger. En tout tat de cause, il faut que ce soit aprs notre programme, et non en plein ladresse de dmarrage 0x00. Le plus simple est donc dimposer ladresse de chargement. Vous vous souvenez sans doute que cela seffectue laide de la directive ORG . Reste choisir ladresse de dpart de nos donnes, donc la valeur placer suite notre directive ORG . Evitons de perdre de la place, crivons-les directement la fin de notre programme. Et comment connatre ladresse de fin de notre programme ? Cest simple, il suffit dy placer une tiquette (attention, AVANT la directive END, sinon cette tiquette ne serait pas prise en compte).
start clrwdt call readflash movwf PORTB call tempo goto start mesdata END ; ; ; ; ; ; ; effacer watch dog lire un octet en flash le mettre sur le PORTB (allumage LEDs) attendre 200ms boucler emplacement de mes donnes (lumdat.inc) directive fin de programme

Maintenant, nous allons dans notre fentre lumdat.inc , et nous crons un en-tte, suivi de la directive ORG complte de ladresse de position de nos donnes, cest--dire mesdata .
;***************************************************************************** ; DONNNEES D'ALLUMAGE * ;***************************************************************************** ;----------------------------------------------------------------------------; un niveau "1" provoque l'allumage de la sortie correspondante ;----------------------------------------------------------------------------ORG mesdata ; dbut des data en fin de programme

A ce stade, nous allons inscrire des donnes dans notre fichier .inc , en nous souvenant que la dernire donne aura son bit numro 8 positionn 1, ce qui indiquera notre programme la fin de la zone. Ecrivons donc en nous souvenant quun 1 allume la LED correspondante, et quun 0 lteint. Ajoutons donc quelque donnes, et pourquoi pas :
DA B'00000001' ; valeurs d'allumage DA B'00000010' 142

DA DA DA DA DA DA DA DA DA DA DA DA

B'00000100' B'00001000' B'00010000' B'00100000' B'01000000' B'10000000' B'01000000' B'00100000' B'00010000' B'00001000' B'00000100' B'00000010'|B'100000000'

; dernire valeur, on force b8

Notez quon aurait pu simplement crire la dernire ligne de la faon suivante :


DA B100000010 ; dernire valeur avec b8 1

Ce qui tait strictement quivalent. Nous pouvons fermer notre fentre de donnes (ce nest pas parce quelle est ferme quelle nest plus utilise), ou, mieux , la minimiser, ce qui nous permettra de la rouvrir si nous voulons modifier les valeurs. Noubliez cependant pas auparavant de sauver en pressant <ctrl><s> ou en utilisant save all . Revenons-en nos moutons je voulais dire notre lecture des donnes :
Lire en Flash Lire un mot en flash Incrmenter pointeur FLASH pour prochaine lecture Bit 8 = 1 ? Si oui, pointer sur premire donne en flash Retour

En regardant cette sous-routine, nous voyons quon utilise un pointeur. Donc, il devra galement tre initialis. Ce pointeur, si vous vous rappelez, est un pointeur cod sur 2 octets, car laccs la totalit de la zone mmoire programme ncessite 13 bits. Ce pointeur est constitu, pour les accs en mmoire programme, des registres EEADR et EEADRH. Nous allons donc les initialiser de faon ce que la premire lecture corresponde la premire donne crite dans notre fichier lumdat.inc . Ajoutons donc nos lignes sur les initialisations des variables. Vous pensez a ?
; initialiser variables ; --------------------RELOAD ; valeur de rechargement cmpt ; dans compteur de passage tmr0 flags ; effacer les flags low mesdata ; adresse basse des data EEADR ; dans pointeur bas FLASH high mesdata ; adresse haute des data EEADRH ; dans pointeur haut FLASH

movlw movwf clrf movlw movwf movlw movwf

Cest bien davoir pens aux directive low et high que nous avons vues prcdemment. Rien redire ?
143

Malheureusement, dans ce cas, vous avez tout faux au niveau du rsultat final. Vous tes tomb dans le pige classique. Les banques, vous les avez oublies ? Et oui, EEADR et EEADRH sont en banque 2. Si donc, vous avez pensez :
; initialiser variables ; --------------------RELOAD ; valeur de rechargement cmpt ; dans compteur de passage tmr0 flags ; effacer les flags STATUS,RP1 ; passer en banque2 low mesdata ; adresse basse des data EEADR ; dans pointeur bas FLASH high mesdata ; adresse haute des data EEADRH ; dans pointeur haut FLASH STATUS,RP1 ; repasser en banque 0

movlw movwf clrf bsf movlw movwf movlw movwf bcf

alors je vous donne un bon point (passez me voir la rcr ). Mais alors, si rellement vous avez pens :
; initialiser variables ; --------------------STATUS,RP0 ; repasser en banque0 RELOAD ; valeur de rechargement cmpt ; dans compteur de passage tmr0 flags ; effacer les flags STATUS,RP1 ; passer en banque2 low mesdata ; adresse basse des data EEADR ; dans pointeur bas FLASH high mesdata ; adresse haute des data EEADRH ; dans pointeur haut FLASH STATUS,RP1 ; repasser en banque 0

bcf movlw movwf clrf bsf movlw movwf movlw movwf bcf

Alors je vous dit BRAVO . En effet, il fallait penser que notre routine dinitialisation des variables tait fausse, puisque nous avions oubli de repasser en banque 0, les initialisations prcdentes concernant la banque 1 (et en plus, ctait not). Donc, rappelez-vous de toujours bien rflchir et de garder lesprit ouvert. Il ne suffit pas de raliser btement tout ce que je vous dis sans rflchir plus loin. Dautant que jestime que les erreurs sont profitables, je vous en fais donc faire quelques-unes classiques volontairement. Il y en aura dautres, quon se le dise (et dans votre intrt, essayez de les trouver sans tricher ). Tout est maintenant prt pour lcriture de notre routine de lecture dun octet en mmoire programme. Essayez dcrire vous-mme cette routine.
;***************************************************************************** ; LIRE UN OCTET EN FLASH * ;***************************************************************************** ;----------------------------------------------------------------------------; Lit un octet en mmoire programme, puis pointe sur le suivant ; retourne l'octet lu dans w ; si le bit 8 du mot lu vaut 1, on pointe sur la premire donne ;----------------------------------------------------------------------------readflash BANK3 ; pointer sur banque3

144

bsf EECON1,EEPGD bsf EECON1,RD nop nop bcf STATUS,RP0 incf EEADR,f btfsc STATUS,Z incf EEADRH,f btfss EEDATH,0 goto readfsuite movlw low mesdata movwf EEADR movlw high mesdata movwf EEADRH readfsuite movf EEDATA,w bcf STATUS,RP1 return

; accs la mmoire programme ; lecture ; 2 cycles d'attente ; ; ; ; ; ; ; ; ; ; pointer sur banque2 incrmenter pointeur bas sur donnes tester si dbord oui, incrmenter pointeur haut tester bit 0 data haute = bit 8 donne si 0, sauter instructions suivantes si 1, adresse basse des data dans pointeur bas FLASH adresse haute des data dans pointeur haut FLASH

; charger 8 bits donne ; repointer sur banque0 ; et retour

Quelques points sont prendre en considration. En premier lieu, lincrmentation du pointeur seffectue bien sur 2 registres, EEADR et EEADRH. Noubliez pas que laccs la mmoire programme complte ncessite 13 bits. On incrmentera donc le registre de poids fort si le registre de poids faible a dbord , cest dire quil est pass de 0xFF 0x00. Si vous aviez pens utiliser pour ce faire linstruction :
btfsc STATUS,C ; tester si dbord

Lide ntait pas mauvaise en soi, malheureusement linstruction incf prcdente nagit pas sur le bit C . Cela naurait donc pas fonctionn. Ensuite, vous voyez que le bit 8 de notre donne correspond fort logiquement au bit 0 de EEDATH. Cest donc ce dernier que nous testons pour constater que nous sommes arrivs en fin de notre zone de donnes. Lancez lassemblage et placez le fichier obtenu dans votre PIC. Alimentez, et regardez : la LED connecte sur RB0 sallume et est la seule sallumer. quelque chose ne nest donc pas pass comme prvu. A votre avis, quel niveau ? Il suffit de raisonner. Il y a eu allumage de la premire LED, donc lecture de la premire valeur en mmoire programme. Si nous vrifions la fin de cette routine (puisque le dbut fonctionne), nous voyons que tout lair correct. On repasse bien en banque 0 avant de sortir, et linstruction return est bien prsente. Le problme provient sans doute dailleurs. En regardant le programme principal, nous voyons que celui-ci appelle la routine de temporisation. Cest probablement dans cette routine que quelque chose cloche . Regardez-la attentivement. Vous ne remarquez rien ? Non ? Et si je vous rappelle que cette routine attend durant 200ms, vous ne pensez toujours rien ? Alors je vous rappelle que nous avons mis le watchdog en service dans notre configuration.

145

Alors la, a doit faire tilt . En effet, le watchdog va vous provoquer un reset aprs une dure approximative de 18ms. Donc, bien avant que notre routine de temporisation soit termine. Il ny a donc aucune chance darriver jusqu lallumage suivant. Ajoutez donc une instruction de reset du watchdog :
tempo clrwdt btfss flagtempo goto tempo bcf flagtempo return ; ; ; ; ; effacer watchdog tester si flag tempo positionn non, attendre oui, effacer le flag et retour

Reprogrammez votre PIC, et contemplez leffet obtenu. Vous pouvez alors votre guise complter les squences dallumage en ajoutant des donnes dans votre fichier lumdat.inc . Noubliez pas que le seul bit 8 1 doit tre celui de votre dernire donne. Vous pouvez galement diminuer la valeur de RELOAD dans les assignations, de faon acclrer le dfilement. Une valeur de 4 devrait vous donner le look K2000 , pour les nostalgiques de la srie. A ce stade, vous pouvez tenter dinterpeller votre pouse. Devant votre air admiratif pour ce jeu de lumire intelligent , il est possible quelle vous gratifie dun regard compatissant. Nen esprez gure plus, cependant, la bont fminine a ses limites. Par contre, cest le moment de montrer le montage votre jeune fils ou votre petit frre. Pour sr, il va vouloir installer le montage dans votre voiture.

146

Notes :

147

Notes :

148

14. Le timer 0
14.1 Gnralits Nous avons dj parl du timer 0 dans le premir livre. Ce timer fonctionne de faon identique celui du PIC16F84. Inutile donc de revenir sur tout ce que nous avons dj vu, toutes les explications restent dapplication. Je vais seulement vous donner quelques complments dinformation, complments qui sappliquent tous les PICs de la gamme MID-RANGE. Remarquez pour commencer que le timer 0 utilise, pour ses incrmentations le registre de 8 bits TMR0. Ce registre contient une valeur indtermine la mise sous tension, si vous dsirez compter partir dune valeur prcise (0 ou autre), placer explicitement cette valeur dans votre programme : clrf TMR0 ; commencer le comptage partir de 0

Jai test sur quelques 16F876 en ma possession, et jai obtenu une valeur de dmarrage constante et gale 0xFF. Ceci ne peut cependant pas tre gnralis, il sagit au plus dun peu de curiosit de ma part. Ceci est valable pour la mise sous tension (reset de type P.O.R) ou pour la rcupration aprs une chute de tension (reset de type B.O.R). Par contre, pour les autres types de reset, le contenu de TMR0 restera inchang par rapport sa dernire valeur. Notez galement quil est impossible darrter le fonctionnement du timer. Une astuce que vous pouvez utiliser, si vous dsirez suspendre le comptage du temps (en mode timer), est de faire passer le timer0 en mode compteur. Ceci, bien entendu, condition que la pin RA4 ne soit pas affecte une autre utilisation. 14.2 Lcriture dans TMR0 Tout dabord, si nous nous trouvons en mode de fonctionnement timer du timer 0, nous pouvons tre amens crire une valeur (ou ajouter une valeur) dans le registre TMR0. Ceci, par exemple, pour raccourcir le temps de dbordement du timer avant ses 256 cycles classiques. Voyons donc ce qui se passe au moment de lcriture dune valeur dans TMR0. La premire chose quil va se passer, est que le contenu du prdiviseur, sil tait affect au timer 0 (souvenez-vous quil sert galement pour le watchdog) va tre remis 0. Attention, je parle du contenu du prdiviseur, pas de la valeur du prdiviseur (je vais expliquer en dtail). Ensuite, les 2 cycles dinstruction suivants seront perdus au niveau du comptage du timer. La mesure de temps reprendra donc partir du 3me cycle suivant. Il importe de comprendre que le prdiviseur fonctionne de la faon suivante :

149

Le prdiviseur compte jusque sa valeur programme par PS0/PS2. Une fois cette valeur atteinte, TMR0 est incrment Le prdiviseur recommence son comptage limpulsion suivante

Supposons par exemple que TRM0 contienne la valeur 0x15, que le prdiviseur soit slectionn sur une valeur de 8 affecte au timer 0. Supposons galement que nous en sommes 2 cycles dinstruction depuis la dernire incrmentation de TMR0. Il reste donc thoriquement 6 cycles dinstruction avant que TMR0 ne passe 0x16. Nous avons donc la situation suivante : Prdiviseur configur : 8 Contenu du prdiviseur : 2 Valeur de TMR0 : 0x15 Imaginons qu cet instant nous rencontrions la squence suivante :
movlw 0x25 movwf TMR0 ; prendre la valeur 0x25 ; dans TMR0

Nous obtenons pour la suite de notre programme :


Instruction suivante Instruction suivante Instruction suivante : TMR = 0x25, contenu prdiviseur = 0, prdiviseur 8. : Pas de comptage, contenu prdiviseur = 0 : redmarrage du comptage : contenu prdiviseur = 1

Vous voyez donc que non seulement nous avons perdu 2 cycles, mais quen plus nous avons perdus les instructions qui avaient t comptabilises dans notre prdiviseur. Lorsque vous crivez dans TMR0, il vous appartient, si ncessaire, de prendre en compte cette perte dinformations. 14.3 le timing en mode compteur Il y a quelques prcautions prendre lorsquon travaille avec le timer 0 en mode compteur. En effet, les lments compter sont, par dfinition, asynchrones avec lhorloge du PIC. Or la sortie du prdiviseur, et donc lincrmentation du registre TMR0 est, elle, synchronise sur le flanc montant de Q4 (Q4 tant le 4me clock de lhorloge du PIC). Dit plus simplement, on peut dire que la sortie du prdiviseur passe 1 au moment o la valeur de comptage est gale la valeur du prdiviseur choisie (8 dans lexemple prcdent). Une fois cette valeur dpasse, la sortie du prdiviseur repasse 0, jusquau multiple de 8 suivant (dans notre exemple). Mais lincrmentation de TMR0 se fait sur le test suivant : On a incrmentation si au moment du flanc montant de Q4, la sortie du prdiviseur est 1.

150

On voit alors tout de suite que si le signal est trop rapide, nous risquons de rater le dbordement du prdiviseur, celui-ci tant repass 0 avant davoir eu apparition du flanc montant de Q4. On peut donc en dduire que pour tre certain de ne rien rater, la sortie de notre prdiviseur devra tre maintenue au minimum le temps sparant 2 Q4 conscutifs. Donc, lquivalent de 4 Tosc (temps doscillateur), soit la dure dun cycle dinstruction. Nous aurons, de plus quelques limites dues llectronique interne. Ceci nous donne les rgles simples suivantes : Si on nutilise pas de prdiviseur, la sortie du prdiviseur est gale son entre, donc : La dure totale du clock ne pourra tre infrieure 4 Tosc. De plus, on ne pourra avoir un tat haut <2 Tosc et un tat bas <2 Tosc De plus, ltat haut et ltat bas ne pourrons durer chacun moins de 20ns.

Si on utilise un prdiviseur, la sortie est gale lentre divise par la valeur du prdiviseur, donc, fort logiquement : La dure totale du clock ne pourra tre infrieure 4 Tosc divise par le prdiviseur De plus, on ne pourra avoir un tat haut <2 Tosc divis par le prdiviseur et idem pour ltat bas De plus, ltat haut et ltat bas ne pourrons durer chacun moins de 10ns.

Calculons quelles sont les frquences maximales que nous pouvons appliquer sur la pin TOCKI un PIC cadenc une frquence de 10MHz : Sans prdiviseur, la contrainte en terme de Tosc nous donne : Tosc = 1/f = 1 / 107 = 10-7 s = 100 ns Temps minimum en terme de Tosc = 4 Tosc = 400 ns Ceci respecte la contrainte de temps absolu qui nous impose un temps > 40ns (20+20). Donc, nous aurons comme frquence maximale : F = 1/Tosc = 1/(4*10-7) = 2,5 * 106 = 2,5 MHz. Il va de soi quavec notre 16F876 cadenc 20 MHz, la frquence maximale utilisable sans prdiviseur est de 5MHz. Nous voyons donc que nous pouvons dire que la frquence maximale (en signal carr) utilisable avec le timer 0 configur en mode compteur sans prdiviseur, est gale au quart de la frquence du quartz employ pour son horloge. Voyons maintenant le cas du prdiviseur. Utilisons la valeur maximale, soit 256 : Temps minimum en terme de Tosc = 4 Tosc divis par le prdiviseur = 400 ns / 256 = 1,56 ns

151

Ce temps minimum ne respecte pas le temps minimum absolu impos, qui est de 10 ns. Cest donc ce dernier temps dont nous tiendrons compte. Notre frquence maximale est donc de : F = 1/T = 1/ (20*10-9) = 50 . 106 Hz = 50 MHz. Donc, vous voyez que vos PICs peuvent, condition dutiliser le prdiviseur une valeur suffisante, compter des frquences pouvant atteindre 50 MHz 14.4 Modification au vol de lassignation du prdiviseur Vous pouvez tre amens, en cours dexcution de votre programme, changer lassignation du prdiviseur entre le watchdog et le timer 0. En effet, il vous suffit de changer le bit PSA du registre pour effectuer cette opration. Il vous faudra cependant respecter les procdures suivantes, sous peine de provoquer un reset non dsir de votre PIC au moment de la modification de PSA. Certains profitent dailleurs de cette anomalie pour provoquer des resets la demande depuis le logiciel. Je vous le dconseille fermement : Dune part parcequun programme qui ncessite un reset de ce type est en gnral un programme mal structur (vous imaginez un programme sur votre PC qui ncessite un reset ?) Dautre part, parce que ce fonctionnement est anormal, et donc, bien videmment, non garanti . En passant sur une nouvelle rvision de PICs, vous risquez que votre astuce ne fonctionne plus.

Nous avons donc 2 possibilits : soit nous affectons le prdiviseur au watchdog alors quil tait affect au timer 0, soit le contraire. 14.4.1 Prdiviseur du timer 0 vers le watchdog Commenons par ce cas de figure. Nous allons devoir distinguer 2 sous-cas particuliers, en fonction de la valeur finale de notre prdiviseur (bits PS0/PS2). Imaginons tout dabord que la valeur de notre prdiviseur finale soit diffrente de 1. Voici alors la procdure respecter scrupuleusement :
Modifpsa clrf TMR0 bsf STATUS,RP0 bsf OPTION_REG,PSA clrwdt movlw Bxxxx1abc movwf OPTION_REG bcf STATUS,RP0 ; ; ; ; ; ; ; effacer timer0, et donc le contenu du prdiviseur passer en banque 1 prdiviseur sur watchdog SANS changer valeur effacer watchdog et prdiviseur dfinir la nouvelle valeur prdiviseur(selon abc) dans registre repasser en banque 0, fin du traitement

152

Si maintenant, nous dsirons que la valeur finale du prdiviseur soit gale 1, nous sommes obligs de passer par une valeur intermdiaire quelconque, mais diffrente de 1. Voici donc la procdure :
Modifpsa bsf STATUS,RP0 movlw Bxx0x0111 movwf OPTION_REG bcf STATUS,RP0 clrf TMR0 bsf STATUS,RP0 movlw Bxxxx1111 movwf OPTION_REG clrwdt movlw Bxxxx1000 movwf OPTION_REG bcf STATUS,RP0 ; ; ; ; ; ; ; ; ; ; ; ; passer en banque 1 prdiviseur quelconque, tmr en compteur dans registre repasser en banque 0 effacer timer0, et donc le contenu du prdiviseur passer en banque 1 passer sur watchdog, mode tmr rel utilis dans registre effacer watchdog et prdiviseur dfinir la nouvelle valeur prdiviseur(1) dans registre repasser en banque 0, fin du traitement

14.4.2 Prdiviseur du watchdog vers le timer 0 Dans ce cas, la procdure est plus simple :
clrwdt bsf STATUS,RP0 movlw Bxxxx0abc movwf OPTION_REG bcf STATUS,RP0 ; ; ; ; ; commencer par effacer le watchdog et le prdiviseur passer en banque 1 passer sur tmr 0 et nouvelle valeur prdiviseur (abc) dans registre repasser banque 0, fin du traitement

Je vous rappelle que le non respect de ces procdures risque de causer un reset imprvu de votre PIC. Une dernire chose prendre en compte, cest que, puisque le comptage ne se fait quau moment de la monte de Q4, il y a un dcalage entre lapparition du clock et le moment o ce clock est pris en compte. Ce dcalage est donc au maximum de 1 cycle dinstruction. Voici donc quelques complments dinformation sur le timer 0, tant donn quau stade o vous en tes, vous avez de plus en plus de chance de raliser des programmes complexes.

153

Notes :

154

15. Le timer 1
Voici un sujet qui ma valu bien du courrier lectronique. Jai constat que beaucoup de mes correspondants avaient des difficults mettre en uvre ce timer. Nous allons voir, que dans ses fonctions de base, la mise en uvre de ce timer nest pas plus complique que celle du timer 0. Je me limiterai dans ce chapitre au fonctionnement du timer en tant que tel. Je verrai ses modes de fonctionnement annexes dans les chapitres concerns (CCP). 15.1 Caractristiques du timer 1 Le timer 1 fonctionne dans son ensemble comme le timer 0. La philosophie est semblable. Il y a cependant de notables diffrences. Tout dabord, ce timer est capable de compter sur 16 bits, comparer aux 8 bits du timer 0. Il sera donc capable de compter de D0 D65535. Pour arriver ces valeurs, le timer 0 devait recourir ses prdiviseurs, ce qui, nous lavons vu dans la premire partie du cours, limitait de ce fait sa prcision lorsquon tait contraint de recharger son contenu (perte des vnements compts par le prdiviseur). Le timer 1 compte donc sur 16 bits, ce qui va ncessiter 2 registres. Ces registres se nomment TMR1L et TMR1H. Je pense quarrivs ce stade, il est inutile de vous prciser lequel contient la valeur de poids faible, et lequel contient celle de poids fort. Souvenez-vous que le contenu de TMR1L et de TMR1H nest pas remis 0 lors dun reset. Donc, si vous voulez compter partir de 0, il vous incombe de remettre 0 ces 2 registres vous-mme. Le timer 1 permet galement, tout comme le timer 0, de gnrer une interruption une fois le dbordement effectu. Le timer 1 dispose, lui aussi, de la possibilit de mettre en service un prdiviseur. Mais ce prdiviseur ne permet quune division maximale de 8. Le rsultat final est cependant meilleur que pour le timer 0, qui ne pouvait compter que jusque 256, multipli par un prdiviseur de 256, soit 65536. Pour notre nouveau timer, nous arrivons une valeur maximale de 65536 multipli par 8, soit 524288. Vous allez me rtorquer que ceci vous permet datteindre des temps plus longs, mais ne vous permet pas dutiliser des temps aussi courts que pour le timer 0, qui peut gnrer une interruption tous les 256 clocks dhorloge, notre timer 1 ncessitant 65536 clocks au minimum.

155

En fait, il suffit que lors de chaque interruption vous placiez la valeur 0xFF dans TMR1H, et vous vous retrouvez avec un compteur sur 8 bits. Vous pouvez naturellement utiliser toute autre valeur qui vous arrange. La souplesse du timer 1 est donc plus importante que celle du timer 0. Et vous allez voir que cest loin dtre termin. Tout comme pour le timer 0, les registres de comptage TMR1L et TMR1H contiennent une valeur indtermine aprs un reset de type P.O.R ou B.O.R. De mme, un autre reset ne modifie pas le contenu prcdent de ces registres, qui demeurent donc inchangs. 15.2 Le timer 1 et les interruptions Il est bien entendu quun des modes privilgis de lutilisation des timers, est la possibilit de les utiliser en tant que gnrateurs dinterruptions. Je vous rappelle donc ici que le timer 1 permet, exactement comme le permet le timer 0, de gnrer une interruption au moment o timer dborde , cest--dire au moment o sa valeur passe de 0xFFFF 0x0000 (souvenez-vous que nous travaillons sur 16 bits). Quelles sont les conditions pour que le timer 1 gnre une interruption ? Et bien, tout simplement que cette interruption soit autorise. Donc : Il faut que le bit dautorisation dinterruption du timer 1 (TMR1IE) soit mis 1. Ce bit se trouve dans le registre PIE1 (banque 1). Pour que le registre PIE1 soit actif, il faut que le bit PEIE dautorisation des interruptions priphriques soit positionn dans le registre INTCON. Il faut galement, bien entendu, que le bit dautorisation gnrale des interruption (GIE) soit positionn dans le registre INTCON.

Moyennant quoi, une interruption sera gnre chaque dbordement du timer1. Cet vnement sera indiqu par le positionnement du flag TMR1IF dans le registre PIR1. Comme dhabitude, il vous incombe de remettre ce flag 0 dans la routine dinterruption, sous peine de rester indfiniment bloqu dans la dite routine. Mais, comme pour les autres interruptions, le fichier maquette que je vous fournis effectue dj ce travail. Voici une squence qui initialise les interruptions sur le timer 1 :
bsf bsf bcf bsf bsf STATUS,RP0 PIE1,TMR1IE STATUS,RP0 INTCON,PEIE INTCON,GIE ; ; ; ; ; passer en banque 1 autoriser interruptions timer 1 repasser banque 0 interruptions priphriques en service interruptions en service

15.3 Les diffrents modes de fonctionnement du timer1

156

Le timer 1 peut, tout comme le timer 0, fonctionner en mode timer (cest--dire en comptage des cycles dinstruction), ou en mode compteur (cest--dire en comptant des impulsions sur une pin externe. Cependant, le timer 1 dispose de 2 modes diffrents de mode de comptage : un mode de type synchrone et un mode de type asynchrone. Vous avez dj eu une ide de ce quest un mode synchrone au chapitre traitant du timer 0. Rappelez-vous que le comptage ne seffectuait, en sortie du prdiviseur, quau moment de la monte de Q4, donc en synchronisme avec lhorloge interne. En plus, nous avons la possibilit, au niveau du timer 1, dutiliser un quartz sur les pins T1OSI et T1OSO afin de disposer dune horloge spare de lhorloge principale. En tout tat de cause, souvenez-vous quun timer utilis en mode timer , neffectue jamais quun simple comptage de cycles dinstruction. Pour rsumer, nous pouvons donc dire que le timer peut fonctionner en tant que : Timer bas sur lhorloge interne (compteur de cycles dinstruction) Timer bas sur une horloge auxiliaire Compteur synchrone Compteur asynchrone. Les modes de fonctionnement sont dfinis en configurant correctement le registre T1CON, que nous allons examiner maintenant. Mais tout dabord, je vous donne le schma-bloc du timer 1 :

15.4 Le registre T1CON Comme je le disais, cest ce registre, situ en banque 0, qui va permettre de configurer le timer1 en fonction de nos besoins. Voici les bits qui le composent : b7 b6 b5 b4 : : : ; Inutilis Inutilis T1CKPS1 T1CKPS0 : : : : lu comme 0 lu comme 0 Timer 1 oscillator ClocK Prescale Select bit 1 Timer 1 oscillator ClocK Prescale Select bit 0
157

b3 b2 b1 b0

: : : :

T1OSCEN : T1SYNC : TMR1CS : TMR1ON :

Timer 1 OSCillator ENable control bit Timer 1 external clock input SYNChronisation control bit TiMeR 1 Clock Source select bit TiMeR 1 ON bit

Je ne sais pas quel est votre sentiment, mais moi, la premire fois que jai rencontr ces bits, jai trouv leur nom particulirement barbare . Mais bon, leur utilisation est heureusement plus simple retenir que les noms. Voyons donc. Tout dabord, les bits T1CKPS1 et T1CKPS0 dterminent eux deux la valeur du prdiviseur dont nous avons dj parl. Le prdiviseur, comme son nom lindique, et comme pour le timer0, permet, je le rappelle, de ne pas compter chaque vnement reu, mais seulement chaque multiple de cet vnement. La valeur de ce multiple est la valeur du prdiviseur. T1CKPS1 0 0 1 1 T1CKPS0 0 1 0 1 Valeur du prdiviseur 1 2 4 8

Vous pouvez donc vrifier que les choix sont plus limits que pour le timer 0, mais ceci est compens par le fait que le timer 1, je le rappelle encore, compte sur 16 bits au lieu de 8. Nous trouvons ensuite T1OSCEN. Ce bit permet de mettre en service loscillateur interne, et donc permet dutiliser un second quartz pour disposer dun timer prcis travaillant une frquence distincte de la frquence de fonctionnement du PIC. Mais rassurez-vous, je vais vous dtailler tout a un peu plus loin. Pour linstant, retenez que si vous placez ce bit 1 , vous mettez en service loscillateur interne, ce qui aura, nous le verrons plus loin, toute une srie de consquences. Maintenant, voyons T1SYNC, qui permet de choisir si le comptage des vnements sera effectue de faon synchrone ou asynchrone avec lhorloge principale du PIC. Il est de fait vident que lorsque TMR1 est utilis en mode timer , T1SYNC na aucune raison dtre, le comptage tant dtermin par loscillateur principal du PIC, il est automatiquement synchrone. Le bit TMR1CS permet de dfinir quel est le mode de comptage du timer 1 : Soit on place TMR1CS 0 , et dans ce cas, il compte les cycles dinstructions du PIC. Dans ce cas, on dira, tout comme pour le timer 0, que TMR1 travaille en mode timer . Soit, on place ce bit 1 , et TMR1 compte alors les flancs montants du signal appliqu sur la pin RC0/T1OSO/T1CKI (la pin porte 3 noms, en fonction de son utilisation). Comme je le disais plus haut, si TMR1CS est 0, alors T1SYNC est ignor. Reste le plus simple, le bit TMR1ON, qui, sil est mis 1 , autorise le timer 1 fonctionner, alors que sil est mis 0 , fige le contenu du timer 1, et donc, le met larrt.

158

15.5 Le timer 1 en mode timer Nous allons maintenant voir avec plus de dtails les diffrents modes de fonctionnement de ce timer, en les examinant chacun en particulier. Et tout dabord, en commenant par le plus simple, le mode timer . Dans ce mode, nous trouvons un fonctionnement tout ce quil y a de plus simple, puisque nous avons affaire un comptage des cycles dinstructions internes du PIC, exactement comme nous avions pour le timer 0. Pour choisir ce mode, nous devons configurer T1CON de la faon suivante : T1CON : B00ab0001 Si vous avez suivi, vous avez dj compris que ab reprsentent la valeur du prdiviseur choisie, et que TMR1CS devra tre mis 0 . TMR1ON permet de mettre le timer en service. Ceci configure le fonctionnement interne du timer 1 comme illustr cidessous :

Voici un exemple dinitialisation du timer 1, configur avec un prdiviseur de 4


clrf clrf movlw movwf bsf TMR1L TMR1H B0010000 T1CON T1CON,TMR1ON ; ; ; ; ; effacer timer 1, 8 lsb effacer timer 1, 8 msb valeur pour T1CON prdiviseur = 4, mode timer, timer off mettre timer 1 en service

15.6 Le timer 1 en mode compteur synchrone Pour travailler dans le mode compteur synchrone , nous devons configurer T1CON de la faon suivante : T1CON : B00ab0011 Evidemment, pour pouvoir compter des vnements sur la pin T1CKI, il faut que cette pin soit configure en entre via le registre TRISC.

159

Donc, en reprenant le schma, vous voyez que la pin RC1 nest pas utilise, pas plus que loscillateur et la rsistance associe. Donc, vous ignorez la partie situe dans le rectangle pointill. Nous entrons donc sur T1CKI (T1 ClocK In) et nous arrivons sur un trigger de Schmitt (2). Nous arrivons ensuite sur la slection du signal via TMR1CS. Comme nous avons mis 1 pour ce bit, nous validons donc lentre de T1CKI et nous ignorons le comptage des instructions internes (mode timer). Ensuite, nous arrivons au prdiviseur, dont la valeur est fixe par T1CKPS1 et T1CKPS0. Vous constatez qu ce niveau, le comptage se fait de faon asynchrone. En effet, par synchrone il faut comprendre : qui est synchronis par lhorloge interne du PIC. . Or, ici, les vnements sont compts dans le prdiviseur indpendamment de lhorloge du PIC. Par contre, la sortie du prdiviseur est envoye galement dans un module de synchronisation. Le bloc de slection T1SYNC permet de dfinir si on utilise la sortie du prdiviseur avant la synchronisation (mode asynchrone) ou aprs la synchronisation (mode synchrone). Nous avons mis notre T1SYNC 0. Comme il est actif ltat bas, ceci nous slectionne le signal synchronis. Le signal est ensuite valid ou non grce TMR1ON, signal qui va servir incrmenter le mot de 16 bits constitu de TMR1H et TMR1L. Si ce mot de 16 bits passe de 0xFFFF 0x0000 (dbordement), le flag TMR1IF est alors positionn, et peut gnrer une interruption, si cest le choix de lutilisateur. Voici donc le schma-bloc du mode compteur synchrone :

Vous voyez que cest trs simple. Reste expliquer le rle du synchronisateur. En effet, vous allez me dire : A quoi sert de synchroniser ou de ne pas synchroniser la sortie du prdiviseur? En fait, la diffrence principale est que si vous choisissez de synchroniser, alors, fort logiquement, vous ne comptez pas si le PIC est larrt. Or, quand le PIC est-il larrt ? Et bien quand vous le placez en mode sleep .
160

Donc, le timer 1 utilis en mode compteur synchrone ne permet pas de rveiller le PIC sur une interruption du timer 1. Cest logique, car, puisquil ny a pas de comptage, il ny a pas de dbordement. Notez que la prsence du synchronisateur retarde la prise en compte de lvnement, puisquavec ce dernier la sortie du prdiviseur ne sera transmise au bloc incrmenteur que sur le flanc montant suivant de lhorloge interne. Il me reste vous prciser que le comptage seffectue uniquement sur un flanc montant de T1CKI, donc sur le passage de T1CKI de 0 vers 1 . Il ne vous est pas possible ici de choisir le sens de transition, comme le permettait le bit TOSE pour le timer 0. De plus, le flanc montant de T1CKI nest pris en compte que sil a t prcd dau moins un flanc descendant. Ce dernier point mrite un complment dinformation. Si vous avez un signal connect au PIC qui est au niveau 0, et dont vous devez compter les passages 1 , alors, lorsque votre PIC recevra son alimentation, le premier flanc montant de T1CKI ne sera pas comptabilis, puisque pas prcd dun flanc descendant. Notez que pour viter de perdre un comptage, les temps minimum des signaux appliqus rpondent exactement aux mmes critres que pour le timer 0. Je vous renvoie donc au chapitre prcdent ce sujet. 15.7 Le timer 1 en mode compteur asynchrone Partant des explications prcdentes, le schma-bloc obtenu ne prsente aucune difficult, il suffit bien entendu de supprimer le bloc de synchronisation :

Donc, vous voyez quun vnement est comptabilis immdiatement, sans attendre une quelconque transition de lhorloge interne. Les contraintes temporelles, concernant les caractristiques des signaux appliqus sont toutefois toujours dapplication (voyez chapitre prcdent).

161

La plus grosse diffrence, cest que le comptage seffectue mme si le PIC est stopp, et que son horloge interne est arrte. Il est donc possible de rveiller le PIC en utilisant le dbordement du timer1. Par contre, cette facilit se paye au niveau de la facilit de lecture et dcriture des registres TMR1H et TMR1L, mais je verrai ceci un peu plus loin. La valeur placer dans le registre T1CON pour travailler dans ce mode est bien videmment : T1CON : B00ab0111 Il faut de nouveau ne pas oublier de configurer T1CKI (RC0) en entre, via le bit 0 de TRISC. 15.8 Le timer 1 et TOSCEN Nous avons presque fait le tour des modes de fonctionnement du timer 1. Reste utiliser loscillateur interne. La valeur placer dans T1CON est donc : T1CON : B00ab1x11 Remarquez la prsence dun x qui vous permet de choisir si vous dsirez travailler en mode synchrone ou asynchrone. Les remarques que jai faite ce sujet pour lutilisation en mode compteur restent dapplication. Bien quil sagisse dune mesure de temps, donc dun timer, il faut bien videmment configurer T1CON pour quil puisse compter les oscillations reues sur la pin T1OSI. Ceci rejoint ce que je vous ai dj signal, savoir quun timer nest rien dautre quun compteur particulier. Arriv ici, vous vous posez peut-tre quelques questions concernant cet oscillateur. Je vais donc tenter danticiper : A quoi sert cet oscillateur ? Et bien, tout simplement utiliser une base de temps diffrente pour le timer et pour le PIC. Ceci permet donc de choisir un quartz multiple exact de la frquence mesurer, tout en conservant une vitesse de traitement maximale du PIC. Souvenez-vous que je vous ai dj parl, dans la premire partie, ddie au 16F84, de la solution de luxe consistant, pour amliorer la prcision, utiliser un second oscillateur pour raliser des mesures de prcision. Lavantage, ici, cest que loscillateur est prsent en interne, il ne suffit donc que de lui ajouter un quartz. Voici un schma typique de lutilisation du timer 1 avec un quartz sur OSCEN :

162

Vous notez donc la prsence de 2 quartz. Le quartz 1 vous permet dutiliser le PIC sa frquence de travail maximale (ici, 20MHz). Le second vous donne une base de temps diffrente pour lutilisation du timer 1. Se pose donc une seconde question : Quelles sont les valeurs du second quartz que je peux utiliser ? En fait, loscillateur a t conu pour fonctionner efficacement une valeur centre sur 32KHz (attention KHz, pas MHz). Mais vous pouvez augmenter cette frquence jusque 200 KHz. Cet oscillateur fonctionne donc une vitesse plus lente que loscillateur principal. La table 6-1 du datasheet vous donne les valeurs des condensateurs utiliser pour quelques frquences typiques. Vous voyez que jai choisi une frquence de 32768 Hz pour mon second quartz. En fait, je nai pas choisi cette valeur par hasard. En effet, si vous vous souvenez que le timer 1 compte sur 16 bits (le contraire serait tonnant, vu le nombre de fois que je vous lai rpt), nous aurons un dbordement du timer 1 au bout du temps : 65536 / frquence. Soit tout juste 2 secondes. Voici qui nous donne une base de temps trs prcise (nutilisant pas de prdiviseur), et qui ne provoque quune interruption toutes les 2 secondes. Si vous voulez une interruption toutes les secondes, il vous suffira de positionner le bit 7 de TMR1H lors de chaque interruption. Ainsi, le timer 1 comptera de 0x8000 0xFFFF, soit

163

un comptage de 32678 cycles. Ceci nous donnera un temps de comptage de 32768/32768 = 1 seconde pile . Il vous suffit donc dans ce cas dajouter la ligne
bsf TMR1H,7 ; forcer bit 7 de TMR1H = bit 15 de TMR1

votre routine dinterruption du timer 1. Ce mode constitue donc le mode idal pour crer une mesure de temps rel. Notez de plus que si vous avez configur le mode asynchrone, le timer continuera de compter mme si votre PIC est en mode sleep . La prcision obtenue est fonction du quartz, mais est typiquement de 20 parts par million. Ceci nous donne une erreur maximale, pour le cas de la ralisation dune horloge, de 1,7 seconde par jour. Je vous donne le schma-bloc correspondant au mode OSCEN, schma que vous aurez probablement trouv vous-mme :

Il me reste cependant vous signaler que dans ce mode particulier, les pins RC0 (T1OSO) et RC1 (T1OSI) sont automatiquement configures en entre, indpendamment des bits correspondants de TRISC. Il est donc inutile de configurer les bits 0 et 1 de ce registre.

15.9 Utilisation du dbordement Programmer le timer1 et comprendre son fonctionnement ne suffit pas. Encore faut-il lutiliser pour mesurer ou compter. La premire mthode dutilisation de notre TMR1 est la plus courante. Je ne la rpte que pour pourvoir faire la distinction avec les piges qui se trouvent dans la seconde mthode dutilisation. Cette mthode consiste utiliser le dbordement de la valeur du timer pour positionner le flag TMR1IF, et, ventuellement, pour dclencher une interruption.

164

La philosophie utilise est donc la suivante : On initialise le timer avec une valeur de dpart (ventuellement 0) On attend son dbordement A ce moment on dtecte que le temps prdfini est coul.

Imaginons donc la squence dinitialisation de notre timer 1 utilis en mode timer. Nous dcidons pour cet exemple que nous avons besoin dun prdiviseur dune valeur de 4. Nous dcidons galement dautoriser linterruption du timer1. Si nous nous souvenons quil nous incombe de remettre le timer 0, nous aurons, par exemple, la squence suivante :
clrf clrf movlw movwf bsf bsf bcf bsf bsf bsf TMR1L TMR1H B00100000 T1CON STATUS,RP0 PIE1,TMR1IE STATUS,RP0 INTCON,PEIE INTCON,GIE T1CON,TMR1ON ; ; ; ; ; ; ; ; ; ; effacer timer 1, 8 lsb effacer timer 1, 8 msb valeur pour T1CON prdiviseur = 4, mode timer, timer off passer en banque 1 autoriser interruptions timer 1 repasser banque 0 interruptions priphriques en service interruptions en service mettre timer 1 en service

Le raisonnement serait identique pour un fonctionnement en mode compteur. Bien entendu, il ne sagit que dun exemple, plusieurs lignes peuvent tre inverses, vous pouvez mettre GIE et PEIE en service en mme temps etc. Certains dentre vous, lil acr, ont peut-tre remarqu que je navais pas remis le flag TMR1IF 0 avant de lancer les interruptions. Cest judicieusement pens, mais ce nest pas utile. En effet, au moment de la mise sous tension, PIR1 est automatiquement mis 0. De plus, T1CON est galement mis 0, ce qui implique que le timer 1 est larrt tant quon ne le met pas en service intentionnellement. Donc, il est impossible que le timer 1 ait provoqu un dbordement de TMR1IF avant sa mise en service. Si, par contre, on ne dsirait pas compter de 0x0000 0xFFFF, on peut initialiser les registres du timer 1 pour diminuer le temps de comptage. Imaginons que nous dsirions compter 3978 cycles dinstructions : En fait, nous devrons donc initialiser le timer1 pour quil dmarre le comptage 3978 cycles avant sa valeur de dbordement. Vous voyez, en raisonnant un peu, quil suffit de mettre dans les registres du timer1, la valeur 0x10000 de laquelle on soustrait la valeur compter. Exemple, pour bien visualiser, si vous dsirez compter 1 cycle, vous mettrez 0x10000 0x01 = 0xFFFF, ce qui est logique, puisquau cycle suivant, nous aurons dbordement. Dans notre cas, il sagira donc de mettre 0x10000 D3978 dans notre timer1, constitu de TMR1H pour le poids fort, et TMR1L pour le poids faible. Plutt que de calculer cette valeur, autant laisser faire MPASM, il est l pour a (entre autres). Dfinissons notre valeur sur 16 bits :

165

VALUE EQU

0x10000 D3978

; calcul de la valeur sur 16 bits

Il suffit alors de remplacer :


clrf clrf TMR1L TMR1H ; effacer timer 1, 8 lsb ; effacer timer 1, 8 msb

par :
movlw movwf movlw movwf LOW VALUE ; 8 bits faibles de la valeur calcule TMR1L ; dans TMR1L HIGH VALUE ; 8 bits forts de la valeur calcule TMR1H ; dans TMR1H

Bien entendu, dans notre exemple, vous devrez remettre le prdiviseur 1, sinon nous aurons interruption aprs 4 * 3978, soit 15912 cycles. De la sorte, notre compteur dbordera 3978 cycles aprs avoir t lanc. Vous voyez donc que vous pouvez obtenir une bien meilleure prcision quavec le timer 0, et, de plus, beaucoup plus facilement. 15.10 Utilisation dune lecture Nous pouvons aussi vouloir dcider dutiliser notre timer dune autre faon. En effet, supposons que nous voulions construire un chronomtre. Nous aurons alors la structure de programme suivante : On presse le bouton dmarrer On lance le timer 1 en mode timer, partir de 0. On presse le bouton lecture de temps intermdiaire On sauve la valeur de TMR1H et TMR1L On presse le bouton stop On arrte le timer 1 On lit la valeur dans TMR1H et TMR1L

Evidemment, il sagit dun exemple, on considre ici, pour simplifier, que le temps mesur est compris entre 0 et 0xFFFF cycles. Si le temps tait plus long, nous devrions en plus compter le nombre de fois que le timer1 a dbord. Nous aurions alors le temps total (en cycles) = (nombre de dbordements * 0x10000) + valeur actuelle du mot de 16 bits : TMR1H TMR1L. La philosophie utilise dans ce mode est donc la suivante : On dmarre le timer partir de 0 On dtecte un vnement On lit la valeur du timer pour calculer le temps coul

Le programme serait alors, par exemple (START, STOP et LAP sont des define qui pointent sur des pins utilises en entre) :
start

166

clrf TMR1L clrf TMR1H attendre btfss START goto attendre bsf T1CON,TMR1ON attendre2 btfss LAP goto attendre2 movf TMR1L,w movwf VALL movf TMR1H,w movwf VALH attendre3 btfss STOP goto attendre3 bcf T1CON,TMR1ON movf TMR1L,w movwf FINALL movf TMR1H,w movwf FINALH

; effacer timer 1, 8 lsb ; effacer timer 1, 8 msb ; tester si le bouton dmarrer est press ; non, attendre ; oui, lancer le timer ; ; ; ; ; ; ; ; ; ; ; ; ; tester si temps intermdiaire press non, attendre lire 8 lsb sauver dans variable lire 8 msb sauver dans variable tester si bouton stop est press non, attendre oui, arrter le timer 1 lire 8 lsb sauver dans variable lire 8 msb sauver dans variable

Examinons ce programme. Noubliez pas quil sagit dun exemple symbolique, donc, je sais que ce programme impose de presser LAP avant de presser STOP , que les boucles introduisent une erreur, quil tait prfrable dutiliser dautres mthodes etc. En fait, ce nest pas ce que je voulais vous monter. Regardez la dernire lecture, celle qui place le timer 1 dans 2 variables, pour obtenir une valeur sur 16 bits. Le rsultat FINALH FINALL reflte effectivement le temps qui sest coul depuis la mise en service du timer 1 jusqu son arrt. Imaginez qu la fin de lexcution de cette squence, nous ayons : FINALL = 0x12 FINALH = 0xDE Nous pouvons dire quil sest coul 0xDE12 cycles, soit 56850 cycles entre le dmarrage et larrt du timer 1. Regardez maintenant ce qui se passe au niveau de la mesure du temps intermdiaire. Supposons que nous avons aprs excution de notre programme : VALL = 0xFE VALH = 0x45 Vous en concluez donc quil sest coul 0x45FE cycles, soit 17918 cycles entre le dmarrage du timer 1 et la mesure de temps intermdiaire (on nglige les temps de raction de stockage des valeurs). Vous tes bien certain de ceci ? Oui ? Alors vrifions ce qui se passe lors de la lecture :
movf TMR1L,w ; lire 8 lsb

167

Comme notre valeur VALL vaut 0xFE, cest donc que notre TMR1L vaut 0xFE. Nous ne connaissons pas encore, ce stade, la valeur de TMR1H. Ensuite :
movwf VALL ; sauver dans variable

Nous sauvons donc bien 0XFE dans VALL, mais un cycle dinstruction sest excut, notre TMR1L continue de compter, et vaut donc maintenant 0xFF. Voyons la suite :
movf TMR1H,w ; lire 8 msb

A ce moment, une nouvelle instruction est excute, donc TMR1L a de nouveau t incrment, et est pass donc 0x00, entranant lincrmentation de TMR1H. Cest donc cette valeur incrmente que nous allons lire. En effet, 0x44FF + 1 = 0x4500.
movwf VALH ; sauver dans variable

Donc, nous sauvons 0x45, puisque cest le rsultat que nous avions obtenu. Donc, nous avons dduit que la valeur du timer au moment de la lecture tait de 0x45FE, alors que nous constatons en ralit que ce compteur valait 0x44FE, soit 17662. Nous nous sommes donc tromps de 256 cycles. Vous allez alors me rpondre quil suffit de vrifier que TMR1L soit infrieur 0xFE. Sil est suprieur, on soustrait 1 de TMR1H. En fait, votre raisonnement nest valable que dans cet exemple prcis. En effet, le comptage des cycles comme je viens de le faire ci-dessus inclus plusieurs conditions : Il faut que le prdiviseur utilis soit gal 1. En effet, dans le cas contraire, si vous avez un TMR1L gal 0xFF, il vous est impossible de savoir sil y a eu incrmentation ou pas de TMR1L. Donc, vous ne pouvez savoir si vous devez ou non rectifier TMR1H. Ensuite, si les interruptions sont en service, vous ne pouvez pas tre certain quil ny a pas eu interruption entre la lecture de TMR1L et celle de TMR1H. Donc, vous ne pouvez pas savoir combien de cycles sparent les 2 instructions. Si nous travaillons en mode compteur, nous ne pouvons pas savoir combien dimpulsions ont t comptabilises entre les lectures.

Forts de tout ceci, nous pouvons en dduire quil est prfrable, pour lire les 16 bits du timer 1 doprer en mettant le dit timer hors-service :
bcf movf movwf movf movwf bsf T1CON,TMR1ON TMR1L,w FINALL TMR1H,w FINALH T1CON,TMR1ON ; ; ; ; ; ; arrter le timer 1 lire 8 lsb sauver dans variable lire 8 msb sauver dans variable remettre timer en service

Mais, bien entendu, ceci nest pas toujours possible. En effet, dans notre exemple, la mesure du temps intermdiaire impose de ne pas arrter le timer pour lire la valeur. Nous allons donc changer de stratgie.

168

Nous pouvons utiliser lalgorithme suivant : On lit le poids fort TMR1H et on le sauve On lit le poids faible On relit le poids fort : sil na pas chang, cest OK, sinon, on recommence.

Si on se dit que le TMR1L ne peut dborder 2 fois de suite, on peut se passer de recommencer le test. Mais, pour que cette condition soit vraie, il faut que le temps sparant les 2 lectures ne soit pas trop grand, donc il faut empcher quune interruption sintercale : - On interdit les interruptions - On lit le poids fort TMR1H et on le sauve - On lit le poids faible TMR1L et on le sauve - On relit le poids fort : sil na pas chang, on va en suite - On relit le poids fort - On relit le poids faible Suite : - On rautorise les interruptions Ca parat plus long, mais si vous encodez la boucle prcdente, vous verrez que le temps ncessaire est plus long (surtout en cas dinterruption). Voici la squence tire de ce pseudocode :
bcf movf movwf movf movwf movf xorwf btfsc goto movf movwf movf movwf suite bsf INTCON,GIE TMR1H,w TEMPOH TMR1L,w TEMPOL TMR1H TEMPOH,w STATUS,Z suite TMR1H,w TEMPOH TMR1L,w TEMPOL INTCON,GIE ; ; ; ; ; ; ; ; ; ; ; ; ; interdire les interruptions charger poids fort sauver valeur lire poids faible sauver valeur relire poids fort comparer les 2 poids forts tester si identiques oui, fin du traitement charger poids fort sauver valeur lire poids faible sauver valeur

; rautoriser les interruptions

Notre programme prcdent devient donc :


start clrf TMR1L clrf TMR1H attendre btfss START goto attendre bsf T1CON,TMR1ON attendre2 btfss LAP goto attendre2 bcf INTCON,GIE movf TMR1H,w movwf VALH movf TMR1L,w movwf VALL ; effacer timer 1, 8 lsb ; effacer timer 1, 8 msb ; tester si le bouton dmarrer est press ; non, attendre ; oui, lancer le timer ; ; ; ; ; ; ; tester si temps intermdiaire press non, attendre interdire les interruptions charger poids fort sauver valeur lire poids faible sauver valeur

169

movf TMR1H xorwf VALH,w btfsc STATUS,Z goto suite movf TMR1H,w movwf VALH movf TMR1L,w movwf VALL suite bsf INTCON,GIE attendre3 btfss STOP goto attendre3 bcf T1CON,TMR1ON movf TMR1L,w movwf FINALL movf TMR1H,w movwf FINALH

; ; ; ; ; ; ; ;

relire poids fort comparer les 2 poids faibles tester si identiques oui, fin du traitement charger poids fort sauver valeur lire poids faible sauver valeur

; rautoriser les interruptions ; ; ; ; ; ; ; tester si bouton stop est press non, attendre oui, arrter le timer 1 lire 8 lsb sauver dans variable lire 8 msb sauver dans variable

Vous voyez quil faut toujours rester attentif, pour viter de tomber dans toutes sortes de piges. Je vous montre les plus classiques, mais il en existera toujours dans une application ou dans une autre. 15.11 Ecriture du timer 1 Nous venons de parler de lecture des 2 registres du timer 1. Vous vous doutez bien que les critures comportent galement des piges. La plus simple des solutions est videmment darrter le timer lors dune criture, de la faon suivante :
bcf movlw movwf movlw movwf bsf T1CON,TMR1ON VALL TMR1L VALH TMR1H T1CON,TMR1ON ; ; ; ; ; ; stopper le timer 1 charger valeur basse dans registre charger valeur haute dans registre remettre timer 1 en service

Mais, vous voudrez peut-tre, pour une application particulire, inscrire une valeur dans le timer sans devoir larrter. Quel problme allez-vous rencontrer ? Voyons donc le cas suivant : On crit la valeur basse dans TMR1L On crit la valeur haute dans TMR1H

Ca a lair tout simple, mais vous devez raisonner de faon identique que pour la lecture, savoir : Il se peut que votre registre TMR1L dborde entre le moment de son criture et le moment de lcriture de TMR1H. Supposons que vous vouliez crire la valeur 0x53FF. Vous commencez par crire 0xFF dans TMR1L Vous crivez 0x53 dans TMR1H, mais durant ce temps, TMR1L a dbord La valeur finale crite est donc : 0x5300
170

Nous retrouvons donc le mme type derreur que pour la lecture, et les mmes remarques. Vous ne pouvez donc pas savoir partir de quelle valeur vous aurez dbordement, sauf pour le cas particulier du mode timer avec prdiviseur 1. A ce stade, jen vois qui disent : Eh, il suffit dcrire dans lautre sens, TMR1H suivi de TMR1L . Cest bien pens, mais, en fait, alors se pose un autre problme. Etant donn que le timer est en train de tourner, vous ne connaissez pas la valeur qui se trouve dans TMR1L au moment de lcriture de TMR1H. Il se peut donc que le TMR1L dborde avant que vous nayez termin. Si on reprend lexemple prcdent : On crit 0x53 dans TMR1H, TMR1L dborde ce moment, et incrmente TMR1H On crit 0xFF dans TMR1L Le rsultat final est donc 0x54FF. De nouveau, nous retrouvons notre erreur. En fait, la solution est simple : si on veut viter que le TMR1L dborde, il suffit de lui placer en premier lieu une valeur suffisamment basse pour tre sr dviter tout dbordement (par exemple 0) . Bien entendu, il faudra galement interdire les interruptions : On interdit les interruptions On crit 0x00 dans TMR1L On crit la valeur haute dans TMR1H On crit la valeur basse dans TMR1L On rautorise les interruptions Voici donc la squence rsultante :
bcf clrf movlw movwf movlw movwf bsf INTCON,GIE TMR1L VALH TMR1H VALL TMR1L INTCON,GIE ; ; ; ; ; ; ; interdire les interruptions 0 dans TMR1L charger valeur haute dans registre charger valeur basse dans registre rautoriser les interruptions

Vous voyez que ce nest pas compliqu, condition de prendre garde aux piges qui vous sont tendus. Il me reste vous signaler quune criture dans un des registres TMR1L ou TMR1H provoque leffacement des lments dj comptabiliss dans le prdiviseur. Une criture dans un de ces registres, si vous utilisez un prdiviseur diffrent de 1, provoquera donc une perte dinformations. Attention, ne confondez pas valeur du prdiviseur et contenu du prdiviseur . La valeur est celle que vous avez place via T1CKPS1 et T1CKPS0. Ces valeurs ne sont pas modifies. Le contenu est le nombre dvnements dj compts par le prdiviseur. Cest donc ce contenu qui est perdu lors dune criture dans TMR1L ou TMR1H.

171

15.12 Exercice pratique A la lecture de tout ce qui prcde, vous devez tre conscient quil mest trs difficile de vous proposer des exercices pour tous les cas de figure possibles. Je vais donc me limiter un seul exemple qui mettra en pratique certains des points que nous avons abords. Puisque nous avons dj pas mal parl du mode timer lorsque nous avons tudi le timer 0 dans la premire partie, je vais vous proposer un exercice qui utilise le timer 1 en mode compteur. Nous allons donc construire un programme qui inverse ltat dune LED chaque fois que 10 impulsions sont reues sur la pin T1CKI . Ces impulsions seront cres par des pressions sur un bouton-poussoir. Si vous vous souvenez de la premire partie, javais parl que les interrupteurs sont soumis des rebonds. Il nous faudra donc, pour que notre montage fonctionne liminer ces rebonds. Jutiliserai pour ce faire la charge dun petit condensateur au travers dune rsistance. Ceci permettra de ralentir (intgrer) la monte du niveau en tension, de faon ce que le passage ltat 1 intervienne aprs la fin des rebonds de linterrupteur. Afin de permettre lutilisation de tous les types dinterrupteurs, jai choisi un temps de retard de 20ms. Voici le schma que nous allons utiliser :

172

15.12.1 Un peu de maths Je pourrais vous donner les valeurs que jai choisies en vous donnant une formule approximative, pourtant largement suffisante. Seulement, jai remarqu quil y a des mticuleux parmi mes lecteurs (je nai pas dit maniaques ). Si je ne donne pas plus de prcision, je vais donc mattirer de nouveau de nombreux courriers. Je prends donc les devants Et puis, il faut bien que je montre de temps en temps que je sais calculer, et, de plus, a va mattirer la sympathie des professeurs de mathmatiques (en esprant que je ne fasse pas derreur). Plus srieusement, je profite de cet exemple pour vous montrer comment calculer des montages particuliers. Ceux que a nintresse pas nont qu excuter le pseudo-code suivant : Dbut Si je naime pas les maths Alors goto chapitre 15.12.2 Sinon On poursuit : Redevenons srieux (a change) : Voyons donc les donnes : Le temps de charge : 20 ms La tension applique : 5V La tension dtecte comme niveau haut par le PIC : 0,7 VDD (datasheet) Les inconnues : R1 R2 C

Et la formule de charge dun condensateur sous une tension constante, et via une rsistance srie :

Uc = U1 (1-e-t/rc)
Avec Uc = tension aux bornes du condensateur aprs le temps t , U1 = tension de charge, t = temps coul depuis le dbut de la charge, r = rsistance de charge, et c = valeur du condensateur. Donc, une quation avec 3 inconnues (R1,R2,C), il va falloir avoir de limagination (comme dhabitude en lectronique). Tout dabord, si on regarde le montage, on constate que si on ferme linterrupteur, en fin de charge du condensateur, la tension finale est dtermine par le pont diviseur form par R1 et R2. Donc, il nous faut choisir R2 fortement suprieur R1, sous peine de ne pouvoir jamais

173

atteindre notre tension de niveau haut gale 0,7 Vdd. Nous prendrons arbitrairement R2 = 10*R1. Vous voyez en effet que si vous choisissez R1 = R2, par exemple, votre tension finale ne pourra dpasser Vdd/2. Ensuite, il ne faut pas que le courant consomm par la pin T1CKI soit trop important par rapport au courant de charge, sous peine de ne pas arriver charger le condensateur. Il faut aussi que limpdance dentre de T1CKI soit assez suprieur R2 pour ne pas fausser nos calculs. Le datasheet (15.3) nous donne un courant dentre de T1CKI = 5A. Forts de tout ceci, nous dduisons la rsistance dentre de notre T1CKI : R = U/I R = 5V / (5 * 10-6A) = 106 Ohms = 1 MOhms Nous choisirons donc, pour viter les interfrences entre courant absorb par T1CKI et charge du condensateur, R2 = Rtocki / 10 Donc : R2 = 100 KOhms Do : R1 = R2 / 10, donc R1 = 10 Kohms Ne reste donc plus quune inconnue, C, quil faut tirer de lquation prcdente. Qui sy colle ? Ben oui, les maths, tout compte fait, a ne sert pas qu lcole. Si a vous tente de faire comme moi, prenez un papier et calculez ce condensateur. Sinon, lisez la suite : Bon, essayons de voir notre formule, il nous faut extraire C qui est dnominateur dun exposant de e . Pas vraiment marrant. Posons : -t/rc = x Ceci rend la lecture plus claire, car nous avons maintenant : Uc = U1 * (1-ex) Nous ne connaissons pas x , mais nous avons besoin, pour le trouver de connatre U1, qui est la tension relle de charge. 5V me direz-vous ? Pas du tout ! La tension de charge est dtermine par le pont diviseur R1/R2. Encore des souvenirs de lcole allez-vous penser. Cest trs simple pourtant, cette fois il suffit dappliquer la loi dOhm (jai horreur de retenir des formules toutes faites, je prfre les retrouver par raisonnement). En fin de charge du condensateur, et tant donn que nous avons choisi les rsistances pour que le courant absorb par T1CKI soit ngligeable, nous pouvons dire que le courant circulant dans R1 (IR1) est le mme que celui circulant dans R2(IR2), et, videmment le mme que celui qui circule dans les deux (IR12R2).

174

Donc, comme I = U/R, IR2 = Vdd / (R1+R2) Donc, la tension aux bornes de R2, qui est notre tension de charge finale, est gale IR2 * R2, donc : UR2 = U1 = (Vdd / (R1+R2)) * R2 U1 = (5V * 105 Ohms) / (105 Ohms + 104 Ohms). U1 = (5V * 105) / (11*104) U1 = 4,54V Le passage au niveau 1 se fait 0,7Vdd, soit 0,7 * 5V = 3,5V. Donc, notre tension finale de 4,54V nous permet bien de faire passer T1CKI au niveau 1 . Reprenons notre quation : Uc = U1 * (1-ex) Donc : 1- ex = Uc/U1 Do ex = 1-(Uc/U1) Mais ce foutu x est toujours en exposant ,il faudrait le faire descendre . En grattant dans vos souvenirs, vous vous souviendrez peut-tre que le logarithme nprien dun nombre la puissance n est gal n . Autrement dit : ln(ex)= x. Pour vous en convaincre, ce nest quun cas particulier des logarithmes. Un logarithme en base x de la base la puissance y est gal y. Simple exemple en base 10 : Log 105 = 5 Le logarithme nprien ntant quun logarithme en base e , vous comprenez maintenant lgalit prcdente. Il suffit donc, pour respecter lgalit, de prendre le logarithme nprien des 2 cts de lidentit. Ceci nous donne : ln (ex) = ln(1-(Uc/U1) ) Ou encore : x = ln(1-(Uc/U1))

175

Uc reprsente la tension aux bornes du condensateur aprs le temps que nous avons dfini (20ms). Or, ce temps est le temps du passage au niveau haut de T1CKI. Ce passage seffectuant lorsque la tension est gale 0,7 Vdd, nous avons donc que Uc = 0,7Vdd, soit 0,7*5V (la tension dalimentation du PIC) = 3,5V. Donc, nous pouvons tirer x : X = ln (1-(3,5V / 4,54V) = -1,473 Ne reste plus qu remplacer de nouveau x par ce quil reprsente : (-t/rc) = -1,473 donc, t = 1,473 * r * c Autrement dit (et crit) : C = t / (1,473 * r) C = 20 * 10-3 F / (1,473 * 104 ) On peut en effet estimer que la rsistance de charge vaut R1. C = 20 * 10-7 F / 1,473 = 2 * 10-6 F / 1,473 = 1,3 * 10-6F Donc, C = 1,3 F. Nous prendrons donc un condensateur de 1 F. Si, pour vrifier, vous remplacez C par sa valeur dans la formule initiale, : Uc = U1 * (1-e-t/rc) , vous trouverez Uc = 3,56V, ce qui nous rend bien les 3,5V de passage du niveau 0 vers le niveau 1 de notre pin T1CKI. C.Q.F.D. Ca fait plaisir de voir quon nest pas encore tout fait rouill, pas vrai ? 15.12.2 Le programme Revenons nos moutons, je veux dire nos PICs. Nous allons construire le pseudo-code de notre programme : Initialiser Initialiser le timer 1 et le charger avec une valeur de -10 Aller au programme principal

176

Interruption timer 1 Inverser LED Recharger timer 1 avec -10 Fin dinterruption Programme principal Rien faire du tout Copiez comme dhabitude votre fichier maquette, et renommez la copie cmpt1.asm . Crez un nouveau projet, et crez len-tte du programme.
;***************************************************************************** ; Exercice d'utilisation du timer 1 en mode compteur. * ; On inverse une LED pour chaque srie de 10 impulsions reues sur T1CKI * ; * ;***************************************************************************** ; * ; NOM: Cmpt1 * ; Date: 07/05/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ; * ; * ;***************************************************************************** ; * ; Notes: Entre des impulsions sur T1CKI (RC0). * ; Impulsions gnres par un bouton-poussoir quip d'un systme * ; anti-rebond par rseau RC. * ; LED de sortie connecte sur RB0 * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ; _BODEN_OFF ;_PWRTE_ON ;_WDT_OFF ;_HS_OSC Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension hors service Dmarrage temporis Watchdog hors service Oscillateur haute vitesse (20Mhz)

Ensuite, les assignations systme, dont on ne conserve que celles utiles. A noter que, vu quon na quun bit positionner pour chacune, et que le niveau est connu au moment de la mise sous tension, on aurait pu se permettre de nutiliser que le positionnement du bit en

177

question (bsf ou bcf) au niveau de la routine dinitialisation. Cependant, pour ne rien omettre, conservons ces assignations.
;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000000' ; Rsistance rappel +5V hors service ; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQUB'01000000' ; autorisation gnrale priphriques ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQUB'00000001' ; interrupt dbordement tmr1

Ensuite, nous avons nos constantes. Ici, nous souhaitons que le timer 1 dborde au bout de 10 pressions sur le bouton-poussoir. Nous devrons donc recharger le timer avec une valeur telle que TMR1H dborde aprs 10 vnements. Donc, comme je vous lai expliqu :
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** RELOAD EQU 0x10000 - D'10' ; valeur de recharge de TMR1

Concernant les macros, ne conservez que la macro de passage en banque 0


;***************************************************************************** ; MACRO * ;***************************************************************************** BANK0 macro bcfSTATUS,RP0 bcfSTATUS,RP1 endm ; passer en banque0

Nous navons, premire vue, pas besoin de variable, vu que cest notre timer 1 qui va compter lui-mme les impulsions reues. Profitons-en galement pour supprimer les variables de sauvegarde dont nous naurons pas besoin. Notre programme principal ne fera rien du tout, il ny a donc rien sauvegarder lors dune interruption.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** CBLOCK 0x20 ; Dbut de la zone (0x20 0x6F) ENDC ;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** CBLOCK 0x70 ; Dbut de la zone (0x70 0x7F) ENDC

178

;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

Voyons notre routine dinterruption, trs simple : Il ny a quune seule interruption, donc, inutile de tester de quelle interruption il sagit. Il ny a rien sauvegarder, donc rien restaurer La routine se contente dinverser la LED et de recharger le timer (compteur) Il ne faut pas oublier deffacer le flag de linterruption timer 1 (TMR1IF)

Nous pouvons donc crire notre routine dinterruption, rduite sa plus simple expression :
;***************************************************************************** ; ROUTINE INTERRUPTION TIMER 1 * ;***************************************************************************** ORG 0x04 movlw B'00000001' xorwf PORTB,f clrf TMR1L movlw HIGH RELOAD movwf TMR1H movlw LOW RELOAD movwf TMR1L bcfPIR1,TMR1IF retfie ; ; ; ; ; ; ; ; ; pour bit 0 inverser LED car criture sans stopper compteur octet fort valeur de recharge dans timer poids fort octet faible valeur de recharge dans timer poids faible effacer flag interupt return from interrupt

Reste maintenant, dans notre routine dinitialisation, initialiser notre timer 1 en mode compteur (nous choisirons asynchrone, mais cela na aucune importance ici), le prcharger avec le valeur RELOAD , de faon ce que le premier allumage de la LED seffectue aprs 10 pressions sur notre bouton-poussoir, et initialiser notre LED en sortie de RB0.
;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------BANK0 ; slectionner banque0 clrf PORTB ; sorties PORTB 0 bsf STATUS,RP0 ; passer en banque1 bcf TRISB,0 ; RB0 en sortie (LED) ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------movlw INTCONVAL ; charger valeur registre interruption

179

movwf INTCON movlw PIE1VAL movwf PIE1

; initialiser interruptions ; Initialiser registre ; interruptions priphriques 1

bcf movlw movwf movlw movwf movlw movwf bsf

; initialiser timer 1 ; -------------------STATUS,RP0 ; passer banque 0 LOW RELOAD ; octet faible de recharge timer 1 TMR1L ; dans registre poids faible HIGH RELOAD ; octet fort de recharge timer 1 TMR1H ; dans registre poids fort B'000000111' ; timer en service en mode compteur asynchrone T1CON ; dans registre de contrle timer 1 INTCON,GIE ; valider interruptions

Reste notre programme principal, qui ne fait strictement rien :


;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start goto start ; boucler END ; directive fin de programme

Programmez votre 16F876, placez la sur votre circuit, et lancez lalimentation. Pressez le bouton calmement une trentaine de fois. Votre LED doit sinverser chaque multiple de 10 pressions. Si , ce niveau, vous obtenez un comportement erratique (la LED sallume nimporte quand, frtille etc.), cest que vous avez travaill avec des fils volants. Or, la rsistance de rappel la masse de T1CKI (R2) est de 100 Kohms, ce qui est une grosse valeur. Il faut savoir que lorsque vous travaillez avec des valeurs suprieures 10 Kohms, les interfrences (parasites) sont de plus en plus susceptibles daffecter votre montage. Dans ce cas, le remde est fort simple : vous remplacez R2 par une rsistance de 10Kohms. Cependant, alors, pour respecter tous nos calculs, il vous faudra alors remplacer R1 par une rsistance de 1Kohms, et C par un condensateur de 10F. Vous voyez quil y a toujours plusieurs paramtres lectroniques prendre en compte pour la ralisation dun montage. En rgle gnrale, utilisez des rsistances dentre infrieures ou gales 10 Kohms. Bon, ce stade, tout le monde est galit, avec un montage oprationnel. Coupez lalimentation, attendez 5 secondes, et relancez lalimentation. Pressez lentement le bouton-poussoir (1 pression par seconde), et comptez les impulsions avant le premier allumage de la LED. Vous vous attendiez 10 impulsions, mais vous devez en trouver 11. Que voil un curieux phnomne, vous ne trouvez pas ? En fait, lexplication est donne par le fonctionnement interne du timer 1 du PIC. La premire impulsion nest prise en compte que si le signal dentre a subit au moins un flanc descendant. Et oui, vous aviez dj oubli ?
180

Ceux qui sen sont souvenus sont trs forts et mritent une mention trs bien . Pour rsumer, la mise sous tension de notre montage, le niveau sur T1CKI est bas. Lorsque vous pressez la premire fois sur le bouton, vous avez un flanc montant. Mais ce flanc montant nayant pas t prcd dun flanc descendant, ce flanc nest tout simplement pas pris en compte. Vous relchez ensuite le bouton, ce qui fait passer votre niveau dentre de 1 0 . Ceci constitue votre premier flanc descendant. Donc, le comptage seffectuera partir de la prochaine pression du bouton-poussoir. Vous pouvez vrifier ceci en constatant que si le premier allumage de la LED seffectue aprs 11 pressions, tous les autres allumages et extinctions seffectueront aprs 10 impulsions. Vous pourriez corriger le programme en initialisant la valeur du timer1 dans la routine de dinitialisation -9 au lieu de -10 . La valeur -10 devant demeurer dans la routine dinterruption. Si vous voulez vrifier, il suffit donc dajouter 1 dans TMR1L (pour les rticents en maths, pour passer de 10 -9 , il faut ajouter 1 et non soustraire . Votre routine dinitialisation du timer devient :
; initialiser timer 1 ; -------------------STATUS,RP0 ; passer banque 0 LOW RELOAD+1 ; octet faible de recharge timer 1 TMR1L ; dans registre poids faible HIGH RELOAD ; octet fort de recharge timer 1 TMR1H ; dans registre poids fort B'000000111' ; timer en service en mode compteur asynchrone T1CON ; dans registre de contrle timer 1 INTCON,GIE ; valider interruptions

bcf movlw movwf movlw movwf movlw movwf bsf

15.13 Errata : Fonctionnement non conforme Je termine sur une remarque importante qui concerne plusieurs versions de 16F87x., quand le timer 1 est configur en mode compteur , que ce soit synchrone ou asynchrone : La lecture du registre TMR1L peut empcher TMR1H dtre incrment durant le temps de la lecture. Inversment, la lecture de TMR1H peut empcher TMR1L dtre incrment durant le temps de la lecture. Ceci peut tre particulirement gnant, si le passage de TMR1L de 0xFF 0x00 ne provoque pas lincrmentation de TMR1H, donnant de ce fait une erreur non ngligeable. Microchip indique que ce point sera prochainement corrig, mais cela fait plusieurs versions de suite que cette correction nest pas effective. A vous, dans le cas o vous utilisez ces possibilits, soit de grer ce phnomne, soit de vous renseigner chez Microchip pour savoir si votre propre version de PIC intgre toujours ce bug.

181

La solution de Microchip pour contourner le problme est pour le moins radicale : il recommande, si vous utilisez le timer 1 avec le bit TMR1CS positionn (mode compteur) et que vous devez lire la valeur 16 bits du compteur, dutiliser tout simplement un autre timer ou une autre mthode pour votre application. Prcisons, pour ceux qui nauraient pas bien compris, que a ne concerne que la lecture ou lcriture des registres TMR1L/TMR1H sans arrter le timer. Les solutions que jai proposes pour viter les erreurs ne fonctionneront donc que sur les versions debugges des PICs. Jai cependant donn ces solutions, car ce sont des grands classiques sur toutes les familles de microcontrleurs utilisant des compteurs sur 16 bits. Connatre ces techniques vous sera toujours utile. De plus, lorsque vous passesez des PICs plus performants, il faudra esprer que Microchip aura rsolu ce problme.

182

Notes :

183

Notes :

184

16. Le debuggage pin-stimulus


Et oui, en cas de problme, la premire question qui vient lesprit est : comment puis-je simuler un programme qui fait intervenir des modifications de niveaux sur les pins ? Jintroduis ce chapitre ici, car vous vous posez peut-tre la question suite vos ventuels dboires concernant lexercice prcdent. En fait, MPLAB dispose de la possibilit de crer un fichier qui contient les vnements envoyer sur une ou plusieurs pins, en fonction du nombre de cycles couls depuis le dbut du programme. Prenez votre projet cmpt1 , que nous allons faire fonctionner en mode simulation. Allez dans le menu : file -> New . Une fentre souvre alors. Dans cette fentre, vous commencez par introduire le mot CYCLE qui sert MPLAB (anciennes versions) dterminer quil sagit dun fichier dvnements. Tapez une tabulation, puis entrez le numro de la pin simuler. Pour notre part, il sagit de T1CKI. Mais comme MPLAB ne reconnat pas ce nom, nous utiliserons son autre nom : RC0 . Vous pouvez ensuite taper dautres noms spars par des tabulations, chaque nom correspondant une pin simuler. Tapez return Sous la colonne CYCLE , vous tapez le numro du cycle auquel se rapporte la modification. Il sagit ici de cycles dinstructions. Pour rappel ,chaque instruction utilise un cycle, sauf les sauts qui en ncessitent 2. Ensuite, sous la colonne RC0 , donc aprs avoir tap une nouvelle tabulation, entrez ltat que vous dsirez pour la pin en question. Procdez de mme pour les autres pins, et vous pouvez terminer par un commentaire spar par un ;

Remarque Pour savoir quels sont les noms de pins que vous pouvez utiliser, allez dans Debug>Simulator Stimulus>Asynchronous . Une fois la fentre ouverte, allez sur Stim 1 (P) et cliquez avec le bouton de droite. Choisissez assign pin et vous avez la liste des noms de pins autoriss. Observez que cette fentre vous donne accs MCLR, donc vous permet de simuler des oprations de reset .

185

Voici le fichier que nous allons crer pour la circonstance :


CYCLE 00 30 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 190 200 210 220 230 240 250 260 270 280 RC0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 ; au dbut, RC0 = 0 ; 30 cycles aprs le dbut, mettre RC0 1 ; puis, tous les 10 cycles, on inverse RC0

A titre dinformation, si vous aviez voulu utiliser 2 pins (RC0 et RC1) , voici ce que vous auriez pu crer :
CYCLE 00 30 40 RC0 0 1 0 RC1 0 0 1 ; au temps 0, RC0 et RC1 sont 0 ; aprs 30 cycles, RC0 = 1 et RC1 = 0 ; 10 cycles plus loin, RC0 = 0 et RC1 = 1

Notez que la valeur des cycles reprsente le nombre de cycles couls depuis le dbut du programme. Maintenant, sauvez votre fichier (file -> save as) en entrant un nom termin par lextension .sti pour STImulus . Ne tenez donc pas compte des suffixes indiqus par dfaut. Vous sauvez alors votre fichier sous cmpt1.sti . Vrifiez imprativement que la case unix format ne soit pas coche. Nous allons ensuite indiquer MPLAB quil doit utiliser ce fichier : Allez dans le menu : debug -> simulator stimulus Choisissez pin-stimulus -> enable

186

Un requester souvre alors, vous demandant de choisir votre fichier. Choisissez cmpt1.sti . A ce stade, allez dans windows -> Stopwatch

Une fentre souvre alors, qui vous indique le nombre de cycles que MPLAB estime coul depuis le dbut de votre simulation. Si la fentre cycles nindique pas 0 , alors pressez Zero . Vous pouvez maintenant lancer la simulation de faon classique. Pressez F6, puis F7 pour chaque pas. Remarquez quau cycle 30, RC0 passe 1 et que TMR1L est incrment. Continuez vos actions sur F7 jusqu ce que une interruption soit gnre. La dernire action de votre fichier sti se termine au cycle 280. Une fois arriv ce cycle, il vous suffit de presser zero dans votre fentre stopwatch pour que la lecture de votre fichier sti reprenne au dbut, sans influencer le droulement de votre programme. Vous constaterez galement que MPLAB incrmente votre registre TMR1L au premier flanc montant de RC0. Le programme ne prsuppose pas des tats prcdents de RC0. Il ne peut donc savoir sil sagit ou non dun premier flanc montant. Ceci doit attirer votre attention sur les limites des simulations. Une simulation reste une simulation, et ne peut donc prendre toutes les contraintes relles en compte. Un autre exemple est que, en mode simulation, toutes les variables en zone RAM sont initialises 0, alors que dans la ralit, leur contenu est alatoire. Idem concernant le contenu des timers. Nous verrons dans le livre suivant une mthode de debuggage beaucoup plus puissante.

187

Notes :

188

17. Le timer 2
Dans ce chapitre, nous allons maintenant tudier le dernier des trois timers de notre PIC. Celui-ci dispose, comme vous pouvez dj vous en douter, de caractristiques diffrentes des 2 autres. Cette approche de Microchip permet lutilisateur davoir un panach des modes dutilisations possibles, tout en conservant, pour chaque timer, une facilit dutilisation et une complexit, donc un cot, abordables. Votre dmarche, lorsque vous choisirez un timer, sera donc fonction de lutilisation envisage. Jy reviendrai. 17.1 Caractristiques du timer 2 Le timer 2 est un compteur sur 8 bits, donc nous ne rencontrerons pas les difficults inhrentes la lecture et lcriture de ses registres. Le timer 2, comme les prcdents, possde un prdiviseur. Celui-ci peut tre paramtr avec une des 3 valeurs suivantes : 1,4, ou 16. Nous sommes donc pauvres ce niveau. Cependant, le timer 2 dispose galement dun postdiviseur, qui effectue une seconde division aprs lunit de comparaison, que nous allons voir. Ce postdiviseur peut prendre nimporte quelle valeur comprise entre 1 et 16, ce qui donne un grand choix possible ce niveau. La valeur du diviseur total, vue par lutilisateur, est bien entendu obtenue en multipliant la valeur du prdiviseur par celle du postdiviseur. Moyennant ceci, il est possible, avec le timer 2, dobtenir les valeurs de diviseur suivantes : 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20,24,28,32,36,40,44,48,52,56,60,64,80,96,112,128 ,144,160,176,192,208,224,240,256 Vous voyez quavec ce timer, vous disposez dun large ventail de diviseurs effectifs. Le timer 2 incrmente pour sa part le registre TMR2, registre unique puisque comptage sur 8 bits. Les valeurs de division minimale et maximale sont donc identiques celles du timer 0, qui disposait galement dun comptage sur 8 bits, avec prdiviseur de 1 256. Le registre TMR2 est remis automatiquement 0 lors dun reset, contrairement au timer 1, pour lequel vous deviez vous en charger. Ceci peut tre un avantage ou un inconvnient, suivant le type de raction que vous attendez du timer. Cest clair quun reset inopin et non dsir du pic remettra dans ce cas votre timer 2 dans son tat initial.

189

Il faut galement tenir compte que ce timer ne dispose daucune entre extrieure via une pin du PIC. Il ne peut donc fonctionner quen mode timer pur. 17.2 Le timer 2 et les interruptions Le timer 2 fonctionne, ce niveau, comme le timer1. Le flag dinterruption se nomme TMR2IF, en toute logique, tandis que le bit dautorisation sappelle TMR2IE. La principale diffrence provient de lvnement qui cause le positionnement de TMR2IF, donc qui cause linterruption. Je vais en parler un peu plus loin. Tout comme pour le timer 1, il sagit dune interruption priphrique, donc, la procdure pour autoriser les interruptions du timer 2 se fera en 3 tapes : Autorisation des interruptions priphriques via le bit PEIE du registre INTCON Autorisation de linterruption timer 2 via TMR2IE du registre PIE1 Autorisation gnrale des interruptions via le bit GIE du registre INTCON Je vous renvoie donc aux chapitres prcdents pour des renseignements plus prcis. 17.2 Le timer 2 et les registres PR2 et T2CON Le principe de fonctionnement des 2 prcdents timers tait le suivant : On incrmente le contenu du TMR (sur 1 ou 2 octets) suivant lvnement choisi et la valeur du prdiviseur. Une fois que le timer dborde , ceci dclenche le positionnement du flag associ

Le principe du timer 2 est diffrent, dans le sens que lvnement dtect nest pas le dbordement ordinaire du timer (cest--dire le passage de 0xFF 0x00), mais le dbordement par rapport une valeur prdfinie. Cette valeur tant mmorise dans le registre PR2 (banque 1). Nous pouvons donc avoir, par exemple, dbordement de 0x56 0x00, en plaant la valeur 0x56 comme valeur maximale dans le registre PR2. On peut donc dire que le fonctionnement du timer est le suivant : On incrmente le contenu du prdiviseur chaque cycle dinstruction Chaque fois que ce contenu correspond un multiple de la valeur du prdiviseur, on incrmente TMR2 (contenu du timer 2) Chaque fois que le contenu de TMR2 dpasse le contenu de PR2, on remet TMR2 0, et on incrmente le contenu du postdiviseur.

190

Chaque fois que le contenu du postdiviseur correspond un multiple de la valeur du postdiviseur, on positionne le flag TMR2IF. Pour clarifier la comprhension, je vous donne le schma-bloc du timer 2.

Vous constatez quil ny a pour ce timer quune seule source de comptage, savoir lhorloge principale du PIC divise par 4, autrement dit le compteur dinstructions. Nous sommes donc bien en prsence dun timer pur . Une prdivision est paramtre par T2CKPS0 et T2CKPS1. La sortie du prdiviseur incrmente le registre TMR2. Cette valeur est compare avec la valeur contenue dans PR2. Chaque fois que les contenus de TMR2 dpasse celle de PR2, la sortie du comparateur incrmente la valeur contenue dans le postdiviseur. Cette sortie effectue galement un reset de TMR2, qui redmarre donc 0. En fait ce schma est trompeur, bien que dorigine Microchip, car il risque dinduire en erreur. En effet, la sortie du comparateur sera prise en compte le cycle suivant lgalit des deux registres, donc lors du dbordement du timer par rapport la valeur de PR2. Donc, le nombre de cycles rellement compts est, abstraction faite des diviseurs, la valeur de PR2 incrmente de 1. En effet, tout comme pour le timer0, vous aviez une impulsion chaque dbordement de 0xFF vers 0x00, pour le cas du timer2, vous aurez dbordement de la valeur de PR2 vers 0x00. Le timer 0 donnait bien, pour une valeur maximale de 0xFF, un nombre dimpulsions comptes de 0x100 (D256), donc valeur maximale + 1. Il sagit donc ici du mme phnomne et du mme calcul. Chaque fois que le contenu du postdiviseur est gal un multiple de la valeur de ce celuici, paramtre par TOUTPS0 TOUTPS3, le flag TMR2IF est forc 1, et une interruption est ventuellement gnre.

191

Ne vous proccupez pas de la flche sortie de TMR2 , le timer2 est utilis en interne pour dautres fonctions que nous tudierons plus tard. Une criture dans le registre TMR2 efface le contenu du prdiviseur et du postdiviseur. Pour rappel, ne pas confondre contenu (nombre dvnements compts) et valeur (dtermine par les bits de configuration). Forts de tout ceci, vous avez maintenant compris que la spcificit du timer 2, et donc son principal avantage, est quil permet de configurer le dbordement sur nimporte quelle valeur de TMR2, associ un large ventail de valeurs de diviseur. Inutile donc dattendre le passage de 0xFF 0x00, quoique cela reste possible, simplement en plaant 0xFF dans PR2. Cet avantage, combin la grande flexibilit de lensemble prdiviseur/postdiviseur, permet dobtenir trs facilement des dures dinterruption prcises sans complications logicielles. Voici prsent le contenu du registre T2CON, qui permet de paramtrer prdiviseur et postdiviseur, ainsi que dautoriser ou non le fonctionnement du timer2. T2CON (en banque 0) - b7 : non utilis, laisser 0 - b6 : TOUTPS3 : Timer2 OUTput PostScale bit 3 - b5 : TOUTPS2 : Timer2 OUTput PostScale bit 2 - b4 : TOUTPS1 : Timer2 OUTput PostScale bit 1 - b3 : TOUTPS0 : Timer2 OUTput PostScale bit 0 - b2 : TMR2ON : TiMeR 2 ON - b1 : T2CKPS1 : Timer 2 ClocK PreScale bit 1 - b0 : T2CKPS0 : Timer 2 ClocK PreScale bit 0 Vous constatez que les bits TOUTPSx permettent de configurer la valeur du postdiviseur. Il y a 16 valeurs possibles (0 15). Comme une valeur de diviseur de 0 na aucun sens, le nombre form par les 4 bits de TOUTPSx est incrment de 1 pour obtenir la valeur effective du postdiviseur. Voici donc les valeurs utilisables :

192

b6 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1

b5 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1

b4 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1

b3 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1

Postdiviseur 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Quant au prdiviseur, on na le choix quentre 3 valeurs : b1 0 0 1 1 b0 0 1 0 1 Prdiviseur 1 4 16 16

Il me reste vous donner la formule de la dure sparant 2 positionnements conscutifs du flag TMR2IF. Vous pourriez retrouver cette formule vous-mme, en suivant les explications prcdentes : Dure totale = temps dune instruction * prdiviseur * postdiviseur * (PR2 +1) La valeur maximale est donc bien, comme pour le timer 0 de 16*16*256 = 65536. 17.3 Utilisation pratique de notre timer 2 Supposons que nous dsirions raliser une interruption toutes les secondes. Nous avions vu quavec nos autres timers, ceci nous posait problme, car nous navions aucune valeur multiple exacte possible, do une erreur au final (sauf utiliser un second quartz sur le timer1 ou diminuer la frquence de fonctionnement du PIC). Nous calculons quavec notre quartz de 20MHz, nous avons 5.000.000 de cycles dinstruction par seconde. Nous allons donc devoir compter le plus prcisment possible jusque 5.000.000.

193

Vous vous rendez bien compte que compter jusque 5.000.000 ne peut tre ralis en un seul passage dans la routine dinterruption, le maximum tant de 65536. Nous allons donc passer un certain nombre de fois dans la routine dinterruption. Cherchons tout dabord le plus grand nombre de 8 bits permettant une division exacte de 5.000.000. Sans tre expert en mathmatiques, le nombre D250 convient trs bien. Nous pouvons donc dcider quaprs 250 passages dans notre routine dinterruption, nous aurons atteint une seconde. Ceci implique que nous devions compter jusque 5.000.000/250 = 20.000 entre 2 passages. La valeur maximale pouvant tre compte par notre timer 2, je le rappelle, est de 256 (si PR2 = 0xFF) multipli par 256 (prdiviseur * postdiviseur), soit 65536. Nous sommes donc en dessous, cela reste donc possible. Nous allons maintenant essayer de trouver une combinaison prdiviseur/postdiviseur qui nous donne un rsultat entier, en commenant par la plus forte valeur, soit 256. Souvenezvous que les valeurs possibles de toutes les combinaisons sont : 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,20,24,28,32,36,40,44,48,52,56,60,64,80,96,112,128 ,144,160,176,192,208,224,240,256 Une valeur de division de 256 nous donne 20.000 / 256 = 78,125 Une valeur de division de 240 nous donne 20.000/240 = 83,33 Nous continuons de la sorte, toujours des rsultats fractionns Une valeur de division de 160 nous donne 20.000/160 = 125

Nous voici donc avec une valeur entire. Nous devons donc configurer PR2 avec une valeur de (125 1), soit 124, ce qui est tout fait dans nos possibilits. Pour raliser notre programme, nous devrons donc : Configurer le prdiviseur 16 Configurer le postdiviseur 10 (10*16 = 160) Plaer D124 dans PR2

Moyennant quoi, pour chaque 250me passage dans la routine dinterruption, nous aurons une dure exacte dune seconde. Ceci nous donne donc une prcision gale celle de notre quartz, sans utiliser de seconde horloge, qui aurait t ncessaire avec le timer1. Vous voyez quavec ce timer2, vous pouvez obtenir des temps configurables de faon trs prcise, et dans un grand ventail. Cest l la puissance du timer 2. Mais bon, je ne vais pas vous laisser tomber ici, nous allons donc raliser rellement un programme qui permet une fois de plus de faire clignoter notre LED une frquence de 1 Hz. 1 Hz, vous avez dit ? Alors ne tombons pas dans le pige, 1Hz cest 0.5 seconde allume et 0.5 seconde teinte, donc nous devons compter non pas 1 seconde, mais 0.5 seconde.

194

Qu cela ne tienne, il nous suffit de passer 125 fois dans notre routine dinterruption au lieu de 250 fois. Simple, non ? Vous auriez besoin du dixime de seconde ? Et bien, comptez jusque 25. Effectuez une copie de votre fichier maquette, et nommez-la led_tmr2.asm . Commenons par crire len-tte et la directive config.
;***************************************************************************** ; Exercice d'utilisation du timer2 : ralisation d'une LED qui clignote * ; une frquence exacte de 1Hz. * ; * ;***************************************************************************** ; * ; NOM: Led_Tmr2 * ; Date: 21/05/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Notes: La LED est connecte sur RB0 * ; On utilise les interruptions timer2 * ; 1 interruption toutes les 4 ms * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;_CP_OFF Pas de protection ;_DEBUG_OFF RB6 et RB7 en utilisation normale ;_WRT_ENABLE_OFF Le programme ne peut pas crire dans la flash ;_CPD_OFF Mmoire EEprom dprotge ;_LVP_OFF RB3 en utilisation normale ; _BODEN_OFF Reset tension hors service ;_PWRTE_ON Dmarrage temporis ;_WDT_OFF Watchdog hors service ;_HS_OSC Oscillateur haute vitesse (4Mhz<F<20Mhz)

Ensuite, les assignations systme, et plus particulirement celles qui nous intressent, savoir les valeurs placer dans les registres de configuration et dinterruptions.
;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000000' ; Rsistances rappel +5V hors service

195

; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQUB'01000000' ; autorisation gnrale priphriques ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQUB'00000010' ; interrupt TMR2

Maintenant, les assignations du programme, soit la valeur utiliser pour le compteur de passages dans la routine dinterruption, et la valeur utiliser pour le registre PR2.
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** PRVAL EQU D'124' COMPTVAL EQU D'125' ; le tmr2 compte jusque (124+1) * 160 * 0,2s = 4ms ; pour 125 passages dans tmr2 = 125 * 4ms = 500ms

Un petit define pour la position de la LED sur RB0 :


;***************************************************************************** ; DEFINE * ;***************************************************************************** #DEFINE LED PORTB,0 ; LED de sortie

La zone des variables se rduit sa plus simple expression : une seule variable est ncessaire pour compter les passages dans la routine dinterruption. Aucune sauvegarde de registre ncessaire, puisque notre programme principal nexcute rien.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 compteur : 1 ENDC ; Dbut de la zone (0x20 0x6F) ; compteur de passages dans tmr2 ; Fin de la zone

On arrive sur ladresse de dmarrage aprs reset :


;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 ; Adresse de dpart aprs reset goto init ; Initialiser

La routine dinterruption na nul besoin des sauvegardes, ni de la gestion des interruptions multiples. Donc, elle ne comportera que la routine dinterruption timer2 en elle-mme.
***************************************************************************** ; ROUTINE INTERRUPTION TMR2 * ;***************************************************************************** ;----------------------------------------------------------------------------; Un passage dans cette routine tous les 160*125*0,2s = 4ms. ; Pas de programme principal, donc pas de sauvegarde effectuer ; Dure d'allumage de la LED = dure d'un cycle * prdiviseur * postdiviser * ; valeur de comparaison du timer * nombre de passages dans cette routine =

196

; 0,2s * 16 * 10 * 125 * 125 = 500.000 s = 500ms = 0.5s ;----------------------------------------------------------------------------org 0x004 ; adresse d'interruption decfsz goto movlw movwf movlw xorwf intend bcf retfie compteur,f intend COMPTVAL compteur B'00000001' PORTB,f PIR1,TMR2IF ; ; ; ; ; ; dcrmenter compteur pas 0, fin interruption valeur de recharge dans compteur de passage valeur pour inverser LED inverser LED

; effacer flag interupt tmr2 ; retour d'interruption

Vous voyez que cette routine dcrmente le compteur de passages, et, lorsque ce dernier atteint 0, il inverse ltat de la LED. Nous trouvons ensuite la routine dinitialisation, qui commence par initialiser le PORTB et les registres dinterruption.
;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; Initialiser portB ; ----------------bsf STATUS,RP0 ; passer banque1 bcf LED ; passer RB0 en sortie ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1

movlw movwf movlw movwf

Il est temps maintenant dinitialiser notre timer 2, ce qui consiste placer les valeurs de pr et de postdiviseur, et de le mettre en service, aprs avoir initialis le registre PR2 avec la valeur de dbordement . Notez que jutilise dessin la notion de dbordement, car celle de comparaison induit en erreur, comme je vous lai dj expliqu.
; initialiser Timer 2 ; ------------------PRVAL ; valeur de "dbordement" de tmr2 PR2 ; dans PR2 STATUS,RP0 ; repasser banque 0 B'01001110' ; postdiviseur 10,prdiviseur 16,timer ON T2CON ; dans registre de contrle

movlw movwf bcf movlw movwf

Ne reste donc plus qu initialiser notre variable, puis autoriser les interruptions.

197

; initialiser variable ; -------------------movlw COMPTVAL ; valeur de recharge movwf compteur ; dans compteur de passage interruption ; autoriser interruptions (banque 0) ; ---------------------------------INTCON,GIE ; valider interruptions

bsf

Quant notre programme principal, il ne fait rien, et donc se contente de boucler sur luimme.
;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start goto start END ; boucler ; directive fin de programme

Voici notre programme termin, lancez lassemblage, placez le fichier .hex dans votre PIC, et observez la LED. Elle clignote la frquence de 1Hz, avec la prcision du quartz. Voici donc une solution de base pour raliser une horloge ou dautres applications bases sur la mesure du temps en secondes. Nul besoin de quartz spcial avec notre timer2. Si vous dcidez de simuler ce programme, vous pouvez, dans la fentre des registres spciaux, remarquer les registres t2pre et t2post (tout en bas) qui indiquent le contenu du pr et du post diviseur du timer. Ces registres ne sont pas accessibles par votre programme, ils sont simuls par MPLAB pour vous montrer le droulement de votre programme. Remarquez en mode pas pas que la valeur D124 dans TMR2 ne provoque ni le reset de TMR2, ni le positionnement du flag TMR2IF. Cest au moment ou TMR2 devrait passer D125 que ces oprations seffectueront.

198

18. Rcapitulatif sur les timers


18.1 Le choix du timer Vous vous demandez peut-tre comment choisir votre timer dans une application particulire. En fait, il sagit de rester logique. Prenons quelques cas concrets : 18.1.1 Vous dsirez mesurer un temps compris entre 256 et 65536 cycles. Dans ce cas, tout dpend de la prcision demande, et de la valeur mesurer. Sil sagit dune valeur multiple de 256 (par exemple 512), vous pouvez utiliser le timer0, 1, ou 2 sans aucun problme et avec une prcision maximale. Si, par contre, vous devez mesurer des nombres de cycles non divisibles par des puissances de 2, et quune grande prcision est demande, vous avez 3 options : Soit vous utilisez le timer2, en utilisant la technique prcdemment dcrite. Soit vous utilisez le timer1, qui dispose de la possibilit de comptage jusque 65536 sans utiliser de prdiviseur Soit vous utilisez le timer0, mais au prix dune perte de prcision ou dune programmation plus dlicate.

18.1.2 Vous dsirez mesurer des temps allant jusque 524288 Vous pouvez alors utiliser le timer 1, avec un prdiviseur. Naturellement, si la valeur atteindre nest pas un multiple exact du prdiviseur, votre prcision en sera altre. 18.1.3 Vous dsirez mesurer des temps quelconques avec une grande prcision Dans ce cas, si on admet que le nombre de cycles mesurer nest pas un multiple exact dune valeur de diviseur, vous disposez de 2 mthodes principales et simples pour obtenir ces mesures de temps : Soit vous utilisez le timer1 avec un second quartz calcul de faon obtenir des multiples exacts. Ceci prsente lavantage de moins utiliser de temps de PIC, puisque la seconde horloge aura une frquence maximale de 200KHz, ce qui permet despacer les interruptions. Par contre, ceci ncessite de sacrifier 2 pins et dutiliser plus de matriel. Soit vous utilisez le timer2, en calculant des valeurs multiples de pr et postdiviseur et en plaant le rsultat dans PR2. Une longue dure sera prise en compte en comptant les passages dans la routine dinterruption. Cette mthode est plus simple et plus conomique, mais ncessite plus de temps dexcution, surtout pour les longues dures pour lesquelles des interruptions seront sacrifies dans le seul but de les compter. Le temps doit bien entendu tre multiple de la dure dune instruction.

199

18.1.4 Vous dsirez compter des vnements Dans ce cas le timer2 est inutilisable, reste donc : Soit le timer 0 qui permet de choisir entre dtection des flancs montants ou descendants, mais limite le comptage 256 (multipli par le prdiviseur) Soit le timer 1 qui permet de compter jusque 65536 sans prdiviseur, mais qui impose de dtecter uniquement les flancs montants de votre signal.

Je pourrais multiplier les exemples, mais le but tait simplement de vous montrer que chaque timer est plus ou moins bien adapt au but recherch. Dans la grande majorit des applications, le choix du timer ncessitera une petite rflexion. Prenez garde que llectronique est lie au logiciel, les deux devront donc tre tudis en parallle. En effet, pour simple exemple, si vous avez besoin des caractristiques du timer1 pour compter des vnements, vous serez alors dans lobligation de connecter le signal sur T1CKI et non sur T0CKI. De plus, si vous dsiriez compter des flancs descendants, vous devrez ajouter un montage qui inverse votre signal dentre. 18.2 Les bases de temps multiples Il vous arrivera probablement de devoir utiliser plusieurs bases de temps dans le mme programme. La premire solution qui vient lesprit est dutiliser plusieurs timers. Ceci peut se rvler la bonne solution, mais ce nest pas toujours ncessaire. En effet, si vos bases de temps disposent dun dnominateur commun, vous pouvez utiliser ce dnominateur comme base de temps gnrale, et compter les passages dans la routine dinterruption. Imaginons par exemple que vous ayez besoin de grer une impulsion sur une broche toutes les 200ms, et que vous dsirez faire clignoter une LED une frquence de 1Hz. Vous pouvez alors dcider dutiliser un timer pour la LED, un autre pour les impulsions. Mais vous pouvez aussi vous dire : jai besoin dune dure de 500ms pour ma LED, et dune autre de 200ms pour les impulsions. Je dcide de crer une dure dinterruption de 100ms. Ma routine dinterruption sera donc du type : Je dcompte compteur 1 Compteur 1 = 0 ? Non, je ne fais rien Oui, je gre mon impulsion, et je recharge compteur 1 avec 2 (200ms) Je dcrmente compteur 2 Compteur 2 = 0 ? Non, fin dinterruption Oui, je gre ma LED et je recharge compteur 2 avec 5 (500ms) Fin dinterruption

200

Vous voyez donc quavec cette mthode, je gre 2 temps diffrents avec le mme timer. Dans la pratique, cest une mthode que vous utiliserez probablement souvent.

18.3 Possibilits non encore abordes Arriv ce stade, vous ne disposez pas encore de toutes les possibilits des performances et des utilisations possibles des timers. Pour complter vos connaissances, et pour apprhender toutes les remarquables diversits dapplication, il vous reste encore tudier les modules CCPx abords dans un chapitre ultrieur. Vous verrez alors que vous pouvez disposer de fonctions supplmentaires, et mme vaincre ce que vous pensiez tre les limites des timers concerns.

201

Notes :

202

19. Le convertisseur analogique/numrique


19.1 Prambule Tout dabord, je vais vous demander de ne pas paniquer. En effet, je vais dtailler le fonctionnement du convertisseur analogique/numrique, afin de permettre de lexploiter dans ses moindres ressources. Ce faisant, vous allez trouver ici des formules mathmatiques pour le moins barbares. Mais, rassurez-vous, dans limmense majorit des cas vous naurez pas besoin de ces formules. En effet, je vous donnerai les valeurs sres employer pour les viter, au prix dune lgre perte de performance au niveau du temps de conversion, perte qui na gnralement pas la moindre importance. Beaucoup de personnes me demandent comment effectuer une conversion analogique/numrique, mais je maperois que peu savent ce que cette conversion reprsente rellement et quelle sont ses limites. Je crains donc quil ne faille commencer par un peu de thorie. De plus, cette approche prsente lavantage de vous permettre de vous sortir de toutes les situations futures quil mest impossible de prvoir lheure actuelle, par exemple, sortie dune nouvelle version de 16F87x avec des caractristiques temporelles diffrentes de celles du PIC tudi ici. Bien entendu, si tout ceci ne vous intresse pas, ou que vous jugez que cela ne peut rien vous apporter, vous tes libres de tourner les pages votre convenance. 19.2 Nombres numriques, analogiques et conversions Mais commenons donc par le commencement. Quest-ce quun Convertisseur Analogique/Digital (CAD), de prfrence nomm Convertisseur Analogique/Numrique (CAN), ou Analogic to Digital Converter (ADC) ? Bien quon utilise souvent la notion de convertisseur analogique/digital , la bonne expression franaise serait plutt convertisseur analogique/numrique . Mais bon, je ne suis pas ici pour vous donner une leon de franais, jen suis du reste incapable. En fait, nous avons vu jusqu prsent que nous pouvions entrer un signal sur les pins du PIC, qui dterminait, en fonction du niveau de tension prsente, si ce signal tait considr comme un 1 ou un 0 logique. Ceci est suffisant pour tout signal binaire, cest--dire ne prsentant que 2 valeurs possibles. Supposons que vous dsiriez, avec votre PIC, mesurer une valeur analogique, cest--dire, en fait, connatre la valeur de la tension prsente sur une pin de votre PIC. Il est des tas dapplications o vous aurez besoin dune telle possibilit, par exemple si vous voulez mesurer la tension de votre batterie laide dun PIC.

203

Comme llectronique interne du PIC ne comprend que les valeurs binaires, il vous faudra donc transformer cette valeur analogique en une reprsentation numrique. Ce procd sappelle numrisation, et, pour leffectuer, vous avez besoin dun convertisseur analogique/numrique. Il importe ce niveau de rappeler ce quest un signal analogique. Commenons par examiner une tension, par exemple la tension de votre alimentation. Cette tension peut sexprimer par une infinit de valeur, et vous pouvez faire varier cette tension de faon continue, sans quil y ait un trou entre 2 valeurs. Un nombre numrique, au contraire, dispose dun nombre fini de valeurs, on parlera de valeurs discrtes . Par exemple, dans un octet, vous pouvez coder 0x01 ou 0x02, mais il ny a pas de valeur intermdiaire, au contraire de votre valeur analogique, pour laquelle vous pouvez toujours insrer une telle valeur. Le problme est donc en premier lieu de savoir comment passer mathmatiquement dune reprsentation lautre, donc comment convertir du numrique vers lanalogique et rciproquement. Vous effectuez couramment, et sans le savoir, des conversions analogiques/numriques. En effet, lorsque vous dcidez darrondir des valeurs, vous transformez une valeur analogique (qui peut donc prendre une infinit de valeur) en une valeur numrique (qui contient un nombre fini dlments). En effet, prenons le nombre 128,135132. Si vous dcidez darrondir ce nombre en conservant 4 digits, vous direz que ce nombre numris devient 128,1. Vous voyez donc quen numrisant vous perdez de la prcision, puisque vous liminez de fait les valeurs intermdiaires. Avec cet exemple, vous pouvez reprsenter les valeurs 128,1 et 128,2, mais toute autre valeur intermdiaire sera transforme en un de ces deux nombres discrets. La prcision obtenue dpend donc du nombre de digits que vous souhaitez conserver pour le rsultat, lequel sera entach dune erreur. Que vaut cette erreur (ne me dites pas 0,1) ? En fait, si vous rflchissez un peu, vous voyez que 128,13 sera converti en 128,1, tandis que 128,16 sera converti en 128,2. Lerreur maximal obtenue est donc de la moiti du plus faible digit. Comme notre digit vaut 0,1, lerreur finale maximale sera donc dans notre exemple de 0,05. Si vous convertissez maintenant dans lautre sens, vous pouvez dire que votre nombre numris de 128,1 reprsente en ralit une grandeur analogique relle comprise entre 128,05 et 128,15. La valeur moyenne tant de 128,1, ce qui est logique. La constatation est qu une seule valeur numrique correspond une infinit de valeurs analogiques dans un intervalle bien dfini. Supposons que nous voulions numriser une valeur analogique comprise entre 0 et 90 en une valeur numrique code sur 1 digit dcimal. Nous voyons tout de suite que la valeur analogique 0 sera traduite en D0, la valeur analogique 10 sera traduite en D1, etc. jusque la valeur 90 qui sera convertie en D9.

204

Si maintenant notre valeur analogique varie entre 10 et 100, la valeur analogique 10 sera convertie en D0, la valeur analogique 20 sera convertie en D1, etc. jusque la valeur analogique 100 qui sera convertie en D9. Donc, puisque nous ne disposons ici que dun digit, qui peut prendre 10 valeurs, de 0 9 pour traduire une valeur qui peut varier dun minimum un maximum, on peut tirer des petites formules. On peut dire que chaque digit numrique reprsente la plage de valeurs de la grandeur analogique divise par la plus grande valeur reprsente par notre nombre numris. Ceci, bien entendu, en partant du principe que nos nombres numriques commencent 0 . Dans le cas contraire, la plus grande valeur sera remplace par la diffrence entre la plus grande et la plus petite valeur possible . Donc, pour prendre notre premier exemple, 1 digit numris reprsente la valeur maximale analogique moins la valeur minimale analogique divis par la valeur maximale numrique. Donc : (90-00)/9 = 10. Vous voyez que 1 en numrique reprsente la valeur analogique 10. Notre grandeur numrique mesure donc les dizaines. Le pas de notre conversion est de 10. Si nous prenons notre second exemple, nous aurons : (100-10)/9 = 10 galement. Jai choisi ces exemples car ils taient simples. Si maintenant, nous dsirons effectuer lopration inverse, cest--dire savoir quelle valeur analogique correspond une valeur numrique, nous pourrons dire que : La valeur analogique typique est gale la valeur analogique minimale reprsente laquelle on ajoute le pas multipli par la valeur numrique correspondante. Cest trs simple comprendre. Supposons que nous ayons la valeur numrique 5. Que reprsente-t-elle comme grandeur analogique relle dans notre premier exemple ? Et bien, tout simplement 0 + (5*10) = 50. Ca semble logique, non ? Quand notre second exemple, cette mme valeur reprsente une grandeur analogique de : 10 + (5*10) = 60. Cest tout aussi logique. Voici un graphique correspondant notre premier exemple, pour vous permettre dy voir clair. Noubliez pas que nous arrondissons (convertissons) toujours au niveau du demi digit. En rouge vous avez toutes les valeurs analogiques possibles de 0 90, tandis quen bleu, vous avez les seules 10 valeurs numriques correspondantes possibles.

205

Vous constatez que la courbe analogique peut prendre nimporte quelle valeur, tandis que la courbe numrique ne peut prendre quune des 10 valeurs qui lui sont permises. Par exemple, la valeur 16 existe sur la courbe analogique, mais la valeur 1,6 nexiste pas sur la courbe numrique. Chaque palier de cette courbe reprsente 1 digit (chelle bleue) et un pas de 10 sur lchelle rouge analogique. Nous voyons galement que pour certains valeurs analogiques (par exemple 15), nous avons 2 valeurs numriques possibles. La mme situation se retrouve dans la vie relle. Si vous donnez 10 euros un commerant pour payer un montant de 7,565 euros, libre lui de considrer que vous lui devez 7,57 plutt que 7,56 euros. Je pense quun juriste aura grand mal vous dpartager, et dailleurs je doute que vous portiez laffaire en justice. Nous venons de voir que nous avons effectu une conversion dune grandeur analogique en grandeur numrique et rciproquement pour une grandeur que nous avons suppose fixe dans le temps. Mais que se passerait-il pour une grandeur qui varie, comme par exemple un signal audio ? En fait, de nouveau, ce signal va varier dans le temps de faon continue. Le PIC, lui (ou tout autre convertisseur existant), va effectuer intervalles rguliers des mesures du signal pour le convertir en valeurs numriques successives. Donc, de nouveau, notre chelle de temps ne sera plus continue, mais constitue de bonds. Cest comme si, par exemple, vous preniez une srie de photos successives avec un appareil rapide. Lorsque vous visualisez lanimation, vous aurez une succession dimages fixes qui restitueront lanimation.

206

Plus les photos sont rapproches dans le temps, plus vous vous rapprochez de la ralit, et moins vous perdez dvnements. Vous passez progressivement de lappareil photo la camra, mais cette dernire travaille galement sur le mme principe. Corollaire : Plus les vnements filmer sont rapides, et plus vos photos devront tre rapproches pour ne pas perdre des vnements. Cest exactement le mme principe pour la numrisation de signaux variables. En fait, vous ralisez une double numrisation. La premire consiste, comme nous lavons vu plus haut, dcouper la valeur en une succession de tranches, la seconde consiste dcouper le temps en une autre succession de tranches. Voici ce que a donne pour un signal quelconque.

Vous voyez que vous perdez 2 fois en prcision. Dune part votre valeur est arrondie en fonction du nombre de digits utiliss pour la conversion, et dautre part, tous les vnements survenus entre 2 conversions (chantillonnages) sont perdus. Vous pouvez en dduire que : 1) Plus vous dsirez de prcision, plus vous devez augmenter le nombre de digits utiliss pour le rsultat

207

2) Plus votre signal volue rapidement, plus vous devez diminuer le temps sparant 2 chantillonnages, autrement dit, augmenter votre vitesse dchantillonnage. Je vous passerai les thories de Fourrier et autres, pour simplement vous dire que pour un signal sinusodal, on admet que la frquence dchantillonnage doit tre suprieure au double de la frquence du signal mesurer. Pour vous donner un exemple, lorsquon convertit un signal audio pour en faire un signal numrique destin tre plac sur un disque CD, les caractristiques de la conversion sont les suivantes : 1) Echantillonnage sur 16 bits, soit 65536 valeurs numriques diffrentes : on se rapproche donc normment du signal analogique original. 2) Frquence dchantillonnage de 44100 Hz, ce qui nous donne, suite au thorme prcdent, une frquence maximale sinusodale digitalise 22 KHz sil ny avait pas de filtrage qui rduit un peu cette frquence (loreille humaine parvient en gnral capter des frquences maximales de 16KHz, 20Khz pour certaines personnes). Anecdote en passant, ceci vous dmontre linutilit des enceintes qui sont annonces avec des frquences maximales de plus de 50 KHz. Dune part, votre CD na pas enregistr de telles frquences, dautre part votre oreille est incapable de les interprter. Quand on voit un chiffre, il faut toujours se demander quelle est la ralit qui se cache derrire (autre que commerciale). 19.3 Principes de conversion sur les 16F87x Jusqu prsent, nous venons de raisonner en dcimal. Les pics, eux travaillent en binaire. Mais, rassurez-vous, ceci reste strictement identique. Souvenez-vous que lorsquon change de base de numrotation, les formules restent toutes dapplication (cours-part1). Notre 16F87x travaille avec un convertisseur analogique / numrique qui permet un chantillonnage sur 10 bits. Le signal numrique peut donc prendre 1024 valeurs possibles. Vous avez vu que pour pouvoir convertir une grandeur, nous devons connatre la valeur minimale quelle peut prendre, ainsi que sa valeur maximale. Les pics considrent par dfaut que la valeur minimale correspond leur Vss dalimentation, tandis que la valeur maximale correspond la tension positive dalimentation Vdd. Nous verrons cependant quil est possible dutiliser dautres valeurs. Nous navons toujours pas parl des mthodes utilises pour convertir physiquement la grandeur analogique en grandeur numrique au cur du PIC. Il est inutile dentrer ici dans un cours dlectronique applique, mais il est bon de connatre le principe utilis, car cela va vous aider comprendre la suite. La squence est la suivante : Le pic connecte la pin sur laquelle se trouve la tension numriser un condensateur interne, qui va se charger via une rsistance interne jusque la tension applique.

208

La pin est dconnecte du condensateur, et ce dernier est connect sur le convertisseur analogique/numrique interne. Le pic procde la conversion.

Plusieurs remarques et questions sont souleves par cette procdure. En tout premier lieu, le condensateur va mettre un certain temps se charger, il nous faut donc connatre ce temps. Ensuite, il nous faut comprendre comment fonctionne la conversion, pour valuer le temps mis pour cette conversion. Ceci nous donnera le temps total ncessaire, afin de savoir quelle est la frquence maximale dchantillonnage pour notre PIC. Remarquez que si le signal varie aprs le temps de charge du condensateur interne, cette variation ne sera pas prise en compte, puisque la pin sera dconnecte du dit condensateur. 19.4 Le temps dacquisition Cest le temps quil faut pour que le condensateur interne atteigne une tension proche de la tension convertir. Cette charge seffectue travers une rsistance interne et la rsistance de la source connecte la pin, les formules sont donc drives de celles que nous avons calcules lors de la ralisation de notre circuit anti-rebond du chapitre sur le timer 1. Ce temps est incrment du temps de raction des circuits internes, et dun temps qui dpend de la temprature (coefficient de temprature). Il faut savoir en effet que les rsistances augmentent avec la temprature, donc les temps de raction des circuits galement. Donc, si on pose : Tacq = temps dacquisition total Tamp = temps de raction des circuits Tc = temps de charge du condensateur Tcoff = temps qui dpend du coefficient de temprature. La formule est donc : Tacq = Tamp + Tc + Tcoff Le temps de raction Tamp est typiquement de 2s, pas donc de problme ce niveau : Tamp = 2s Pour le coefficient de temprature, il nest ncessaire que pour les tempratures suprieures 25C. Dans les autres cas, il nentre pas en compte. Ce coefficient est typiquement de 0,05 s par C qui est suprieur 25C. Il sagit bien entendu de la t du PIC, et non de la temprature ambiante.

209

Donc, ce temps Tcoff sera au minimum de 0 ( moins de 25C) et au maximum de (5025)*0.05, soit 1,25 s. La t du pic ne pouvant pas, en effet, excder 50C. 0 Tcoff 1,25s Premire constatation, si vous voulez bnficier dune frquence maximale, vous devez maintenir le PIC sous 25C. Reste le temps de charge. Ce temps de charge dpend de la rsistance place en srie avec le condensateur. En fait, il y a 2 rsistances, celle de votre source de signal, et celle lintrieur du PIC. Il est recommand que la rsistance de votre source reste infrieure 10KOhms. Celle interne au PIC est directement lie la tension dalimentation. Plus la tension baisse, plus la rsistance est leve, donc plus le temps de chargement est long. Donc, de nouveau, pour obtenir de hautes vitesses, il vous faudra alimenter le PIC avec la tension maximale supporte, soit 6V lheure actuelle pour le 16F876. La rsistance interne totale (compose de 2 rsistances internes) varie de 6Kohms 6V pour arriver 12Kohms sous 3V, en passant par 8Kohms sous 5V. De plus, comme la charge du condensateur dpend galement de la rsistance de la source du signal, pour augmenter votre vitesse, vous devez galement utiliser une source de signal prsentant la plus faible impdance (rsistance) possible. Sachant que le condensateur interne une valeur de 120pF pour les versions actuelles de PIC (16F876), les formules que je vous ai donnes pour le calcul du temps de chargement dun condensateur restant valables, la formule du temps de charge du condensateur est : Tc = -C * (Rinterne + Rsource) * ln (1/2047) Le 2047 provient de ce que pour numriser avec une prcision de bit, la numrisation utilisant une valeur maximale de 1023, la charge du condensateur doit tre au minimum de 2046/2047me de la tension mesurer. Comme C est fixe et ln(1/2047) galement, je vais vous calculer la constante une fois pour toutes (nest-ce pas que je suis bon avec vous ?) : -C * ln(1/2047) = 0,914895 * 10-9 La formule devient donc : Tc = 0,914895 * 10-9 * (Rinterne + Rsource) Si on se place dans le cas le plus dfavorable (tension de 3V, et rsistance source = 10Kohms), notre temps de chargement est de = Tc = 0,914895 * 10-9 * (10 * 10 + 12 * 10)

210

Tc maximal = 20,12 s Maintenant le cas le plus favorable (tension de 6V, et rsistance source ngligeable) : Tc = 0,914895 * 10-9 * (0 * 10 + 6 * 10) Tc minimal : 5,48 s. Vrifiez dans le datasheet actuel les tensions maximales autorises pour les PICs. Ces dernires sont sujettes fraquentes modifications. A lheure actuelle, la tension maximale autorise est de 7,5V. CONSULTEZ LES DATASHEETS LES PLUS RECENTS SIL ENTRE DANS VOS INTENTIONS DUTILISER CETTE TENSION MAXIMALE. Si, maintenant, nous prenons un cas typique, savoir une tension dalimentation de 5V et une rsistance de source de 10 Kohms, nous aurons : Tc = 0,914895 * 10-9 * (10 * 10 + 8 * 10) Tc typique = 16,46 s. Nous allons maintenant calculez les temps minimum, maximum, et typique du temps total dacquisition Tacq. Le cas le plus dfavorable est : une temprature de 50C et un Tc maximal, ce qui nous donne : Tacq = Tamp + Tac + Tcoff Tacq maximum = 2s + 20,12s + 1,25s = 23,37 s Le cas le plus favorable, une temprature infrieure ou gale 25C et un Tc minimal, nous donne : Tacq minimum = 2s + 5,48s = 7,48 s. Maintenant, pour nos utilisations classiques, sous 5V, nous aurons dans le pire des cas : Tacq sous 5V = 2s + 16,46s + 1,25s = 19,71s. Donc, nous prendrons un Tacq de 20s pour notre PIC alimente sous 5V. Mais vous devez vous souvenir que si vous travaillez sous une tension diffrente, il vous faudra adapter ces valeurs. De mme, si vous avez besoin de la plus grande vitesse possible dans votre cas particulier, vous possdez maintenant la mthode vous permettant de calculer votre propre Tacq. Pour ma part, dans la suite de ce cours, je travaillerai avec la valeur standard de 20 s. Remarquez que ces valeurs sont donnes telles quelles dans les datasheets. Je vous ai dmontr mathmatiquement do provenaient ces valeurs. Ceci vous permettra de connatre les temps ncessaires pour votre application particulire, et ainsi, vous autorisera la plus grande vitesse possible.

211

19.5 La conversion Arriv ce stade, aprs le temps Tacq, on peut considrer que le condensateur est charg et prt tre connect sur lentre du convertisseur analogique/digital. Cette connexion prend de lordre de 100ns. Une fois le condensateur connect, et donc, la tension numriser prsente sur lentre du convertisseur, ce dernier va devoir procder la conversion. Je ne vais pas entrer ici dans les dtails lectroniques de cette conversion, mais sachez que le principe utilis est celui de lapproximation successive. Cest une mthode de type dichotomique, cest un bien grand mot pour exprimer une mthode somme toutes assez intuitive. Il sagit tout simplement de couper lintervalle dans lequel se trouve la grandeur analogique en 2 parties gales, et de dterminer dans laquelle de ces 2 parties se situe la valeur numriser. Une fois cet intervalle dtermin, on le coupe de nouveau en 2, et ainsi de suite jusqu obtenir la prcision demande. Prenons un exemple pratique : vous avez un livre de 15 pages, vous en choisissez une au hasard, supposons la numro13. Voici comment vous allez procder pour trouver de quelle page il sagit. On coupe lintervalle en 2, arrondi lunit suprieure, soit 8. Le numro de page est-il suprieur, infrieur ou gal 8 ? Le numro est suprieur, donc dans lintervalle 8-15 On coupe cet intervalle en 2, soit 12 Le numro est-il suprieur, infrieur, ou gal 8 ? Le numro est suprieur, donc dans lintervalle 12-15 On coupe lintervalle en 2, soit 14 Le numro est-il suprieur, infrieur, ou gal 14 ? Le numro est infrieur, donc dans lintervalle 12-14 Le numro est donc 13, puisquil ntait gal ni 14, ni 12

Cette mthode peut paratre curieuse, mais elle est dune grande efficacit en terme de temps. Chaque question se traduisant par une opration effectuer au niveau lectronique, on peut dire que moins de question il y a, moins de temps lopration de conversion prendra. Si vous choisissez une page au hasard parmi un livre norme de 65535 pages, vous verrez que vous pouvez trouver le bon numro de page en moins de 16 questions. Appliqu la numrotation binaire, cette mthode est de plus particulirement bien adapte, puisque couper un intervalle en 2 revient dire quon force simplement un bit 1. Reprenons notre exemple prcdent, mais en raisonnant en binaire. Notre nombre de pages maximum est de B1111, soit D15, la page choisie est B1101, soit D13. Effectuons notre conversion. On coupe lintervalle en 2, soit B1000. Le numro de page est-il infrieur ? Non, donc compris entre B1000 et B1111 On coupe lintervalle en 2, soit B1100

212

Le numro de page est-il infrieur? Non, donc compris entre B1100 et B1111 On coupe lintervalle en 2, soit B1110 Le numro de page est-il infrieur? Oui, donc compris entre B1100 et B1101 On coupe lintervalle en 2, soit B1101 Le numro de page est infrieur ? Non, donc le numro de page est B1101

Vous voyez quen fait vous avez une question (approximation) par bit du rsultat. Autrement dit, en raisonnant en terme de bits : On place le bit 3 1, donc B1000 Le rsultat est-il infrieur? Non, alors le bit 3 vaut effectivement 1 On place le bit 2 1, donc B1100 Le rsultat est infrieur? Non, alors le bit 2 vaut effectivement 1 On place le bit 1 1, donc B1110 Le rsultat est-il infrieur ? Oui, alors le bit 1 ne valait pas 1 , mais 0 On place le bit 0 1, donc B1101 Le rsultat est infrieur ? Non, alors le bit 0 valait bien 1 , le rsultat est donc B1101

Pour rsumer, le temps ncessaire la conversion est gal au temps ncessaire la conversion dun bit multipli par le nombre de bits dsirs pour le rsultat. Concernant notre PIC, il faut savoir quil ncessite, pour la conversion dun bit, un temps quon va nommer Tad. Ce temps est driv par division de lhorloge principale. Le diviseur peut prendre une valeur de 2, 8 ou 32. Attention, on divise ici lhorloge principale, et non le compteur dinstructions. Donc, une division par 2 signifie un temps 2 fois plus court que celui ncessaire pour excuter une instruction, puisque ce temps dexcution est de Tosc/4. Il est galement possible dutiliser une horloge constitue dun oscillateur interne de type RC. Cet oscillateur donne un temps de conversion compris entre 2 et 6s, avec une valeur typique de 4s. Pour les versions LC du 16F876, ce temps passe entre 3 et 9s. Si la frquence du PIC est suprieure 1Mhz, vous ne pourrez cependant lutiliser quen cas de mise en sommeil du PIC dans lattente du temps de conversion. En effet, durant le mode sleep , lhorloge principale est stoppe, donc seul cet oscillateur permettra de poursuivre la conversion. Cependant, je le rappelle encore une fois, pour votre PIC tournant plus de 1Mhz, vous tes contraints en utilisant cette horloge de placer votre PIC en mode sleep jusque la fin de la conversion. Dans le cas contraire, le rsultat serait erron. Je vous conseille donc de nutiliser cette mthode que si vous dsirez placer votre PIC en mode sleep durant la conversion, ou si vous utilisez une frquence de PIC trs basse, et de toute faon sous les 1MHz.

213

Le temps de conversion Tad ne peut descendre, pour des raisons lectroniques, en dessous de 1,6s pour les versions classiques de 16F87x, et en dessous de 6s pour les versions LC. Donc, en fonction des frquences utilises pour le quartz du PIC, il vous faudra choisir le diviseur le plus appropri. Voici un tableau qui reprend les valeurs de diviseur utiliser pour quelques frquences courantes du quartz et pour les PICs de type classique. La formule dobtention des temps Tad est simple, puisquil sagit tout simplement, comme expliqu cidessus, du temps dinstruction (Tosc) divis par le diviseur donn. Exemple, 20Mz, le temps dinstruction est de 1/20.000.000, soit 50ns. Donc, avec un diviseur de 2, on aura 100ns. Diviseur 2 8 32 Osc RC 20Mhz 100ns 400ns 1,6s 2-6s 5Mhz 400ns 1,6s 6,4s 2-6s 4Mhz 500ns 2s 8s 2-6s 2Mhz 1s 4s 16s 2-6s 1,25Mhz 333,3Khz 1,6s 6s 6,4s 24s 25,6s 96s 2-6s 2-6s

Les valeurs en vert sont celles qui correspondent au meilleur diviseur en fonction de la frquence choisie. Les valeurs en bleu sont inutilisables, car le temps Tad serait infrieur 1,6s. Quand aux valeurs en jaune, je rappelle que pour lutilisation de loscillateur interne RC ces frquences, la mise en sommeil du PIC est impratif durant le temps de conversion. Vous remarquez que, du ces diviseurs, il est possible, par exemple de numriser plus vite avec un PIC tournant 1,25 Mhz quavec le mme PIC muni dun quartz 4 Mhz. Il faut prsent prciser que le PIC ncessite un temps Tad avant le dmarrage effectif de la conversion, et un temps supplmentaire Tad la fin de la conversion. Donc, le temps total de conversion est de : Tad : avant le dbut de conversion (le temps de connexion du condensateur est inclus) 10 * Tad pour la conversion des 10 bits du rsultat Tad supplmentaire pour la fin de la conversion de b0

Soit, au total, un temps de 12 Tad, soit dans le meilleur des cas, un temps de 12 * 1,6s = 19,2 s. Notez quun temps quivalent 2 * Tad est ncessaire avant de pouvoir effectuer une nouvelle conversion. Rsumons donc le temps ncessaire pour effectuer lensemble des oprations : On charge le condensateur interne (ncessite le temps Tacq) On effectue la conversion (ncessite le temps 12 * Tad) On doit attendre 2 * Tad avant de pouvoir recommencer une autre conversion

214

Nous voici donc arriv la question principale. A quelle frquence maximale pouvonsnous chantillonner notre signal ? En fait, nous avons vu que cela dpendait des conditions, puisque Tacq dpend de la tension dalimentation et dautres paramtres. Calculons le cas le plus dfavorable sur une tension dalimentation de 5V avec un PIC tournant une vitesse permettant un Tad de 1,6 s (attention, je ne travaille que sur les PICs 16F87x standards, pour les modles diffrents, je vous ai donn toutes les mthodes de calcul) : T entre 2 chantillonnages = Tacq + 12 Tad + 2 Tad = Tacq + 14 Tad, donc : T = 19,71s + 14 * 1,6s = 42,11 s. Ceci correspond donc une frquence de : F = 1/T = 1 / 42,11 * 10-6 = 23747 Hz. Cette frquence vous permet donc dchantillonner des signaux sinusodaux dune frquence maximale de 11874 Hz (la moiti de la frquence dchantillonnage). Si vous vous placez dans le meilleur des cas, vous ramenez Tacq 7,5s, ce qui vous donne : T = 7,5s + 14 * 1,6s = 29,9s, soit une frquence de F = 33445 Hz. Soit la possibilit de numriser des signaux sinusodaux dune frquence maximale de 16722 Hz (si ce ntait la rsolution sur 10 bits au lieu de 16, on serait dans le domaine de la hi-fi. 19.6 Compromis vitesse/prcision Nous avons vu que la conversion seffectue sur 10 bits, avec une frquence maximale possible de 33445 Hz. Si vous avez besoin dune vitesse plus grande, il est possible dutiliser quelques trucs. La premire chose savoir, cest que laugmentation de vitesse au del ce ces limites ne peut se faire quau dtriment de la prcision en terme de nombre de bits significatifs du rsultat. Cette augmentation se paye galement par une plus grande difficult au niveau de la programmation. Voici le raisonnement employ. Si on accepte de limiter la prcision un nombre infrieur 10 bits, et en constatant que la numrisation seffectue en commenant par les bits les plus significatifs, on peut se dire : N bits me suffisent, donc en regardant notre figure prcdente, on dcide de stopper la conversion aprs que les bits en question aient t numriss. Donc, on se dit que pour conserver N bits significatifs, on aurait :

215

Tacq pour charger le condensateur Tad pour le dmarrage N * Tad pour la conversion

Soit, pour une conversion en conservant 4 bits au lieu de 12, cela ramnerait notre temps total Tacq + Tad + 4 Tad, soit Tacq + 5 Tad. Nous conomisons donc 7 Tad. Malheureusement, larrt de la numrisation nest pas possible, car le rsultat ne serait pas enregistr dans les registres concerns. Il faut donc amliorer cette astuce. En fait, on ne peut arrter la numrisation, mais on peut changer la valeur du diviseur en cours de digitalisation. Les bits numriss aprs la diminution du diviseur ne seront pas valides, mais ce nest pas important, puisque nous avons dcid de ne pas les utiliser. Supposons donc que nous travaillions avec un PIC 20MHz. Nous utilisons donc le diviseur par 32. Chaque Tad vaut donc 32 Tosc. Notre temps de conversion effectif prend donc normalement 12 Tad, soit 12 * 32 Tosc, soit 19,2s. Si nous avons besoin de 4 bits valides, nous pouvons donc dcider de raliser les oprations suivantes : Tacq pour charger le condensateur Tad pour le dmarrage = 32 Tosc N * Tad pour la conversion = N * 32 Tosc Modification du diviseur pour passer 2 (11 N) Tad restants pour le reste de la conversion (qui sera errone), donc (11-N) * 2Tosc

Donc, nous pargnons en ralit (11-N) * (32-2)Tosc, soit pour notre numrisation sur 4 bits : (11-4) * 30 Tosc = 210 Tosc, soit 210 * 50ns = 10,5s. La formule gnrale du temps de conversion pour numriser sur N bits est donc : Temps de conversion sur N bits = Tad + N * Tad + (11-N) (2Tosc) Autrement dit : Temps de conversion = (N+1) Tad + (11-N) (2Tosc). Ce qui nous donnera, pour notre conversion sur 4 bits pour un PIC 20Mhz: Temps de conversion = (4+1) * 1,6s + (11-4) * 100ns = 8s + 0,7s = 8,7s. Donc le temps total de numrisation sera : Temps numrisation = Tacq + Temps de conversion = 7,48s + 8,7s = 16,18s.

216

A ceci nous ajoutons 2Tad, soit 200ns, ce qui nous donne un temps sparant 2 chantillonnages de 16,38s Ceci nous donnera une frquence dchantillonnage de : Frquence dchantillonnage = 1/16,38s = 61050 Hz. avec 16 valeurs diffrentes possibles. La seconde astuce utilisable, est de constater que si on ne dsire pas utiliser tous les bits, il est galement inutile de charger le condensateur au maximum. On peut donc reprendre les calculs de charge du condensateur, afin de diminuer galement Tacq, ce qui permet encore daugmenter lgrement la frquence dchantillonnage. Remarquez que lutilisation de ces techniques vous donnera toujours un rsultat sur 10 bits, mais dont seuls les N premiers reprsenteront encore une valeur utilisable. Les autres bits devront donc tre ignors. Bien entendu, il vous incombera de dterminer le temps qui spare le dbut du processus de conversion de celui de modification du diviseur. Cette modification interviendra logiquement un temps Tad (N+1) aprs lancement de la conversion. Tad tant un multiple exact (gnralement 32) de loscillateur principal, une simple petite boucle, ou lutilisation dun timer pourra faire laffaire. Par exemple, pour digitaliser sur 6 bits avec un PIC 20MHz : Vous lancez lacquisition Aprs le temps Tacq, vous lancez la conversion Aprs le temps (6+1) * Tad vous modifiez le diviseur Vous attendez la fin de la conversion

Le temps dattente (6+1) Tad vaut 7 * Tad, donc, 7 * 32 Tosc. Sachant que le temps dexcution dune instruction est de Tosc/4, le temps dattente sera de 7 * 32 / 4 = 56 cycles dinstruction. 19.7 Les valeurs reprsentes Nous avons vu toutes les formules concernant les temps de numrisation. Ne restent plus que les formules qui nous donnent les relations entre valeurs analogiques et reprsentations numriques. Nous en avons dj parl au moment de lvocation des techniques darrondissement. Si nous dfinissons : VREFVREF+ VIN Val : Tension minimale analogique (rfrence ngative) : Tension maximale analogique (rfrence positive) : Tension dentre numriser : valeur numrique obtenue sur 10 bits

Nous pouvons dire que pour une numrisation sur 10 bits, on obtiendra la valeur numrique :

217

Val = ((VIN - VREF-) / (VREF+ - VREF-)) * 1023) Et rciproquement, la valeur typique qui a t numrise correspond une tension de : VIN = ((Val/1023) * (VREF+ - VREF-)) + VREFSi nous utilisons une tension de rfrence ngative de 0V, cest--dire que la rfrence de tension ngative est en ralit Vss, nous obtenons 2 formules simplifies : Val = (VIN / VREF+ ) * 1023) VIN = (Val/1023) * VREF+ En donnant un exemple concret, si nous dcidons que la tension de rfrence positive est de 5V, et que la tension de rfrence ngative est de 0V, nous avons : Val = (VIN / 5) * 1023) VIN = (Val/1023) * 5 Dernire remarque : La tension dentre ne peut tre suprieure la tension dalimentation Vdd du PIC, ni infrieure sa tension Vss. Si vous voulez mesurer une tension suprieure, par exemple une tension de 15V maximum, il vous faudra raliser un diviseur de tension partir de 2 rsistances pour que la tension applique reste dans les limites prvues. Les formules sont tires de la loi dohm, et ont dj t utiliss dans le calcul du circuit anti-rebond dont nous nous sommes servis dans le cadre du circuit de comptage de notre timer1. 19.8 Conclusions pour la partie thorique Pour rsumer, vous disposez des prescriptions suivantes pour utiliser en pratique le convertisseur A/D avec un PIC standard : Si une frquence dchantillonnage de lordre de 23KHz vous suffit, vous utilisez un temps Tacq de 20s et vous digitalisez sur 10 bits. Aucun besoin de calculs. Si vous avez besoin dune frquence comprise entre 23 et 33Khz avec une rsolution de 10 bits, vous pouvez optimiser votre montage en rduisant limpdance de la source (par exemple en utilisant un amplificateur oprationnel), et en utilisant la tension maximale supporte par le PIC. Si vous avez besoin dune frquence suprieure, vous devrez rduire le nombre de bits de numrisation, comme expliqu plus haut. Si aucune de ces solutions ne vous convient, vous devrez renoncer numriser avec votre PIC, et utiliser un convertisseur externe. Pour plus de facilit, je vous rassemble ici toutes les formules :

218

Val numrise = ((VIN - VREF-) / (VREF+ - VREF-)) * 1023) VIN analogique = ((Val/1023) * (VREF+ - VREF-)) + VREFTemps de conversion sur N bits = Tad + N * Tad + (11-N) (2Tosc) Tad = Tosc * diviseur 1,6 s Temps de conversion sur 10 bits = 12 Tad Tacq = 2s + Tc = 0,914895 * 10-9 * (Rinterne + Rsource) + 0,05(T - 25C) avec T 25C Et galement, les valeurs que vous pouvez utiliser dans la majorit des cas : Tacq courant : 19,7s Temps de conversion courant : 19,2s. Temps entre 2 numrisation successives : 3,2s 19.9 La thorie applique aux PICs : pins et canaux utiliss Maintenant vous savez ce quest une conversion analogique/numrique, et comment calculer les diffrentes valeurs utiles, moins que vous nayez saut les paragraphes prcdents. Reste savoir comment connecter notre ou nos signal (signaux) analogique(s) sur notre PIC. La premire chose comprendre, cest que notre PIC ne contient quun seul convertisseur, mais plusieurs pins sur lesquelles connecter nos signaux analogiques. Un circuit de commutation slectionnera donc laquelle des pins sera relie au condensateur de maintien interne durant le temps Tacq. Ces diffrentes entres seront donc des canaux diffrents dun seul et mme convertisseur. Corollaire : si vous avez plusieurs canaux chantillonner, vous devrez les chantillonner tour de rle, et donc le temps total ncessaire sera la somme des temps de chaque conversion. Donc, plus vous avez de signaux chantillonner, moins la frquence dchantillonnage pour chaque canal pourra tre leve. Le 16F876 dispose de 5 canaux dentre analogique. Vous pouvez donc chantillonner successivement jusque 5 signaux diffrents avec ce composant. Les pins utilises sont les pins AN0 AN4 (qui sont en fait les dnominations analogiques des pins RA0 RA3 + RA5). Le 16F877, quant lui, dispose de 8 canaux dentre analogique. Vous pourrez donc chantillonner jusque 8 signaux diffrents sur les pins AN0 AN7. Les pins AN0 AN4 sont les dnominations analogiques des pins RA0 RA3 + RA5, tandis que les pins AN5 AN7 sont les dnominations analogiques des pins RE0 RE2.

219

19.10 Les tensions de rfrence Nous avons vu dans ltude thorique gnrale de la conversion analogique/digitale, que cette conversion ncessitait une tension de rfrence minimale (Vref-) et une tension de rfrence maximale (Vref+). Au niveau de notre PIC, nous avons 3 modes de fonctionnement possibles : Utilisation de Vss (masse du PIC) comme tension Vref- et de Vdd (alimentation positive du PIC) comme tension Vref+. Dans ce mode, les tensions de rfrences sont tires en interne de la tension dalimentation. Il ny a donc pas besoin de les fournir. Utilisation de la pin Vref+ pour fixer la tension de rfrence maximale Vref+, et utilisation de Vss comme tension de rfrence Vref-. Dans ce cas, la tension Vref+ doit donc tre fournie au PIC via la pin RA3. Utilisation de la pin Vref+ pour fixer la tension de rfrence maximale Vref+, et utilisation de la pin Vref- pour fixer la tension de rfrence minimale Vref-. Dans ce cas, les 2 tensions de rfrences devront tre fournies au PIC via RA3 et RA2.

Notez que la broche Vref+ est une dnomination alternative de la broche RA3/AN3, tandis que la broche Vref- est une dnomination alternative de la broche RA2/AN2. Donc, lutilisation dune pins comme entre analogique interdit son utilisation comme entre numrique (pin entre/sortie normale ). De mme, lutilisation des rfrences Vref+ et Vref- interdit leur utilisation comme pin entre/sortie ou comme pin dentre analogique. Notez galement que les pins ANx sont des pins dentre. Il nest donc pas question desprer leur faire sortir une tension analogique. Ceci ncessiterait un convertisseur numrique/analogique dont nest pas pourvu notre PIC. Nous pouvons maintenant dessiner le schma symbolique des entres de notre convertisseur analogique/numrique. Ce schma correspond un 16F877, pour le 16F876, les canaux AN5 AN7 nexistent bien entendu pas.

220

On voit trs bien sur ce schma que les pins AN2 et AN3 servent selon la position du slecteur dentre analogique ou de tension de rfrence. Le slecteur de canal permet de slectionner lequel des 8 canaux va tre appliqu au convertisseur analogique/digital. Remarquez que la slection de la source des tensions de rfrence dpend de bits du registre ADCON1, tandis que le canal slectionn pour tre numris dpend de ADCON0. Nous allons en parler. Le convertisseur en lui-mme, en toute bonne logique, na besoin que de la tension dentre (la pin ANx slectionne), et des 2 tensions de rfrence. Il sort un nombre numrique de 10 bits, dont nous verrons la destination. Donc, notre procdure de numrisation, pour le cas o on utilise plusieurs canaux, devient la suivante (aprs paramtrage) :

221

On choisit le canal numriser, et on met en route le convertisseur On attend Tacq On lance la numrisation On attend la fin de la numrisation On attend 2 Tad On recommence avec le canal suivant.

Je vais prsent vous donner un exemple de schma mettant en uvre ces tensions de rfrence. Imaginons que vous vouliez chantillonner une tension qui varie de 2V 4V en conservant une prcision maximale. Vous avez 2 solutions : La premire qui vient lesprit est dutiliser une entre analogique sans tension de rfrence externe, comme pour lexercice prcdent. Dans ce cas votre valeur numrique ne pourra varier, en appliquant la formule Val = (VIN / VREF+ ) * 1023) , que de : (2/5) * 1023 = 409 pour une tension de 2V (4/5) * 1023 = 818 pour une tension de 4V. Votre prcision sera donc de 409 pas sur les 1023 possibles, les autres valeurs tant inutiliss. Vous avez donc une perte de prcision. Pour faire une quivalence avec le monde de la photo, vous ralisez ici un zoom numrique sur la plage 2/4V. Par contre, si vous utilisez une tension de rfrence de 2V comme Vref- et une de 4V comme Vref+, vous aurez une valeur numrique de 0 pour la tension 2V, et une valeur de 1023 pour la tension de 4V. Vous conservez donc un maximum de prcision, puisque votre intervalle de mesure correspond 1024 paliers. Dans notre analogie avec les appareils photographiques, nous avons ralis par cette mthode, un zoom optique sur la plage de tension 2/4V. Comme avec le zoom optique, le reste du clich (le reste des tensions) nest pas captur par le PIC, mais, en contrepartie, la prcision reste maximale dans la zone cadre. Voici le schma que vous devrez utiliser, aprs avoir paramtr ADCON1 en consquence :

222

Les rsistances seront calcules en fonction des diodes zener et de la loi dohm : la rsistance est gale la tension au borne de la rsistance (5V tension de la zener) divise par le courant qui doit traverser la zener (gnralement quelques mA.). Vous constatez que ceci vous permet davoir des tensions de rfrence qui ne varient pas en fonction de la tension dalimentation. Il peut donc tre pratique, si votre alimentation nest pas stable, dutiliser des tensions de rfrences fortement stabilises. Ceci est un schma thorique. Mais la bonne pratique lectronique recommande de : Placer un condensateur en parallle avec chaque diode zener, ceci afin dliminer le bruit des jonctions de ces diodes (pour ce montage particulier) Placer une rsistance et un condensateur sur la pin MCLR (pour tous vos montages rels) Placer un condensateur de dcouplage sur chaque pin dalimentation Vdd (pour tous vos montages rels).

Bien entendu, ces accessoires ne sont absolument pas ncessaires pour vos platines dexprimentation. Ils sont ncessaires pour une utilisation sur un circuit imprim comportant dautres circuits perturbateurs ou pouvant tre perturbs, et dans un circuit dapplication rel, pour lesquels un plantage est toujours gnant.

223

Je vous donne, titre dinformation, le circuit prcdent modifi pour une application relle :

Notez que si vous dsirez mesurer, par exemple, la position dun potentiomtre, lerreur sannulera delle-mme si vous nutilisez pas les tensions de rfrence externes. En effet, si la tension dalimentation varie, la rfrence interne +Vdd variera galement. Comme le potentiomtre pourra tre aliment par la mme tension dalimentation, la tension quil fournira variera dans les mmes proportions, donc lerreur sannulera. Il nest donc pas toujours prfrable dimposer une tension de rfrence distincte de votre alimentation. Tout dpend de lapplication. Nous allons poursuivre ltude de notre convertisseur par ltude des registres dont nous venons de dvoiler le nom. Mais, auparavant : 19.11 Mesure dune tension alternative Nous avons vu que notre tension pouvait varier entre Vss et Vdd. Nous ne pouvons donc pas chantillonner directement une tension alternative, comme un signal audio, par exemple. Celui-ci est en effet de la forme :
224

Nous allons donc devoir nous arranger pour que la tension reste en permanence positive. Ceci seffectue en forant la tension dentre vide une tension Vdd/2, et en amenant la tension alternative via un condensateur, de faon effectuer une addition des 2 tensions. Regardez le schma suivant :

225

Ceci ramnera la tension mesurer vue par le PIC :

Vous voyez que maintenant, lintgralit de la tension mesurer est positive et peut donc tre mesure. A vous dinterprter que la valeur centrale correspond une tension alternative dentre de 0, et quune tension de Vdd ou de 0V correspond un maximum de tension. Reste calculer la valeur du condensateur. Cest assez simple. Vous considrez que la rsistance dentre vaut approximativement la moiti de la valeur des rsistances utilises (je vous passe la thorie), soit dans ce cas 5 Kohms. Pour que le condensateur ninfluence pas sur la mesure, son impdance doit tre ngligeable vis--vis de la rsistance dentre. Le terme ngligeable dpend bien entendu de la prcision de la conversion souhaite. La valeur de limpdance diminue avec laugmentation de frquence suivant la formule : Zc = 1 / (2 * * f * C), donc C = 1 / (2 * * f * Zc) Avec : Zc = impdance = 3,1415 f = frquence en Hz C = capacit en farads Donc, on se place dans le cas le plus dfavorable, cest--dire la frquence la plus basse. Prenons un cas concret : On doit numriser une frquence audio, de 50 Hz 10 Khz. On dcide que Zc doit tre 10 fois plus petite que 5 Kohms On aura donc un condensateur au moins gal :
226

C = 1 / (2 * * 50Hz * 500Ohms) = 6,37 F * 10-6 = 6,37 F. Notez que dans ce cas, limpdance de la source de votre signal devra galement tre plus petite que 5Kohms, sous peine dune forte attnuation. 19.12 Les registres ADRESL et ADRESH Jattire votre attention sur le fait que le convertisseur donne un rsultat sur 10 bits, et donc que ce rsultat devra donc obligatoirement tre sauvegard dans 2 registres. Ces registres sont tout simplement les registres ADRESL et ADRESH. Comme 2 registres contiennent 16 bits, et que nous nen utilisons que 10, Microchip vous a laiss le choix sur la faon dont est sauvegard le rsultat. Vous pouvez soit justifier le rsultat gauche, soit droite. La justification droite complte la partie gauche du rsultat par des 0 . Le rsultat sera donc de la forme : 0 0 0 ADRESH 0 0 0 b9 b8 b7 b6 b5 ADRESL b4 b3 b2 b1 b0

La justification gauche procde bien videmment de la mthode inverse : b9 b8 ADRESH b7 b6 b5 b4 b3 b2 b1 b0 ADRESL 0 0 0 0 0 0

La justification droite sera principalement utilise lorsque vous avez besoin de lintgralit des 10 bits de rsultat, tandis que la justification gauche est trs pratique lorsque 8 bits vous suffisent. Dans ce cas, les 2 bits de poids faibles se trouvent isols dans ADRESL, il suffit donc de ne pas en tenir compte. Cette approche est destine vous pargner des dcalages de rsultats. Merci qui ? Merci Microchip. Le choix de la mthode seffectue laide du bit 7 de ADCON1, registre dont je vais maintenant vous parler. ATTENTION : Le registre ADRESH se situe en banque 0, alors que ADRESL se trouve en banque 1. 19.13 Le registre ADCON1 Je vais parler maintenant, pour des raisons de commodit, de ce registre. Il permet de dterminer le rle de chacune des pins AN0 AN7. Il permet donc de choisir si une pin sera utilise comme entre analogique, comme entre/sortie standard, ou comme tension de rfrence. Il permet galement de dcider de la justification du rsultat.

227

Notez dj que pour pouvoir utiliser une pin en mode analogique, il faudra que cette pin soit configure galement en entre par TRISA et ventuellement par TRISE pour le 16F877. Le registre ADCON1 dispose, comme tout registre accessible de notre PIC, de 8 bits, dont seulement 5 sont utiliss : ADCON1 - b7 : - b6 : - b5 : - b4 : - b3 : - b2 : - b1 : - b0 : ADFM Inutilis Inutilis Inutilis PCFG3 PCFG2 PCFG1 PCFG0 : A/D result ForMat select : lu comme 0 : lu comme 0 : lu comme 0 : Port ConFiGuration control bit 3 : Port ConFiGuration control bit 2 : Port ConFiGuration control bit 1 : Port ConFiGuration control bit 0

Le bit ADFM permet de dterminer si le rsultat de la conversion sera justifi droite (1) ou gauche (0). Nous trouvons dans ce registre les 4 bits de configuration des pins lies au convertisseur analogique/numrique. Ces bits nous permettent donc de dterminer le rle de chaque pin. Comme nous avons 16 combinaisons possibles, nous aurons autant de possibilits de configuration (en fait, vous verrez que nous nen avons que 15). Je vous donne le tableau correspondant ces combinaisons pour le 16F877: PCFG AN7 AN6 AN5 AN4 AN3 AN2 AN1 AN0 Vref3 0 RE2 RE1 RE0 RA5 RA3 RA2 RA1 RA0 0000 A A A A A A A A Vss 0001 A A A A Vref+ A A A Vss 0010 D D D A A A A A Vss 0011 D D D A Vref+ A A A Vss 0100 D D D D A D A A Vss 0101 D D D D Vref+ D A A Vss 0110 D D D D D D D D 0111 D D D D D D D D 1000 A A A A Vref+ Vref- A A RA2 1001 D D A A A A A A Vss 1010 D D A A Vref+ A A A Vss 1011 D D A A Vref+ Vref- A A RA2 1100 D D D A Vref+ Vref- A A RA2 1101 D D D D Vref+ Vref- A A RA2 1110 D D D D D D D A Vss 1111 D D D D Vref+ Vref- D A RA2 Vref A/D/R + Vdd 8/0/0 RA3 7/0/1 Vdd 5/3/0 RA3 4/3/1 Vdd 3/5/0 RA3 2/5/1 0/8/0 0/8/0 RA3 6/0/2 Vdd 6/2/0 RA3 5/2/1 RA3 4/2/2 RA3 3/3/2 RA3 2/4/2 Vdd 1/7/0 RA3 1/5/2

Avant de vous donner le tableau concernant le 16F876, et qui est le mme sans les 3 pins AN7/AN5, je vais vous donner un petit mot dexplication sur linterprtation de celui que je viens de vous donner.

228

La premire colonne contient les 16 combinaisons possibles des bits de configuration PCFG3 PCFG0. Remarquez dj que les valeurs 0110 et 0111 donnent les mmes rsultats. Vous avez donc en ralit le choix entre 15 et non 16 combinaisons. Les colonnes AN7 AN0 indiquent le rle qui sera attribu chacune des pins concerne. Un A dans une ce ces colonnes indique que la pin correspondante est configure comme entre analogique (ne pas oublier TRISx), un D indiquera que la pin est en mode Digital, cest--dire quelle se comportera comme une pin dentre/sortie classique . La colonne AN3/RA3 peut contenir galement la valeur Vref+ qui indiquera que cette pin devra recevoir la tension de rfrence maximale. Il en va de mme pour AN2/RA2 qui pourra contenir Vref- , qui indiquera que cette pin doit recevoir la tension de rfrence minimale. La colonne Vref- indique quelle tension de rfrence minimale sera utilise par le convertisseur. Il ne pourra sagir que de la tension dalimentation Vss ou de la pin RA2. Cette colonne est donc lie au contenu de la colonne RA2 . Raisonnement identique pour la colonne Vref+ , lie RA3 . Cette colonne indique quelle sera la tension de rfrence maximale. De nouveau, il ne pourra sagir que de la tension dalimentation Vdd ou de la tension prsente sur la pin RA3. La dernire colonne A/D/R rsume les colonnes prcdentes. Le premier chiffre reprsente le nombre de pins configures en tant quentres analogiques, le second en tant quentres/sorties numriques, et le dernier le nombre de pins servant lapplication des tensions de rfrence. Comme il y a 8 pins concernes pour le 16F877, la somme des 3 chiffres pour chaque ligne sera bien entendu gale 8. Vous voyez que si vous avez le choix du nombre de pins configures en entres analogiques, vous navez cependant pas le choix de leur attribution. Par exemple, si vous avez besoin de configurer ces ports pour disposer de 3 entres analogiques et de 5 entres/sorties numriques, vous devez chercher dans la dernire colonne la ligne 3/5/0 . Cette ligne vous indique que vous devez configurer les bits PCFGx 0100, et que les pins utilises comme entres analogiques seront les pins RA0,RA1, et RA3. Vous devez donc en tenir compte au moment de concevoir votre schma. Une fois de plus, logiciel et matriel sont troitement lis. Vous voyez galement que lors dune mise sous tension, les bits PCFGx contiennent 0000. Le PORTA et le PORTE seront donc configurs par dfaut comme ports compltement analogiques. Ceci vous explique pourquoi lutilisation de ces ports comme ports dentres/sorties classiques implique dinitialiser ADCON1, avec une des valeurs des 2 lignes compltement en bleu dans le tableau. Je vous donne maintenant le tableau quivalent pour le 16F876 :

229

PCFG AN4 AN3 AN2 AN1 AN0 Vref3 0 RA5 RA3 RA2 RA1 RA0 0000 A A A A A Vss 0001 A Vref+ A A A Vss 0010 A A A A A Vss 0011 A Vref+ A A A Vss 0100 D A D A A Vss 0101 D Vref+ D A A Vss 0110 D D D D D 0111 D D D D D 1000 A Vref+ Vref- A A RA2 1001 A A A A A Vss 1010 A Vref+ A A A Vss 1011 A Vref+ Vref- A A RA2 1100 A Vref+ Vref- A A RA2 1101 D Vref+ Vref- A A RA2 1110 D D D D A Vss 1111 D Vref+ Vref- D A RA2

Vref A/D/R + Vdd 5/0/0 RA3 4/0/1 Vdd 5/0/0 RA3 4/0/1 Vdd 3/2/0 RA3 2/2/1 0/5/0 0/5/0 RA3 3/0/2 Vdd 5/0/0 RA3 4/0/1 RA3 3/0/2 RA3 3/0/2 RA3 2/1/2 Vdd 1/4/0 RA3 1/2/2

Ce tableau est identique celui du 16F877, except la disparition du PORTE. Du fait de cette disparition, vous trouverez plusieurs configuration de PCFGx qui donneront le mme rsultat. Dans ce cas, choisissez celle que vous voulez. Bien videmment, la somme des chiffres de la dernire colonne donnera la valeur 5 pour chacune des lignes, et non 8, comme ctait le cas sur le 16F877. 19.14 Le registre ADCON0 Ce registre est le dernier utilis par le convertisseur analogique/numrique. Il contient les bits que nous allons manipuler lors de notre conversion. Sur les 8 bits de notre registre, 7 seront utiliss. - b7 : ADCS1 - b6 : ADCS0 - b5 : CHS2 - b4 : CHS1 - b3 : CHS0 - b2 : GO/DONE - b1 : Inutilis - b0 : ADON : A/D conversion Clock Select bit 1 : A/D conversion Clock Select bit 0 : analog Channel Select bit 2 : analog Channel Select bit 1 : analog Channel Select bit 0 : A/D conversion status bit : lu comme 0 : A/D ON bit

Nous avons parl maintes reprises de diviseur, afin de dterminer lhorloge du convertisseur en fonction de la frquence du quartz utilis. Vous pouvez choisir ce diviseur laide des bits ADCSx.

230

ADCS1 0 0 1 1

ADCS0 0 1 0 1

Diviseur Fosc/2 Fosc/8 Fosc/32 Osc RC

Frquence maximale du quartz 1,25Mhz 5Mhz 20 Mhz Si > 1MHz, uniquement en mode sleep

Souvenez-vous que: La conversion durant le mode sleep ncessite de configurer ces bits sur Osc RC , car loscillateur principal du PIC est larrt durant ce mode Par contre, lemploi de loscillateur RC pour les PICs connectes un quartz de plus de 1MHz vous impose de placer le PIC en mode sleep durant la conversion. La mise en sommeil du PIC durant une conversion, alors que loscillateur nest pas configur comme oscillateur RC entranera un arrt de la conversion en cours, et une absence de rsultat, mme au rveil du PIC.

Vous avez vu que vous pouvez configurer, via ADCON1, plusieurs pins comme entres analogiques. Vous avez vu galement que vous ne pouvez effectuer la conversion que sur une pin la fois (on parlera de canal). Vous devez donc tre en mesure de slectionner la canal voulu. Ceci seffectue via les bits CHSx. CHS2 0 0 0 0 1 1 1 1 CHS1 0 0 1 1 0 0 1 1 CHS0 0 1 0 1 0 1 0 1 Canal 0 1 2 3 4 5 6 7 Pin AN0/RA0 AN1/RA1 AN2/RA2 AN3/RA3 AN4/RA5 AN5/RE0 (Uniquement pour 16F877) AN6/RE1 (Uniquement pour 16F877) AN7/RE2 (Uniquement pour 16F877)

Le bit ADON permet de mettre en service le convertisseur. Si le canal a t correctement choisi, le positionnement de ce bit permet de dmarrer la charge du condensateur interne, et donc dtermine le dbut du temps dacquisition. Quant au bit Go/DONE, il sera plac 1 par lutilisateur la fin du temps dacquisition. Cette action dtermine le dbut de la conversion en elle-mme, qui, je le rappelle, dure 12 Tad. Une fois la conversion termine, ce bit est remis 0 ( Done = Fait ) par llectronique du convertisseur. Cette remise 0 est accompagne du positionnement du flag ADIF du registre PIR1. Ce bit permettra ventuellement de gnrer une interruption. Vous disposez donc de 2 faons pratiques de connatre la fin de la dure de conversion :

231

Si votre programme na rien dautre faire durant lattente de la conversion, vous bouclez dans lattente du passage 0 du bit GO/Done. Si votre programme continue son traitement, vous pouvez utiliser linterruption gnre par le positionnement du flag ADIF.

Attention : Si vous arrtez manuellement la conversion en cours, le rsultat ne sera pas transfr dans les registres ADRESL et ADRESH. Vous nobtenez donc aucun rsultat, mme partiel. De plus, vous devrez quand mme respecter un temps dattente de 2Tad avant que ne redmarre automatiquement lacquisition suivante. 19.15 La conversion analogique/numrique et les interruptions Cette partie ne comporte aucune difficult particulire. En effet, linterruption gnre par le convertisseur est une interruption priphrique, et doit donc tre traite comme telle. Les diffrentes tapes de sa mise en service sont donc : Positionnement du bit ADIE du registre PIE1 Positionnement du bit PEIE du registre INTCON Positionnement du bit GIE du registre INTCON

Moyennant quoi, toute fin de conversion analogique entranera une interruption. Il vous suffira de remettre 0 le flag ADIF aprs traitement de cette interruption. 19.16 Lutilisation pratique du convertisseur Arriv ce stade, vous disposez de toutes les informations pour effectuer votre mesure de grandeur analogique. Voici un rsum des oprations concrtes effectuer pour chantillonner votre signal : 1) Configurez ADCON1 en fonction des pins utilises en mode analogique, ainsi que les registres TRISA et TRISE si ncessaire. 2) Validez, si souhaite, linterruption du convertisseur 3) Paramtrez sur ADCON0 le diviseur utilis 4) Choisissez le canal en cours de digitalisation sur ADCON0 5) Positionnez, si ce nest pas dj fait, le bit ADON du registre ADCON0 6) Attendez le temps Tacq (typiquement 19,7s sous 5V) 7) Dmarrez la conversion en positionnant le bit GO du registre ADCON0 8) Attendez la fin de la conversion

232

9) Lisez les registres ADRESH et si ncessaire ADRESL 10) Attendez un temps quivalent 2Tad (typiquement 3,2s) 11) Recommencez au point 4 Notez que puisque lacquisition redmarre automatiquement aprs le temps 2 Tad , vous pouvez relancer lacquisition directement, votre charge dattendre non pas le temps Tacq pour la fin de lacquisition, mais le temps Tacq + 2Tad. Ceci vous pargne une temporisation. En effet, 2 temporisations qui se suivent peuvent tre remplaces par une temporisation unique de temps cumul. Si donc, nous prenons notre PIC cadence 20Mhz, sous une tension dalimentation de 5V, nous aurons : 1) Configurez ADCON1 en fonction des pins utilises en mode analogique, ainsi que les registres TRISA et TRISE si ncessaire. 2) Validez, si souhait, linterruption du convertisseur (PEIE, ADIE, GIE) 3) Paramtrez le diviseur 32 sur ADCON0 (B10000000) 4) Choisissez le canal en cours de digitalisation sur ADCON0 et lancez le convertisseur (B10xxx001) 5) Attendez le temps (Tacq+2Tad), soit 19,7s + 3,2s = 22,9s 6) Dmarrez la conversion en positionnant le bit GO du registre ADCON0 7) Attendez la fin de la conversion 8) Lisez les registres ADRESH et si ncessaire ADRESL 9) Recommencez au point 4 Notez que vous pouvez, comme montr, raliser plusieurs oprations en mme temps. Cependant, fort logiquement, vous ne pouvez pas positionner les bits ADON et GO/DONE en mme temps, puisque le temps Tacq doit imprativement les sparer. Remarque : Lorsque vous disposez de beaucoup de temps entre 2 lectures de la valeur analogique, je vous conseille deffectuer plusieurs mesures intermdiaires, et deffectuer la moyenne de ces mesures. Ainsi, un parasite ventuel, ou une lgre fluctuation de votre tension sera fortement attnue. Il est pratique dans ce cas deffectuer un nombre de mesures qui est une puissance de 2 (2 mesures, 4,8,16). Ainsi, pour effectuer votre moyenne, il suffira deffectuer la somme de toutes les valeurs, la division seffectuant par simple dcalage.

233

Exemple : vous faites 4 mesures successives. Le rsultat moyen sera donc (somme des 4 valeurs) / 4, donc, au niveau programmation, somme des 4 valeurs, suivie de 2 dcalages vers la droite (division par 4). Seconde remarque, vous verrez, dans le chapitre concernant les modules CCP, que des mcanismes sont prvus pour vous faciliter la vie au niveau de lautomatisation des squences. Jy reviendrai ce moment, mais je vous conseille de suivre dans lordre, pour ne pas tre submerg dinformations parfois assez lourdes digrer. 19.17 Exercice pratique sur le convertisseur A/D Oui, oui, je sais. Vous devez vous dire : ce nest pas trop tt . En effet, je vous ai un peu assomm de thorie durant toute cette partie. Cependant, ce nest pas de linformation perdue, elle vous sera ncessaire si vous dcidez dutiliser le convertisseur la limite de ses possibilits. Certes, ce ne sera pas toujours le cas, mais qui peut le plus peut le moins, et, de toutes faons, je vous ai donn des valeurs standard qui vous permettent dignorer toute la partie calcul si cela ne vous est pas utile. Vous allez donc voir que paradoxalement, la mise en application est plus simple que la thorie relative la conversion. Mais assez de discours, commenons notre exercice par llectronique associe. Nous allons nous servir de notre petit chenillard lum1 , et lamliorer en y ajoutant 2 potentiomtres destins rgler, dune part le temps dallumage de chaque LED, et dautre part, le temps dextinction entre 2 allumages successifs. Le matriel ncessaire supplmentaire consistera donc en 2 potentiomtres de type linaire , et dune valeur peu critique, comprise entre 1Kohms (recommand), et 10 Kohms. Etant donn que nous avons effectu nos calculs de temps sur la base dune alimentation de 5V et dune rsistance de source gale 10Kohms, ces valeurs permettront une numrisation suffisamment rapide de notre tension dentre. Lachat ne vous ruinera pas, le prix dun potentiomtre avoisinant 1 Euro. Vous pouvez mme utiliser des rsistances ajustables pour circuit imprim, qui vous fera encore baisser le prix de faon significative. Nous allons monter nos potentiomtres en mode diviseur de tension , ce qui fait que la tension prsente sur le curseur sera proportionnelle la position du bouton sur son axe de rotation. En clair, plus vous tournerez le bouton du potentiomtre vers la droite, plus la tension sur le curseur augmentera. Comme nous alimentons ces potentiomtres partir de Vdd et de Vss, la tension minimale prsente sur le curseur sera de Vss, tandis que la tension maximale sera de Vdd. Nous devons donc choisir un mode de fonctionnement comportant 2 pins configures en entres analogiques, et aucune tension de rfrence externe (2/x/0). Ceci, en consultant notre tableau ADCON1, nous indique que cette possibilit nexiste pas. Par contre, nous avons la possibilit dutiliser 3 entres analogiques sans tension de

234

rfrence externe. Nous choisirons donc cette possibilit, quitte ne pas utiliser un des canaux analogiques alors disponibles. PCFG AN4 AN3 AN2 AN1 AN0 Vref- Vref 3 0 RA5 RA3 RA2 RA1 RA0 + 0100 D A D A A Vss Vdd A/D/R 3/2/0

Nous devons de ce fait connecter nos 2 potentiomtres sur 2 des 3 pins suivantes : RA3, RA1,RA0. Pour rester logiques, nous choisissons RA0 pour le potentiomtre 1, et RA1 pour le potentiomtre 2. Voici donc notre schma (remarquez que nous avons t oblig de consulter les options possibles au niveau du logiciel avant de pouvoir le dessiner ) :

Il me reste vous donner le brochage des potentiomtres. En fait, si vous placez celui-ci bouton face vous, et les 3 pins de connexion diriges vers le bas, la pin 1 se trouve votre droite, la 2 au centre, et la 3 votre gauche. Linversion des broches 1 et 3 inversera le sens

235

de fonctionnement du potentiomtre, linversion de la broche 2 avec une des 2 autres se traduira par. un joli petit nuage de fume.

Maintenant, nous allons calculer les temporisations. Le potentiomtre 1 va nous servir pour tablir la dure dallumage des LEDs de sortie, le potentiomtre 2 va rgler la dure de lextinction. Nous allons dcider que la dure minimale dclairage sera de 1/10me de seconde, la dure maximale de 2,5S. La dure dextinction variera dans le mme registre. Nous aurons donc un algorithme du type :
Allumage des LEDs Attente suivant position potentiomtre 1 de 0,1 2,5s. Extinction des LEDs Attente suivant position du potentiomtre 2 de 0,1 2,5S Allumage suivant : on recommence.

On va dcider, pour des raisons de facilit, dutiliser 256 valeurs intermdiaires pour nos potentiomtres. Ceci nous permet de coder la valeur sur 1 octet. Un pas de variation vaudra donc plus ou moins : 2500ms / 256 = 9,77 ms. Notre timer 0 nous donne un temps de dbordement, sans prdiviseur, de 0,2 * 256 = 51,2s. Pour obtenir 9,77ms, nous avons besoin dun prdiviseur de 9770 / 51,2 = 190,8. Cette valeur nexiste pas, mais comme notre application ne requiert pas de temps critiques, et que nous avons choisi ces valeurs au pif , nous allons prendre un prdiviseur de 256. De ceci, nous tirons : Le temps sparant 2 passages successifs dans notre routine dinterruption sera de : 0,2s * 256 * 256 = 13,1072 ms Pour attendre la dure minimale de 100ms, nous devrons passer 100 / 13,1072 = approximativement 8 fois dans notre routine dinterruption. Pour attendre la dure maximale de 2500ms, nous devrons passer 2500 / 13,1072 = 190 fois dans cette mme routine.

236

Donc, notre potentiomtre devra faire varier notre compteur de passage entre 8 et 190 selon sa position. Notre potentiomtre devra donc pouvoir prendre 191 8 = 183 valeurs, ce qui nest pas pratique. Nous utiliserons donc 256 valeurs, pour rester dans les puissances de 2. Nous pourrons donc faire varier le nombre de passages entre 8 (potentiomtre au minimum) et (8 + 255), soit 263 (potentiomtre au maximum). Ceci nous donne un temps minimum de 104 ms et un temps maximum de 3,45 secondes. Ceci est tout fait valable pour notre petit chenillard. Dautant que nous pourrons rgler la vitesse avec des pas de 10 ms. Nous allons commencer par crire notre pseudo-code sans nous proccuper de la conversion analogique/numrique. Nous avons dj tudi notre programme principal. Nous voyons que nous avons besoin dune routine dinterruption. Nous pouvons nous dire que pendant que nous attendons, nous navons rien dautre faire, inutile donc dutiliser une interruption, une simple boucle fera laffaire. Nous utiliserons cependant notre timer 0 pour faciliter le calcul de la dure. On attend 8 fois le dbordement du timer0 ( 8 * 13,10ms) Tant que le compteur est suprieur 0, On attend 13,10ms supplmentaires On dcrmente le compteur On doit alors intercaler la lecture des diffrents potentiomtres. Comme nous nutilisons quun potentiomtre la fois (on allume, on attend en fonction du potentiomtre 1, on teint, on attend en fonction du potentiomtre 2), et que le temps dattente minimum est de 8 * 13,10ms, on remarque quon a largement le temps deffectuer plusieurs lectures de chaque valeur. En effet, pour lire un potentiomtre, il nous faut approximativement 20s de temps dacquisition, et peu prs le mme temps (19,2s) pour effectuer la conversion. Ceci nous prend donc de lordre de 40s, comparer aux 13,10ms (13100 s) de chacun des dbordements de notre timer 0. Comme nous avons au minimum 8 temps dattente, pourquoi donc ne pas en profiter pour raliser 8 lectures du potentiomtre concern ? Notre routine de temporisation devient donc : Pour chacun des 8 premires boucles On lance une conversion A/D On attend que le timer 0 dborde (13,10ms) Dlai supplmentaire On calcule la moyenne des 8 conversions effectues Tant que le rsultat est > 0 On attend que le timer 0 dborde On dcrmente le rsultat Il nous faut maintenant crire la routine de conversion A/D. Pour bien appliquer ce que nous avons appris, nous allons effectuer les oprations suivantes :

237

On choisit le canal numriser (canal 0 = potentiomtre 1, canal1 = pot2) On lance lacquisition (charge du condensateur) en mettant ON le convertisseur On attend 20s On lance la conversion en mettant GO 1 dans ADCON0

Bien entendu, nous avons besoin de mesurer nos 20s. Ceci correspond 100 cycles dinstructions. Nous pouvons donc utiliser notre timer 2, sans diviseur, et avec une valeur de 100-1 dans PR2. Il nous suffira donc dattendre le positionnement du flag TRM2IF dans le registre PIR1. Le temps dattente se ralisera donc comme suit, PR2 tant initialis D99 dans la routine dinitialisation. On efface TMR2 On efface TMR2IF On lance le timer 2 On attend le positionnement de TMR2IF On arrte le timer 2

Ne reste donc plus qu sauvegarder notre rsultat (sur 10 bits) dans 2 variables. Ceci pourra se faire, par exemple, dans notre routine dinterruption AD, qui sera dclenche automatiquement la fin de la conversion. Cette routine se bornera crire les 2 registres du rsultat dans les variables pointes. Donc, pour rsumer, voici comment les vnements vont se succder chronologiquement pour chacune des 8 premires dures de 13,10ms. On met en service le convertisseur On attend 20s On lance la conversion 19,2s plus tard, linterruption A/D est gnre On sauve le rsultat et on arrte le convertisseur On attend le temps qui reste de nos 13,10ms du dpart On recommence

Larrt du convertisseur et du timer 2 ne sont pas obligatoires, mais diminuent la consommation(et donc lchauffement) du PIC. Cest donc une bonne habitude de procder de la sorte. Donc, le temps qui spare 2 conversions est dapproximativement 13,10ms 20s 19,2s, soit bien plus que les 2Tad ncessaires (3,2s). Donc, aucun soucis de ce ct. Voyons maintenant la ralisation pratique de notre programme. Commencez par copier le fichier lum1.asm , et renommez la copie en lum2.asm . Editez len-tte du programme :

238

;***************************************************************************** ; Ralisation d'un mini-chenillard 8 LEDs avec 2 potentiomtres de * ; rglage. * ; * ;***************************************************************************** ; * ; NOM: Lum2 * ; Date: 31/05/2002 * ; Version: 1.0 * ; Circuit: Circuit maquette * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; lumdat.inc * ; * ; * ; * ;***************************************************************************** ; * ; Notes: les 8 sorties sont sur le PORTB. Un niveau haut allume la LED * ; correspondante. * ; Exercice sur les conversions A/D, l'aide de 2 potentiomtres * ; Le potentiomtre 1 est sur RA0 * ; Le potentiomtre 2 est sur RA1 * ; Le potentiomtre 1 rgle le temps d'allumage * ; Le potentiomtre 2 rgle le temps d'extinction * ; La frquence du quartz est de 20MHz * ; * ;*****************************************************************************

Ensuite, la configuration, qui ne pose aucun problme :


LIST p=16F876 #include <p16F876.inc> #include <lumdat.inc> ; Dfinition de processeur ; fichier include ; donnes d'allumage

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_ON & _HS_OSC ;_CP_OFF Pas de protection ;_DEBUG_OFF RB6 et RB7 en utilisation normale ;_WRT_ENABLE_OFF Le programme ne peut pas crire dans la flash ;_CPD_OFF Mmoire EEprom dprotge ;_LVP_OFF RB3 en utilisation normale ; _BODEN_OFF Reset tension hors service ;_PWRTE_ON Dmarrage temporis ;_WDT_ON Watchdog en service ;_HS_OSC Oscillateur haute vitesse (20Mhz)

On trouve ensuite la valeur pour le registre OPTION


;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000111' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; PSA b3 : 0= Assignation prdiviseur sur Tmr0

239

; PS2/PS0

b2/b0 valeur du prdiviseur = 256

Nous naurons quune seule source dinterruption, le convertisseur A/D, qui est une interruption priphrique. Ceci nous impose donc de programmer INTCON et PIE1 :
; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQU B'01000000' ; PEIE b6 : masque autorisation gnrale priphriques ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQU B'01000000' ; ADIE b6 : masque interrupt convertisseur A/D

Comme il nexiste pas de mode 2 canaux analogiques sans tension de rfrence externe , nous utiliserons le mode 3 canaux . Nous choisissons une justification droite, pour conserver, avant calcul de la moyenne, lintgralit des 10 bits de rsultat. Ce nest quaprs avoir calcul la moyenne, que nous ne conserverons que les 8 bits les plus significatifs.
; REGISTRE ADCON1 (ANALOGIQUE/DIGITAL) ; -----------------------------------ADCON1VAL EQUB'10000100' ; 3 Entres analogiques, rsultat justifi ; droite

Le PORTA sera configur en entre, le PORTB en sortie. Nous aurions pu nous abstenir de configurer TRISA, celui-ci tant positionn en entre au moment de la mise sous tension. Cependant, je laisse cette configuration pour vous rappeler que TRISA intervient dans linitialisation du convertisseur.
; DIRECTION DES PORTS I/O ; ----------------------DIRPORTA EQUB'00111111' ; Direction PORTA (1=entre) DIRPORTB EQUB'00000000' ; Direction PORTB

Les macros ne posent aucun problme. Jen profite pour rappeler que les macros non utilises ne consomment aucune place dans la mmoire du PIC, puisquelles ne sont tout simplement pas traduites.
***************************************************************************** ; MACRO * ;***************************************************************************** ; Changement de banques ; ---------------------BANK0 macro bcf STATUS,RP0 bcf STATUS,RP1 endm BANK1 macro bsf STATUS,RP0 bcf STATUS,RP1 endm ; passer en banque0;

; passer en banque1

240

BANK2 macro bcf STATUS,RP0 bsf STATUS,RP1 endm BANK3 macro bsf STATUS,RP0 bsf STATUS,RP1 endm

; passer en banque2

; passer en banque3

Nous arrivons notre zone de variables. Nous avons besoin dune variable sur 8 bits pour compter les boucles de notre routine de temporisation. Les 8 lectures successives du potentiomtre concern ncessitera 16 emplacements (8 * 2 octets). Pour pouvoir calculer la moyenne de ces valeurs, nous devrons en faire laddition, ceci ncessitera donc une variable de 2 octets. Ne reste plus que le flag qui va nous informer si nous sommes en train de nous occuper de notre potentiomtre 1 ou de notre potentiomtre 2. Tout ceci nous donne :
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 cmpt : 1 flags : 1 potar : 16 result : 2 ENDC ; ; ; ; Dbut de la zone (0x20 0x6F) compteur de boucles flags divers b0 : 0 = potar1, 1=potar2

; valeurs du potentiomtre lu(msb,lsb) ; rsultat de la somme des valeurs ; Fin de la zone

#DEFINE numpotar flags,0 ; flag de temporisation

Notre zone commune se borne aux registres sauvegarder :


***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

Le dmarrage du programme, comme toujours en adresse 0x00 :


; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; ////////////////////////////////////////////////////////////////////////////

241

;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

Puis notre routine dinterruption, qui ne contient que linterruption du convertisseur. Le test de linterruption est donc inutile :
;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp BANK0 ; passer en banque0 ; Interruption AD ; --------------bcf ADCON0,ADON ; teindre convertisseur movf ADRESH,w ; charger poids fort conversion movwf INDF ; sauver dans zone de sauvegarde incf FSR,f ; pointer sur poids faible bsf STATUS,RP0 ; passer banque 1 movf ADRESL,w ; charger poids faible conversion bcf STATUS,RP0 ; passer banque 0 movwf INDF ; sauver dans zone de sauvegarde incf FSR,f ; pointer sur poids fort suivant bcf PIR1,ADIF ; effacer flag interupt ;restaurer registres ;------------------restorereg swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W ; sans modifier Z swapf w_temp,w ; Rinversion de L et H dans W ; W restaur sans modifier status retfie ; return from interrupt

Une petite remarque, ce sujet. Nous utilisons FSR dans le programme principal et dans la routine dinterruption, pourtant nous ne le sauvons pas. Ceci est d au fait que la modification de FSR effectue dans la routine dinterruption est utilise dans le programme principal. Ceci est possible uniquement parce que la routine dinterruption intervient un endroit connu de notre programme, et nest donc pas rellement asynchrone. Cest le signe que le traitement de ce morceau de code pouvait tre effectu dans le programme principal, une routine dinterruption ntait donc pas indispensable. Nanmoins, jai utilis une interruption dessin pour montrer son utilisation de faon didactique. Je rappelle quil nest nullement question doptimisation ici, bien quon pouvait

242

simplifier facilement le programme, mais plutt de vous montrer la faon gnrale de procder. Vous aurez plus facile de supprimer ce qui est inutile plutt que dajouter ce que je ne vous aurai pas montr. La routine dinitialisation est semblable celle de lum1 , ceci prs quil a fallu initialiser le timer 2 et son registre PR2.
; //////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------BANK0 ; slectionner banque0 clrf PORTA ; Sorties PORTA 0 clrf PORTB ; sorties PORTB 0 bsf STATUS,RP0 ; passer en banque1 movlw ADCON1VAL ; PORTA en mode digital/analogique movwf ADCON1 ; criture dans contrle A/D movlw DIRPORTA ; Direction PORTA movwf TRISA ; criture dans registre direction movlw DIRPORTB ; Direction PORTB movwf TRISB ; criture dans registre direction ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1

movlw movwf movlw movwf

; timer 2 (banque 1) ; -----------------movlw D'99' ; 99 + 1 passages = 20s movwf PR2 ; dans comparateur ; initialiser variables ; --------------------; passer en banque 2 low mesdata ; adresse basse des data EEADR ; dans pointeur bas FLASH high mesdata ; adresse haute des data EEADRH ; dans pointeur haut FLASH STATUS,RP1 ; repasser en banque 0 potar ; adresse de la zone de stockage FSR ; dans pointeur T2CON ; pr = postdiviseur = 1 ; autoriser interruptions (banque 0) ; ---------------------------------PIR1 ; effacer flags 1

BANK2 movlw movwf movlw movwf bcf movlw movwf clrf

clrf

243

bsf goto

INTCON,GIE start

; valider interruptions ; programme principal

Rien de chang pour la routine de lecture dun octet en mmoire programme :


;***************************************************************************** ; LIRE UN OCTET EN FLASH * ;***************************************************************************** ;----------------------------------------------------------------------------; Lit un octet en mmoire programme, puis pointe sur le suivant ; retourne l'octet lu dans w ; si le bit 8 du mot lu vaut 1, on pointe sur la premire donne ;----------------------------------------------------------------------------readflash BANK3 ; pointer sur banque3 bsf EECON1,EEPGD ; accs la mmoire programme bsf EECON1,RD ; lecture nop ; 2 cycles d'attente nop bcf STATUS,RP0 ; pointer sur banque2 incf EEADR,f ; incrmenter pointeur bas sur donnes btfsc STATUS,Z ; tester si dbord incf EEADRH,f ; oui, incrmenter pointeur haut btfss EEDATH,0 ; tester bit 0 data haute = bit 8 donne goto readfsuite ; si 0, sauter instructions suivantes movlw low mesdata ; si 1, adresse basse des data movwf EEADR ; dans pointeur bas FLASH movlw high mesdata ; adresse haute des data movwf EEADRH ; dans pointeur haut FLASH readfsuite movf EEDATA,w ; charger 8 bits donne bcfSTATUS,RP1 ; repointer sur banque0 return ; et retour

La routine de temporisation est conforme ce que nous avons dfini, avec dans lordre, les 8 premiers passages, avec lancement de la numrisation
;***************************************************************************** ; Temporisation * ;***************************************************************************** ;----------------------------------------------------------------------------; Base de temps de 13,1072ms. ; On attend d'office 8 * 13,10 ms. Durant cette attente, on lance 8 ; numrisations du potentiomtre concern. ; Ensuite, on calcule la moyenne des 8 conversions, et on garde le rsultat ; sur 8 bits. Cette valeur indique le nombre de multiples de 13,10ms ; supplmentaires attendre. ;----------------------------------------------------------------------------tempo ; gestion des 8 premiers passages ; ------------------------------movlw 0x08 ; pour 8 * 13,10ms movwf cmpt ; dans compteur tempo1 call acquire ; lancer acquisition call wait ; attendre 13,1072ms decfsz cmpt,f ; dcrmenter compteur de boucles goto tempo1 ; boucle suivante

244

Ensuite, la somme des 8 valeurs obtenues :


; calcul de la somme des valeurs ; -----------------------------movlw 0x08 ; pour 8 boucles movwf cmpt ; dans compteur de boucles clrf result ; effacer rsultat clrf result+1 ; idem pour poids faible tempo2 decf FSR,f ; pointer sur poids faible movf INDF,w ; charger poids faible addwf result+1,f ; ajouter au poids faible rsultat btfsc STATUS,C ; tester si dbordement incf result,f ; oui, poids fort +1 decf FSR,f ; pointer sur poids fort movf INDF,w ; charger poids fort addwf result,f ; ajouter au poids fort rsultat decfsz cmpt,f ; dcrmenter compteur de boucles goto tempo2 ; pas dernire, suivante

Remarquez lastuce utilise : la routine dinterruption du convertisseur incrmente FSR depuis le premier octet de poids fort des valeurs numrises, jusqu loctet qui suit le dernier octet de poids faible. Il suffit donc de dcrmenter successivement FSR pour additionner toutes les valeurs en commenant par le dernier poids faible. La mthode est celle de laddition classique sur 2 octets : on additionne les 2 poids faibles, sil y a dbordement, on incrmente le poids fort. On additionne ensuite les 2 poids forts. Comme nous avons 8 valeurs additionner, et que chaque valeur ne comporte que 10 bits valides, le rsultat tiendra sur 13 bits. Donc, il ny aura pas dbordement en additionnant les poids forts. Nous procdons ensuite au calcul de la moyenne. Comme nous voulons un rsultat sur 8 bits, et que nous en avons 13, on pourrait se dire quon doit dcaler le rsultat 5 fois vers la droite. Le rsultat final se trouvant dans le poids faible du rsultat. En fait, il est plus simple de tout dcaler de 3 rangs vers la gauche, le rsultat se trouvant alors dans le poids fort du rsultat. Faites le test sur papier pour vous en convaincre.
; calcul de la moyenne sur 8 bits ; ------------------------------result+1,f ; dcaler poids faible vers la gauche result,f ; idem poids fort avec b7 poids faible result+1,f ; dcaler poids faible vers la gauche result,f ; idem poids fort avec b7 poids faible result+1,f ; dcaler poids faible vers la gauche result,f ; idem poids fort avec b7 poids faible ; on avait 5 + 8 bits, on a 8 + 5 bits

rlf rlf rlf rlf rlf rlf

De nouveau, la mthode est classique : on dcale le poids faible vers la gauche, le bit 7 tombe dans le carry. On dcale ensuite le poids fort, le carry devient le bit0. On a donc dcal les 16 bits vers la gauche. Notez que nous aurions du mettre le carry 0 avant le dcalage du poids faible, mais ceci est inutile ici, car nous nutiliserons pas le poids faible du rsultat, seuls comptent les 8 bits finaux du poids fort. Donc peut importe si nous avons fait entrer un 1 indsirable dans le poids faible.

245

Il reste attendre (result * 13,10ms). Nous devons tester result avant la boucle, de faon ce quune valeur de 0 ne provoque pas 256 boucles, ce qui aurait t le cas en utilisant une boucle de type decfsz .
; attendre result * 13,10ms ; ------------------------; ; ; ; ; ; tester rsultat tester si 0 oui, fin de l'attente attendre dcrmenter dure restante boucle suivante

tempo3 movf result,f btfsc STATUS,Z return call wait decfsz result,f goto tempo3

Il faut crire maintenant la petite sous-routine dattente du dbordement du timer 0 :


;***************************************************************************** ; Attendre fin timer 0 * ;***************************************************************************** wait clrf TMR0 ; effacer timer0 bcf INTCON,T0IF ; effacer flag dbordement wait1 btfss INTCON,T0IF ; tester si timer a dbord goto wait1 ; non, attendre return ; fin d'attente

Passons maintenant la routine de dmarrage de la numrisation. Cette dernire, comme prvu, se compose de 3 parties : Lancement de lacquisition, attente du temps Tacq, dmarrage du convertisseur :
;***************************************************************************** ; Acquisition de la valeur analogique * ;***************************************************************************** ;----------------------------------------------------------------------------; Si numpotar vaut 0, on travaille sur le potar1, sinon, c'est le potar2 ; On slectionne le canal, et on lance l'acquisition ; on attend 20 s (Tacq), puis on lance la numrisation ; la fin de numrisation sera dtecte par interruption ;----------------------------------------------------------------------------acquire ; lancer l'acquisition ; -------------------movlw B'10000001' ; diviseur 32, canal 0, convertisseur ON btfsc numpotar ; tester si c'est potentiomtre 2 movlw B'10001001' ; oui, alors canal 1 movwf ADCON0 ; paramtrer convertisseur, lancer acquisition ; attendre 20 s ; -------------TMR2 ; effacer timer2 PIR1,TMR2IF ; effacer flag dbordement T2CON,TMR2ON ; lancer timer 2 ; tester si temps coul ; non, attendre ; oui, arrt du timer 2

clrf bcf bsf acquire1 btfss PIR1,TMR2IF goto acquire1 bcf T2CON,TMR2ON

246

; dmarrage du convertisseur ; -------------------------bsf ADCON0,GO ; lancer conversion A/D return ; fin de l'acquisition

Le programme principal est tout simple :


;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start clrwdt call readflash movwf PORTB bcfnumpotar call tempo clrf PORTB bsfnumpotar call tempo goto start mesdata END ; ; ; ; ; ; ; ; ; ; ; effacer watch dog lire un octet en flash le mettre sur le PORTB (allumage LEDs) pour potentiomtre 1 attendre en fonction du potentiomtre teindre LEDs pour potentiomtre 2 attendre en fonction du potentiomtre boucler emplacement des donnes (lumdat.inc) directive fin de programme

Lancez lassemblage, chargez le programme, et lancez lalimentation. Votre chenillard est maintenant rglable avec les 2 potentiomtres. Tiens, le vtre reste obstinment bloqu sur la premire LED ? Que se passe-t-il donc ? Jattribue 10 points avec mention du jury ceux qui ont dj compris, les autres, je vous conseille de rflchir un peu avant de poursuivre la lecture. Pour rechercher la cause, passons dans MPLAB en mode simulation en pas pas. Aprs lassemblage, pressez <F6>, puis des pressions successives de <F7> jusqu la ligne :
call readflash ; lire un octet en flash

du programme principal. Continuez ensuite les pressions successives de <F7> jusqu ce que vous arriviez la ligne suivant ltiquette tempo de votre routine de temporisation. Pressez alors successivement <F7> jusque la ligne :
call acquire ; lancer acquisition

Pressez alors sur <F8>. Ceci vous permet dexcuter la sous-routine en une seule fois, sans devoir entrer manuellement lintrieur. Vous tes maintenant sur la ligne : callwait ; attendre 13,1072ms

Pressez de nouveau <F8>. La barre infrieure passe en jaune, preuve que le simulateur travaille. En effet, cette routine dure trs longtemps du point de vue du PIC. En effet, 13,10ms reprsentent plus de 65000 instructions excuter. Au bout dun moment, votre programme devrait se retrouver la ligne suivante, savoir :

247

decfsz cmpt,f

; dcrmenter compteur de boucles

En fait, vous vous retrouvez de faon incomprhensible :


org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

Pour ceux qui disent mais bon sang, cest normal , je donne 8 points. Pour les autres, je donne un indice pour 5 points : Ladresse 0x00 est ladresse de reset, notre PIC a donc effectu un reset durant notre routine de temporisation. Vous avez trouv ? Sinon, reste savoir quel reset, parmi les types possibles. En procdant par limination, on trouve assez facilement quil sagit dun reset provoqu par le watchdog. En effet, on va attendre au minimum 8 * 13,10ms dans la routine de temporisation, alors que le temps minimal de reset par watchdog sans prdiviseur se situe sous cette valeur. Donc, ne tombez pas dans le pige, pensez que vous devez intercaler des instructions clrwdt avant que ne soit provoqu un reset par le mcanisme du watchdog. Le meilleur emplacement pour le faire est bien entendu dans la boucle dattente du dbordement du timer 0, dans la sous-routine wait .
wait clrf TMR0 bcf INTCON,T0IF wait1 clrwdt btfss INTCON,T0IF goto wait1 return ; effacer timer0 ; effacer flag dbordement ; ; ; ; effacer watchdog tester si timer a dbord non, attendre fin d'attente

Vous pouvez maintenant relancer lassemblage et reprogrammer votre PIC. Notez alors que le potentiomtre 1 vous permet de rgler le temps durant lequel les LEDs restent allumes, alors que le potentiomtre 2 permet de rgler le temps durant lequel elles restent teintes. 19.18 Conclusion Nous en avons maintenant termin avec notre convertisseur A/D. Tout ceci a du vous paratre compliqu et laborieux , mais vous avez vu dans notre application pratique quon pouvait en gnral se passer des calculs. Ces calculs vous seront par contre utiles pour les applications qui ncessitent dexploiter le convertisseur au maximum de ses possibilits, ce qui rendait impratif les explications thoriques. Grce ce convertisseur, vous allez pouvoir mesurer des rsistances, des tensions continues ou alternatives, des tempratures, et toute autre grandeur analogique. Ceci ouvre donc les portes de lanalogique votre composant numrique.

248

Notes :

249

Notes :

250

20. Les modules CCP1 et CCP2


20.1 Gnralits Les 16F87x disposent de 2 modules CCP. CCP signifie Capture, Compare, and PWM. Ceci vous indique dj que nous pourrons diviser ce chapitre entre 3 parties distinctes, correspondant autant de modes de fonctionnement. Ces modules CCP sont fortement lis et dpendant des timers 1 et 2, aussi jaurais pu placer ce chapitre directement aprs ltude des timers. Cependant, ils sont galement lis au convertisseur A/D, et, de plus, jaurai besoin de ce dernier pour pouvoir vous proposer un exercice pratique en fin dtude thorique. Ceci explique pourquoi jen parle maintenant. Cependant, sachez dj que ces modules augmentent les capacits des timers, et donc pondrent la conclusion les concernant quand leurs utilisations classiques Il faut savoir que les 2 modules CCP1 et CCP2 sont strictement identiques, except la possibilit, pour le module CCP2, de dmarrer automatiquement la conversion A/D. Jen reparlerai. 20.2 Ressources utilises et interactions Au niveau ressources utilises, nous pouvons simplement dire que les modules CCPx utiliss en mode compare et en mode capture font appel au timer 1, alors que le mode PWM ncessite lutilisation du timer 2. Vous comprenez dj que vous allez vous heurter 2 types de contraintes : Dune part, lutilisation des timers dans les modes tudis prcdemment et dun module CCPx va tre soumise des contraintes inhrentes aux registres utiliss. Vous comprenez en effet quil va tre impossible, par exemple, de charger TMR1L et TMR1H simultanment avec une valeur qui vous arrange pour lutilisation en mode timer, et une autre pour lutilisation du CCP1 en mode compare. Dautre part, lutilisation de 2 modules CCP simultanment va entraner galement des contraintes, dans la mesure o ils devront utiliser les mmes ressources. Tout sera donc question dtude et de compromis.

Nous allons principalement nous intresser aux interactions lors de lutilisation de 2 modules simultanment. Les contraintes lies lutilisation classique des timers seront abordes dans chaque cas particulier. Puisque chacun des modules dispose de 3 modes de fonctionnement, nous aurons 9 possibilits dinteraction. Mais, comme les 2 modules sont en fait identiques, les interactions se rsument 6 cas possibles. En effet, les contraintes lies par exemple CCP1 en mode capture et CCP2 en mode compare sont strictement identiques aux contraintes pour CCP1 en mode compare et CCP2 en mode capture.

251

Pour notre tableau des contraintes, CCPx concerne CCP1 ou CCP2 au choix, alors que CCPy concerne forcment lautre CCP. Mode de CCPx Mode de CCPy Interaction Capture Capture Les modules doivent utiliser la mme base de temps du timer 1 Capture Compare Si le module compare est utilis en mode trigger, le reset peut perturber les mesures du module capture. Capture PWM Aucune interaction, les modules utilisent un timer diffrent. Compare Compare En mode trigger, le premier reset survenu empche lautre comparateur datteindre sa valeur Compare PWM Aucune interaction, les modules utilisent un timer diffrent. PWM PWM La frquence sera identique, ainsi que les mises jour via linterruption TMR2 En somme, rien que du logique, ne vous tracassez pas pour les termes que vous ne comprenez pas, nous allons parler de tout a en dtail. 20.3 Les registres CCP1CON et CCP2CON Tout dabord, ne confondez pas, ces registres ont la mme fonction, simplement CCP1CON concerne le module CCP1, tandis que CCP2CON concerne le module CCP2. Ce registre CCPxCON permet donc, en toute logique, de dterminer le mode de fonctionnement du module. Voici son contenu, x remplace 1 ou 2 suivant le module utilis dans tout le reste du chapitre. CCPxCON b7 : Inutilis b6 : Inutilis b5 : CCPxX b4 : CCPxY b3 : CCPxM3 b2 : CCPxM2 b1 : CCPxM1 b0 : CCPxM0 : Lu comme 0 : Lu comme 0 : module Capture Compare and Pwm x bit X : module Capture Compare and Pwm x bit Y : module Capture Compare and Pwm x Mode select bit 3 : module Capture Compare and Pwm x Mode select bit 2 : module Capture Compare and Pwm x Mode select bit 1 : module Capture Compare and Pwm x Mode select bit 0

Tout dabord, voyons les bits CCPxX et CCPxY. Ces bits sont en fait les 2 bits de poids faible qui compltent le nombre de 10 bits utilis pour le mode de fonctionnement PWM. Jen parlerai donc au moment de ltude de ce mode de fonctionnement. Dans les autres modes, ces bits sont donc inutiliss. Les bits CCPxM3 CCPxM0 servent dterminer quel sera le mode de fonctionnement du module concern. Les possibilits sont les suivantes :

252

CCPM 0000 0100 0101 0110 0111 1000 1001 1010 1011 11xx

Fonctionnement Module CCPx larrt Mode capture valid sur chaque flanc descendant Mode capture valid sur chaque flanc montant Mode capture valid sur chaque multiple de 4 flancs montants Mode capture valid sur chaque multiple de 16 flancs montants Mode compare, place la sortie 1 sur dbordement (+ bit CCPxIF = 1) Mode compare, place la sortie 0 sur dbordement (+ bit CCPxIF = 1) Mode compare, positionne CCPxIF sans affecter la sortie Mode compare, positionne CCPxIF sans affecter la sortie, et gnre le trigger Mode PWM

Au niveau du mode compare gnrant le trigger, il faut distinguer laction du module CCP1 de celle du module CCP2 : Pour CCP1, lvnement trigger remet TMR1 0 (reset) Pour CCP2, lvnement trigger remet TMR1 0 (reset) et lance automatiquement la conversion A/D (si le module A/D est en service).

Notez quun reset provoque larrt des modules CCP, le contenu du prdiviseur est de plus remis 0. Noubliez pas que pour pouvoir utiliser les modules CCP, il faut que le timer utilis soit correctement configur, et, bien entendu, mis en service. 20.4 Le mode capture Nous allons commencer par tudier le plus simple des 3 modes, savoir le mode capture. La premire chose remarquer est que ce mode fait intervenir une pin comme vnement dclencheur. Il sagit donc dune entre. Il est donc impratif de configurer la pin CCPx en entre via le registre TRISC avant de pouvoir utiliser le module CCPx en mode capture . Comme cest le cas par dfaut aprs une mise sous tension, on a cependant peu de chance de loublier. 20.4.1 Principe de fonctionnement Le mode capture est simple comprendre. Il est en troite liaison avec les pins RC1/CCP2 et RC2/CCP1 du PIC. Attention linversion des chiffres, la logique ne coule pas de source. En fait, le principe est le suivant : Au moment de lapparition de lvnement dclencheur sur la pin concerne, la valeur (16 bits) du timer 1 contenue dans les registres TMR1H et TMR1L est copie dans les registres CCPR1H et CCPR1L. (Ah, tiens, voici un moyen dannuler le bug concernant la lecture au vol de TMR1).

253

Simultanment, le bit CCP1IF du registre PIR1 est valid, et une interruption intervient si elle est configure.

Lvnement dclencheur est une variation du signal sur la pin CCP1/RC2 pour le module CCP1, et sur la pin CCP2/RC1 pour le module CCP2. Lvnement qui provoque la capture dpend des bits CCPxM3 CCPxM0. Vous avez plusieurs possibilits : Soit, la capture seffectue chaque fois que la tension sur la pin CCPx passe de Vdd Vss (CCPM = 0100) Soit la capture seffectue chaque fois que la tension sur la pin CCPx passe de Vss Vdd (CCPM = 0101) Ou alors, la capture seffectue au bout de 4 transitions niveau bas/niveau haut de la pin CCPx, on comprend quil sagit dun prdiviseur de signal par 4 (CCPM = 0110) Ou enfin, la capture seffectue de faon identique, mais aprs chaque multiple de 16 transitions niveau bas/niveau haut, il sagit donc dun prdiviseur de signal par 16 (CCPM = 0111).

Si vous avez bien compris ce qui prcde, vous pouvez vous imaginer le schma-bloc correspondant. Je vous le donne cependant explicitement, remplacez les termes x par 1 ou 2 en fonction du numro de module utilis

Vous constatez, propos des contraintes, que vous avez bien un flag CCPxIF par module, une pin CCPx, un registre de configuration CCPxCON, et un registre 16 bits de sauvegarde de la valeur capture. Par contre, vous navez quun seul timer, savoir TMR1, utilis pour les 2 modules. Remarquez que le prdiviseur ne sapplique que pour la dtection des signaux flancs montants.

254

20.4.2 Champs dapplication Vous voyez probablement dj plusieurs applications typiques de ce mode de fonctionnement. Sachant que le timer 1 peut fonctionner en mode timer ou en mode compteur, vous pouvez : - Dterminer le temps sparant 2 ou plusieurs vnements. Ceci vous permet par exemple de raliser un chronomtre de haute prcision. Si le temps est suprieur au temps de comptage sur 16 bits de TMR1, celui-ci peut gnrer une interruption, et donc incrmenter une variable qui comptera les multiples de 65536 vnements. Compter un nombre dvnements compts par le timer 1 survenus entre 2 ou plusieurs flancs prsents sur la pin CCPx.

Je vous donne un pseudo-code pour la ralisation dun chronomtre. Le bouton-poussoir (sans rebond, bien sr) est connect sur CCPx On lance le timer1, on autorise interruptions TMR1 et CCPx Dans linterruption TMR1, on incrmente une variable On presse sur le bouton (start) Linterruption CCPx sauve les registres CCPRxH et CCPRxL ainsi que la variable (variable1) On presse sur le bouton (stop) Lors de linterruption CCPx on sauve la variable (variable2) Le temps exact coul entre les 2 vnements en nombre de cycles sera : ((variable2variable1) * 65536 + (CCPRxH CCPRxH sauv) *256 + (CCPRxL CCPRxL sauv)) * prdiviseur.

Lavantage, cest que la capture se fait au moment prcis de lvnement, et donc le temps de raction du programme nintervient plus dans le calcul du temps. De mme, plus aucun problme, ni de bug, pour la lecture des 2 octets de TMR1. Et dire que Microchip recommandait de changer de timer pour ce genre dapplications Attention, il sagit dun pseudo-code simplifi, il vous faudra, pour en faire un programme oprationnel, grer les risques de dbordements de la variable, pour ne citer quun exemple. Mais cela vous montre les avantages de cette mthode. 20.4.3 Remarques et limites dutilisation Lutilisation de ce mode de fonctionnement impose lapplication de certaines rgles et conseils de prudence. Il importe dtre attentif. Certaines de ces rgles sont dailleurs dapplication pour le mode compare . Voici ces rgles :

255

-Le timer 1 doit imprativement tre configur en mode timer ou en mode compteur synchrone . En mode compteur asynchrone, la capture ne fonctionne pas. Je vous renvoie au chapitre sur le timer 1 pour plus de renseignements. Tout changement de configuration du mode capture peut provoquer un positionnement indsirable du bit CCPxIF. Avant tout changement, vous devez donc interdire les interruptions CCPx, et forcer CCPxIF 0 avant de rautoriser les interruptions. Pour le CCP1 :
bsf bcf bcf movlw movwf bcf bsf bsf bcf STATUS,RP0 PIE1,CCP1IE STATUS,RP0 nouveau_mode CCP1CON PIR1,CCP1IF STATUS,RP0 PIE1,CCP1IE STATUS,RP0 ; ; ; ; ; ; ; ; ; passer en banque 1 interdire interruptions CCP1 repasser en banque 0 nouveau mode pour CCP1 dans registre de commande effacer flag dinterruption passer en banque 1 rautoriser interruptions CCP1 repasser en banque 0

Pour le CCP2 :
bsf bcf bcf movlw movwf bcf bsf bsf bcf STATUS,RP0 PIE2,CCP2IE STATUS,RP0 nouveau_mode CCP2CON PIR2,CCP2IF STATUS,RP0 PIE2,CCP2IE STATUS,RP0 ; ; ; ; ; ; ; ; ; passer en banque 1 interdire interruptions CCP2 repasser en banque 0 nouveau mode pour CCP2 dans registre de commande effacer flag dinterruption passer en banque 1 rautoriser interruptions CCP2 repasser en banque 0

Ceci, bien entendu, ne concerne que le cas o vous utilisez les interruptions des modules concerns. Attention, CCP1IE se trouve dans PIE1, alors que CCP2IE se trouve dans PIE2. Mme remarque pour CCP1IF qui se situe dans PIR1, alors que CCP2IF est dans PIR2. Tout arrt du mode capture (changement de mode, ou arrt du module CCPx) provoque leffacement du contenu du prdiviseur (les vnements dj compts sont perdus). La modification du prdiviseur en cours de fonctionnement peut positionner le bit CCPxIF de faon non souhaite. Autrement dit, vous devez commencer par interdire les interruptions CCPx, comme expliqu ci-dessus. Le contenu du prdiviseur nest pas effac lors de cette modification. Il est donc conseill deffacer dabord CCPxCON avant de choisir le nouveau prdiviseur. Ainsi, le contenu de celui-ci sera effectivement effac.
clrf CCPxCON ; arrt du module CCPx movlw nouvelle_valeur ; nouveau prdiviseur et CCPx en service movwf CCPxCON ; modifier prdiviseur

256

Dans le cas o la pin CCPx serait configure en sortie, ltablissement dun niveau sur cette pin par logiciel serait interprt comme un niveau entrant, et pourrait donc gnrer la prise en compte dun vnement de capture, exactement comme si la modification de niveau sur la pin tait due un vnement extrieur. Ceci vous permet donc de crer des captures pilotes par soft.

20.4.4 Mode sleep et astuce dutilisation Si vous placez votre PIC en sommeil (sleep), votre timer 1 ne pourra plus compter, puisque le mode asynchrone nest pas autoris dans lutilisation des modules CCP. Cependant le prdiviseur fonctionne, lui, de faon asynchrone, et est donc dans la possibilit de positionner le flag CCPxIF. De ce fait, une interruption provoque par CCPx pourra rveiller votre PIC, bien que la mise jour des registres CCPRxH et CCPRxL ne se ralise pas dans ce cas. Vous pouvez penser que ceci ne sert donc rien, et vous avez en partie raison. Cependant, en rflchissant un peu, vous constatez que cette astuce vous permet en ralit de disposer de 2 pins supplmentaires (CCP1 et CCP2) capables de vous gnrer des interruptions. Donc, si vous ne vous servez pas dun module CCP, mais que vous avez besoin dune entre dinterruption supplmentaire, rien ne vous interdit de configurer le module CCP de votre choix en mode capture pour disposer automatiquement de lentre dinterruption CCPx correspondante. Il suffit alors dignorer le contenu des registres CCPRxH et CCPRxL. Bien videmment, vous pouvez galement utiliser simultanment les 2 entres dinterruption CCP1 et CCP2. Voici donc une astuce qui peut se rvler trs pratique, dautant que ces pins peuvent tre configures en tenant compte dun prdiviseur ou du choix du sens de transition. Elles se comportent donc comme une RB0/INT amliore. 20.5 Le mode compare Ce mode de fonctionnement est bas sur la correspondance de la valeur du timer 1 (TMR1H/TMR1L) avec la valeur contenue dans CCPRxH/CCPRxL. Lgalit de ces valeurs entranera les ractions souhaites. ATTENTION : Le rsultat dune galit ne crera leffet quau moment de lexcution du cycle suivant. Il y a donc toujours un retard de 1 cycle dinstruction entre la vrification de lgalit, et laction qui en dcoule. Ceci explique toutes les expressions +1 dans les formules de calcul des diffrents temps. Souvenez-vous quil en tait de mme pour le timer 2, pour lequel nous avions positionnement du flag TMR2IF au moment o TMR2 passait de la valeur de PR2 0x00. Le reset de TMR2 intervenait donc bien le cycle suivant son galit avec PR2.

257

Les temps calculs de gnration dinterruption taient donc : contenu de PR2 + 1. Il en sera de mme pour le mode compare de nos modules CCPx. Tous ces retards sont dus au mode de fonctionnement synchrone des PICs. Certaines configurations de ce mode font intervenir la pin CCPx en tant que sortie. Vous devrez donc dans ce cas configurer cette pin en sortie en effaant le bit de TRISC correspondant. 20.5.1 Principe de fonctionnement Comme je viens de lexpliquer, lgalit entre les registres du timer 1 (TMR1H/TMR1L) et la valeur de consigne fixe par les registres CCPRxH/CCPRxL entrane une ou plusieurs actions en fonction du mode choisi. Si nous prenons le mode dfinit par les bits CCPxM3 CCPxM0 configurs 1010 , nous aurons le fonctionnement suivant : Quand le contenu du mot de 16 bits form par TMR1H/TMR1L atteint celui form par CCPRxH/CCPRxL, le flag CCPxIF est positionn ds le cycle suivant. Une interruption a ventuellement lieu si elle a t pralablement configure

Attention, le timer 1 ne dborde pas (ne repasse pas 0x0000), il continue de compter normalement. La prochaine correspondance entranera donc une nouvelle interruption 65536 cycles plus tard (si on na pas modifi les registres concerns entre-temps). Nous ne sommes donc pas en prsence dun fonctionnement semblable celui du timer 2, pour lequel le dpassement de la valeur entranait automatiquement la remise 0 de celui-ci. Voici un graphique qui prsente lvolution du contenu de TMR1 (TMR1H/TMR1L) en fonction du temps, et le positionnement de CCPxIF en fonction de CCPRx (CCPRxH/CCPxL). Attention, vu lchelle utilise, le temps dun cycle nest pas reprsentable. Noubliez pas que les actions seffectuent toujours le cycle suivant la comparaison.

258

Autour de ce fonctionnement de base, encore appel mode software , nous avons 3 variantes. La combinaison 1000 des bits CCPxM3 CCPxM0, condition que la pin CCPx correspondante soit place en sortie via TRISC, induit le fonctionnement THEORIQUE suivant : Au moment de la configuration de CCPxCON, la sortie CCPx est force automatiquement 0 , indpendamment du contenu prcdemment plac dans PORTC par le programme Le timer 1 compte. Au cycle suivant la correspondance des valeurs du TMR1 et de consigne, le flag CCPRxIF est positionn, une interruption a ventuellement lieu. La pin CCPx passe 1 automatiquement, et y reste jusqu sa modification par le programme, ou par une modification de CCPxCON

La courbe CCPx commence linstant de la programmation de CCPxCON. Avant cet instant, son niveau peut tre quelconque (1 ou 0). La dure du signal utile (ici, la dure de ltat bas) de notre pin CCPx sera : T = (CCPRx + 1) * 4 * Tosc * prdiviseur La combinaison 1001 des bits CCPxM3 CCPxM0 induit un fonctionnement semblable, except que les niveaux sur CCPx sont inverss : Au moment de la configuration de CCPxCON, la sortie CCPx est force automatiquement 1 , indpendamment du contenu prcdemment plac dans PORTC par le programme Le timer 1 compte. Au cycle suivant la correspondance des valeurs du TMR1 et de consigne, le flag CCPRxIF est positionn, une interruption a ventuellement lieu. La pin CCPx passe 0 automatiquement, et y reste jusqu sa modification par le programme, ou par une modification de CCPxCON

259

ATTENTION : le fonctionnement rellement observ est le suivant : la pin CCPx NEST PAS positionne automatiquement sur la valeur inverse de celle obtenue au moment de la comparaison. En fait, nous devrons trouver une astuce pour forcer cette pin manuellement son niveau de dpart. Je vous encourage donc lire la mthode que jai imagine et utilise dans le second exercice. Jai interrog Microchip au sujet de cette discordance entre datasheet mid-range et fonctionnement rel. La rponse que les responsables techniques mont donne est que les datasheets ont t crits avant la sortie du 16F876, et que ce dernier ne correspond pas, ce niveau, aux caractristiques annonces. Comprenez : Il sagit dun bug . Jen profite pour un apart : Ce nest pas parce que quelquun sens reprsenter la rfrence dit ou crit quelque chose, que ceci doit faire office de vrit toute puissante et non vrifie. Tout le monde peut faire des erreurs (de bonne ou de mauvaise foi), vous de toujours vous interroger sur la validit des messages reus et prmchs (je pense principalement aux informations souvent orientes reues via les media). Reste une dernire variante notre option de base, lorsquon configure nos bits de contrle CCPxM3 CCPxM0 avec la valeur 1011 . Dans ce mode, la pin CCPx reste inchange, mais nous gnrons un signal trigger qui permet de commander automatiquement un ou deux vnements supplmentaires. Le trigger en question provoque, sur les 2 modules CCP, le reset automatique du timer 1. Donc, ceci nous ramne exactement au fonctionnement du timer 2, mais sur 16 bits : Le cycle suivant lgalit des registres TMR1H/TMR1L avec la valeur de consigne CCPRxH/CCPRxL, le timer 1 est remis 0 (il dborde sur la valeur fixe par CCPRxH/CCPRxL) Au moment du dbordement, le flag CCPxIF est positionn, et une interruption a lieu si elle a t configure.

Notez dj que le dbordement provoqu par le module CCP ninduit pas le positionnement du flag TMR1IF, SAUF si la valeur place dans CCPRxH/CCPRxL tait

260

0xFFFF. Dans ce dernier cas, le positionnement des 2 flags CCPxIF et TMR1IF sera simultan. Mais le trigger provoque une seconde action, uniquement pour le module CCP2. Pour ce module, en mme temps que les vnements prcdemment cits, le bit GO du registre ADCON0 sera automatiquement positionn, lanant la conversion (si le convertisseur A/D tait en service, bien entendu).

Donc, si vous avez compris, et que vous voulez utiliser votre timer 1 avec une valeur de comparaison (comme pour le timer 2), vous utiliserez le module CCP1 ou CCP2 en mode compare avec trigger, et vous serez prvenu (avec interruption ventuelle) de la fin de la dure par le positionnement de CCP1IF ou CCP2IF, et non par TMR1IF. Le temps de cycle induit par ce mode de fonctionnement, sera donc, en toute logique : T = (CCPRxHL + 1) * Tcy * prdiviseur Ou encore : T = (CCPRxHL + 1) * 4 * Tosc * prdiviseur Avec CCPRxHL le nombre de 16 bits form par la combinaison de CCPRxH et CCPRxL Pour rsumer les diffrentes possibilits du mode compare , on peut dire : Ce mode est bas sur la comparaison du contenu du timer 1 avec une valeur de consigne. Le cycle suivant lgalit de ces registres, le flag CCPxIF est positionn De plus il est possible simultanment (mais non obligatoire), : Soit de forcer la sortie CCPx 0 ou 1 Soit de remettre 0 le timer 1, de faon provoquer un phnomne de dbordement pour le module CCP1

261

Soit, pour le module CCP2, de resetter le timer 1 ET de lancer la conversion A/D si le convertisseur tait en service. Nous verrons lutilisation pratique de cette fonction dans nos exercices de fin de chapitre. Vous voyez donc que vous disposez de toute une panoplie de nouvelles fonctions lies au timers 1. Voici le schma-bloc du module CCP2 :

Le module CCP1 est identique, except quil ne dispose pas de la possibilit de positionner le bit GO du registre ADCON0 20.5.2 Champs dapplication De nouveau, toutes ces possibilits nous ouvrent la voie de nouvelles mises en uvre de nos applications. Le mode software pourra tre utilis pour signaler quun temps tabli de faon prcise (comptage sur 16 bits) a t atteint, ou quune quantit dvnements comptabilise par le timer 1 a galement t atteint. Donc, en gnral, pour toute occurrence unique dun comptage particulier. La modification de la sortie CCPx permet de fournir un signal qui intervient un temps prcis aprs le dmarrage du timer, ou qui dure un temps prcis, ou encore de fournir un signal de sortie aprs comptabilisation dun nombre dtermin dvnements reus par le timer 1. Lavantage de cette mthode est quelle nintroduit pas de retard de raction d au traitement logiciel de loccurrence constate. Nous verrons un exemple pratique de cette possibilit. Le trigger permet bien videmment de transformer le timer 1 en un timer grande flexibilit. Ce mode permet de fait de disposer de lavantage de la flexibilit du timer 2, en conservant un comptage sur 16 bits. Ce mode devrait pouvoir vous tirer de toutes les situations de mesure de temps et dvnements que vous rencontrerez. Le trigger du module CCP2, appliqu au convertisseur analogique/numrique permet de faciliter grandement la mthode de numrisation.

262

Je dveloppe un peu ce dernier point. La mthode dchantillonnage utilise sur les PICs implique les oprations suivantes : On dmarre le convertisseur et on slectionne le canal On attends le temps Tacq de charge du condensateur On lance la conversion. Ces tapes peuvent tre transformes, en utilisant le mode trigger du module comparateur : On dmarre le convertisseur et on slectionne le canal On programme le temps Tacq dans le module CCP2.

Une fois le temps Tacq termin, le module CCP2 se chargera lui-mme de dmarrer la conversion. Vous navez donc plus vous charger de dtecter, par interruption ou par pooling, la fin de la dure Tacq. 20.5.3 Remarques et limites dutilisation De nouveau, jattire votre attention sur certains points de ce mode de fonctionnement : Le module CCPx en mode compare ne peut fonctionner que si le timer 1 fonctionne en mode timer ou en mode compteur synchrone . Le fonctionnement en mode compteur asynchrone ne permet pas le fonctionnement de ce module. Lutilisation de la pin CCPx dans ce mode nest possible quaprs avoir configur la pin concerne en sortie, en effaant le bit du registre TRISC correspondant Il nest pas possible de mlanger les modes de fonctionnement du mode compare . Par exemple, il est impossible dobtenir le positionnement de la pin CCPx et dutiliser simultanment le trigger.

20.5.4 Le mode sleep Le timer 1 configur en timer ou en compteur synchrone ne peut compter si le PIC est plac en mode sommeil. Donc, il ny a aucune chance dans ces conditions que la correspondance de valeurs entre TMR1H/TMR1L et CCPRxH/CCPRxL intervienne. Comme la suite des vnements dpend de cette occurrence, le mode compare est donc inutilisable durant la mise en sommeil du PIC. Lorsque le PIC se rveille, suite un autre vnement, le fonctionnement se poursuit o il avait t arrt. Si une pin CCPx est utilise comme sortie du module, le mode sleep maintient cette pin dans ltat actuel.

263

20.5.5 Fonctionnement non conforme Je rappelle ici la remarque que jai prcdemment faite lors de ltude thorique du mode compare avec pilotage de la pin CCPx : Attention, ce qui suit concerne exclusivement les modles de 16F87x disponibles au moment de la ralisation de cet ouvrage. Il vous appartiendra, si vous voulez vous assurer dune ventuelle correction, de vous renseigner sur le site de Microchip, ou de tenter vousmme lexprience avec les PICs en votre possession. Le datasheet des pics mid-range de Microchip est trs clair : si on inscrit la valeur B00001001 dans le registre CCP1CON, la sortie CCP1 (si elle est configure en sortie) doit passer instantanment 1 . En fait, il nen nest rien. Si le module CCP force effectivement cette pin 0 une fois la comparaison entre CCPR1HL et TMRHL ralise, par contre lcriture dune valeur dans CCP1CON ne permet pas sa remise 1 instantane. Il est possible que Microchip corrige ce problme dans les futures versions du 16F876. Dans le cas contraire, voyez lexercice 2 de pilotage de servomoteur dans lequel je donne une mthode maison pour corriger ce phnomne. Autre remarque : Microchip indique que lutilisation dun module CCP utilis en mode compare en mme temps quun module utilis en mode capture , ou de 2 modules CCP utiliss en mode compare , est soumis la contrainte suivante : Chacun des modules compare prcdemment impliqus devra tre utilis exclusivement en mode compare avec trigger . Non seulement jai trouv ceci illogique (car le premier reset empche le second dtre excut), mais, de plus, les essais que jai raliss dmontrent que ce nest pas du tout le cas. Je nai donc pas retenu cette contrainte, preuve en est que le second exercice utilise 2 modules CCP en mode compare, dont un seul est utilis avec trigger. De nouveau, en cas de nouvelle version de 16F87x , vous de vrifier si ce fonctionnement reste constant (pour ma part, je pense que oui) Mes propres conclusions (qui nengagent donc que moi) sont les suivantes : La premire erreur est un bug prsent dans les 16F87x, preuve en est le nombre de corrections sur le mme sujet prsentes sur le site Microchip pour toute une srie dautres PICs. La seconde erreur est une erreur dans les datasheets, le fonctionnement des PICs semble beaucoup plus logique que la contrainte impose par les datasheets. En effet, lutilisation, par exemple, de 2 modules CCP en mode compare avec trigger na pas le moindre sens (un reset du timer1 empche lautre module CCP datteindre sa valeur de comparaison).

264

20.6 Le mode PWM Nous voici dans la partie la plus dlicate des possibilits des modules CCP. Non que les principes soient compliqus, mais plutt quil nest pas simple de les expliquer de faon claire et concise quel que soit le niveau du lecteur. Je vais donc tenter de faire de mon mieux, mais cela ncessitera un peu de thorie (comme dhabitude, allez-vous me dire). Et oui, les 16F87x sont des PICs de pros qui ncessitent plus de cette thorie indispensable pour exploiter correctement toutes les fonctions que le petit 16F84. Notez que si vous tes dj un vrai pro , ceci doit vous embter, mais il faut bien penser tout le monde, non ? Et puis, un peu de lecture, a ne fait de tort personne.

20.6.1 La thorie du PWM PWM signifie Pulse Width Modulation , ce quon pourrait traduire par modulation de largeur dimpulsion. En somme, il sagit dun signal binaire de frquence fixe dont le rapport cyclique peut tre modul par logiciel. Etant donn quun signal binaire na plus de secret pour vous, vous savez donc quil sagit dun signal qui peut prendre 2 tats. Notre module PWM utilisera une pin de notre PIC configure en sortie. Il me faut aborder la notion de rapport cyclique, la modulation tant simplement lexpression du fait que celui-ci peut tre modifi en permanence. Le rapport cyclique dun signal binaire frquence fixe peut tre dfini comme tant le rapport entre le temps o il se trouve ltat 1 par rapport au temps total dun cycle. Un cycle ntant constitu, par dfinition, que dun tat 1 suivi dun tat 0 , la somme des temps des 2 tats tant constante. Notez donc quil y a 2 paramtres qui dfinissent un signal PWM : La dure dun cycle complet (ou, par dduction, sa frquence de rptition) Le rapport cyclique Donc, si on pose : Tc = Dure dun cycle Rc le rapport cyclique Th = dure de ltat haut Tb = dure de ltat bas

, on peut dire :

265

Tc = Th + Tb (Dure dun cycle en secondes = dure de ltat haut + dure de ltat bas) Frquence du signal (en hertz) = 1/Tc Rc = Th / Tc (rapport cyclique en % = temps ltat haut divis par le temps de cycle)

Je vais donc commencer par illustrer, de faon claire, quelques exemples de signaux de ce type. Exemple dun signal PWM avec un rapport cyclique de 50% :

Vous voyez que ce signal est effectivement de frquence fixe, puisque chaque temps Tc est identique. Le temps Th est identique au temps Tb, ce qui donne bien un rapport cyclique de 50%, puisque Rc = Th / Tc = Th / (Tb + Th) = Th / (2 Th) = = 50% Un tel signal sappelle signal carr. Cest un cas particulier dun signal PWM. Exemple dun signal PWM avec un rapport cyclique de 10% :

Vous pouvez constater que ce signal possde strictement le mme temps de cycle Tc que le prcdent. Sa frquence est donc identique. Cependant, son rapport cyclique a t modifi. Th reprsente maintenant 10% du temps de cycle total, alors que Tb reprsente 90%

266

Voyons maintenant un rapport cyclique de 90% :

De nouveau, le temps Tc est inchang, seul le rapport cyclique a t modifi. Le temps Th dure maintenant 90% du temps Tc, Tb occupant fort logiquement les 10% restants. Reste maintenant voir les 2 cas particuliers restants, savoir le rapport cyclique de 0%, et celui de 100%. Fort logiquement, un signal avec un rapport cyclique de 0% est un signal dont le temps ltat haut occupe 0% du temps total. Cest donc un signal qui est constamment ltat bas. De mme, un signal avec un rapport cyclique de 100% est un signal qui est constamment ltat haut. Cependant, si vous dessinez de temps signaux, vous constatez quil vous est impossible dindiquer le temps Tc, donc sa frquence. Et, de fait, ce signal ne variant plus, nest plus un signal priodique au sens lectronique du terme (les mathmaticiens vont me sortir des temps infinis, mais cela ne correspond pas la ralit lectronique). Le rapport cyclique dun signal PWM doit donc tre suprieur 0% et infrieur 100%. Voici une partie de la thorie aborde, vous avez maintenant compris que le module PWM de notre PIC permet de crer un signal priodique dont il est possible de faire varier (moduler) le rapport cyclique. 20.6.2 La thorie applique aux PICs Nous avons vu que nous avons besoin de 2 choses pour crer notre signal. Dune part, le temps TC, qui dtermine la frquence (fixe) de notre signal, et dautre part le rapport cyclique (variable) de ce dernier. Concernant le rapport cyclique, les PICs influencent plutt un autre paramtre, cest--dire le temps Th. Les 2 valeurs utilises dans la programmation seront donc Tc et Th. Si vous avez besoin dune autre valeur (Rc), il vous suffit de la tirer des formules simples prcdentes.

267

Nous allons maintenant tudier la faon de grer le premier paramtre, savoir le temps Tc. Ce temps est dfini tout simplement par le timer 2. Vous programmez ventuellement le prdiviseur, vous chargez la valeur adquate dans PR2, et le temps mis par votre TMR2 pour dborder vous donne votre temps TC. Dj une remarque trs importante : Le postdiviseur nest pas utilis dans le module PWM. Donc nintervient pas dans le calcul de Tc. Ceci implique galement que vous pouvez utiliser votre timer 2 en tant que gnrateur pour le module PWM, et, grce au postdiviseur, travailler avec un autre temps (multiple du premier) dans le reste de votre programme. Pour faire simple, avec le mme timer TMR2, avec Tcy = dure dun cycle dinstruction, et en vous souvenant de la formule donne au moment de ltude du timer 2 : Temps du cycle utilis pour le module PWM : Tc = (Tcy * prdiviseur) (PR2 + 1) Temps du timer2 utilis classiquement dans le programme = Tc = (Tcy * prdiviseur * postdiviseur) (PR2 + 1)

Inconvnient : il ne sera pas possible dobtenir des temps longs avec notre module PWM. Celui-ci est donc prvu pour travailler avec des frquences relativement importantes. Nous avons dj parl du timer 2 dans son emploi classique, ce qui nous intresse donc ici est la premire formule utilise dans le module PWM. Le module PWM travaille avec le timer 2, et au niveau de Tcy de la faon suivante : Vous entrez votre valeur de dbordement dans PR2 A chaque fois que TMR2 dborde de PR2 0x00, la pin CCPx passe au niveau 1. Donc, si nous reprenons un signal PWM quelconque, nous avons dj :

268

Comme vous constatez, nous avons dtermin lemplacement de monte du signal de 0 1 . Or, comme le temps Tc est le temps sparant 2 montes successives du signal (ou 2 descentes), nous avons dfini Tc. Pour rappel : TC = (PR2 + 1) * Tcy * prdiviseur Ou encore Tc = (PR2 + 1) * 4 * Tosc * prdiviseur En effet, un cycle dinstruction vaut 4 cycles doscillateur. Reste donc dfinir lemplacement de redescente de notre signal pour obtenir notre signal complet. Ce quil nous faudrait, cest un deuxime registre PR2 , qui nous indiquerait, au moment o TMR2 atteindrait cette valeur, que le signal doit retomber 0 . Et bien, cest ce qui est implment, en gros, dans notre PIC. Je vais commencer par le principe gnral, et je vais me rapprocher au fur et mesure de lexpos de la situation relle. Le principe va donc tre le suivant : Le Timer 2 compte : on imagine que le signal CCP vaut actuellement 0 : TMR2 arrive la valeur de PR2 Au cycle suivant, TMR2 repasse 0 , CCPx passe 1 TMR2 arrive la seconde valeur de consigne, CCPx passe 0 TMR2 arrive la valeur de PR2 Au cycle suivant, TMR2 = 0, CCPx vaut 1 TMR2 arrive la seconde valeur de consigne, CCPx passe 0 Et ainsi de suite Ceci vous donne la reprsentation suivante :

Vous pouvez immdiatement faire la constatation suivante :

269

Pour que le systme fonctionne, la valeur inscrite en tant que seconde rfrence doit tre infrieure celle de (PR2+1), sans quoi TMR2 natteindrait jamais cette valeur. De plus, cette valeur, contrairement PR2 ne doit pas provoquer le reset du timer, sans quoi il lui serait impossible datteindre la valeur de PR2. Quelle sera donc notre marge de manuvre, et de ce fait la prcision obtenue ? Et bien, elle dpendra de la valeur inscrite dans le registre PR2. Plus cette valeur est faible, moins nous avons le choix des valeurs inscrire en tant que seconde rfrence, cette dernire devant rester infrieure PR2 et suprieure 0. Or PR2 dpend du calcul de notre temps Tc. Nous ne pouvons donc pas y mettre ce que nous voulons pour notre application pratique, moins de dcider de choisir le quartz le mieux adapt notre application. Supposons donc que notre PR2 contienne la valeur dcimale D50. Pour faire varier notre rapport cyclique de 0 100%, nous ne pouvons mettre que des valeurs de D0 D50. Donc, nous ne pouvons rgler notre rapport cyclique quavec une prcision de 1/50me, soit 2%. Dans la plupart des applications, cette prcision risque dtre insuffisante. Cest pourquoi Microchip vient une fois de plus notre secours. Comme nous ne pouvons pas augmenter la valeur de rfrence, nous allons simplement y ajouter des dcimales , ceci affinera notre possibilit de rglage, et donc la prcision finale obtenue dans les mmes proportions. Notez que le terme dcimal est impropre dans ce cas puisque nous sommes en systme binaire, et non dcimal. Mais jai trouv limage plus parlante . Par la suite, je parlerai de nombres fractionnaires. Il a t dcid, pour nos PICs, dajouter 2 dcimales binaires notre compteur TMR2. Ce compteur se prsentera donc comme tant de la forme :

b7 b6 b5 b4 b3 b2 b1 b0 ,b-1 b-2
Ceci formant un nombre de 10 bits effectifs, et donc multiplie la prcision par 4. Bien entendu, pour que a serve quelque chose, notre seconde valeur de comparaison disposera galement de ces 2 digits supplmentaires. Maintenant, que reprsentent ces digits aprs la virgule ? Cest simple. Tout comme le premier chiffre aprs la virgule dun nombre dcimal reprsente des multiples de 1/10 (10-1), le second des multiples de 1/100(10-2), le premier bit aprs la virgule reprsente des multiples de (2-1), le second des multiples de (2-2) Nos chiffres b-1 b-2 reprsentent donc le nombre de du temps dincrmentation de notre TMR2. Donc, en toute bonne logique : Avec un prdiviseur de 1, la prcision est de Tcy / 4, soit Tosc Avec un prdiviseur de 4, la prcision est de Tcy, soit 4 Tosc Avec un prdiviseur de 16, la prcision est de 4 * Tcy, soit 16 Tosc

270

Comme notre PR2, et donc le temps Tc continue de fonctionner, lui, sur 8 bits, notre cycle se droule donc de la faon suivante : Le Timer 2 compte : on imagine que le signal CCP vaut actuellement 0 TMR2 (8 bits) arrive la valeur de PR2 (8 bits) Au cycle suivant, TMR2 repasse 0 , CCPx passe 1 TMR2 + 2 dcimales atteint la seconde valeur de consigne (8 bits + 2 dcimales ), CCPx passe 0 , le timer 2 continue de compter. TMR2 (8 bits) arrive la valeur de PR2 (8 bits) Au cycle suivant, TMR2 = 0, CCPx vaut 1 , et ainsi de suite

Je sais que a peut paratre ardu, mais si vous avez compris ce qui prcde, alors vous naurez aucun problme pour la suite. Voici dailleurs reprsent sous forme dun graphique, la chronologie des vnements en fonction du contenu du registre TMR2 du timer 2 :

Faites attention aux datasheets de Microchip, ils ne parlent pas de nombres fractionnaires, ils parlent de nombres de type b9 b8 .. b0. Noubliez pas quen fait, exprims de la sorte, ces valeurs sur 10 bits concernant le module PWM reprsentent des quarts de valeurs entires. La prcision obtenue pour le rglage du rapport cyclique dpend de la valeur inscrite dans PR2, complte par les 2 digits fractionnaires ajouts. Un exemple :

271

Avec un PIC cadenc 20MHz, et une frquence de votre signal PWM de 78,125Khz : Le temps Tc vaut : 1/78,125 Khz = 12,8 s Tc = (PR2 + 1) * Prdiviseur * 4 * Tosc Tosc = 1/20MHz = 50 ns PR2 = (TC / (prdiviseur * 4 * Tosc) 1 PR2 = (12,8s / 200ns) 1 = 64 1 = 63 (avec prdiviseur = 1) Donc, on placera 63 dans notre registre PR2 Il sera alors possible dajuster notre rapport cyclique sur des valeurs comprises entre B00000000,00 et B 00111111,11 , soit 256 valeurs diffrentes, donc une prcision sur 8 bits, ou encore une marge derreur de 0,4%. En somme, la prcision vaut 1 / ((PR2+1) * 4), et est donc en toute logique multiplie par 4 par rapport une comparaison sans utiliser de fractions (sur 8 bits). Il reste prciser un point. Les signaux PWM sont en gnral utiliss, comme leur nom lindique, pour moduler le rapport cyclique dun signal, et donc pour faire varier continuellement celui-ci en fonction des rsultats obtenir. Comme il faut intervenir sur des valeurs de consigne de 10 bits, lcriture ne pourra pas se faire en une seule fois, et donc pourra provoquer une valeur temporaire indsirable. Ce phnomne est appel glitch . Mais, de nouveau, Microchip nous fourni une solution, en utilisant un registre intermdiaire qui servira de valeur de comparaison, et qui sera charg au moment du dbordement de TMR2. La procdure exacte est donc la suivante : Le Timer 2 compte : on imagine que le signal CCP vaut actuellement 0 TMR2 arrive la valeur de PR2 Au cycle suivant, TMR2 repasse 0 , CCPx passe 1 En mme temps, la valeur programme comme consigne par lutilisateur est copie dans le registre final de comparaison. TMR2 arrive la valeur de la copie de la seconde valeur de consigne, CCPx passe 0 TMR2 arrive la valeur de PR2 Au cycle suivant, TMR2 = 0, CCPx vaut 1 , et ainsi de suite

Donc, vous en dduirez que toute modification de la valeur de comparaison, et donc de la dure de Th ne sera effective qu partir du cycle suivant celui actuellement en cours. On conclut cette partie fortement thorique en rsumant les formules qui seront utiles. Tout dabord le calcul de PR2, qui fixe le temps total de cycle Tc :

272

PR2 = (TC / (prdiviseur * 4 * Tosc) 1 Ensuite, la formule qui lie les 10 bits de notre second comparateur, sachant que ces 10 bits (COMPAR) expriment un multiple de quarts de cycles dinstructions, donc un multiple de temps doscillateur, avec le temps du signal ltat haut (Th). Th = COMPAR * prdiviseur * Tosc. Donc, COMPAR = Th / (prdiviseur * Tosc) On peut galement dire, en fonction du rapport cyclique (Rc), que Th = Tc * Rc Il faut encore rappeler que pour comparer TMR2 avec COMPAR cod sur 10 bits, il faut que TMR2 soit galement cod sur 10 bits. Il faut donc complter TMR2 avec 2 bits qui reprsentent les quarts de cycle dinstruction. En fait, tout se passe trs simplement. Si vous choisissez un prdiviseur de 1, TMR2 sera complt, en interne, par le numro (de 0 3) du temps Tosc en cours. Si, vous utilisez un prdiviseur de 4, ce nombre sera complt par le nombre dvnements dj comptabiliss par le prdiviseur (de 0 3). Si, par contre, vous utilisez un prdiviseur de 16, ce nombre sera complt par le nombre de multiples de 4 vnements dj comptabiliss par le prdiviseur. De cette faon, TMR2 sera bien complt avec 2 bits qui reprsentent des quarts de valeur. Inutile donc de vous en proccuper, cest llectronique interne qui sen charge, mais, au moins, comme a, vous le savez. 20.6.3 Les registres utiliss Nous voici dbarrass de la thorie pure, passons maintenant un peu de concret. Nous avons besoin de plusieurs registres pour programmer toutes ces valeurs. En fait, nous les avons dj tous rencontrs, ne reste donc qu expliquer leur rle dans ce mode particulier. Nous avons besoin dune valeur de dbordement pour notre timer 2, cette valeur se trouve, comme je lai dj dit dans le registre PR2. Cest donc une valeur sur 8 bits. La valeur de la seconde comparaison (celle qui fait passer la sortie de 1 0) est une valeur de 8 bits complte de 2 bits fractionnaires. Le nombre entier sera inscrit dans le registre CCPRxL. Les 2 bits fractionnaires qui compltent ce nombre sont les bits DCxB1 et DCxB0 du registre CCPxCON.

273

Comme nous lavons vu, ce nombre de 10 bits sera copi en interne par le PIC vers un autre registre, qui sera le registre CCPRxH complt de 2 bits internes non accessibles. Notez quen mode PWM , il vous est impossible dcrire dans ce registre. Vous navez donc pas, en fait, vous en proccuper, le PIC sen charge. Pour lancer le mode PWM , nous devons donc procder aux initialisations suivantes : 1) On initialise PR2 en fonction de la dure totale du cycle (Tc): PR2 = (TC / (prdiviseur * 4 * Tosc) 1 2) On calcule la valeur de comparaison DCB en valeur fractionnaire suivant la formule : DCB = Th / (prdiviseur * Tosc) On place les bits 9 2 dans CCPRxL (valeur entire), les bits 1 et 0 (fraction) tant positionns dans DCxB1 et DCxB0 du registre CCPxCON 3) On place la pin CCPx en sortie en configurant TRISC 4) On lance le timer 2 en programmant son prdiviseur 5) On configure CCPxCON pour travailler en mode PWM . Vous voyez quaprs avoir ingurgit toute la thorie, la mthode vous semble simple, et cest le cas. Je vous donne le schma-bloc simplifi du module PWM :

274

Je commente un peu. Vous voyez en dessous le fonctionnement normal de notre timer 2, qui dborde sur PR2 grce au comparateur 8 bits. Ceci entrane en mme temps la recopie des 10 bits de CCPRxL/DCxB1/DCxB0 vers CCPRxH/2 bits internes. Au mme moment, la sortie est force 1 via la bascule et ltage de sortie, valid par le registre TRISC. Pour information, le fonctionnement de la bascule est simple : une impulsion sur lentre set , force la sortie 1 , tandis quune impulsion sur lentre reset force cette mme sortie 0 . Entre les 2 impulsions, la sortie ne change pas dtat. Quand la valeur TMR2, complte par 2 dcimales devient identique la valeur qui a t copie dans CCPRxH, la sortie est force 0 . Vous voyez, rien de magique, cela devrait commencer devenir clair. 20.6.4 Champs dapplication En fait, vous utiliserez ce module chaque fois que vous aurez besoin dun signal de frquence fixe, mais de rapport cyclique variable. Citons par exemple lexemple typique du pilotage dun servomoteur utilis en modlisme (jy reviendrai pour lexercice pratique), ou la variation de vitesse dun moteur courant continu (modlisme), et bien dautres applications. 20.6.5 Remarques et limites dutilisation Pour que tout fonctionne comme prvu, il faut veiller respecter certaines contraintes, auxquelles jai dj fait allusion. Un rappel ne fait cependant pas de tort : La valeur de rfrence encode dans CCPRxL ne peut tre suprieure la valeur contenue dans PR incrmente de 1. Dans le cas contraire, le signal ne pourrait jamais atteindre cette consigne, et la pin CCPx resterait bloque au niveau haut (rapport cyclique > 100%) Il est possible dutiliser le timer 2 la fois comme timer classique et comme gnrateur pour le module PWM. Dans ce cas, prdiviseur et valeur de PR2 seront forcment identiques. Par contre, comme le postdiviseur nest pas utilis dans le module PWM, il reste possible dobtenir des temps de timer multiples du temps utilis dans le module PWM. Le registre CCPRxH nest pas accessible en criture lors du fonctionnement en mode PWM. La prise en compte du changement de valeur du rapport cyclique ne se fera quaprs la fin du cycle en cours. 20.6.6 Le mode sleep La mise en sommeil du PIC provoque larrt du timer 2. Le module PWM ne pourra donc pas continuer de fonctionner dans ces conditions.

275

La pin CCPx maintiendra la tension quelle dlivrait au moment du passage en sommeil du PIC. Au rveil de celui-ci par un vnement dclencheur, le cycle se poursuivra lendroit o il avait t arrt. 20.7 Exercice pratique : commande dun servomoteur par le PWM Enfin, une application concrte. Jai choisi de vous faire raliser le pilotage dun servomoteur de modlisme, dont la position sera tributaire de la position dun potentiomtre de rglage. De cette faon, sous allons mettre en uvre dans un premier temps : Le fonctionnement PWM du module CCP Lutilisation du mode compare avec trigger du module CCP pour lancer la conversion A/D La mthode efficace dutilisation du convertisseur A/D

Voyons tout dabord les contraintes. Un module en mode PWM et un autre en mode compare ne pose aucun problme de contrainte. Mais le seul module capable de dmarrer le convertisseur A/D est le CCP2. Ceci nous impose donc dutiliser CCP1 pour le mode PWM , et donc, de fait, de piloter ce servomoteur via la pin CCP1/RC2 Une fois de plus, programmation et lectronique devront tre tudis ensemble. Le potentiomtre nous envoie une valeur analogique. Nous navons pas besoin de tension de rfrence externe. Le tableau de paramtrage de PCFG nous donne, pour une entre analogique sans tension de rfrence : PCFG AN4 AN3 AN2 AN1 AN0 Vref- Vref 3 0 RA5 RA3 RA2 RA1 RA0 + 1110 D D D D A Vss Vdd A/D/R 1/4/0

Ceci nous impose lutilisation de AN0/RA0 comme pin dentre analogique. Vous voyez de nouveau que notre logiciel nous impose 2 contraintes matrielles. Plus vous utilisez les fonctions ddicaces des PICs, plus vous devez vous proccuper de votre logiciel avant de pouvoir dessiner votre schma. Le schma, donc, sera du type :

276

Remarquez que votre servo dispose de 3 pins, dont 2 destines lalimentation, et une recevant un signal de type PWM destin indiquer la position de consigne souhaite. Ce servo est sens fonctionner sous 5V, et avec un courant de commande IN infrieur 20mA. Dans le cas contraire, adaptez votre schma en consquence. Remarquez pour ceux qui ne disposent pas dun servo et qui dsirent raliser ce montage moindre cot, quils disposent dautres mthodes pour vrifier le fonctionnement correct du montage : Ceux qui ont un voltmtre aiguille peuvent placer ce dernier sur la pin RC2. La tension moyenne indique sera fonction du rapport cyclique fourni. Ceux qui disposent dun oscilloscope peuvent placer celui-ci sur la pin RC2, ils pourront voir directement la forme des signaux obtenus. Reste maintenant tablir quels sont les signaux ncessaires notre servomoteur.

277

La commande dun servomoteur ncessite la fourniture dimpulsions sur sa broche de commande. Ces impulsions devront tre rptes un intervalle de temps infrieur 20ms. La largeur dimpulsion fournit langle de rotation du servomoteur par rapport sa position centrale (0) : Une impulsion de 1,5 ms gnre un angle de 0 Une impulsion de 1ms gnre un angle de 90 Une impulsion de 2ms gnre un angle de +90

Nous voyons donc quen faisant varier la largeur dimpulsion entre 1 et 2 ms, nous pouvons obtenir nimporte quel angle de notre servomoteur sur une plage de 180. Faire varier une largeur dimpulsion, cest exactement dans les possibilits de notre module PWM. Souvenez-vous quil nous faudra fixer le temps de cycle, la largeur du niveau haut tant dfinie par le servomoteur : Ce temps de cycle devra tre infrieur 20ms (impos par la norme utilise par les servomoteurs) et suprieur 2ms (temps ltat haut maximum). Le temps entre 2 incrmentations de notre Timer 2 vaut : prdiviseur * Tcy. Ou prdiviseur * 4 * Tosc Avec un prdiviseur de 1, nous pouvons obtenir une dure maximale de : Tc = 1 * 4 * Tosc * 256 (PR2 maxi + 1)= 51,2 s, ce qui est trop court. Avec un prdiviseur de 4, la dure maximale est de : Tc = 4 * 4 * 50ns * 256 = 204 s, ce qui est encore trop court Avec un prdiviseur de 16, la dure maximale est de : Tc = 16 * 4 * 50ns * 256 = 819 s, ce qui reste trop court.

278

Et bien zut, a ne marche pas. Cest d au fait que notre postdiviseur est inactif pour le timer 2. Pourtant on voudrait bien pouvoir utiliser notre module PWM pour cette application. Reprenons donc notre formule : Tc = prdiviseur * 4 * Tosc * (PR2+1). Comme nous avons dj un prdiviseur et un PR2 leur valeur maximale, comment encore allonger TC maximum ? Tout simplement en augmentant Tosc. Tosc, le temps de loscillateur, dpend du quartz utilis pour cadencer notre PIC. Pour rappel : Tosc = 1/Fquartz. Donc, en remplaant, pour faire simple, notre quartz de 20Mhz, par celui de 4MHz que nous avons utilis pour notre PIC 16F84, et que vous devez donc, en bonne logique, possder, nous aurons : Tosc = 1 / 4Mhz = 0,25s. Dans ces conditions, notre Tc maximum devient : Tc maxi = 16 * 4 * 0,25s * 256 = 4096 s. = 4,096 ms Cette fois cest bon. Notez que je vous informe dj quavec ce type de signal, et les dures trs longues mises en jeu, la mthode consistant utiliser le PWM est loin dtre la meilleure. Cest pourquoi je vous proposerai une mthode bien plus efficace. Mais le but de cet exercice est de comprendre et de mettre en uvre les CCP en mode PWM et en mode compare avec trigger . Remplaons donc dans notre montage le quartz de 20 Mhz par celui de 4Mhz. Dans ce cas, nous mettrons la valeur 0xFF dans PR2, pour obtenir un temps TC gal 2,176ms. Notre valeur Th devra varier de 1 2 ms, ce qui correspondra des valeurs de : Th minimum = 1000s, donc CCPR1H minimum = 1000 / (16*4*0,25) = 62,5 Th maximum = 2000s, donc CCPR1H maximum = 2000 / (16*4*0,25) = 125 Donc, nous devrons transformer notre valeur de potentiomtre, qui varie de 0 1023, en une valeur qui varie de 0 62,5 (donc, 250 valeurs diffrentes). Ceci nous permettre de placer dans CCPR1L la valeur rsultant de : CCPR1L = 62,5 + valeur variable de 0 62,5 par pas de 1/4 Notre potentiomtre permet, grce ses 10 bits, de gnrer 1024 valeurs diffrentes. En conservant la plus proche puissance de 2 disponible de ce dont nous avons besoin (250 valeurs), nous voyons que nous pouvons retenir 256 valeurs diffrentes.

279

Donc, nous conserverons 8 bits (256 valeurs) pour la valeur de notre potentiomtre, alors que nous avons besoin de 250 valeurs. Nous rpartirons approximativement le surplus de part et dautre des limites, ce qui nous donnera : CCPR1L = 61,75 + (valeur potentiomtre sur 8 bits / 4) Soit une valeur minimale de 61,75 et une valeur maximale de 61,75 + (255/4) = 125,5 Notre temps Th pourra donc varier de : Th min = 61,75 * 16 * 4 * 0,25s = 0,998 ms Thmax = 125,5 * 16 * 4 * 0,25s = 2,008 ms Nous avons donc bien gnr notre signal qui varie de 1 2 ms. Il faut maintenant se poser la question de la rsolution (prcision) obtenue. Cest trs simple : Dans lintervalle utile, nous avons 250 valeurs intermdiaires, donc notre prcision sera de 180 degrs de rotation / 250 = 0,72 degrs. Nous pourrons donc positionner notre servomoteur avec une prcision de 0,72 degrs. Ceci correspond une variation minimale du temps de 1ms pour 180, soit (1ms / 180) * 0,72 = 4s. Cette mme prcision, exprime en %, nous donne : 1/250 = 0,4 % Donc, pour rsumer tout ceci, on peut dire : Nous mettrons 0xFF dans notre PR2, ce qui nous donne un temps Tc de 4,096ms Nous chargerons la valeur de notre potentiomtre, de 0 1023 Nous conservons les 8 bits de poids fort (soit 6 bits entiers + 2 dcimales ) Nous ajoutons (en quarts de valeurs) (61,75 * 4), soit 247 Le rsultat tient alors sur 10 bits (en ralit le bit 9 vaudra toujours 0 dans ce cas) Nous mettrons les 8 bits de poids fort comme partie entire dans CCPR1L Nous mettrons les 2 bits de poids faible (quarts) dans CCP1X et CCP1Y Pour exemple, si notre potentiomtre vaut 0, on aura : Valeur sur 10 bits = 00 On conserve les 8 bits de poids fort, soit B00000000 On ajoute 247, on obtient donc D247, soit B0011110111 sur 10 bits On place B00111101 dans CCPR1L On place B11 dans CCP1X/CCP1Y De la sorte, notre valeur minimale est de :

280

Partie entire = B00111101 = 61 Partie fractionnaire (quarts) = B11 quarts = = 0,75 Valeur complte = 61,75 Si notre potentiomtre vaut 1023, on aura : Valeur sur 10 bits = D1023 = B1111111111 On conserve les 8 bits de poids fort, soit B11111111, soit D255 On ajoute D247, on obtient donc D502, soit B0111110110 sur 10 bits On place B01111101 dans CCPR1L On place B10 dans CCP1X/CCP1Y De la sorte, notre valeur maximale est de : Partie entire = B01111101 = 125 Partie fractionnaire = B10 = 2/4 = 0,5 Valeur complte = 125,5 Je sais que jai insist assez lourdement, et que jai effectu de nombreuses rptitions, mais jai prfr passer pour un radoteur plutt que de vous laisser dans lhsitation concernant certaines procdures. Que ceux qui ont compris du premier coup veuillent bien mexcuser. Nous avons parfaitement rgl le problme de notre module CCP1, configur en PWM , occupons-nous maintenant de notre module CCP2, configur en compare avec trigger . Aprs le temps Tacq, la conversion A/D dmarrera automatiquement (du fait du trigger), et gnrera une interruption aprs un temps de 12 Tad. Grce au tableau que je vous ai fourni dans le chapitre sur le convertisseur A/D, vous constatez que, pour un PIC 4Mhz, vous devez choisir un prdiviseur du convertisseur = 8, ce qui vous donne un temps Tad = 2s. Le temps d chantillonnage prendra donc 12 * 2s = 24 s. Le temps Tacq est de minimum 20s, si on ne veut pas se casser la tte calculer. Donc, une conversion complte, avec son acquisition, durera donc 20s + 24s = 44 s. Nous avons besoin de la valeur du potentiomtre au maximum une fois par cycle PWM . Inutile en effet de remettre jour plusieurs fois CCPR1L durant le mme cycle, il nest de toute faon transfr dans CCPR1H qu la fin du cycle en cours. A cela il faut ajouter 2 TAD avant lacquisition suivante, soit 44s + 4s = 48 s sparant au minimum 2 acquisitions. Ce cycle PWM dure 4096s, ce qui nous laisse le temps deffectuer : 4096 / 48 = 85 mesures dans lintervalle.

281

Nous allons profiter de cette opportunit, tout en nous montrant plus modeste, en effectuant 16 mesures dans cet intervalle. De cette faon, nous pourrons limiter linfluence dun parasite. Ceci nous donne un temps sparant 2 chantillonnages de 4096 s/ 16 = 256 s. Comme notre temps dchantillonnage est fixe (12 TAD), nous allons allonger le temps sparant le dbut de lacquisition du dbut de la conversion. Ceci revient en fait allonger le temps dacquisition. Ce nouveau temps sera donc de : 256s 24s = 232s. Le temps sparant 2 dmarrages dchantillonnage successifs sera lui de 256s, puisque durant le temps de conversion, le timer1 continue de compter. Je vous donne la chronologie des vnements avec ce petit graphique :

Vous voyez quune fois lanc, vous ne vous occupez plus de rien, vous serez prvenu par une interruption chaque fois quune nouvelle valeur du potentiomtre sera disponible. Nous allons donc configurer notre timer 1 avec un temps de dbordement de 256s. Nous allons utiliser le temps de charge de notre condensateur (temps dacquisition) Tacq de 220s.Nous placerons donc dans CCPR1H/CCPR1L la valeur 16 bits tire de la formule : Temps de cycle du timer 1 = (CCPR2HL + 1) * 4 * Tosc * prdiviseur = 256s Soit, avec un prdiviseur de 1 : CCPR2HL = (256 / (4 * Tosc) 1 = 255 (ne pas oublier que notre quartz est maintenant 4Mhz).

282

Donc, D255 = B 0000000011111111 sur 16 bits. Nous placerons donc : CCPR2H = B00000000 = 0x00 CCPR2L = B11111111 = 0xFF Pour ceux qui nont pas suivi lastuce, la mthode normale aurait t de dire : Je configure CCP1 pour une dure de Tacq de 2Tad + 20 s La conversion est termine, jattends 256s 48 s Je reconfigure CCP1 pour une dure de Tacq de 2Tad + 20s La conversion est termine, jattends 256s 48 s Etc. Ceci ncessitait 2 mesures de temps distinctes. Jai donc simplifi en : Je configure CCP1 avec 256 s. On aura donc une conversion toutes les 256s La conversion est termine en 12Tad, ce qui nous laisse 256s-12Tad = 232s comme Tacq. Ce temps est suprieur aux (20s + 2Tad) minimum requis, donc aucun problme.

Le temps total est donc identique (256s), la frquence dchantillonnage galement, qui est suffisante pour obtenir 16 mesures du potentiomtre entre 2 modifications du PWM . Mais on na plus besoin de la boucle de temporisation supplmentaire. Une fois tout configur, notre module CCP1 fonctionnera donc de la faon suivante, dans la routine dinterruption du convertisseur A/D : On sauve la valeur lue

Ben oui, cest tout. En effet, intervalle de 256 s, une nouvelle conversion sera automatiquement lance, nous navons plus nous occuper que de sauver les valeurs. Nous sommes prvenu de la fin dune conversion par une interruption ADIF. Nous obtenons donc, entre 2 mises jour de PWM , 16 valeurs dont il nous suffira de tirer la moyenne. Cette moyenne devra donc tre crite dans nos paramtres du module PWM comme expliqu plus haut. Quand allons-nous crire cette valeur ? En fait, nous avons 2 possibilits ; Soit nous mettons PWM jour toutes les 16 mesures du potentiomtre (dans ce cas, cette mise jour se fera dans la routine dinterruption A/D Soit nous mettons PWM jour une fois par cycle Tc, et donc chaque interruption de dbordement du timer 2.

Nous navons que lembarra du choix. Je choisis la seconde solution, qui est la bonne. En effet, comme nous modifierons les valeurs au dbut du cycle, nous sommes certains de ne pas obtenir de fonctionnement alatoire puisque la copie de notre valeur de consigne sur 10 bits seffectuera au dbut de linterruption suivante. Nous sommes donc certains que cette copie ninterviendra pas entre le moment o nous modifions nos 2 bits fractionnaires et le moment o nous modifions nos 8 autres bits.

283

Notre routine dinterruption du timer 2 va donc : Calculer la moyenne des 16 mesures du potentiomtre sauves Entrer cette moyenne comme paramtre du module PWM .

Jen profite pour faire une petite remarque. Jen vois dici qui se disent : On est dans un exercice pratique, et Bigonoff fait toujours de la thorie et des mathmatiques, il ny a pas moyen de sen passer ? . La rponse est malheureusement non. En effet, je nai pas donn toutes ces formules uniquement pour faire joli, et pour ne les utiliser que dans la thorie. Tout ce qui concerne les mesures de temps, conversion etc. ncessitent de pralablement calculer toutes les valeurs de consignes dont vous aurez besoin. Il ny a malheureusement pas de miracle. En lectronique traditionnelle, il fallait calculer des constantes de temps avec des condensateurs, des rsistances, et autres composants. En lectronique programmable, on calcule galement, mais simplement en employant des formules diffrentes. Les contraintes restent identiques. Mais vous allez voir que toute cette thorie, qui semble ne plus finir, se traduit finalement par un programme ultra-simple. Faites un copier/coller de votre fichier m16f876.asm et renommez la copie en servo1.asm . Crez un nouveau projet qui utilise ce fichier. Commenons, comme toujours, par diter len-tte :
;***************************************************************************** ; Exercice sur l'utilisation des modules CCP. * ; Pilotage d'un servomoteur en utilisant le module CCP1 en mode PWM * ; et le module CCP2 en mode compare avec trigger * ; * ;***************************************************************************** ; * ; NOM: Servo1 * ; Date: 06/06/2002 * ; Version: 1.0 * ; Circuit: Platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Le PIC DOIT tre cadenc avec un quartz de 4Mhz * ; L'entre de commande du servo est connecte sur CCP1/RC2 * ; Le potentiomtre de rglage de position du servo est connect sur AN0 * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

284

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _PWRTE_ON & _WDT_ON & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ; _BODEN_ON ;_PWRTE_OFF ;_PWRTE_ON ;_WDT_ON ;_HS_OSC Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension en service Dmarrage rapide Dmarrage temporis Watchdog en service Oscillateur haute vitesse (4Mhz<F<20Mhz)

Remarquez que qui dit servomoteurs, dit en gnral fonctionnement sur piles ou accumulateurs. Jai donc actionn le bit de configuration BODEN_ON qui provoque le reset du PIC en cas de tension dalimentation insuffisante. A vous donc, dans une utilisation pratique, de grer ces situations. Nous trouvons ensuite les assignations systme :
;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQU B'10000000' ; RBPU b7 : 1= Rsistance rappel +5V hors service ; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQU B'01000000' ; PEIE b6 : masque autorisation gnrale priphriques ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQU B'01000010' ; ADIE b6 : masque interrupt convertisseur A/D ; TMR2IE b1 : masque interrupt TMR2 = PR2 ; REGISTRE ADCON1 (ANALOGIQUE/DIGITAL) ; -----------------------------------ADCON1VAL EQU B'00001110' ; 1 entre analogique, justification gauche

Pas grand-chose de particulier dire ici. Comme nous gardons 8 bits pour le convertisseur, nous alignerons gauche, en nutilisant quune seule entre analogique. Nos 2 sources dinterruptions AD et TRM2 sont valides. Ensuite, nous dfinissons les constantes qui vont tre utilises dans notre programme :
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** OFFSET EQU PR2VAL EQU CCPR2VAL EQU D'247' 0xFF 0x00FF ; valeur mini de CCPRx en quarts de valeurs ; valeur d'initialisation de PR2 ; temps Tacq pour module CCP2

Suivies par nos macros :

285

;***************************************************************************** ; MACRO * ;***************************************************************************** ; Changement de banques ; --------------------BANK0 macro ; passer en banque0 bcf STATUS,RP0 bcf STATUS,RP1 endm BANK1 macro ; passer en banque1 bsf STATUS,RP0 bcf STATUS,RP1 endm

Nos variables en banque 1 se limitent la zone de stockage des 16 valeurs analogiques lues, un compteur qui indique lemplacement, parmi les 16 disponibles utiliser pour la prochaine lecture, et une variable sur 16 bits destine recevoir les calculs concernant les consignes PWM
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 zoneval : 0x10 numval : 1 consigne : 2 ENDC ; ; ; ; ; Dbut de la zone (0x20 0x6F) 16 emplacements de sauvegarde numro de l'emplacement de sauvegarde calcul de la consigne Fin de la zone

En zone commune, nous avons nos habituelles variables de sauvegarde pour les routines dinterruption. Notez que notre programme principal ne fait rien. Mais jai conserv quelques sauvegardes pour que vous puissiez amliorer cet exemple pour en faire un programme oprationnel.
;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

La routine dinterruption principale contient les tests pour les 2 interruptions utilises. Notez que, cette fois, pour varier les plaisirs, jai enlev les instructions goto restoreg , ce qui induit que si 2 interruptions simultanes se produisent, elles seront traites en mme temps. Ceci pour vous montrer quil y a plusieurs faons de procder.

286

;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;sauvegarder registres ;--------------------0x004 ; adresse d'interruption w_temp ; sauver registre W STATUS,w ; swap status avec rsultat dans w status_temp ; sauver status swapp ; passer en banque0

org movwf swapf movwf BANK0

; Interruption convertisseur A/D ; -----------------------------btfss PIR1,ADIF ; tester si interrupt en cours goto intsw1 ; non sauter call intad ; oui, traiter interrupt bcf PIR1,ADIF ; effacer flag interupt ; Interruption TMR2 ; ----------------PIR1,TMR2IF restorereg inttmr2 PIR1,TMR2IF ; ; ; ; tester si interrupt en cours non, fin d'interrupt oui, traiter interrupt effacer flag interupt

intsw1 btfss goto call bcf

;restaurer registres ;------------------restorereg swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W swapf w_temp,w ; Rinversion de L et H dans W retfie ; return from interrupt

La routine dinterruption du convertisseur A/D est toute simple, puisquelle se contente de sauvegarder la valeur lue dans lemplacement dsign, et dincrmenter ce pointeur demplacement.
;***************************************************************************** ; INTERRUPTION CONVERTISSEUR A/D * ;***************************************************************************** ;----------------------------------------------------------------------------; Sauvegarde la valeur du potentiomtre sur 8 bits dans un des 16 emplacements ; prvus. numval contient le numro de l'emplacement ; Relance une nouvelle conversion, avec Tacq = 232s, ce qui permet au moins ; 16 mesures durant le temps de cycle du module PWM (4096 s) ; ----------------------------------------------------------------------------

287

intad movf numval,w andlw 0x0F addlw zoneval movwf FSR movf ADRESH,w movwf INDF incf numval,f return

; ; ; ; ; ; ; ;

numro de l'emplacement de sauvegarde garder valeurs de 0 15 ajouter emplacement dbut zone dans pointeur charger 8 bits forts du convertisseur A/D sauvegarder valeur incrmenter numro de valeur fin d'interruption

La routine dinterruption du timer 2 permet le calcul de la moyenne des 16 valeurs prcdemment sauvegardes, et la dduction de la nouvelle valeur de rglage du PWM.
;***************************************************************************** ; INTERRUPTION TIMER 2 * ;***************************************************************************** ;----------------------------------------------------------------------------; Calcule la moyenne des 16 valeurs du potentiomtre, ajoute l'offset, et ; rgle le module PWM en consquence ; 1) on ajoute les 16 valeurs, on obtient donc 16 * valeur moyenne, donc ; rsultat sur 12 bits ; 2) On divise rsultat par 16, donc on obtient la valeur moyenne sur 8 bits ; 3) On ajoute l'offset exprim en 1/4 de valeurs, le rsultat tient sur 9 bits ; 4) On sauve les 2 bits faibles dans CCP1X et CCP1Y ; 5) on divise les 9 bits par 4 pour obtenir la valeur entire et on sauve les ; 8 bits 0xxxxxxx obtenus dans CCPR1L ;----------------------------------------------------------------------------inttmr2 ; faire la somme des 16 valeurs ; ----------------------------movlw zoneval ; adresse de dbut de la zone movwf FSR ; dans pointeur clrf consigne ; efface poids fort du rsultat clrf consigne+1 ; idem poids faible inttmr2l movf INDF,w ; charger valeur pointe addwf consigne+1,f ; ajouter poids faible rsultat btfsc STATUS,C ; tester si dbordement incf consigne,f ; oui, incrmenter poids fort incf FSR,f ; incrmenter pointeur btfss FSR,4 ; tester si termin (FSR = 0x30) goto inttmr2l ; non, valeur suivante ; division du rsultat par 16 ; -------------------------consigne,f ; poids fort / 2, inutile d'effacer carry ; car b7/b4 seront inutiliss consigne+1,f ; poids faible divis par 2 avec b7=carry consigne,f ; poids fort / 2, inutile d'effacer carry consigne+1,f ; poids faible divis par 2 avec b7=carry consigne,f ; poids fort / 2, inutile d'effacer carry consigne+1,f ; poids faible divis par 2 avec b7=carry consigne,f ; poids fort / 2, inutile d'effacer carry consigne+1,f ; poids faible divis par 2 avec b7=carry

rrf rrf rrf rrf rrf rrf rrf rrf

; ajouter la valeur minimale (offset) ; ----------------------------------movlw OFFSET ; charger offset (ici, il tient sur 8 bits) addwf consigne+1,f ; ajouter poids faible btfsc STATUS,C ; tester si dbordement incf consigne,f ; oui, incrmenter poids fort

288

; sauver les 2 bits fractionnaires ; -------------------------------bcf CCP1CON,CCP1X ; effacer bit 1 bcf CCP1CON,CCP1Y ; effacer bit 0 btfsc consigne+1,1 ; tester si futur bit bsf CCP1CON,CCP1X ; oui, mettre bit 1 btfsc consigne+1,0 ; tester si futur bit bsf CCP1CON,CCP1Y ; oui, mettre bit 0

"-1" = 1 1 "-2" = 1 1

; placer valeur entire sur 8 bits ; -------------------------------rrf consigne,f ; rcuprer futur b6 dans carry rrf consigne+1,f ; diviser par 2 avec entre de b6 dans b7 rrf consigne,f ; rcuprer futur b7 dans carry rrf consigne+1,w ; diviser par 2, b6 et b7 en place movwf CCPR1L ; placer nouvelle valeur de consigne return ; fin d'interruption

Rien de bien compliqu, donc, il sagit tout simplement de lapplication des formules prcdemment expliques. Vient le tour de notre routine dinitialisation. Cest ici que tout se passe, en fait, puisque tout sera automatique par la suite. Jai volontairement spar nettement les diffrentes tapes, afin que vous puissiez voir dun coup dil les diffrentes fonctions initialises.
; //////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------BANK1 ; slectionner banque1 Bcf TRISC,2 ; CCP1/RC2 en sortie ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1

movlw movwf movlw movwf

; configurer le module CCP1 ; ------------------------bcf STATUS,RP0 ; passer banque 0 movlw B'00001100' ; pour mode PWM movwf CCP1CON ; dans registre de commande CCP movlw PR2VAL ; valeur de dbordement

289

bsf movwf bcf movlw movwf

STATUS,RP0 PR2 STATUS,RP0 B'00000110' T2CON

; ; ; ; ;

passer en banque 1 dans registre de comparaison repasser en banque 0 timer 2 on, prdiviseur = 16 dans registre de contrle

movlw bsf movwf bcf movlw movwf

; configurer le convertisseur A/D ; ------------------------------ADCON1VAL ; 1 entre analogique STATUS,RP0 ; passer banque 1 ADCON1 ; criture dans contrle1 A/D STATUS,RP0 ; repasser banque 0 B'01000001' ; convertisseur ON, prdiviseur 8 ADCON0 ; dans registre de contrle0 ; configurer le module CCP2 ; ------------------------B'00001011' ; pour mode compare avec trigger CCP2CON ; dans registre commande CCP2 high CCPR2VAL ; charger poids fort valeur de comparaison CCPR2H ; dans registre poids fort low CCPR2VAL ; charger poids faible valeur de comparaison CCPR2L ; dans registre poids faible T1CON,TMR1ON ; lancer le timer 1 ; autoriser interruptions (banque 0) ; ---------------------------------PIR1 ; effacer flags 1 PIR2 ; effacer flags 2 INTCON,GIE ; valider interruptions

movlw movwf movlw movwf movlw movwf bsf

clrf clrf bsf

Remarquez que nous navons pas dintialisation de variables. Ceci peut se comprendre. En effet : Concernant le pointeur demplacement, on garde dans la routine dinterruption, les 4 bits de poids faible. Donc, inutile dinitialiser, car, commencer sauvegarder un emplacement ou un autre na aucune importance, puisquon aura sauvegard de toute faon dans les 16 emplacements avant leur utilisation. La zone des valeurs sauvegardes sera remplie par la routine dinterruption AD avant quon nutilise ces valeurs dans la routine dinterruption TMR2. Donc, inutile non plus dinitialiser. Le rsultat consigne est initialis avant chaque utilisation, et donc, une fois de plus, inutile de le configurer. Ne reste que notre programme principal, qui, le pauvre, na rien dautre faire qu se tourner les pouces. Ceci pour vous dire que la gnration de notre signal PWM vous laisse normment de temps pour grer autre chose. Imaginez que vous utilisez une lecture de potentiomtre et la gnration dun signal PWM, et que tout ceci se ralise de faon pratiquement transparente pour votre programme principal.

290

Lancez lassemblage, placez le fichier servo1.hex dans votre PIC, noubliez pas de changer le quartz, et lancez lapplication. Votre potentiomtre vous permet maintenant de rgler un beau signal PWM avec un tat haut dune dure de 1 2 ms. Votre servomoteur, pour peu quil soit compatible avec la norme et quil fonctionne sous 5V, sera positionn en suivant les volutions de votre potentiomtre. Attention cependant la consommation de la pin dentre de votre servomoteur (cela ne devrait cependant pas poser de problme). 20.8 Exercice 2 : une mthode plus adapte Nous avons vu que des temps trs longs comme ceux requis par la commande dun servomoteur ntaient pas trs appropris lchelle de temps du module PWM . Ce dernier est en effet prvu pour tourner bien plus vite. Il permet de plus de disposer de temps haut et bas de dure trs prcise. Si nous regardons les chronogrammes ncessaires pour le pilotage de notre servomoteur, nous voyons que nous avons besoin de 2 temps diffrents : Le temps de maintien ltat haut du signal, entre 1 et 2 ms. Ce temps est critique et demande une grande prcision, car cest lui qui dtermine la position de notre servomoteur Le temps total de cycle, qui lui nintervient en aucune faon sur cette position, il rclame seulement dtre infrieur 20ms. Ce temps nest donc absolument pas critique. Donc, on peut dire que pour piloter notre servo, on doit : Placer un signal ltat haut durant une priode trs prcise Attendre un temps non critique avant de recommencer.

Ceci nous amne tout naturellement utiliser pour cette application le module CCP1 en mode compare avec gestion de la pin CCP1 Souvenez-vous en effet, que la combinaison 1001 des bits CCPxM3 CCPxM0 induit le fonctionnement suivant :

291

La largeur de ltat haut dpend trs prcisment du timer 1 et de CCPR. Aucun retard ou perturbation logiciel nintervient. Il nous suffit donc de reprogrammer CCP1CON intervalle de moins de 20ms pour gnrer notre signal de faon parfaite. Par contre, nous ne devons pas oublier que notre timer 1 est galement utilis dans notre module CCP2, utilis, lui, en mode trigger . Nous avons donc des contraintes dutilisation du fait de lutilisation des 2 modules CCP. Il nous suffit de rflchir pour constater que, dans ce cas, le reset de notre timer 1 ne pourra intervenir avant que la valeur CCPR1HL ne soit atteinte. Dans le cas contraire, notre ligne CCP1 ne pourrait jamais repasser 0 . Ceci implique que notre temps sparant 2 chantillonnages ne pourra tre infrieur , dans le pire des cas, 2ms. Sachant que 20ms sparent au maximum 2 impulsions, cela nous permet de raliser 10 mesures de notre potentiomtre. Le chronogramme vous est donn plus bas. Nous aurions pu galement dcider deffectuer les conversions A/D au rythme dtermin par un autre timer (tmr0 ou tmr2). Ceci nous dlivrait de cette contrainte, sous condition de lancer nous-mmes la conversion A/D (pas dutilisation du module CCP2 en mode trigger). La mthode de conversion tant alors celle utilise dans notre exercice lum2 . Bien entendu, il sagit ici dun exercice dapplication des modules CCP. Je vais donc choisir la premire solution. Pour rsumer, nous choisirons donc une valeur de comparaison CCPR1HL variable, qui nous donnera, en fonction de la valeur de notre potentiomtre, un temps variable entre 1 et 2 ms. Nous choisirons de fait une valeur de CCPR2HL fixe, qui nous fixera le temps sparant 2 conversions, en tant conscient que cette valeur CCPR2HL devra tre imprativement plus grande que la valeur CCPR1HL. Ceci nous donnera donc un temps au minimum de 2 ms. Nous allons choisir 2,4ms, et 8 mesures de conversion A/D intermdiaires ce qui nous donnera un temps approximatif sparant 2 impulsions de 2,4 * 8 = 19,2ms.

292

Nous utiliserons pour cet exercice notre quartz habituel de 20Mhz. Notre timer1, utilis avec un prdiviseur de 1, nous permet des mesures de temps de 0,2s (1 cycle) 13,1072 ms (65536 cycles). Nous aurons besoin de temps compris entre 1 2,4 ms, ce qui est parfaitement dans les possibilits de notre timer 1. Vous voyez que les dures mises en uvre et le type de signal sont plus adaptes cette faon de procder. La dure de limpulsion rpond la formule suivante (Jappelle CCPR1HL la valeur 16 bits forme par CCPR1H/CCPR1L, de mme TMR1HL la valeur forme par la concatnation de TMR1H et de TMR1L): T = (CCPR1HL + 1) * Tcy * prdiviseur Autrement dit : CCPR1HL = ( T / (Tcy * prdiviseur) ) 1 Une dure de 1ms se traduira donc par une valeur de : CCPR1HL minimum = ( 1ms / 0,2s) 1 = 4999 Une dure de 2 ms rclamera une valeur de : CCPR1HL maximum = (2ms / 0,2s) 1 = 9999 La dure sparant 2 conversions A/D rpond la mme formule, mais relative CCPR2HL : CCPR2HL = (2,4ms / 0,2s) 1 = 11999 Nous allons donc excuter les procdures suivantes (aprs initialisation) : On lance le timer 1 et on lance le module CCP1 (dbut de limpulsion) Aprs le temps dpendant de CCPR1HL, on obtient la fin de limpulsion On aura ensuite 8 interruptions A/D durant lesquelles on mesure le potentiomtre Lors de la 8me interruption, on mesure la moyenne, on calcule une nouvelle valeur de consigne CCPR1HL, on arrte et on remet 0 le timer 1. On recommence les oprations

Notez que linterruption intervient la fin de la conversion A/D, alors que le reset du timer 1 (trigger) annonce la fin du temps dacquisition Tacq et donc le dbut de la conversion. Comme cette conversion dure 12 Tad, soit 19,2s avec notre quartz de 20Mhz, le dbut de limpulsion suivante se fera donc 12 Tad aprs le dbut de la 8me conversion. Donc un temps total sparant 2 impulsions de (8 * 2,4ms) + 19,2s = 19,2192 ms.

293

Si on veut tre prcis, on doit encore ajouter le temps entre le dbut de linterruption, et le redmarrage par logiciel du timer 1. Le chronogramme suivant nen tient pas compte. Limportant dans cette application est que nous restions sous le temps total de 20ms. Nous aurons donc le chronogramme simplifi suivant :

Juste un petit mot dexplications. sur notre chronogramme, chaque flche pointe vers le bas indique un des dbuts de conversion A/D comprise entre chaque impulsion. Le dernier dbut de conversion seffectue aprs 19,2ms. 12 * Tad plus tard (19,2s), la fin de cette conversion a lieu. A ce moment, on resette tmr1, et on relance une nouvelle impulsion (qui intervient donc un encore un peu plus tard, le temps dexcuter les quelques instructions ncessaires). Reste un petit dtail mettre au point. En effet, on doit simultanment mettre CCP1 et TMR1 en service pour lancer limpulsion, ce qui est impossible. On va donc devoir, soit : Mettre TMR1 en service, puis CCP1, ce qui implique que lorsque CCP1 passe 1 , TMR1 a dj compt un cycle Soit mettre CCP1, puis TMR1 en service, ce qui implique que quand TMR1 commencera compter, il y aura dj un cycle que CCP1 sera 1 .

En fait, cela na aucune importance pour cette application, mais il est bon que vous preniez garde ce phnomne, pour le cas o votre application ncessiterait un crneau fixe dune grande prcision. De toute faon, lanomalie de fonctionnement explique dans la partie thorique va nous imposer une faon pratique de faire lgrement diffrente. Voyons maintenant ce qui se passe au niveau du potentiomtre. Nous avons une valeur comprise entre 0 et 1023. Or, nous avons besoin dune valeur comprise, pour CCPR1HL entre 4999 et 9999. Ceci nous donne une plage de variation de 5000.

294

Nous allons donc dcider de multiplier la valeur de notre potentiomtre par 5, ce qui nous donnera des valeurs comprises entre 0 et 5115. Nous rpartirons le surplus de part et dautre des valeurs limites, ce qui nous donne : Valeur minimale de CCPR1HL = 4999 ((5115-5000)/2) = 4942 Valeur maximale de CCPR1HL = 4942 + 5515 = 10057. Ceci nous donnera un temps compris entre : T = (CCPR1HL+1) * Tcy * prdiviseur T minimum = 4943 * 0,2s = 988,6s = 0,9886 ms T maximum = 10058 * 0,2 = 2011,6s = 2,0116ms Nous avons donc bien ralis nos objectifs. La valeur placer dans CCPR1HL sera donc : CCPR1HL = (potentiomtre * 5 ) + 4942 Nous constatons que nous conservons lintgralit des 10 bits de la conversion A/D. Ceci nous donne dans ce cas 1000 valeurs utiles intermdiaires, soit une prcision de : Prcision relative = 1/1000 = 0,1% Ceci nous permet de rgler notre angle avec une prcision de : Prcision angulaire = 0,1% * 180 degrs = 0,18 degr. Voyez la prcision obtenue. Et encore, il faut savoir que, si vous avez suivi, cette prcision est celle de notre convertisseur A/D, donc de notre potentiomtre. Vous pourriez trs bien rgler le servomoteur daprs une autre rfrence (consigne externe,), ce qui vous permettrait davoir une prcision de 1 cycle dinstruction (1Tcy), soit 0,2s. La prcision serait alors de : 1/5000 = 0,02% Vous pourriez donc positionner votre servomoteur avec une prcision thorique de 0,036 degr. Ceci vous montre les possibilits de votre PIC. Il nous reste dterminer comment effectuer la multiplication par 5. Lidal est de se servir de puissances de 2, afin dviter de se servir de programmes de multiplication (longs). Rien de plus simple : Multiplier un nombre par 5 revient le multiplier par 4, puis ajouter le rsultat au nombre initial. Les multiplications par 4 sont constitues de dcalages, quant aux additions, elles sont gres par le PIC. Si on se souvient que nous avons effectu 8 mesures intermdiaires de notre potentiomtre, la somme de ces 8 valeurs nous donne 8 fois la valeur moyenne de notre potentiomtre.

295

Donc, pour obtenir 4 fois la valeur de notre potentiomtre, nous diviserons cette somme par 2. Reste ajouter la valeur unitaire, valeur obtenue en divisant de nouveau la somme par 4 (division totale par 8) Ceci nous donne lalgorithme suivant (avec pot = valeur du potentiomtre): On additionne les 8 valeurs de mesure (on obtient 8 * pot) On dcale le rsultat vers la droite (division par 2), et on sauve le rsultat (4 * pot) On dcale encore de 2 rangs vers la droite (division par 8). Rsultat = pot On ajoute la valeur obtenue la valeur prcdemment sauvegarde (4 * pot + pot = 5 * pot).

Il est temps, maintenant, de passer la ralisation de notre programme. Faites un copier/coller de votre fichier m16F876.asm et renommez cette copie servo2.asm . Commenons par diter len-tte et la configuration :
;***************************************************************************** ; Exercice sur l'utilisation des modules CCP. * ; Pilotage d'un servomoteur en utilisant le module CCP1 en mode * ; "compare avec sortie sur CCP1" et le module CCP2 en mode * ; "compare avec trigger" * ; * ;***************************************************************************** ; * ; NOM: servo2 * ; Date: 10/06/2002 * ; Version: 1.0 * ; Circuit: Platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Le PIC est cadenc 20 Mhz * ; L'entre de commande du servo est connecte sur CCP1/RC2 * ; Le potentiomtre de rglage de position du servo est connect sur AN0 * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _PWRTE_ON & _WDT_ON & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ; _BODEN_ON ;_PWRTE_ON ;_WDT_ON Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension en service Dmarrage temporis Watchdog en service

296

;_HS_OSC

Oscillateur haute vitesse (4Mhz<F<20Mhz)

Maintenant, les assignations systme :


;***************************************************************************** ; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQU B'10000000'; RBPU b7: 1= Rsistance rappel +5V hors service ; REGISTRE INTCON (contrle interruptions standard) ; ------------------------------------------------INTCONVAL EQU B'01000000'; PEIE b6: masque autorisation gnrale priphriques ; REGISTRE PIE1 (contrle interruptions priphriques) ; ---------------------------------------------------PIE1VAL EQU B'01000000'; ADIE b6: masque interrupt convertisseur A/D ; REGISTRE ADCON1 (ANALOGIQUE/DIGITAL) ; -----------------------------------ADCON1VAL EQUB'10001110' ; 1 entre analogique, justification droite

Rien de bien particulier. On nutilise quune seule source dinterruption. Concernant le convertisseur A/D, on utilise une seule entre analogique. Comme on conserve le rsultat sur 10 bits, on alignera cette fois le rsultat droite, ce qui facilite les additions. Nos assignations du programme seront les suivantes :
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** OFFSET = D'4942' CCPR2VAL = D'11999' ; valeur minimale pour CCPR1HL ; valeur pour CCPR2HL (Tacq = 2,4ms)

Pour varier, jai utilis le signe = au lieu de la directive EQU , ce qui est similaire. Nous utilisons 2 constantes dans notre programme, la valeur minimale ajouter notre moyenne de la valeur du potentiomtre et qui correspond une dure dimpulsion de 998ms, et la valeur fixe de notre second module CCP, qui prcise la dure sparant 2 numrisations successives. Rien de spcial concernant les macros :
;***************************************************************************** ; MACRO * ;***************************************************************************** ; Changement de banques ; ---------------------BANK0 macro ; passer en banque0 bcf STATUS,RP0 bcf STATUS,RP1 endm BANK1 macro ; passer en banque1

297

bsf bcf endm

STATUS,RP0 STATUS,RP1

Concernant les variables en banque 0, nous avons besoin de 8 emplacements pour sauver les 8 lectures du potentiomtre, une pour sauver le rsultat, et une autre pour indiquer lemplacement de sauvegarde. En fait, comme vous commencez devenir des pros , on peut commencer tout doucement utiliser de temps en temps des astuces. Les 8 valeurs vont devoir tre additionnes, pour pouvoir calculer la moyenne. On peut donc, au lieu de sauvegarder la dernire valeur lue, ladditionner directement une des 7 autres. Cela nous vite une sauvegarde, et un emplacement de sauvegarde. Donc, ceux-ci passent de 8 7. Et puisque nous avons dj commenc additionner dans un des emplacements, autant conserver celui-ci pour le rsultat, ce qui nous conomise la variable de rsultat. Noubliez pas que les emplacements des valeurs du potentiomtre doivent pouvoir contenir 10 bits, donc utilisent 2 octets. Le rsultat, qui sera au maximum de 8 fois la valeur maximale dun potentiomtre tiendra donc sur 10 + 3 = 13 bits. Ceci nous pargne donc 2 octets de RAM pour le rsultat, et 2 autres pour lemplacement de sauvegarde conomis. Dans notre application, cela importe peu, mais il nen sera pas toujours ainsi. Jai des applications qui utilisent lintgralit des emplacements RAM. Il est dailleurs possible en sus dutiliser des registres non utiliss matriellement dans lapplication concerne (ADRESL, EEDATA,etc.) pour y sauver des variables. Voici donc notre zone RAM en banque 0 :
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 zoneval : D'14' numval : 1 ENDC ; ; ; ; Dbut de la zone (0x20 0x6F) 7 emplacements de 2 octets pour pot numro de l'emplacement de sauvegarde Fin de la zone

Dans notre zone RAM commune, jai laiss toutes les sauvegardes, pour le cas o vous dsireriez complmenter ce programme pour raliser une application pratique :
;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 FSR_temp : 1 ; ; ; ; Dbut de la zone (0x70 0x7F) Sauvegarde registre W sauvegarde registre STATUS sauvegarde FSR (si indirect en interrupt)

298

PCLATH_temp : 1 ENDC

; sauvegarde PCLATH (si prog>2K)

Voyons maintenant notre routine dinterruption. Comme nous navons quune seule source dinterruption, inutile de tester de laquelle il sagit. Commenons donc par sauvegarder nos registres :
;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org goto 0x000 init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;----------------------------------------------------------------------------; Sauvegarde la valeur du potentiomtre sur 10 bits dans un des 8 emplacements ; prvus. numval contient le numro de l'emplacement (! 2 octets) ; Si emplacement = 0, en plus on lance une nouvelle impulsion ; ---------------------------------------------------------------------------;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp movf FSR , w ; charger FSR movwf FSR_temp ; sauvegarder FSR movf PCLATH , w ; charger PCLATH movwf PCLATH_temp ; le sauver clrf PCLATH ; on est en page 0 BANK0 ; passer en banque0

Ensuite, on dtermine si on est en train de raliser la 8me mesure de la valeur du potentiomtre, auquel cas, on devra en plus calculer la moyenne et lancer une nouvelle impulsion.
; Interruption convertisseur A/D ; -----------------------------; tester si 8me interruption ; --------------------------incf numval,f ; incrmenter pointeur emplacement btfsc numval,3 ; tester si numval = 8 goto intpulse ; oui, dmarrer impulsion

Si ce nest pas le cas, on sauve la valeur lue dans lemplacement prvu (attention, 2 octets) :
; pointer sur emplacement de sauvegarde ; -------------------------------------

299

movlw movwf bcf rlf addwf

zoneval-2 FSR STATUS,C numval,w FSR,f

; ; ; ; ;

premier emplacement de sauvegarde -1 zone emplacement dans pointeur effacer carry charger numro interrupt * 2 FSR = emplacement concern (de 0 6)

; sauver valeur potentiomtre ; --------------------------movf ADRESH,w ; charger 8 bits forts du convertisseur A/D movwf INDF ; sauver le poids fort incf FSR,f ; pointer sur emplacement poids faible bsf STATUS,RP0 ; passer en banque 1 movf ADRESL,w ; charger poids faible convertisseur A/D bcf STATUS,RP0 ; repasser banque 0 movwf INDF ; sauver le poids faible goto restorereg ; fin du traitement

Si, par contre, nous sommes en train de lire la 8me valeur du potentiomtre, nous commenons par stopper et resetter notre timer 1.
; arrt et reset du timer1 ; -----------------------; arrt timer 1 ; effacer timer 1 poids faible ; idem poids fort

intpulse bcf T1CON,TMR1ON clrf TMR1L clrf TMR1H

Ensuite, on ajoute la valeur lue la valeur sauve dans le premier emplacement (qui devient le futur rsultat). Attention, nous travaillons toujours avec des grandeurs codes sur 2 octets. Noubliez pas quil faudra donc grer le dbordement de loctet faible vers loctet fort (CARRY) :
; ajouter valeur potentiomtre au rsultat ; le rsultat sera zoneval et zoneval+1 ; ---------------------------------------movf ADRESH,w ; charger 8 bits forts du convertisseur A/D addwf zoneval,f ; ajouter au poids fort emplacement 0 (lecture 1) bsf STATUS,RP0 ; passer banque1 movf ADRESL,w ; charger poids faible bcf STATUS,RP0 ; repasser banque 0 addwf zoneval+1,f ; ajouter au poids faible emplacement 0 btfsc STATUS,C ; tester si dbordement incf zoneval,f ; oui, incrmenter poids fort

Nous avons donc dans le rsultat, la somme de la lecture 1 et de la lecture 8. Il nous reste donc additionner les 6 valeurs intermdiaires. Nous commencerons par le dernier octet, car ceci nous permet de raliser en premier laddition des poids faibles, ce qui est plus simple au niveau algorithme (si vous ntes pas convaincus, crivez le code correspondant au sens inverse). On profite den avoir termin avec le compteur demplacements pour lutiliser comme compteur de boucles. Non seulement a nous conomise une variable, mais, de plus, la sortie de cette boucle, numval se retrouve automatiquement 0 . Ceci pour vous montrer les rflexes qui seront les vtres lorsque vous programmerez depuis un certain temps les microcontrleurs aux ressources limites.
; ajouter les 6 valeurs restantes 300

; ------------------------------movlw zoneval+D'13' ; pointer sur dernier poids faible movwf FSR ; dans pointeur movlw 0x06 ; 6 additions effectuer movwf numval ; numval = compteur de boucles intpl movf INDF,w ; charger poids faible addwf zoneval+1,f ; ajouter poids faible rsultat btfsc STATUS,C ; tester si dbordement incf zoneval,f ; oui, incrmenter poids fort decf FSR,f ; pointer sur poids fort movf INDF,w ; charger poids fort addwf zoneval,f ; ajouter poids fort rsultat decf FSR,f ; pointer sur poids faible prcdent decfsz numval,f ; dcrmenter compteur de boucles goto intpl ; pas 0, addition suivante

Maintenant nous avons besoin de calculer notre valeur moyenne. Comme nous disposons pour le moment de 8 fois cette valeur, il nous faut donc diviser par 8. Cependant, comme nous aurons besoin de 4 fois la valeur pour la multiplication par 5, nous profiterons de cette division pour sauvegarder la premire division par 2 (8 fois la valeur divis par 2 gal 4 fois la valeur).
; calculer 4* valeur moyenne (zoneval) ; et valeur moyenne (zoneval+2) ; -----------------------------------bcf STATUS,C ; effacer carry rrf zoneval,f ; diviser poids fort par 2 rrf zoneval+1,f ; idem poids faible, avec carry bcf STATUS,C ; effacer carry rrf zoneval,w ; charger poids fort / 4 movwf zoneval+2 ; sauver rrf zoneval+1,w ; charger poids faible avec carry movwf zoneval+3 ; sauver bcf STATUS,C ; effacer carry rrf zoneval+2,f ; calculer poids fort / 8 rrf zoneval+3,w ; charger poids faible / 8

Suite ceci, la multiplication finale de la valeur moyenne par 5 se rsume additionner la valeur moyenne avec la valeur moyenne multiplie par 4.
; calculer 5 fois valeur moyenne ; -----------------------------addwf zoneval+1,f ; ajouter poids faibles btfsc STATUS,C ; tester si carry incf zoneval,f ; oui, incrmenter poids fort movf zoneval+2,w ; charger poids fort addwf zoneval,w ; ajouter poids fort

Reste ajouter notre offset (galement sur 2 octets) pour obtenir notre plage de consigne dsire, le rsultat sera stock dans zoneval et zoneval+1
; calculer nouveau temps de pulse ; ------------------------------addlw HIGH OFFSET ; ajouter poids fort offset movwf zoneval ; dans poids fort rsultat movf zoneval+1,w ; charger poids faible rsultat

301

addlw btfsc incf movwf

LOW OFFSET STATUS,C zoneval,f zoneval+1

; ; ; ;

ajouter poids faible offset tester si dbordement oui, incrmenter poids fort rsultat sauver poids fort rsultat

Reste lancer limpulsion pour une dure lie la valeur prcdemment calcule :
; lancer l'impulsion durant CCPR1HL ; --------------------------------zoneval,w ; charger poids fort rsultat CCPR1H ; dans poids fort consigne zoneval+1,w ; charger poids faible rsultat CCPR1L ; dans poids faible consigne B'00001001' ; comparateur, CCP1 = 0 sur galit T1CON,TMR1ON ; lancer timer 1 CCP1CON ; dans registre de contrle

movf movwf movf movwf movlw bsf movwf

En fait, si vous faites ceci, votre pin CCP1 doit passer 1 au moment de lcriture dans CCP1CON. Sur les PICs qui sont en ma possession (16F876SP-20), cela ne fonctionne pas, comme je lai expliqu dans la partie thorique. Si cela fonctionne chez vous, avec les PICs en votre possession au moment de la lecture de cet ouvrage, utilisez cette mthode, cest la plus simple et la plus logique. Dailleurs, si vous passez ceci dans MPLAB 5.5, ceci ne fonctionne pas non plus. Microchip a donc intgr cette anomalie dans son mulateur. En fait, si on la force 1 , la pin CCP1 passe bien 0 la fin du temps coul (CCPR1HL = TMR1HL), mais, par contre la pin CCP1 ne passe jamais 1 au moment de lcriture du registre CCP1CON. Qu cela ne tienne, il nous suffira donc de forcer notre pin 1 avant de lancer le comparateur. On pourrait penser :
; lancer l'impulsion durant CCPR1HL ; --------------------------------zoneval,w ; charger poids fort rsultat CCPR1H ; dans poids fort consigne zoneval+1,w ; charger poids faible rsultat CCPR1L ; dans poids faible consigne B'00001001' ; comparateur, CCP1 = 0 sur galit T1CON,TMR1ON ; lancer timer 1 PORTC,2 ; forcer pin CCP1 1 CCP1CON ; dans registre de contrle

movf movwf movf movwf movlw bsf bsf movwf

Cela fonctionne effectivement dans MPLAB en mode simulateur (passage 1 de RC2 (CCP1), puis passage 0 le cycle suivant lidentit : CCPR1HL = TMR1HL). Le problme, cest que la pin reste dsesprment 0 sur notre PIC relle place sur son circuit. Pourquoi ? En fait, cest trs simple et trs logique. Une fois le mode compare avec gestion CCP lanc, la pin RC2 nest plus commande par le PORTC, mais par le comparateur du module CCP.

302

Donc, MPLAB a beau vous indiquer que PORTC,2 vaut 1 , la pin RC2 reste en ralit 0 . Ce nest pas le PORTC qui commande RC2 (encore les limites de la simulation). Il nous faut donc trouver une autre astuce. Jai trouv la suivante (aprs avoir crit, comme indiqu, Microchip, leur technicien ma rpondu votre mthode pour forcer la pin CCP semble la mieux indique ). Si vous trouvez une meilleure astuce, nhsitez pas vous en servir (et men faire part, bien entendu). Cest le module CCP1 qui doit placer la pin CCP1 1 . Il suffit donc de le mettre en mode B00001000 et de forcer manuellement lgalit entre TMR1HL et CCPR1HL. Ainsi, la pin CCP1 passe immdiatement 1 , puisque ce nest que linitialisation de CCPxCON qui pose problme, et non son fonctionnement sur lgalit. Il suffit ensuite de rebasculer sur notre mode normal, qui remettra la pin CCP1 0 au bout du temps dfini. Notez que pour crer un pulse 0 , nous aurions du agir de faon strictement inverse (dabord B00001001, puis B00001000 dans CCPxCON). Pour crer une galit immdiate, il suffit de mettre dans CCPR1HL la mme valeur que dans TMR1HL. Comme nous avons remis notre timer 0, nous ferons de mme avec la valeur de consigne.
; forcer CCP1 "1" ; ----------------clrf CCPR1L ; effacer comparateur consigne poids faible clrf CCPR1H ; idem poids fort (galit force avec TMR1HL) movlw B'00001000' ; comparateur, passe CCP1 1 sur galit movwf CCP1CON ; lancer comparateur sur galit force (pin = 1 sur ; cycle suivant bsf T1CON,TMR1ON ; lancer timer 1 (donc ici, CCP1 passe 1)

Vous voyez que cest trs simple : on efface CCPR1LH, puis on force le mode compare avec passage de CCP1 1 sur galit. Comme lgalit est prsente (le timer est stopp), la pin CCP1 passe 1 au cycle suivant, soit juste au moment o on lance notre timer (qui commence compter le temps de mise 1 de CCP1). Nous navons donc aucun dcalage entre valeur de consigne et dure de limpulsion. Il nous faut ensuite lancer le module dans son mode normal , cest--dire en mode compare avec remise 0 de CCP1, le cycle suivant lgalit entre TMR1HL et CCPR1HL. Jespre que cest clair, et que vous ntes pas en train de fumer . Si cest le cas, je vous accorde une pause. Prenez une collation, prsentez vos excuses la personne (pouse, enfant) qui a eu la malencontreuse ide de vous distraire durant la lecture de ce chapitre (et qui vous navez pas manqu de faire un commentaire peu courtois) et revenez relire le tout dans quelques minutes.

303

movf movwf movf movwf movlw movwf

; lancer l'impulsion durant CCPR1HL ; --------------------------------zoneval,w ; charger poids fort rsultat CCPR1H ; dans poids fort consigne zoneval+1,w ; charger poids faible rsultat CCPR1L ; dans poids faible consigne B'00001001' ; comparateur, CCP1 = 0 sur galit CCP1CON ; dans registre de contrle

Notez que le timer 1 a dj t redmarr, inutile donc de le faire une seconde fois. Nous navons plus qu restaurer les registres sauvegards :
; restaurer registres ; ------------------; ; ; ; ; ; ; ; ; ; ; ; effacer flag interupt recharger ancien PCLATH le restaurer charger FSR sauv restaurer FSR swap ancien status, rsultat dans w restaurer status Inversion L et H de l'ancien W sans modifier Z Rinversion de L et H dans W W restaur sans modifier status return from interrupt

restorereg bcf PIR1,ADIF movf PCLATH_temp,w movwf PCLATH movf FSR_temp,w movwf FSR swapf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w retfie

Bon, le plus dur est fait. Passons maintenant notre routine dinitialisation. RC2 en sortie, registres dinterruption et doption ne posant pas de problme :
; //////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS (banque 0 et 1) ; -----------------------------------BANK1 ; slectionner banque1 bcf TRISC,2 ; CCP1/RC2 en sortie ; Registre d'options (banque 1) ; ----------------------------movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option ; registres interruptions (banque 1) ; ---------------------------------INTCONVAL ; charger valeur registre interruption INTCON ; initialiser interruptions PIE1VAL ; Initialiser registre PIE1 ; interruptions priphriques 1

movlw movwf movlw movwf

304

Ensuite, on configure notre convertisseur A/D, avec un prdiviseur de 32, puisque nous avons remis notre quartz de 20MHz :
; configurer le convertisseur A/D ; ------------------------------ADCON1VAL ; 1 entre analogique STATUS,RP0 ; passer banque 1 ADCON1 ; criture dans contrle1 A/D STATUS,RP0 ; repasser banque 0 B'10000001' ; convertisseur ON, prdiviseur 32 ADCON0 ; dans registre de contrle0

movlw bsf movwf bcf movlw movwf

Puis notre module CCP2, qui va lancer nos conversions A/D. Le module CCP1 na pas tre lanc, puisquil le sera dans la routine dinterruption A/D.
; configurer le module CCP2 ; ------------------------B'00001011' ; pour mode compare avec trigger CCP2CON ; dans registre commande CCP2 high CCPR2VAL ; charger poids fort valeur de comparaison CCPR2H ; dans registre poids fort low CCPR2VAL ; charger poids faible valeur de comparaison CCPR2L ; dans registre poids faible T1CON,TMR1ON ; lancer le timer 1, synchrone, prdiv = 1

movlw movwf movlw movwf movlw movwf bsf

Ne reste plus qu initialiser notre compteur 0 , et lancer les interruptions. On aurait pu se passer deffacer PIR1, mais au diable lavarice.
; initialiser variable ; -------------------numval ; on commence par la premire interruption ; autoriser interruptions (banque 0) ; ---------------------------------PIR1 ; effacer flags 1 INTCON,GIE ; valider interruptions start ; programme principal

clrf

clrf bsf goto

Reste notre programme principal, qui ne fait rien, une fois de plus. Libre vous dutiliser cette ossature pour crer un vrai programme de pilotage de modle rduit. Et en plus de passer son temps derrire son ordi, Madame, il joue avec des petites voitures (ou des petits avions ) . Vraiment, vous cumulez, vos oreilles doivent souvent siffler, ou alors vous avez une pouse comprhensive (comme la mienne quoi que, parfois). Mais bon, moi je ne fais pas de modlisme (jai essay, mais mon avion tait plus souvent latelier quen lair H oui, pas dou, il parat). Fin du break, on retourne aux choses srieuses :
;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start clrwdt ; effacer watch dog

305

goto start END

; boucler ; directive fin de programme

Lancez lassemblage, placez servo2.hex dans votre pic, alimentez, et vous voici en prsence dun signal de pilotage de servomoteur parfaitement standard. Notez que si vous avez besoin de 2 servomoteurs, rien ne vous interdit dutiliser CCP2, quitte lancer alors manuellement la conversion A/D. Vous avez besoin de plus de servomoteurs ? Pas de problme, vous activez les entres de commande des servomoteurs les uns aprs les autres en vous servant dautres pins du PIC. Ces sorties attaquent des portes logiques qui laissent passer les pins CCP1 et CCP2 successivement sur les entres concernes des servomoteurs. Ne reste qu diminuer dans les mmes proportions le temps de cycle total. Petit exemple thorique : On configure CCP1 et CCP2 chacun pour piloter une sortie. On affecte un temps Tc identique aux 2 modules de 10ms La pin RB0 sert slectionner servo1/servo2 ou servo3/servo4 Aprs 10ms, on gnre les 2 pulses destins aux servos 1 et 2 Aprs 10 ms, on change ltat de RB0 On gnre les 2 pulses destins aux servos 2 et 3 On modifie RB0, et on recommence

Vous voyez que chaque servo aura bien reu son impulsion dans lintervalle de 20ms. Avec cette mthode simple, vous pouvez pilotez (20ms/2ms) * 2 = 20 servomoteurs simultans, tout en nutilisant que trs peu de temps CPU, et en conservant une prcision imbattable. Le timer 2 peut servir dterminer votre temps de cycle total. 20.9 Conclusion Ceci termine le chapitre consacr aux modules CCP. Vous voyez que ces modules ajoutent en fait des fonctions trs puissantes aux timers 1 et 2. Lorsque vous avez des signaux calibrs gnrer, des mesures dvnements raliser, des temps prcis mesurer, votre premier rflexe doit consister regarder si un des modes de fonctionnement des modules CCP ne serait pas adapt votre problme. Jai donn un exemple dutilisation, mais il y en a bien dautres. Par exemple, au niveau de certaines transmissions srie qui ncessitent des bits de largeur variable, bits qui sont particulirement simples grer avec la procdure prcdente.

306

Notes :

307

Notes :

308

21. Le module MSSP en mode SPI


21.1 Introduction sur le module MSSP Le module MSSP, pour Master Synchronous Serial Port, permet lchange de donnes du PIC avec le mode extrieur, en utilisant des transmissions srie synchrones. Il nest pas le seul proposer des communications, nous avons dj vu la liaison parallle dans le module PSP, et nous verrons dautres communications srie avec le module USART. Vous verrez que lensemble de ces modules vous ouvre toutes les voies royales de la communication, que ce soit avec votre PC, avec des afficheurs, des mmoires de type eeprom, des dtecteurs de temprature, des horloges en temps rel, etc. Une fois matrises ces possibilits, et je vais vous y aider, vous pourrez raliser des applications mettant en uvre dautres circuits que votre simple PIC. Nous sommes donc en prsence dun chapitre trs important. Ceci explique les nombreuses redondances dexplications que je vais vous fournir. Je prfre en effet rpter plusieurs fois la mme chose avec des termes diffrents, que de laisser des lecteurs en chemin, cause dune erreur dinterprtation ou dune incomprhension. Notez que nous avons dj rencontr des communications srie dans le chapitre consacr la norme ISO 7816 dans la premire partie du cours (16F84). Il nous fallait alors grer chaque bit reu. Nous verrons quavec ces modules, nous navons plus nous occuper de la transmission des bits en eux-mmes, mais nous les recevrons et mettrons directement sous forme doctet, toute la procdure de srialisation tant alors automatique. Outre le gain en taille programme qui en dcoulera, vous vous rendez compte que les vitesses de transmission pourront tre amliores de faon notable. Au lieu que votre programme ne soit interrompu lors de la rception ou de lmission de chaque bit, il ne le sera plus que pour chaque octet. Ceci multiplie allgrement la vitesse maximale possible dans un facteur de plus de 10. Il y a tellement de modes possibles de fonctionnement, quil ne me sera pas possible de donner systmatiquement des exemples. Je me limiterai donc des situations courantes. Nous verrons que le module MSSP peut travailler selon 2 mthodes. Soit en mode SPI, soit en mode I2C. Jai dcid de sparer ces fonctions en 2 chapitres distincts, car, bien quelles utilisent le mme module, et donc les mmes registres, elles ncessitent des explications assez diffrentes. Mais commenons par le commencement 21.2 Les liaisons srie de type synchrone Je viens de vous parler de liaisons srie synchrones, encore convient-il de vous donner un minimum dexplications sur ce que cela signifie :

309

Une liaison srie est une liaison qui transfre les donnes bit aprs bit (en srie), au contraire dune liaison parallle, qui transmet un mot la fois (mot de 8 bits, 16 bits, ou plus suivant le processeur). La notion de synchrone est bien entendu de la mme famille que synchronis . Ceci signifie simplement que lmetteur/rcepteur fournira un signal de synchronisation qui dterminera non seulement le dbut et la fin de chaque octet, mais galement la position de chaque tat stable des bits. Nous voyons donc que ce fonctionnement ncessite en plus des lignes de communication (entre et sortie de donnes), une ligne qui vhicule le signal de synchronisation (on parlera dhorloge). Cette mthode prsente donc lavantage de permettre la lecture des bits toujours au moment le plus favorable, et vite les problmes dus aux imprcisions de la vitesse. Il ne ncessite pas non plus dans le cas du SPI de bit de dpart (start-bit), ni de fin (stop-bit). Par contre, elle prsente linconvnient de ncessiter une ligne supplmentaire pour vhiculer lhorloge. Cette ligne, parcourue par dfinition par un signal haute frquence, raccourcira en gnral la longueur maximale utilisable pour la liaison. Vous avez bien entendu 2 faons denvoyer les bits la suite les uns des autres : Soit vous commencez par le bit 7, et vous poursuivez jusquau bit 0. Cest la mthode utilise par le module MSSP. Soit vous procdez de faon inverse, dabord le bit 0 jusquau bit de poids le plus fort. Cest de cette faon que fonctionnera notre module USART, que nous tudierons plus tard.

Voici un exemple tout fait gnral de rception dun mot de 8 bits en mode srie synchrone. Cest un exemple, les synchronisations et les niveaux varient dun circuit lautre, ainsi que nous allons le voir plus loin. Dans cet exemple, la lecture seffectue sur le front descendant du signal dhorloge :

310

Vous constatez que : La lecture seffectue un endroit stable du bit concern (vers le milieu de sa dure) Il y a 2 lignes rouges, car le bit peut prendre 2 valeurs (0 ou 1). Le passage dun niveau lautre nest pas instantan, ce qui explique les lignes rouges obliques, et la zone de transition est le temps durant laquelle une lecture ne donnerait pas une valeur fiable (la transition entre les niveaux nest pas encore compltement termine).

Plus la vitesse est lente, plus cette priode de transition est petite par rapport la dure de ltat stable du bit. A une vitesse plus faible, les transitions apparaissent donc de faon pratiquement verticale. Voici un second exemple, qui donne une lecture concrte de loctet B10011010

Cet exemple concret vous permet dassimiler la diffrence de reprsentation entre le cas gnral (premire figure avec double ligne rouge) et un cas particulier (seconde figure avec ligne rouge simple dfinissant loctet). Les habitus des chronogrammes de ce type voudront bien mexcuser pour cette prcision. 21.3 Le mode SPI SPI signifie Serial Peripheral Interface. Ce mode correspond donc un fonctionnement standard du port srie synchrone. Il permet dinterconnecter de faon flexible et paramtrable diffrents composants avec les 16F87x. Le mode SPI ncessite sur notre PIC : Une entre de donnes srie (SDI pour Serial Data Input) Une sortie de donnes srie (SDO pour Serial Data Output) Une entre/sortie horloge (SCK pour Serial ClocK) Une entre optionnelle de slection du PIC (SS pour Slave Select)

Vous pouvez retrouver ces pins sur le brochage de nos PICs. Comme dhabitude, ces fonctions sont multiplexes avec les ports classiques.

311

SDI est multiplex avec RC4 SDO est multiplex avec RC5 SCK est multiplex avec RC3 SS est multiplex avec RA5.

Dans ce mode, le module peut videmment mettre et recevoir. Il peut grer lui-mme lhorloge (mode master ) ou subir lhorloge gre par un autre microprocesseur (mode slave ). Dans ce dernier cas, il peut galement y avoir dautres esclaves (mode multislave ). Vous constaterez donc, que, si on excepte le fil de masse, une liaison de ce type ncessite au minimum 2 fils (lignes) reliant les 2 composants. Le nombre de lignes peut cependant crotre suivant les besoins de lutilisateur. La ligne dhorloge est toujours ncessaire, du fait mme de la dfinition de synchrone .On peut donc avoir les modes de liaisons suivants : Liaison 2 fils : Permet de vhiculer linformation dans un seul sens un moment donn. On pourra distinguer la liaison unidirectionnelle (cest toujours le mme composant qui met, et toujours le mme qui reoit) et la liaison bidirectionnelle half-duplex (chacun met et reoit tour de rle et sur le mme fil). Liaison 3 fils : Permet de vhiculer linformation dans les 2 sens (bidirectionnelle), un fil diffrent tant dvolu chaque sens de transfert dinformation. Comme ceci permet la communication simultane dans les 2 sens de transmission, on parlera de liaison fullduplex.

Nous verrons plus loin quen ralit le PIC met et reoit toujours simultanment lorsquon utilise le module SPI. Loctet qui nest ventuellement pas utilis sera un octet fictif. Lajout dun fil supplmentaire ddicac (SS) par esclave, ces 2 modes prcdents, permet de connecter plusieurs esclaves sur la mme ligne dhorloge. Le signal de slection permet de choisir quel esclave ragira sur la gnration de lhorloge.

Prenez garde que tous ces signaux ncessitent forcment une rfrence de tension (masse). Donc, si votre metteur et votre rcepteur ne partagent pas la mme alimentation, il vous faudra interconnecter les 2 tensions Vss de vos circuits. Ceci ncessitera donc un fil supplmentaire. Tout ceci nous donne une plthore de modes de fonctionnement, que nous allons tudier en squence. 21.4 Les registres utiliss Tout au long de ltude de notre module MSSP, nous allons retrouver 2 registres de configuration et de status, savoir SSPSTAT , SSPCON , plus un registre SSPCON2 utilis uniquement pour le mode I2C, et dont nous ne parlerons donc pas dans ce chapitre.

312

Ces registres contiennent des bits de contrle et des indicateurs dont le rle dpend du mode utilis actuellement par le module. Ceci implique que la description gnrale de ces registres ncessite de nombreuses conditions (telle fonction si tel mode, telle autre pour tel autre mode etc.). Ceci risque de rendre les explications confuses. De plus, lutilisateur se sert en gnral du module, au sein dun programme, pour une seule fonction. Peut donc lui importe davoir une vue absolument gnrale de toutes les fonctions. Jai donc choisi de dcrire ces registres et les bits qui les composent pour chaque mode dutilisation particulier. Le lecteur pourra se reporter au datasheet de Microchip sil souhaite une vue densemble. A ces registres sajoutent le SSPSR (Synchronous Serial Port Shift Register), qui contient la donne en cours de transfert, et le registre SSPBUF (Synchronous Serial Port BUFfer) qui contient loctet envoyer, ou loctet reu, suivant linstant de la communication. Les autres registres utiliss sont des registres dont nous avons dj tudi le fonctionnement. Le mcanisme gnral nest pas trs compliqu comprendre. Voyons tout dabord du ct de lmetteur. Loctet envoyer est plac dans le registre SSPBUF. La donne est recopie automatiquement par le PIC dans le registre SSPSR, qui est un registre destin srialiser la donne (la transformer en bits successifs). Ce second registre, non accessible par le programme, est tout simplement un registre qui effectue des dcalages. Comme le premier bit envoyer est le bit 7, le registre devra dcaler vers la gauche. Tout fonctionne donc un peu comme linstruction rlf , except que le bit sortant nest pas envoy vers le carry, mais directement sur la ligne SDO. Le mcanisme se poursuit jusqu ce que les 8 bits soient envoys. Ct rcepteur, cest videmment le mme genre de mcanisme. Le bit reu sur la ligne SDI est entr par le ct droit du mme registre SSPSR, donc par le bit 0. Ce registre subit alors un dcalage vers la gauche qui fait passer ce bit en position b1. Le bit suivant sera alors reu en position b0, et ainsi de suite. Le dernier bit reu entrane automatiquement la copie de la donne contenue dans SSPSR vers le registre SSPBUF. Donc, on rsume la squence de la faon suivante : Lmetteur copie sa donne de SSPBUF vers SSPSR Pour chacun des 8 bits Au premier clock, lmetteur dcale SSPSR vers la gauche, le bit sortant (ex b7) est envoy sur SDO Au second clock, le rcepteur fait entrer le bit prsent sur SDI et le fait entrer dans SSPSR en dcalant ce registre vers la gauche. Ce bit se trouve maintenant en b0. On recommence pour le bit suivant

313

Le rcepteur copie SSPSR dans SSPBUF

A la fin de la transmission, loctet envoyer qui avait t plac dans SSPBUF aura t remplac par loctet reu. Ce quil faut retenir, cest quil faut 2 synchronisations diffrentes. La premire pour placer le bit envoyer sur la ligne de transmission, et la seconde pour dire au rcepteur quon peut lire ce bit, qui est maintenant stable. Comme le signal dhorloge prsente 2 flancs (un flanc montant et un flanc descendant), nous avons donc nos 2 repres avec une seule horloge. Vous avez bien entendu compris qumission et rception sont simultanes. Vous pouvez parfaitement illustrer ce fonctionnement en crant un petit programme passer au simulateur de MPLAB. Vous allez faire passer le contenu de lmetteur dans le rcepteur. On cre 2 variables, une sappelle emis , et lautre recu . Vous imaginerez le carry comme tant la ligne de transmission.
movlw 0x08 movwf cmpt boucle rlf emis,f rlf recu,f decfsz cmpt,f goto boucle nop ; Pour 8 octets ; dans compteur de boucles ; ; ; ; ; on dcale lmetteur, le bit envoyer est dans le carry on fait entrer le carry par la droite dans le rcepteur 8 bits traits ? non, suivant oui, les 8 bits de lmetteur sont dans le rcepteur

Remarquez bien que le transfert seffectue en 2 tapes non simultanes (les 2 lignes rlf successives, chacune excute sur un cycle dhorloge diffrent). Si vous avez compris ceci, alors vous avez compris ce quest une liaison srie synchrone. Remarquez quil ny a quun seul registre SSPSR, et un seul SSPBUF, ce qui vous indique qumission et rception se font simultanment au sein dun mme PIC de la faon suivante : On transfre la donne mettre dans SSPBUF Le PIC copie cette donne dans SSPSR On opre 8 dcalages vers la gauche, chaque bit sortant est envoy vers SDO, chaque bit entrant provient de SDI Le PIC copie la donne vers SSPBUF, donc remplace la donne mettre par la donne reue. A ce moment, le bit BF est positionn, indiquant que SSPBUF contient une donne lire, et le flag SSPIF est positionn galement pour indiquer la fin du cycle.

Donc, toute mission saccompagne automatiquement dune rception, et rciproquement, toute rception ncessite une mission. Loctet ventuellement non ncessaire (reu ou mis) sera un octet factice, sans signification, souvent appel dummy . Pour bien comprendre, mme si, par exemple, vous nutilisez pas votre pin SDI, lmission dun octet sur SDO saccompagnera automatiquement de la rception dun octet fictif (dummy) sur la ligne SDI. A vous de ne pas en tenir compte.

314

A linverse, pour obtenir la rception dun octet sur la ligne SDI, le matre est contraint denvoyer un octet sur sa ligne SDO, mme si celle-ci nest pas connecte. Il enverra donc galement un octet fictif.

Voici le tout sous forme de dessins. En rouge les bits reus, en bleu les bits envoys :

315

Une dernire petite remarque, avant de passer aux tudes dtailles : La pin SDO conservera ltat quelle avait lors de la fin de la transmission. Etant donn que le bit 0 est transmis en dernier, si loctet envoy est un octet pair (b0 = 0), la ligne SDO restera fige ltat bas jusquau prochain envoi dun octet. Par contre, si loctet est impair (b0 = 1), cette ligne restera fige ltat haut dans les mmes conditions. Ceci na aucune importance concernant la transmission, mais les amateurs doscilloscopes pourront se poser des questions sils examinent les signaux prsents sur cette pin. 21.5 Le mode SPI Master Je poursuis mes descriptions par lutilisation du module MSSP en mode SPI, utilis dans sa fonction de Master ou matre . Vous commencez vous rendre compte du nombre de combinaisons, qui va rendre les exercices systmatiques impossibles (ou alors vous risquiez davoir votre cours aprs la cessation de fabrication des composants). Rassurez-vous, vous en aurez cependant suffisamment pour pouvoir passer de la thorie la pratique. 21.5.1 Mise en uvre Rien ne vaut une petite figure pour vous montrer linterconnexion de votre PIC configure dans ces conditions. Le schma de principe suivant illustre une connexion bidirectionnelle sur 3 fils. Pour rappel, ce mode permet la rception et lmission simultane (mais non obligatoire) de loctet utile:

Remarquez quil y a bien une ligne par sens de transfert de linformation, et que cest le matre qui envoie lhorloge. Cest le matre qui gre lhorloge, et cest lui qui dcide de linstant de la transmission. Le circuit esclave peut tre aussi bien un autre PIC quun circuit ou un ensemble de circuits quelconque. Pour le cas o vous navez besoin que dun seul sens de transfert, il suffit de ne pas connecter la ligne non dsire. Souvenez-vous cependant que, malgr que vous nutilisez pas

316

la ligne en question, au niveau interne au PIC, il y aura toujours mission et rception simultanes. Si vous dcidez dinterconnecter 2 PICs, un tant le matre et lautre lesclave, toute transmission se traduit par lchange du contenu du registre SSPBUF du matre avec celui de lesclave. Il se peut galement que vous dsiriez transfrer vos donnes de faon bidirectionnelle, mais en nutilisant quune seule ligne. La solution sera alors de connecter ensemble SDI et SDO, et de placer SDO en entre (haute impdance) au moment de la rception dun octet. Ainsi, SDO ninfluencera pas la ligne en mode rception. Voici le schma de principe correspondant :

Concernant la mthode de programmation du module, ces 2 modes ninduisent aucune diffrence. Il suffit simplement ici dajouter la gestion de la validation ou non de la pin SDO, suivant que lon se trouve en mission ou en rception. Il faut videmment que lesclave fasse de mme de son ct, dune faon ou dune autre. En pratique, cela seffectuera de la faon suivante :
Routine de rception bsf STATUS,RP0 ; passage en banque 1 bsf TRISC,5 ; mettre SDO en entre (haute-impdance) bcf STATUS,RP0 ; repasser banque 0 (ventuellement) suite ; traitement normal de la rception Fin de la routine de rception Routine dmission bsf STATUS,RP0 ; passage en banque 1 bcf TRISC,5 ; on met SDO en sortie bcf STATUS,RP0 ; repasser banque 0 (ventuellement) suite ; traitement normal de lmission Fin de la routine dmission

Ce mode particulier ne diffre donc des traitements normaux de rception et dmission que nous allons voir que par la gestion du mode entre/sortie de RC5 (SDO). Je nen parlerai donc plus.

317

Bien entendu, il va de soi que lmission et la rception dun octet utile ne pourra dans ce cas tre simultane (mode half-duplex). De nouveau, de faon interne au PIC, la rception et lmission seront toujours simultanes. Ce mode implique donc que lorsquon met dans cette configuration, on reoit un octet qui ne provient pas en ralit de lesclave. Je termine ces schmas de principe, en indiquant comment notre PIC peut, par exemple, tre connecte plusieurs esclaves diffrents :

Les pins Rxx et Ryy sont des pins quelconques du PIC configures en sortie. Elles sont connectes sur les pins de slection de la transmission sur chacun des esclaves. Les 3 autres connexions (entre/sortie/horloge) sont communes tous les circuits et constituent un bus. Notez quil est impossible quun esclave communique avec un autre esclave. Toutes les transmissions sont gres par le matre. Cest le cas des communications full-duplex, qui ncessitent des lignes croises. Evidemment, si on croise les lignes entre A et B et entre A et C, elles ne seront pas croises entre B et C qui ne pourront donc communiquer (sauf ajout dlectronique de slection).

318

Pour communiquer, le PIC matre slectionne un esclave via la pin Rxx ou Ryy (nimporte quelle pin du PIC), puis lance la communication de faon classique. Au niveau du PIC matre, seule la gestion de ces pins de slection (par exemple bsf PORTx,y) est supplmentaire par rapport aux transmissions classiques. Jutiliserai cette possibilit dans lexercice propos en fin de chapitre. 21.5.2 Le registre SSPSTAT Commenons maintenant ltude de nos registres pour ce mode particulier. Attention, je ne parlerai que des bits utiliss dans le fonctionnement dcrit actuellement (SPI en mode master). Un bit non dcrit ne veut pas dire quil nest pas utilis dans un autre mode de fonctionnement. En cas de doute, consultez le datasheet. Ce registre, Synchronous Serial Port STATus register, situ en banque 1, dispose de 2 bits de configuration (b7 et b6), et de 6 bits dindication de status (b5 b0), do son nom. Les 2 premiers cits sont accessibles en lecture et en criture, les suivants sont accessibles en lecture seule. Le positionnement de ces derniers est automatique en fonction de ltat de la transmission. SSPSTAT en mode SPI MASTER b7 : SMP : SaMPle bit (0 = milieu, 1 = fin) b6 : CKE : ClocK Edge select (0 = repos vers actif, 1 = actif vers repos) b5 : non b4 : non b3 : non b2 : non b1 : non b0 : BF : Buffer Full (0 = buffer vide,1 = octet reu) Le bit SMP permet de dfinir quel moment du cycle dhorloge on effectue la capture du bit prsent sur SDI. Si SMP vaut 0, la capture a lieu au milieu du cycle dhorloge en cours. Sil vaut 1, la capture a lieu la fin de ce cycle. Bien entendu vous choisirez le mode le plus appropri en fonction du chronogramme de fonctionnement de lesclave connect. Vous dterminerez alors quel moment la donne prsente par celui-ci sera stable. On peut dire que si lesclave place sa donne au dbut du cycle, le matre devra lire au milieu de ce cycle. Par contre, sil place sa donne au milieu, le matre lira celle-ci en fin de cycle. Le bit CKE dtermine quel sens de transition de lhorloge accompagne le placement du bit sur la ligne SDO. SI CKE vaut 0, la ligne dhorloge SCK sera force vers son tat actif, tandis que si CKE vaut 1, lhorloge passera ltat de repos au moment de lapparition de notre bit sur SDO. En milieu de cycle, lhorloge prendra ltat oppos. Le bit BF est un indicateur (lecture seule) qui, sil vaut 1 , indique que le buffer de rception (SSPBUF) contient un octet complet reu via SDI. Ce bit est lecture seule, pour leffacer, vous devez lire le registre SSPBUF, et ce mme si vous navez aucun usage de loctet quil contient.

319

21.5.3 Le registre SSPCON Second et dernier registre utilis pour commander notre mode SPI, le registre SSPCON nous livre maintenant ses secrets. SSPCON en mode SPI MASTER b7 : non b6 : non b5 : SSPEN b4 : CKP b3 : SSPM3 b2 : SSPM2 b1 : SSPM1 b0 : SSPM0

: SSP ENable (1 = module SSP en service) : ClocK Polarity select bit (donne le niveau de ltat de repos) : SSP Mode bit 3 : SSP Mode bit 2 : SSP Mode bit 1 : SSP Mode bit 0

Le bit SSPEN permet tout simplement de mettre le module SSP en service (quel que soit le mode). Les pins SCK, SDO et SDI sont, une fois ce bit valid, dconnectes du PORTC, et prises en charge par le module SSP. ATTENTION : il vous est toujours, cependant, ncessaire de configurer ces lignes en entre et en sortie via TRISC. SCK (RC3) est la pin dhorloge, donc dfinie en sortie sur notre PIC matre SDI (RC4) est lentre des donnes, donc dfinie en entre SDO (RC5) est la sortie des donnes, donc dfinie en sortie. Donc TRISC devra contenir, pour le mode SPI master : TRISC = Bxx010xxx Souvenez-vous cependant que, si vous utilisez une ligne commune pour lmission et la rception, vous devrez placer SDO en entre durant la rception, afin de ne pas bloquer le signal reu du fait de limposition dun niveau (0 ou 1) sur la ligne par SDO. CKP dtermine ce quon appelle la polarit du signal dhorloge. En fait, il dtermine si, au repos, la ligne dhorloge se trouve ltat bas (CKP = 0) ou ltat haut (CKP = 1). Ltat actif tant loppos de ltat de repos. Vous trouverez couramment dans les datasheets la notion de idle qui prcise ltat de repos ( opposer donc ltat actif). Les bits SSPMx sont les bits de slection du mode de fonctionnement. Je vous donne naturellement dans ce chapitre les seules configurations qui concernent le mode SPI MASTER. b3 0 0 0 0 b2 0 0 0 0 b1 0 0 1 1 b0 0 1 0 1 Mode SPI master, priode dhorloge : Tcy = 4 * Tosc SPI master, priode dhorloge : Tcy * 4 = Tosc * 16 SPI master, priode dhorloge : Tcy * 16 = Tosc * 64 SPI master, priode = sortie du timer 2 * 2

320

Vous constatez que la slection dun de ces modes influence uniquement la frquence dhorloge. Les 3 premiers modes vous donnent des horloges lies la frquence de votre quartz, alors que le dernier de ces modes vous permet de rgler votre frquence dhorloge en fonction de votre timer 2. Dans ce cas, chaque dbordement de ce timer2 inverse ltat du signal dhorloge, ce qui explique que le temps dun cycle complet (2 flancs) ncessite 2 dbordements du timer 2. Bien entendu, vous navez quun seul timer 2, donc, si vous dcidiez dutiliser ce mode, cela induirait des contraintes dans la mesure o votre timer 2 serait dj dvolu un autre usage au sein de votre programme. A vous de grer ces contraintes. Si vous utilisez le timer2 comme source dhorloge, le prdiviseur sera actif, mais pas le postdiviseur, qui ninterviendra pas dans le calcul de la frquence de lhorloge SPI. Nous avons parl du dbut de la transmission, il nous reste savoir quel vnement annonce la fin de la communication. En fait, nous disposons de 2 bits pour indiquer cet tat notre programme : Le bit SSPIF du registre PIR1 est positionn ds que lchange dinformations est termin. Ce bit pourra gnrer une interruption, si celle-ci est correctement initialise. Le bit BF du registre SSPSTAT sera positionn ds quune donne reue est inscrite dans le registre SSPBUF. Ce bit ne sera effac que si on lit la donne contenue dans ce registre.

21.5.4 Choix et chronogrammes Nous avons vu que nous avons plusieurs bits qui influent sur la chronologie des vnements. Je vais tenter de vous montrer en quoi ces choix influent sur la faon dont sont lus et envoys les octets. Vous savez maintenant quune transmission dmarre toujours automatiquement par lcriture de loctet envoyer dans SSPBUF par le matre (pour autant que le module SSP soit en service) et se termine par le positionnement du flag SSPIF du registre PIR1 et par le positionnement du flag BF. Remarquez que lmission et la rception commencent, du point de vue des chronologies, par lapparition des clocks dhorloge. Ceci induit automatiquement lmission et la rception simultane des donnes. Donc, au niveau de votre PIC matre, vous placerez une donne dans SSPBUF pour dmarrer aussi bien une rception quune mission. Si vous navez rien envoyer, il suffira de placer nimporte quoi dans SSPBUF. De mme, si lesclave na rien vous renvoyer en retour, il suffira de ne pas traiter la valeur automatiquement reue. Donc, du point de vue de votre programme, une mission / rception simultanes doctets utiles (full-duplex) se traduira par la squence suivante : On place la donne envoyer dans SSPBUF

321

Une fois SSPIF positionn, on lit dans SSPBUF la valeur reue simultanment Si nous scindons mission et rception dun octet utile en 2 tapes (half-duplex), nous obtiendrons dans le cas o lesclave rpond linterrogation du matre :

On place la donne envoyer dans SSPBUF Une fois SSPIF positionn, on ignore la valeur reue (octet inutile) On place une donne fictive envoyer (octet inutile) Une fois SSPIF positionn, on lit la valeur reue dans SSPBUF Ou le contraire, si le matre rceptionne un octet de lesclave et doit lui rpondre :

On place une donne fictive dans SSPBUF Une fois SSPIF positionn, on traite la valeur lue On rpond en plaant la rponse dans SSPBUF Une fois SSPIF positionn, on ignore la valeur fictive reue dans SSPBUF

Je vais maintenant vous montrer les chronogrammes. Vous avez dj compris quil y a 4 combinaisons dhorloge possibles en fonction de CKE et de CKP : CKP 0 0 1 1 CKE 0 1 0 1 horloge SCK 0 au repos,le placement de la donne induit un flanc montant de SCK SCK 0 au repos,le placement de la donne induit un flanc descendant de SCK SCK 1 au repos,le placement de la donne induit un flanc descendant de SCK SCK 1 au repos,le placement de la donne induit un flanc montant de SCK

Donc, ceci implique quil y a 4 mthodes pour le dbut de lmission. Il y a par contre 2 faons de dterminer le moment de la lecture pour la rception, en fonction de SMP. Soit au milieu du cycle, soit la fin du cycle. Ceci nous donne 8 modes de fonctionnement possibles au total. Afin de vous permettre de mieux comprendre, je vous spare ce chronogramme en 3 parties. Dabord la chronologie de lmission dun octet par le matre, et ensuite celles de la rception par le mme matre. Noubliez pas quen ralit mission et rception sont simultanes, et donc superposables. Donc, voyons le chronogramme dmission :

322

Vous constatez que lmission des bits sur SDO est synchronise avec lhorloge SCK, qui peut prendre 4 volutions diffrentes. Vous choisirez le mode suivant le fonctionnement de lesclave connect. Sur le datasheet de ce dernier, le constructeur vous indiquera quelle forme le signal dhorloge doit prendre au moment de la lecture du bit que votre PIC aura envoy. Un cycle est la distance sparant 2 flches rouges. Vous remarquerez que, quelle que soit la configuration, lesclave devra toujours lire la donne du matre au milieu du cycle (flche bleue) La lecture du bit que vous envoyez, sera imprativement synchronise par votre horloge (comme toutes les actions en mode synchrone), et doit se faire dans la zone stable du bit. Si vous prenez par exemple le mode CKP = 0 et CKE = 0, vous voyez que le bit est plac par le matre sur le flanc montant de SCK. La lecture par lesclave devra se faire imprativement sur le flanc redescendant de SCK, qui est le seul signal prsent durant ltat stable du bit. Voyons maintenant la rception dun octet plac par lesclave sur la ligne SDI. Nous avons 2 modes possibles, dpendants de SMP. De nouveau, ce choix dcoule directement de la chronologie de votre composant esclave. Le moment o son fonctionnement provoque le placement de la donne induit le moment o vous devrez procder sa lecture. Je vais de nouveau scinder les 2 cas. Imaginons tout dabord que llectronique de lesclave soit conue pour que le bit destin au matre soit plac en dbut de cycle (donc en mme temps que le matre place son propre bit sur SDO). Nous aurons :

323

Vous voyez dans ce cas que le choix de linstant de lecture nest pas possible. Vous devez lire le bit au milieu du cycle. Ceci vous impose de placer le bit SMP du registre SSPSTAT 0. Examinons maintenant le cas o lesclave choisit de placer son bit au milieu du cycle (donc au moment o il procde la lecture du bit reu du matre) :

Vous constatez cette fois que, puisque lesclave place son bit au milieu du cycle, il vous faudra attendre la fin de celui-ci (qui concide au dbut du cycle suivant) pour procder la

324

capture du bit concern. Ceci imposera donc de configurer SMP 1 . Dans ce dessin, en effet, un cycle est dlimit par 2 flches bleues. Remarquez que bien que vous travailliez en mode matre , ce mot ne concerne que la gnration de lhorloge. Pour la programmation, vous ntes en fait matre de rien du tout. Comme cest vous qui disposez du composant programmable, cest vous de vous plier aux exigences du composant esclave connect. Cest donc ce dernier qui va dcider de votre faon de travailler, et non linverse. Quand vous travaillerez en mode esclave, vous serez de nouveaux soumis aux exigences du matre connect. Cest donc toujours vous qui devrez vous soumettre aux exigences matrielles (except si vous dveloppez la fois le logiciel du matre et de lesclave). Vous vous souviendrez donc que : Le matre place toujours sa donne en dbut de cycle On en dduit que lesclave lira toujours la donne en milieu de cycle Lesclave peut placer sa donne, soit en dbut, soit en milieu de cycle Ceci implique que le matre lira la donne reue, soit en milieu, soit en fin de cycle.

Ces implications sont dues au fait quon ne peut lire que lorsque le bit est stable, cest-dire aprs le moment o le bit est plac, et avant quil ne disparaisse au profit du suivant. Comme nous sommes synchroniss lhorloge, le seul moment possible de lecture se situe donc un demi cycle aprs le positionnement du bit. 21.5.5 Vitesses de transmission Voyons maintenant, mais jen ai dj parl, des vitesses de transmission disponibles. La slection de ces vitesses est dtermine par les bits SSPM3 SSPM0 du registre SSPCON. Vous avez le choix entre une vitesse fixe en fonction de lhorloge principale de votre PIC et une vitesse fixe en fonction de votre timer2 Sachant que F = 1/T, avec un quartz de 20Mhz, vous disposerez alors des options suivantes : 0000 donnera une vitesse de Fcy (frquence de cycle), soit 5 MHz 0001 donnera une vitesse de Fcy / 4, soit 1,25 MHz 0010 donnera une vitesse de Fcy / 16, soit 312,5 KHz 0011 donnera une vitesse dpendant de votre timer 2, suivant la formule suivante : T = (PR2 + 1) * Tcy * prdiviseur * 2 Ou encore F = Fcy / ((PR2 + 1) * prdiviseur * 2)

325

Je rappelle que le postdiviseur nest pas utilis dans ce cas. La vitesse maximale permise pour la liaison srie synchrone est donc de Fosc/4, soit, pour un PIC cadenc 20MHz, de 5MHz, 5.000.000 de bits par seconde, ou encore 5.000.000 bauds (5MBauds). Vous constatez quil sagit dune vitesse assez importante, qui ncessite des prcautions de mise en uvre (qualit et longueur des liaisons par exemple). La vitesse minimale est celle utilisant le timer 2 avec prdiviseur 16. Nous aurons, pour un quartz de 20MHz, une vitesse minimale de Fcy / (2 * prdiviseur * (PR2+1)), soit 5Mhz / (2 * 16 * 256) = 610,3 bauds. Ceci vous donne une grande flexibilit dans le choix de la frquence de lhorloge, frquence qui dpend une fois de plus des caractristiques de lesclave connect. 21.5.6 Initialisation du mode SPI Master Si vous avez compris tout ce qui prcde, alors vous navez mme pas besoin de cette redondance dinformation. Pour le cas o un petit rsum ne serait pas superflu, voici les procdures dinitialisation de notre module : On initialise SMP en fonction du moment de capture de la donne reue (milieu ou fin du cycle) On initialise CKP en fonction de ltat de repos de la ligne dhorloge (polarit) On slectionne CKE suivant le flanc dhorloge souhait au moment de lcriture dun bit sur SDO On choisit la vitesse de lhorloge suivant SSPMx On met le module en service via SSPEN On configure SCK et SDO en sortie, SDI en entre, via TRISC.

Rien donc de bien compliqu, une fois assimil ce qui prcde. Le dmarrage dune mission (et donc de la rception simultane) seffectue simplement en plaant un octet dans SSPBUF. La fin de lmission (et donc de la rception) seffectuera en vrifiant le positionnement de SSPIF, en grant linterruption SSP correspondante (si configure) ou en vrifiant le positionnement du bit BF, mthode moins souvent utilise. Pour raliser un exercice pratique, il nous faut bien entendu un matre et un esclave. Comme je ne sais pas quels circuits vous avez sous la main, je vous proposerai un exercice mettant en uvre 2 PICs, un configur en matre, et lautre en esclave. Il nous faudra ds lors tudier ce second mode avant de pouvoir raliser notre exercice pratique.

326

21.5.7 Le mode sleep Le passage en mode sleep induit larrt la fois de lhorloge principale du PIC, et du timer 2. Vous navez donc plus aucune possibilit de gnrer lhorloge. La transmission sera donc suspendue, et reprendra do elle se trouvait arrte, une fois le PIC rveill par un autre vnement externe (except un reset, bien sr). 21.6 Le mode SPI SLAVE Comme vous laurez dj compris depuis longtemps, ce mode (slave ou esclave) prsente la particularit de subir lhorloge de synchronisation au lieu de limposer. Ceci va induire des contraintes diffrentes, contraintes paramtres de nouveau par les mmes registres que pour le mode master . 21.6.1 Mise en uvre Je vais maintenant vous donner les 2 configurations possibles de votre PIC connecte en mode SPI esclave. Le premier cas est donn si le PIC est le seul esclave du systme. Nul besoin, alors, priori, de le slectionner en particulier. Nous verrons cependant que cela nest pas toujours vrai.

Bien entendu, on trouve aussi le cas pour lequel votre PIC nest pas le seul esclave du systme. Dans ce cas, il faut bien que votre PIC sache quand cest lui que le matre sadresse :

327

Vous remarquez la prsence de la pin SS, configure en entre. Cette pin, lorsquelle est place au niveau bas, indique au PIC que la communication en cours lui est destine. Il prend alors en compte les fluctuations de lhorloge. Si le PIC a t configur pour tenir compte de la pin SS, et que celle-ci se trouve ltat haut, le PIC ignorera tout signal en cours sur la ligne dhorloge, et donc ne rceptionnera ni nenverra aucun bit. Notez que vous disposez galement, comme pour le mode matre , de la possibilit dinterconnecter SDO et SDI pour tablir une liaison half-duplex. Les mthodes de gestion et les limitations seront donc les mmes. 21.6.2 Le registre SSPSTAT De nouveau, peu de bits utiliss pour ce mode dans ce registre. Le bit SMP doit tre forc 0. Cest logique, tant donn que nous avons prcis dans ltude du fonctionnement en matre que lesclave est oblig de lire sa donne au milieu du cycle.

328

SSPSTAT en mode SPI SLAVE b7 : doit tre positionn 0 (lecture en milieu de cycle) b6 : CKE : ClocK Edge select (0 = repos vers actif, 1 = actif vers repos) b5 : non b4 : non b3 : non b2 : non b1 : non b0 : BF : Buffer Full (0 = buffer vide,1 = octet reu) CKE a strictement la mme fonction que pour le mode master, je ne mattarderai donc pas. Si CKE vaut 0, la transition vers ltat actif dtermine le dbut du cycle (instant o le matre place sa donne). Si CKE vaut 1, la transition vers ltat de repos dtermine le dbut du cycle. BF indique que le registre SSPBUF contient une donne reue en provenance du matre. La seule faon deffacer ce bit est de lire le registre SSPBUF (mme si vous navez aucun besoin de la donnes quil contient). Si vous ne lisez pas SSPBUF, la prochaine rception dun octet donnera lieu une erreur overflow , et loctet qui sera alors reu ne sera pas transfr dans SSPBUF. Il sera donc perdu. En mode esclave, il est donc impratif de lire chaque octet reu, sous peine darrt de la rception des octets. 21.6.3 Le registre SSPCON Ce registre utilise, pour le mode esclave, deux bits de plus que pour le mode matre . SSPCON en mode SPI SLAVE b7 : WCOL b6 : SSPOV b5 : SSPEN b4 : CKP b3 : SSPM3 b2 : SSPM2 b1 : SSPM1 b0 : SSPM0 : Write COLlision detect bit : SSP receive Overflow indicator bit (1 = perte de loctet reu) : SSP ENable (1 = module SSP en service) : ClocK Polarity select bit (donne le niveau de ltat de repos) : SSP Mode bit 3 : SSP Mode bit 2 : SSP Mode bit 1 : SSP Mode bit 0

Le bit SSPOV permet de dtecter une erreur de type overflow . Cette erreur intervient (SSPOV = 1) si la rception termine dun octet induit la tentative de transfert de cette donne depuis SSPSR vers SSPBUF, alors que votre programme na pas encore t lire la donne prcdente contenue dans SSPBUF (bit BF toujours positionn) Cette situation intervient en gnral lors de la dtection par pooling de la rception dun octet. La mthode dinterruption tant moins sujette ce type derreur (raction immdiate la rception dun octet). Ce bit reste 1 jusqu ce quil soit remis 0 par votre programme.

329

Il vous incombera galement de lire le registre SSPBUF, afin deffacer le bit BF qui avait provoqu lerreur, sans cela, la prochaine rception dun octet donnerait de nouveau lieu la mme erreur. En cas derreur de ce type, loctet contenu dans SSPSR nest pas copi dans SSPBUF. Cest donc le dernier octet reu (contenu dans SSPSR) qui est perdu, celui contenu dans SSPBUF ne sera pas cras . Le positionnement de lindicateur WCOL vous informe que vous avez crit un nouvel octet envoyer dans SSPBUF, alors mme que lmission de loctet prcdemment crit est toujours en cours. Il vous appartient deffacer cet indicateur une fois lerreur traite. Concernant les bits SSPMx, ils vont de nouveau dterminer les modes de fonctionnement possibles du SPI en mode esclave. Plus question de vitesse, cette fois, puisquelle est dtermine par le matre. SSPMx 0100 0101 Mode SPI esclave, la pin SS permet de slectionner le port SPI SPI esclave, la pin SS est gre comme une pin I/O ordinaire

Souvenez-vous que la pin SS (optionnelle) peut servir dans le cas des esclaves multiples. Dans ce cas, cette entre valide le signal dhorloge reu. Elle permet donc de ne ragir que lorsque le PIC est rellement concern. Le registre TRISC devra tre correctement configur. Nous allons voir quen fait lutilisation de la pin SS est souvent moins facultative quelle ne le semble au premier abord. 21.6.4 Les autres registres concerns De nouveau, nous allons retrouver nos registres SSPSR et SSPBUF, sur lesquels je ne reviendrai pas. Concernant le registre TRISC, qui dfinit entres et sorties, nous pouvons dj dire que les pins SDO et SDI conservent leur fonction, alors que SCK subit maintenant lhorloge impose par le matre, et donc devient une entre. TRISA,5 permet de dfinir SS en entre, pour le cas o vous souhaitez utiliser cette possibilit SCK (RC3) est la pin dhorloge, donc dfinie en entre sur notre PIC esclave SDI (RC4) est lentre des donnes, donc dfinie en entre SDO (RC5) est la sortie des donnes, donc dfinie en sortie. SS (RA5) est lentre de slection du module SPI (optionnelle) Donc TRISC devra contenir, pour le mode SPI slave : TRISC = Bxx011xxx Si vous dsirez utiliser SS, vous devrez placer TRISA,5 1 . Notez, de plus, que dans ce cas vous devrez aussi dfinir RA5 comme entre numrique en configurant correctement le registre ADCON1 (comme expliqu dans le chapitre sur le convertisseur A/D).

330

21.6.5 Choix et chronogrammes Nous pouvons distinguer ici 2 chronogrammes diffrents, dpendant de ltat de CKE. Pour chacun des niveaux de CKE, nous avons 2 signaux dhorloge possibles. Voyons tout dabord le cas le plus simple, celui pour lequel CKE = 0. Souvenez-vous que vous disposez de la possibilit dutiliser la pin SS. Dans ce cas, elle devra tre place 0 au dbut de la transmission, faute de quoi, le PIC esclave ne ragirait pas lhorloge envoye par le matre. Cette fois, ce nest plus le placement dune valeur dans SSPBUF qui dclenche le transfert. Cest en effet le PIC matre qui dcide du dbut de cette transmission. Si SCK vaut 0, le PIC esclave dtecte le dbut de la transmission au moment de la dtection du premier flanc actif de SCK.

Le PIC esclave placera sa donne sur sa ligne SDO au moment de la premire transition entre tat de repos et tat actif de SCK (si lentre SS optionnelle est utilise, il faudra de plus quelle se trouve au niveau bas). Le matre est sens faire de mme sur la ligne SDI de notre pic esclave. Donc, la lecture de la ligne SDI seffectuera lors de la transition entre tat actif et tat de repos de SCK. Tous les instants de transition sont parfaitement dtermins par lhorloge. La ligne SS, qui valide la communication est optionnelle, et dpend du mode choisi (avec ou sans gestion de SS). Si le mode sans gestion de SS est choisi, ltat de la ligne SS naura aucune importance. Si, par contre, le mode avec gestion de SS est programm, alors les transferts ne seffectueront que si cette ligne est place au niveau bas par le matre. Voyons maintenant ce qui se passe si nous choisissons de travailler avec CKE = 1

331

Dans ce cas, le placement de la valeur seffectue sur la transition entre le niveau actif et le niveau de repos de SCK. Malheureusement, il va de soi que la premire transition, concernant le bit 7, est impossible dtecter. En effet, la ligne tant au repos, la premire transition modifierait la ligne SCK depuis son niveau actuel (repos) vers le niveau repos. Il ny a donc pas de transition. Rien ne permet donc lesclave de savoir que la transmission a commenc. Il lui est donc impossible de placer son bit b7 sur la ligne. La solution trouve est de se servir de la ligne de slection SS, dont le passage ltat bas remplace la premire transition manquante de SCK. Le premier bit sera donc plac par lesclave au moment de la slection de celui-ci via la pin SS. Il va donc de soi que dans cette configuration, la ligne SS devient indispensable au fonctionnement de lensemble. Donc, pour rsumer, si vous utilisez la communication en mode SPI esclave et que vous choisissez CKE = 1, alors vous devrez choisir le mode SSPMX = 0100, qui met la pin SS en service. 21.6.6 Vitesses de transmission Je ne vais pas entrer ici dans trop de dtails. Si vous dcidez dinterfacer 2 PICs identiques ensemble, vous pouvez estimer que la vitesse maximale en mode master est gale la frquence maximale en mode slave. Si vous utilisez un autre composant externe comme matre, il faudra vous assurer que ses signaux prsentent certaines caractristiques compatibles avec le datasheet du PIC (par exemple, le temps de maintien de la ligne SDI aprs le moment de la lecture). Ceci vous permettra de calculer avec prcision la vitesse maximale commune entre les 2 composants. 21.6.7 Initialisation du mode SPI SLAVE Voici les procdures dinitialisation de notre module :

332

On initialise CKP en fonction de ltat de repos de la ligne dhorloge (polarit). On slectionne CKE suivant le flanc dhorloge correspondant au moment de lcriture par le matre dun bit sur SDI On choisit la mise en service ou non de SS, via SSPMx On met le module en service via SSPEN On configure SDO en sortie, SDI et SCK en entre, via TRISC. On configure ventuellement SS (RA5) en entre via TRISA et ADCON1

Le dmarrage dun transfert seffectue simplement lors de la premire transition de SCK (si CKE = 0) ou lors du flanc descendant de SS (si CKE = 1) La fin du transfert seffectuera en vrifiant le positionnement de SSPIF, en grant linterruption SSP correspondante (si configure) ou en vrifiant le positionnement du bit BF. 21.6.8 Le mode sleep Le passage en mode sleep narrte pas lhorloge SCK, puisque cette dernire est gnre par le matre. Le PIC en mode esclave est ds lors parfaitement capable de recevoir et dmettre des donnes dans ce mode. Chaque fin dmission/rception pourra alors rveiller le PIC, qui pourra lire loctet reu et prparer le suivant mettre lors du prochain transfert. 21.6.9 Scurit de la transmission Au niveau du PIC en esclave risque de se poser un problme. En effet, supposons quon perde une impulsion de lhorloge SCK, ou, au contraire quun parasite ajoute une fausse impulsion lors du transfert dun octet. Pour prendre exemple du premier cas, voici ce qui va se passer : Le matre envoie son octet et 8 impulsions dhorloge. Lesclave ne reoit que 7 impulsions, et donc dlivre (du point de vue du matre) 2 fois le mme bit. Il lui reste donc le bit 0 envoyer. Notez que le matre ne peut savoir que lesclave na envoy que 7 bits utiles, de mme lesclave ne peut savoir que le matre a procd la rception de 8 bits. Le matre envoie loctet suivant et 8 nouvelles impulsions dhorloge. Lesclave croit alors que la premire impulsion concerne le bit 0 du transfert prcdent, et interprte les 7 suivantes comme les 7 premires du transfert courant.

Dans cette seconde transaction, le matre aura donc reu un octet compos du bit 0 de loctet prcdent, complt par les bits 7 1 de loctet courant.

333

Toutes les communications seront donc dcales. Il importe donc de permettre lesclave de se resynchroniser afin quune erreur de rception de lhorloge nait dinfluence que sur un seul octet. Ceci peut seffectuer de diverses faons, mais la plus simple est le recours systmatique lutilisation de la pin SS. En effet, chaque fois que cette pin va repasser ltat haut, le module SPI va se remettre 0, et, ds lors, saura que la prochaine impulsion dhorloge concerne un nouvel octet. Je conseille de ce fait de toujours utiliser en mode SPI esclave, si cest possible, le mode qui met en service la gestion de la pin SS. 21.7 Exercice pratique : communication synchrone entre 2 pics Nous voici arriv au moment tant attendu de mettre toute cette thorie en application. La premire chose comprendre, cest que pour tablir une communication, il faut 2 interlocuteurs (le mono-dialogue est trs peu enrichissant). Donc, pour permettre tous de raliser cet exercice, je vais devoir choisir 2 composants que vous connaissez. Je choisis donc sans hsiter 2 PICs 16F876. Oui, je sais, il vous faudra en acheter un second, mais il vous servira sans aucun doute un jour ou lautre, puisque vous lisez ce cours. Voici donc ce que je vous propose. Nous allons programmer un PIC matre qui va compter des positions depuis 0 jusque 255. Ce PIC va envoyer ce numro intervalles de 1 seconde un PIC esclave, qui lui renverra alors un octet contenu dans sa mmoire eeprom la position demande. Le matre recevra cet octet et sen servira pour lenvoyer sur son PORTB, et provoquer ainsi lallumage des LEDs correspondantes qui y seront connectes. Ainsi, nous aurons ralis une rception et une mission par un SPI matre, ainsi quune mission et une rception par un SPI esclave. Nous aurons vu de la sorte la plupart des mcanismes dans un seul exercice. Un bouton-poussoir plac sur la pin RB0 de notre PIC esclave, permettra dinverser loctet envoyer au PIC matre. Voyons le schma de notre montage :

334

Ce montage nest gure compliqu. Le PIC matre reoit les 8 leds, et le PIC esclave le bouton-poussoir. Ils sont interconnects via les lignes de communication du module SPI. Les donnes dallumage sont videmment contenues dans leeprom du PIC esclave. Il est important de travailler proprement, si vous emmlez des longs fils, vous aurez toutes les chances, vu les vitesses mises en jeu, dobtenir un comportement erratique. Les 2 condensateurs de 0.1f permettent un dcouplage des 2 PICs. Par scurit, la pin SS de lesclave est mise en service, et commande par une pin quelconque du PIC matre (jai choisi alatoirement RC6). Si vous dcidez dutiliser ce montage en affectant chaque PIC une source dalimentation diffrente, noubliez pas quil vous faudra alors ajouter un fil de liaison entre les 2 masses (tension Vss). Il faut toujours, en effet, avoir une tension de rfrence (essayez de mesurer une tension avec un voltmtre en nutilisant quun seul fil). Le terme diffrence de potentiel en lieu et place de tension prend alors tout son sens. Nous allons tout dabord utiliser un protocole full-duplex. Si vous avez tout suivi, vous savez que nous allons envoyer et recevoir simultanment loctet utile de notre communication. En imaginant n reprsentant le numro de loctet envoy, ceci peut se traduire par : Le matre slectionne lesclave en mettant la pin RC6 0 . Le matre place son octet n dans SSPBUF, la transmission commence Le matre reoit la rponse loctet n-1 qui se trouvait dans SSPSR de lesclave Lesclave reoit en mme temps loctet n envoy par le matre Le matre stoppe la communication en replaant la pin RC6 1

335

Lesclave lit loctet n en provenance du matre, interprte, et place le rsultat dans SSPBUF pour quil soit envoy lors du prochain transfert. On recommence le tout avec loctet n+1 pour le matre, et rponse loctet n pour lesclave

Remarquez quil est trs important de comprendre que loctet reu par le matre nest pas la rponse loctet quil vient denvoyer, mais la rponse sa question prcdente. En effet, mission et rception tant simultane, il est impossible pour lesclave de prparer sa rponse avant davoir reu la question dans son intgralit. Nous verrons comment rsoudre ce problme par la suite. Bon, passons la pratique. Nous allons tablir une communication entre 2 PICs, il importe donc que ces 2 PICs parlent le mme langage. Nous devons donc dfinir les paramtres de notre communication. Jai choisi arbitrairement : Liaison 312,5 Kbauds, donc Fosc/64 avec un quartz 20MHz Parit de lhorloge : 0V au repos Le matre place son bit sur la transition : tat de repos vers tat actif. Lesclave renvoie son propre bit en mme temps que le matre (dbut du cycle). Le matre lit donc ce bit un demi cycle plus tard, en milieu de cycle.

Qui dit 2 PICs dit 2 logiciels. Commenons par crer celui de notre PIC matre. Effectuez un copier/coller de votre fichier maquette, et renommez cette copie SPIMast1.asm . Crez un nouveau projet du mme nom, et ditez len-tte.
;***************************************************************************** ; Programme de communication srie synchrone entre 2 PICs 16F876. * ; Logiciel pour le master. * ; * ; Le matre envoie le numro de l'affichage dsir * ; L'esclave renvoie l'octet correspondant de sa mmoire eeprom * ; Le matre rcupre l'octet et l'envoie sur son PORTB (8 LEDs) * ; * ; Liaison full-duplex. L'esclave renvoie donc l'octet correspondant la * ; demande prcdente. * ; * ;***************************************************************************** ; * ; NOM: SPIMast1 * ; Date: 21/06/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Notes: Les 8 LEDS sont connectes sur le PORTB * ; Les 2 PICs sont interconnects via SDO,SDI, et SCK * ; La frquence des quartz est de 20 MHz * ; La vitesse de communication est de Fosc/64, soit 312,5 KHz * ; * ;*****************************************************************************

336

LIST p=16F876 #include <p16F876.inc>

; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC

Jai choisi denvoyer un numro toutes les secondes. Jai choisi dutiliser le timer 2. Ltude dj ralise dans le chapitre qui lui est consacr, nous donne : Prdiviseur : 16 Postdiviseur : 10 Valeur de comparaison : 249 Nombre de passages dans la routine : 125 Le temps total sera en effet donn par la formule : T = (PR2+1) * prdiviseur * postdiviseur * Tcy * nombre de passages T = (249+1) * 16 * 10 * 0,2s * 125 = 1.000.000s = 1s. La ligne de slection de lesclave (SS) tant connecte la pin RC6 de notre matre, voici nos dfinitions et assignations :
;***************************************************************************** ; DEFINITIONS ET ASSIGNATIONS * ;***************************************************************************** #DEFINE PR2VAL CMPTVAL SELECT PORTC,6 EQUD'249' EQUD'125' ; slection de l'esclave ; Valeur de comparaison timer 2 ; 125 passages dans la routine d'interruption

Nos variables seront peu nombreuses, nous avons besoin dun compteur de passages dans la routine dinterruption du timer2, de loctet que lon va incrmenter et envoyer lesclave, et dun flag qui va nous servir savoir quand les 125 passages dans la routine dinterruption auront t excuts.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 num : 1 cmpt : 1 flags : 1 ENDC ; ; ; ; ; ; Dbut de la zone (0x20 0x6F) numro envoyer l'esclave compteur de passages d'interruption 8 flags d'usage gnral b0 : une seconde s'est coule Fin de la zone

Les variables de sauvegarde ne concernent que les registres STATUS et W .

337

;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

La routine dinterruption est toute simple. Jai choisi de grer le module SPI par pooling, afin de varier les plaisirs. Nous naurons donc quune seule source dinterruption, le timer2. Celui-ci dcrmente une variable initialise 125. Ds que cette variable atteint 0, le flag est positionn, ce qui signale au programme principal que la dure est coule. La routine dinterruption recharge elle-mme le compteur, et le timer nest pas stopp. Cette mthode permet dobtenir 1 seconde dintervalle entre 2 positionnements du flag. Si on avait choisi de lancer le timer et le compteur dans la routine dattente (wait), on aurait obtenu un temps de 1 seconde partir de ce moment. Cest un peu compliqu expliquer, mais plus simple comprendre avec un petit graphique :

Le positionnement du flag signale la sortie de la routine de temporisation. La premire mthode permet donc de grer des vnements intervalles rguliers (notre cas). Le temps sparant 2 positionnements du flag est constant et ne dpend pas de linstant o on entre dans notre routine de temporisation. La seconde mthode permet dattendre un temps prcis dans une routine de temporisation. Elle ne comptabilise pas le temps coul entre le positionnement prcdent du flag et linstant o on entre dans la routine dinterruption.

338

;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org goto 0x000 init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;----------------------------------------------------------------------------; La routine d'interruption timer 2 est appele toutes les ; (0,2s * 160 * 250) = 8ms. ; au bout de 125 passages, une seconde s'est coule, on positionne ; le flag ;----------------------------------------------------------------------------;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp bcf STATUS,RP0 ; passer banque0 bcf STATUS,RP1 ; interruption timer 2 ; -------------------bcf PIR1,TMR2IF ; effacer le flag d'interruption decfsz cmpt,f ; dcrmenter compteur de passages goto restorereg ; pas 0, fin de l'interruption movlw CMPTVAL ; valeur de recharge du compteur movwf cmpt ; recharger compteur bsf flags,0 ; positionner flag ;restaurer registres ;------------------restorereg swapf status_temp,w ; swap ancien status, rsultat dans w movwf STATUS ; restaurer status swapf w_temp,f ; Inversion L et H de l'ancien W ; sans modifier Z swapf w_temp,w ; Rinversion de L et H dans W ; W restaur sans modifier status retfie ; return from interrupt

Nous arrivons maintenant dans notre routine dinitialisation. Commenons par les PORTs :
;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS ; --------------------

339

BANKSEL PORTB clrf PORTB clrf PORTC bsf SELECT BANKSEL TRISB clrf TRISB movlw B'10010111' movwf TRISC

; ; ; ; ; ; ; ;

slectionner banque 0 sorties PORTB 0 sorties PORTC 0 dslectionner esclave slectionner banque 1 PORTB en sortie SDO et SCK, et select en sortie direction PORTC

Vous remarquez la prsence dune nouvelle macro, BANKSEL . Celle-ci place RP0 et RP1 de faon similaire nos macro BANKx . Cependant, on prcise comme argument un nom de registre ou un emplacement mmoire. BANKSEL effectue le changement en fonction de la banque de ladresse spcifie. BANKSEL est une macro intgre MPASM. Il ne sagit pas proprement parler dune directive, puisquelle sera remplace par des instructions au moment de lassemblage du programme Dans notre exemple, BANKSEL PORTB dtecte que PORTB se trouve en banque 0. La macro va donc crer les 2 lignes suivantes :
bcf bcf STATUS,RP0 STATUS,RP1

Plus loin, nous trouvons BANKSEL TRISB . TRISB se trouvant en banque 1, la macro va induire :
bsf bcf STATUS,RP0 STATUS,RP1

Lavantage apparent est que vous navez pas besoin de connatre la banque du registre que vous utilisez, vous ne risquez donc pas de vous tromper. Linconvnient est que si vous ne connaissez pas cette banque, vous ne pouvez pas non plus savoir si le registre suivant que vous utiliserez fait partie ou non de la mme banque, vous tes donc contraint dutiliser de nouveau BANKSEL mme si ce registre ne ncessitait pas un nouveau changement de banques. A vous de voir. Notez que BANKSEL positionne toujours les 2 bits, RP0 et RP1, mme si un seul ncessitait dtre modifi. Elle sera donc toujours traduite en 2 instructions, et ncessitera donc 2 cycles dexcution. Le reste ne pose pas de problme, on prpare lextinction des LEDs (ds que TRISB sera configur), et on prpare la dslection de lesclave en prparant le positionnement de la ligne RC6 1 . Ensuite, on initialise et on lance notre timer 2. Le premier intervalle de 1 seconde commence donc ce moment.
; initialiser timer 2 ; ------------------movlw PR2VAL ; charger valeur de comparaison BANKSEL PR2 ; passer banque 1 movwf PR2 ; initialiser comparateur movlw B'01001110' ; timer2 on, prdiv = 16, post = 10 BANKSEL T2CON ; passer banque 0

340

movwf T2CON

; lancer timer 2

Viennent ensuite les initialisations des variables :


; initialiser variables ; --------------------clrf num ; on commence par l'lment 0 movlw CMPTVAL ; pour dure initiale d'une seconde movwf cmpt ; dans compteur de passages clrf flags ; effacer flags

De nouveau, rien de particulier, on initialise le compteur de passages dans timer2, qui na pu encore se produire, tant donn que linterruption timer2 nest pas encore en service. Il nest de ce fait pas grave de linitialiser directement aprs avoir lanc le timer. Le flag est effac, et le premier octet envoy lesclave sera 0x00. Nous allons maintenant initialiser le module SPI. Une fois de plus, toute cette thorie amne une initialisation ultra-simple.
; initialiser SPI ; --------------movlw B'00100010' ; SPI matre, Tosc/64, repos = 0 movwf SSPCON ; dans registre de contrle

Nous avons dcid que le matre lirait le bit reu au milieu du cycle, donc le bit SMP du registre SSPSTAT vaudra 0 . La donne est transmise sur le flanc repos vers actif de lhorloge, donc CKE du registre SSPSTAT = 0. Nous devrons donc initialiser SSPSTAT avec B00000000, ce qui est justement sa valeur au moment dun reset. Inutile donc de linitialiser. Cest la raison pour laquelle il napparat mme pas dans notre programme. Concernant SSPCON, il faut juste mettre le SPI en service (SSPEN), et choisir la fonction matre avec Fosc/64, ce qui nous donne SSPMx = 0010. Enfantin, nest-ce pas ? Ne reste plus qu mettre notre interruption du timer 2 en service :
; lancer interruption timer 2 ; --------------------------STATUS,RP0 ; passer banque 1 PIE1,TMR2IE ; interruption timer 2 en service STATUS,RP0 ; repasser banque 0 INTCON,PEIE ; interruptions priphriques en service INTCON,GIE ; lancer les interruptions

bsf bsf bcf bsf bsf

Ceci termine notre initialisation. Le programme principal va effectuer lenvoi dun octet toutes les secondes, rceptionner la rponse lenvoi prcdent, puis envoyer loctet reu sur le PORTB. Une premire temporisation supplmentaire dune seconde permet de sassurer que lesclave a bien termin son initialisation et est prt recevoir (inutile dans ce cas-ci, car notre esclave mettra moins dune seconde pour tre prt, mais prendre en considration lors de lutilisation dun priphrique quelconque).

341

;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** ;----------------------------------------------------------------------------; Attend qu'une seconde soit coule depuis l'mission prcdente ; Envoie l'octet num, et rceptionne en mme temps la rponse l'octet ; envoy lors du prcdent transfert. ; Envoie l'octet reu sur les LEDs, et incrmente l'octet num pour le ; prochain envoi ;----------------------------------------------------------------------------call wait ; attendre 1 seconde supplmentaire pour ; tre sr que l'esclave soit prt loop call wait ; attendre 1 seconde call send ; envoyer l'octet num movf SSPBUF,w ; charger l'octet reu de l'esclave movwf PORTB ; l'envoyer sur les 8 LEDs incf num,f ; incrmenter numro envoyer goto loop ; boucler

La routine dattente wait est toute simple, puisquelle se borne attendre le positionnement du flag par la routine dinterruption, puis effacer ce flag.
;***************************************************************************** ; Attendre 1 seconde * ;***************************************************************************** ;----------------------------------------------------------------------------; attendre qu'une seconde se soit coule depuis le prcdent passage dans ; cette routine ;----------------------------------------------------------------------------wait btfss flags,0 ; flag positionn? goto wait ; non, attendre flag bcf flags,0 ; reset du flag return ; et retour

Nous arrivons maintenant au cur de notre exercice, lmission et la rception dun octet. De nouveau, vous allez voir que la complexit de ltude thorique se traduit par une norme facilit de lapplication dans un programme. Cest dailleurs le but des modules intgrs que de vous donner accs de faon simple des fonctions puissantes. Essayez donc de raliser ce transfert synchrone en pilotant vous-mme SDI, SDO, et SCK. Bon courage. De plus, vous verrez que les vitesses que vous pourrez atteindre seront nettement infrieures celles possibles par lutilisation du module SPI, et en monopolisant votre programme pour cette unique tche.
;***************************************************************************** ; Envoyer l'octet num * ;***************************************************************************** ;----------------------------------------------------------------------------; Slectionne l'esclave, envoie l'octet sur la liaison srie et reoit ; l'octet prpar par l'esclave. ; Attend la fin de l'change avant de sortir, aprs avoir dslectionn ; lesclave ;----------------------------------------------------------------------------send bcf SELECT ; slectionner l'esclave

342

bcf PIR1,SSPIF movf num,w movwf SSPBUF sendloop btfss PIR1,SSPIF goto sendloop bsf SELECT return END

; effacer flag ; charger octet envoyer ; lancer le transfert ; ; ; ; ; tester si transfert termin non, attendre dslectionner l'esclave et retour directive fin de programme

Attention, jai plac cette fois les sous-routines aprs le programme principal, noubliez pas la directive END . Cette sous-routine est trs simple : on place loctet envoyer dans SSPBUF, et on attend simplement que le bit SSPIF soit positionn, signalant que le transfert est termin. A ce moment, loctet reu a remplac celui envoy dans SSPBUF, il ne restera plus qu lire ce registre dans le programme principal. Lancez lassemblage, et placez le fichier .hex obtenu dans votre PIC matre. Pour terminer ce programme, et avant de voir le programme destin lesclave, je vous donne un petit chronogramme de lchange dinformations, afin de bien vous faire comprendre ce que vous envoyez et ce que vous recevez.

Vous constatez que le matre reoit la rponse sa question prcdente, et non la question en cours. Vous visualisez bien le fait quun transfert synchrone quivaut lchange entre le contenu de SSPBUF du matre et celui de lesclave. Lchelle de temps nest pas correcte, en ralit le temps de calcul de lesclave est trs trs petit par rapport la seconde qui spare 2 transferts, la double et la simple flche bleues sont donc pratiquement superposes. Bon, passons ltude du programme de notre esclave. Les contraintes ont dj t tablies, effectuez un copier/coller de votre fichier maquette, et nommez cette copie SPISlav.asm . Crez un nouveau projet ce nom, puis ditez len-tte.

343

;***************************************************************************** ; Programme de communication srie synchrone entre 2 PICs 16F876. * ; Logiciel pour l'esclave. * ; * ; Le matre envoie le numro de l'affichage dsir * ; L'esclave renvoie l'octet correspondant de sa mmoire eeprom * ; Si le bouton-poussoir est activ, l'octet est invers avant renvoi * ; Le matre rcupre l'octet et l'envoie sur son PORTB (8 LEDs) * ; * ; Liaison full-duplex. L'esclave renvoie donc l'octet correspondant la * ; demande prcdente. * ; * ;***************************************************************************** ; * ; NOM: SPISlav * ; Date: 22/06/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichiers requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Notes: Le bouton-poussoir est connect sur RB0 * ; Les 2 PICs sont interconnects via SDO,SDI, et SCK * ; La frquence des quartz est de 20 MHz * ; La vitesse de communication est de Fosc/64, soit 312,5 KHz * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;***************************************************************************** ; DEFINE * ;***************************************************************************** #DEFINE BP PORTB,0 ; bouton-poussoir

On conservera la macro de lecture en eeprom, tant donn que nous allons y placer quelques donnes.
;***************************************************************************** ; MACRO * ;***************************************************************************** ; oprations en mmoire eeprom ; ----------------------------REEPROM macro clrwdt bcf STATUS,RP0 ; lire eeprom(adresse & rsultat en w) ; reset watchdog ; passer en banque2

344

bsf movwf bsf bcf bsf bcf movf bcf endm

STATUS,RP1 EEADR STATUS,RP0 EECON1,EEPGD EECON1,RD STATUS,RP0 EEDATA,w STATUS,RP1

; ; ; ; ; ; ;

pointer sur adresse eeprom passer en banque3 pointer sur eeprom ordre de lecture passer en banque2 charger valeur lue passer en banque0

Aussi curieux que cela paraisse, et ce qui vous dmontre la simplicit de mise en uvre de la communication, nous naurons besoin daucune variable, exceptes celles ncessaires la sauvegarde des registres pour la routine dinterruption. Jai choisi en effet de travailler par interruptions sur SPI, comme cela, vous avez un exemple avec interruptions et un exemple avec pooling.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 ; Dbut de la zone (0x20 0x6F) ENDC ; Fin de la zone ;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

Je vous ai laiss la dclaration de la zone banque 0, pour le cas o vous dsireriez utiliser ce programme comme base pour une application particulire. Nous allons ensuite programmer quelques donnes dans la zone eeprom. Ces donnes constituent les donnes renvoyes au matre. Celui-ci envoie le numro (ladresse) de loctet souhait, lesclave renvoie loctet prsent cet emplacement. Je me suis limit 16 emplacements, le but tant de montrer les transferts, et non de crer une application pratique.
;***************************************************************************** ; DONNEES EEPROM * ;***************************************************************************** ORG0x2100 ; adresse zone data DE DE DE DE DE DE DE DE B'00000001' ; data d'allumage B'00000010' B'00000100' B'00001000' B'00010000' B'00100000' B'01000000' B'10000000'

345

DE DE DE DE DE DE DE DE

B'00000001' B'00000100' B'00010000' B'01000000' B'00000010' B'00001000' B'00100000' B'10000000'

Souvenez-vous que ladresse absolue de la zone eeprom est 0x2100, et que la directive permettant dinitialiser une zone data est DE . Je vous mets les 1 en vert, vous voyez ainsi quel allumage de LED correspond le mot dclar. Nous voici notre routine dinterruption, qui ne comprend que linterruption SPI. Notre PIC entre dans cette interruption quand le transfert est termin. Il lit en eeprom loctet dont le numro est maintenant dans SSPBUF (reu du matre), et place cet octet dans son SSPBUF, qui sera transmis lors du prochain transfert au matre. Lorsque le bouton-poussoir est press, loctet est invers avant dtre envoy. Les LEDs teintes seront donc allumes et vice et versa. Ceci va savrer pratique pour dmontrer par la pratique le dcalage entre interrogation et rponse.
;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;----------------------------------------------------------------------------; A chaque rception d'un octet en provenance du matre, l'octet est trait ; et la rponse replace dans SSPBUF ;----------------------------------------------------------------------------;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp ; Interruption SSP ; ---------------BANKSEL PIR1 ; passer banque 0 bcf PIR1,SSPIF ; effacer flag interupt movf SSPBUF,w ; charger numro reu du matre andlw 0x0F ; on n'a que 16 positions valides REEPROM ; lire l'octet correspondant en eeprom btfss BP ; tester si bouton press xorlw 0xFF ; oui, inverser l'octet reu movwf SSPBUF ; placer rponse dans SSPBUF qui sera ; envoye en mme temps que la rception

346

; de l'octet suivant ;restaurer registres ;------------------; ; ; ; ; swap ancien status, rsultat dans w restaurer status Inversion L et H de l'ancien W Rinversion de L et H dans W return from interrupt

restorereg swapf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w retfie

Notre routine dinitialisation commence par soccuper des PORTs.


; //////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS ; -------------------BANKSEL TRISC ; passer banque 1 bcf TRISC,5 ; SDO en sortie movlw 0x06 ; pour PORTA en numrique movwf ADCON1 ; dans registre de contrle

Notez que, bien que la pin SS soit prise en charge par le module SPI, le fait doublier de configurer ADCON1 pour limposer comme pin numrique, la ferait dtecter en priorit comme une entre analogique. A propos, en me relisant, je tiens prciser que SS est en italique puisquil sagit dune pin active ltat bas, non pour voquer une idologie que par ailleurs je rprouve. Je tenais cette prcision pour viter un dangereux amalgame quant mes intentions. Je sais que certains sont trs forts pour dcouvrir des messages cachs dans les livres, tableaux et autres uvres, mais je tiens rassurer tout le monde : il sagit dun cours, non dune uvre , et il ny a aucun message cach. Tous mes messages sont en clair dans le texte . Le mode SPI esclave ne requiert quune seule sortie, savoir la pin SDO (RC5). Linitialisation du module SPI est aussi simple que pour le matre, on choisira le mode esclave avec gestion de la pin SS. On initialise SSPBUF 0, de faon que le premier transfert envoie cette valeur au matre.
; initialiser SPI ; --------------bcf STATUS,RP0 ; repasser banque 0 movlw B'00100100' ; esclave avec SS active movwf SSPCON ; dans registre clrf SSPBUF ; premier octet envoy = 0

Il nous faut maintenant mettre linterruption SPI en service :


; autoriser interruptions ; -----------------------

347

bsf bsf bcf bsf bsf

STATUS,RP0 PIE1,SSPIE STATUS,RP0 INTCON,PEIE INTCON,GIE

; ; ; ; ;

passer banque 1 autoriser interrupts SPI repasser banque 0 autoriser interruptions priphriques valider interruptions

Et le reste de notre programme ? Vous allez rire :


;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** start goto start ; boucler END ; directive fin de programme

Difficile, de nouveau, de faire plus simple. Lesclave pouvant tre mis en mode sommeil durant la rception, vous pouvez ajouter :
start sleep goto start END ; mise en sommeil ; boucler ; directive fin de programme

Attention, dans ce cas. Le rveil du PIC provoque la remise en service de loscillateur, qui est un signal fortement perturbateur. Si votre PIC est incorrectement dcouple, si vos fils sont longs ou sujets aux perturbations, vous risquez dobtenir un fonctionnement alatoire. Je vous dconseille donc dutiliser cette instruction sleep sur une platine dexprimentation, comportant, de surcrot, 2 PICs disposant de leur propre oscillateur. Noubliez pas que les vitesses de transmission sont assez importantes, et donc facilement sensibles aux perturbations. Lancez lassemblage, placez les 2 PICs sur le circuit et lancez lalimentation. Au bout de quelques secondes, les LEDs commencent clignoter sur le matre, preuve que les transferts fonctionnent. Si vous maintenez press le bouton-poussoir, vous constatez que lallumage suivant est toujours non invers. Ce nest que 2 allumages plus tard que les LEDs seront inverses. Ceci vous dmontre que la rponse concerne toujours loctet prcdent. 21.8 Exercice 2 : alternative de fonctionnement Si ce fonctionnement ne nous convient pas, cest--dire si nous voulons que lallumage des LEDs corresponde ladresse envoye, nous navons dautre alternative que de travailler en half-duplex, cest--dire, mettre loctet, puis recevoir la rponse par une autre transmission. Comme la transmission comporte automatiquement une mission et une rception simultanes, nous ignorerons simplement le premier octet reu. Une transmission se fera donc de la faon suivante : On envoie le numro de loctet

348

On rceptionne loctet correspondant la requte prcdente, on lignore (octet dummy) On attend que lesclave ait eu le temps de placer loctet dans SSPBUF On envoie nimporte quoi (octet dummy) On rceptionne loctet correspondant la requte prcdente (octet courant) On place cet octet sur le PORTB.

Vous voyez que de la sorte, on procde en 2 tapes : lmission dun octet utile avec rception dun octet ne servant rien, suivie par mission dun octet ne servant rien avec rception dun octet utile Entre les 2 transmissions, il est impratif dattendre que lesclave ait eu le temps de traiter linformation. Nous allons nous servir de nouveau de notre routine dinterruption timer 2, pour positionner un autre flag. Le temps sparant 2 passages dans cette routine est de 8ms, ce qui est largement suprieur au temps de raction de lesclave. Pour calculer ce dernier, il suffit de compter les cycles dinstructions survenus entre le moment o le matre est interrompu par sa routine dinterruption timer2, et le moment o lesclave a plac sa rponse dans SSPBUF. 8ms correspondent 40.000 cycles dinstruction, vous voyez que lesclave a eu tout le temps ncessaire pour placer sa rponse. Voici ce que a donne sur un petit dessin (attention, de nouveau pas lchelle) :

On voit trs nettement que chaque change dinformation utile ncessite prsent 2 transmissions conscutives. Par contre, lavantage est que loctet reu est bien loctet de rponse la requte actuelle, et non plus la prcdente. Le programme de lesclave reste inchang, puisquil se contente de rpondre aux interrogations. Il lui importe peu que linterrogation soit un dummy (pour rappel, octet inutile) ou un octet rellement important. En modifiant le programme de lesclave, on aurait

349

pu se passer de calculer la rponse au dummy reu, et placer directement nimporte quoi dans SSPBUF. Cela na cependant aucune importance pour cette application. Par contre, le matre devra grer cette double transmission. Effectuez un copier/coller de votre programme SPIMast1.asm et renommez la copie en SPIMast2.asm . Crez un nouveau projet ce nom. Le dbut ne pose pas problme :
;***************************************************************************** ; Programme de communication srie synchrone entre 2 PICs 16F876. * ; Logiciel pour le master. * ; * ; Le matre envoie le numro de l'affichage dsir * ; L'esclave renvoie l'octet correspondant de sa mmoire eeprom * ; Le matre rcupre l'octet et l'envoie sur son PORTB (8 LEDs) * ; * ; Liaison half-duplex. L'esclave renvoie donc l'octet correspondant la * ; demande actuelle. L'change d'informations ncessite cependant 2 * ; cycles d'mission/rception * ; * ;***************************************************************************** ; * ; NOM: SPIMast2 * ; Date: 23/06/2002 * ; Version: 1.0 * ; Circuit: platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Notes: Les 8 LEDS sont connectes sur le PORTB * ; Les 2 PICs sont interconnects via SDO,SDI, et SCK * ; La frquence des quartz est de 20 MHz * ; La vitesse de communication est de Fosc/64, soit 312,5 KHz * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC ;***************************************************************************** ; DEFINITIONS ET ASSIGNATIONS * ;***************************************************************************** #DEFINE PR2VAL CMPTVAL SELECT PORTC,6 EQU D'249' EQU D'125' ; ; ; ; ; slection de l'esclave Valeur de comparaison timer 2 125 passages dans la routine d'interruption dure = Tcy*(PR2+1)*prdiv*postdiv*cmptval = 0,2s * 250 * 16 * 10 * 125 = 1s.

350

Ensuite, les variables. Il suffit pour nous dutiliser un second bit comme flag dans la variable flags , nous choisirons arbitrairement le bit 1.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 num : 1 cmpt : 1 flags : 1 ENDC ; ; ; ; ; ; ; Dbut de la zone (0x20 0x6F) numro envoyer l'esclave compteur de passages d'interruption 8 flags d'usage gnral b0 : une seconde s'est coule b1 : 8 ms se sont coules Fin de la zone

Rien dire non plus sur les variables en zone commune :


;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

La routine dintialisation est strictement identique, except le positionnement du flag 1 chaque passage dans la routine dinterruption.
;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;----------------------------------------------------------------------------; La routine d'interruption timer 2 est appele toutes les ; (0,2s * 160 * 250) = 8ms. ; au bout de 125 passages, une seconde s'est coule, on positionne ; le flag ;----------------------------------------------------------------------------;sauvegarder registres ;--------------------0x004 ; adresse d'interruption

org

351

movwf swapf movwf bcf bcf

w_temp STATUS,w status_temp STATUS,RP0 STATUS,RP1

; ; ; ;

sauver registre W swap status avec rsultat dans w sauver status swapp passer banque0

; interruption timer 2 ; -------------------bcf PIR1,TMR2IF ; effacer le flag d'interruption bsf flags,1 ; 8 ms coules decfsz cmpt,f ; dcrmenter compteur de passages goto restorereg ; pas 0, fin de l'interruption movlw CMPTVAL ; valeur de recharge du compteur movwf cmpt ; recharger compteur bsf flags,0 ; positionner flag ;restaurer registres ;------------------; ; ; ; ; ; ; swap ancien status, rsultat dans w restaurer status Inversion L et H de l'ancien W sans modifier Z Rinversion de L et H dans W W restaur sans modifier status return from interrupt

restorereg swapf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w retfie

Rien ne change pour linitialisation


;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS ; -------------------BANKSEL PORTB ; slectionner banque 0 clrf PORTB ; sorties PORTB 0 clrf PORTC ; sorties PORTC 0 bsf SELECT ; dslectionner esclave BANKSEL TRISB ; slectionner banque 1 clrf TRISB ; PORTB en sortie movlw B'10010111' ; SDO et SCK, et select en sortie movwf TRISC ; direction PORTC ; initialiser timer 2 ; ------------------movlw PR2VAL ; charger valeur de comparaison BANKSEL PR2 ; passer banque 1 movwf PR2 ; initialiser comparateur movlw B'01001110' ; timer2 on, prdiv = 16, post = 10 BANKSEL T2CON ; passer banque 0 movwf T2CON ; lancer timer 2 ; initialiser variables ; --------------------clrf num ; on commence par l'lment 0 movlw CMPTVAL ; pour dure initiale d'une seconde movwf cmpt ; dans compteur de passages clrf flags ; effacer flags

352

; initialiser SPI ; --------------movlw B'00100010' ; SPI matre, Tosc/64, repos = 0 movwf SSPCON ; dans registre de contrle ; lancer interruption timer 2 ; --------------------------STATUS,RP0 ; passer banque 1 PIE1,TMR2IE ; interruption timer 2 en service STATUS,RP0 ; repasser banque 0 INTCON,PEIE ; interruptions priphriques en service INTCON,GIE ; lancer les interruptions

bsf bsf bcf bsf bsf

Cest dans le programme principal que se situe la diffrence de fonctionnement. Vous noterez la prsence de la temporisation des 8ms, et la double mission/rception. Afin dviter dutiliser une sous-routine spcifique, loctet dummy envoy sera de nouveau loctet num . Ceci na aucune importance, loctet dummy pouvant, par dfinition, tre nimporte quoi, donc pourquoi pas loctet num ?
;***************************************************************************** ; PROGRAMME PRINCIPAL * ;***************************************************************************** ;----------------------------------------------------------------------------; Attend qu'une seconde soit coule depuis l'mission prcdente ; Envoie l'octet num et rception un dummy ; 8 ms aprs le dbut de la premire transmission, envoie un dummy (ici, ; pour des raisons de facilit, envoie l'octet num comme dummy, cela ; vite d'utiliser une seconde sous-routine) et rceptionne la rponse ; l'octet num. ; Envoie l'octet reu sur les LEDs, et incrmente l'octet num pour le ; prochain envoi ;----------------------------------------------------------------------------call wait ; attendre 1 seconde supplmentaire pour ; tre sr que l'esclave soit prt loop call wait ; attendre 1 seconde call send ; envoyer l'octet num, recevoir dummy bcf flags,1 loop2 btfss flags,1 goto loop2 call movf send SSPBUF,w ; effacer flag 1 ; tester si 8 ms coules ; non, attendre ; envoyer l'octet dummy, recevoir rponse ; l'octet num ; charger l'octet reu de l'esclave ; l'envoyer sur les 8 LEDs ; incrmenter numro envoyer ; boucler

movwf PORTB incf num,f goto loop

Lancez lassemblage, placez votre PIC matre sur le circuit, et envoyez lalimentation. Vous ne constatez aucune diffrence. Cest normal, tant donn que vous ne voyez pas les correspondances entre octet envoy et octet reu. Ceci explique pourquoi jai ajout un bouton-poussoir. Pressez-le et vous constatez que les LEDs seront inverss ds le clignotement suivant. Ceci dmontre que loctet reu est bien la rponse loctet en cours de traitement, et non plus la rponse au prcdent.

353

21.9 Conclusions Voil, vous tes maintenant devenus des spcialistes du module SPI. Une corde de plus est donc ajoute votre arc de programmeur es-PICs. Noubliez pas que les transmissions srie synchrones sont utilises, en gnral, pour les cas suivants : Liaisons hauts dbits Liaisons courtes distances (le plus souvent entre 2 composants de la mme carte). Communications avec des circuits spcifiques ncessitant des liaisons synchrones

Elles sont souvent sensibles aux perturbations, et il convient dtre soigneux dans la ralisation dapplications pratiques. Les exercices prsents navaient nul besoin de grer les conditions derreur. Il nen sera pas toujours de mme pour vos applications pratiques. Pensez donc vrifier les indicateurs concerns dcrits dans la partie thorique, si vous avez des raisons de penser quil peut arriver quun des indicateurs derreur se retrouve positionn. De nouveau, cest vous de savoir sil y a risque ou non de collision ou de dbordement, daprs la structure de votre programme, et du priphrique connect.

354

Notes :

355

Notes :

356

22. Le bus I2C


22.1 Introduction Jai dcid, plutt que de passer directement ltude du module MSSP en mode IC, de commencer par vous expliquer en quoi consiste ce bus. En effet, bien que le module prenne en charge tous les aspects de gestion de ce bus, il ma sembl important de comprendre ce qui se passait effectivement au niveau des lignes qui le constituent. De plus, labsence de ces informations vous poserait problme au moment dtablir une communication avec un composant IC. En effet, lorsque vous rencontrerez un nouveau composant, tout ce dont vous disposerez son sujet sera, en gnral, son datasheet. Sans une connaissance assez prcise du bus IC, vous vous retrouveriez confront quelque chose qui risquerait bien de vous paratre incomprhensible. Et puis, mieux vaut avoir une information quon nutilisera ventuellement pas quune absence dinformation au moment o on en a besoin. Vous ne pensez pas ? 22.1 Caractristiques fondamentales Le bus IC permet dtablir une liaison srie synchrone entre 2 ou plusieurs composants. Il a t cr dans le but dtablir des changes dinformations entre circuits intgrs se trouvant sur une mme carte. Son nom, dailleurs, traduit son origine : Inter Integrate Circuit, ou I.I.C., ou plus communment IC (I carr C). Ce bus est le descendant du CBUS, qui est de moins en moins utilis. Son domaine dapplication actuel est cependant bien plus vaste, il est mme, par exemple, utilis en domotique. Il comporte des tas de similitudes avec le SMBUS dIntel (System Management BUS), mais je ne parlerai pas de ce bus ici. Si cela vous intresse, vous trouverez des informations sur Internet. Sachez simplement que nos 16F87x peuvent crer des signaux compatibles avec cette norme. L IC permettait, ses dbuts, de travailler des frquences maximales de 100 Kbits/seconde, vitesses assez rapidement portes 400 Kbits/seconde. Il existe maintenant des familles de circuits pouvant atteindre des vitesses de 3.4 Mbits/seconde. Il nest pas dans mon propos dtudier ces modles hi-speed particuliers. De toute faon, le fonctionnement thorique reste identique. Le bus IC est constitu de 2 uniques lignes bidirectionnelles : La ligne SCL (Serial Clock Line), qui, comme son nom lindique, vhicule lhorloge de synchronisation La ligne SDA (Serial DAta line), qui vhicule les bits transmis.

357

Il est important de vous souvenir que : la ligne SCL est gre par le matre (nous verrons que par moment, lesclave peut prendre provisoirement le contrle de la ligne). la ligne SDA, un moment donn, est pilote par celui qui envoie une information (matre ou esclave).

Tous les circuits sont connects sur ces 2 lignes. Il existe 2 sortes de circuits pouvant tre connects : Les circuits matres, qui dirigent le transfert et pilotent lhorloge SCL. Les circuits esclaves, qui subissent lhorloge et rpondent aux ordres du matre. Chacun de ces 2 types peut mettre et recevoir des informations. Une particularit est quon peut placer plusieurs matres sur le mme bus IC. Ceci implique que, sans prcautions, si 2 matres dsirent prendre le contrle du bus en mme temps, on encourrait, sans prcautions particulires, 2 risques : La destruction de llectronique des circuits, pour le cas o lun dentre eux impose un niveau haut et lautre un niveau bas (court-circuit). La corruption des donnes destines ou en provenance de lesclave.

Fort heureusement, vous vous doutez bien que Philips (linventeur de lIC) a trouv des solutions pour parer ces ventualits. Je commence par les contraintes lectroniques. Je parlerai des corruptions de donnes plus loin dans ce chapitre. Pour la partie lectronique, la parade est simple. On travaille avec des tages de sortie qui ne peuvent imposer quun niveau 0, ou relcher la ligne, qui remonte delle-mme au niveau 1 via des rsistances de rappel. De cette faon, on naura jamais de court-circuit, puisque personne ne peut placer la tension dalimentation sur la ligne. Ces tages sont des montages que vous trouverez dans la littrature sous la dnomination de collecteur ouvert ou de drain ouvert suivant la technologie utilise. La pin RA4 de votre PIC utilise dailleurs la mme technique. Notez que vous pouvez faire de mme, si un jour vous dcidez de piloter une ligne partir de 2 pics, par exemple. Imaginons le montage suivant :

358

Imaginons que vous dsiriez allumer la LED depuis nimporte lequel des 2 PICs. Si vous crez les sous-routines suivantes dans chaque PIC :
Allumage bcf PORTB,1 return extinction bsf PORTB,1 return

Si maintenant, votre PIC1 tente dteindre la LED, il placera +5V sur la ligne RB1. Si au mme instant votre PIC2 dsire allumer la LED, il placera 0V sur la ligne RB1. Et nous voici en prsence dun beau court-circuit, avec risque de destruction des 2 PICs. Si on regarde de faon schmatique ce qui se passe au niveau des 2 PICs, on voit :

Le courant passe directement depuis le +5V du PIC1 vers le 0V du PIC2, ce qui provoque un court-circuit. Il va de soi que si on avait utilis la pin RA4 (prvue pour cet usage) on naurait pas rencontr ce problme. Mais il existe une solution simple permettant dutiliser nimporte quelle pin, et qui consiste ne jamais autoriser le PIC envoyer un niveau haut. On modifie donc le programme comme suit :
Initialisation bcf PORTB,1 Allumage BANK1 bcf TRISB,1 BANK0 ; Si RB1 en sortie, alors RB1 = 0 ; passer banque 1 ; RB1 en sortie, donc allumage de la LED ; repasser banque 0

359

return extinction BANK1 bsf TRISB,1 BANK0 return ; passer banque 1 ; RB1 en entre, donc non connecte -> Led teinte ; repasser banque 0

On obtient alors un schma quivalant thorique du type :

Si on enclenche un ou lautre interrupteur, on allume la LED (on force la ligne RB1 ltat bas). Si on tente dteindre la LED, il faut que les 2 PICs librent la ligne. Cest exactement ce qui se passe pour le bus I2C. Chaque circuit peut forcer la ligne SCL ou SDA 0, mais aucun circuit ne peut la forcer ltat haut. Elle repassera ltat haut via les rsistances de rappel (pull-up) si tous les circuits connects ont libr la ligne. Le schma dinterconnexion des circuits sur un bus IC est donc le suivant :

360

ATTENTION : souvenez-vous que la masse de tous ces circuits, qui sert de rfrence, doit videmment tre commune. De ce fait, si ces circuits se situent sur des cartes diffrentes, ou ne partagent pas la mme rfrence, il vous faudra une ligne supplmentaire dans votre connexion, afin de transmettre la tension de rfrence. On parle donc couramment de liaison 2 lignes, mais en ralit 3 lignes sont ncessaires pour communiquer. 22.2 Les diffrents types de signaux Nous allons voir que cette norme joue intelligemment avec les niveaux prsents sur les 2 lignes, afin de raliser diverses oprations. Plutt que de vous donner directement un chronogramme de transmission, je vais scinder les diffrentes tapes. De cette faon je pense que ce sera beaucoup plus simple comprendre. 22.2.1 Le bit ordinaire Tout dabord, la mthode utilise pour crire et recevoir des bits est diffrente de celle utilise dans ltude de notre module SPI. En effet, pour ce dernier, un bit tait mis sur un flanc de lhorloge, et tait lu sur le flanc oppos suivant de la dite horloge. Au niveau du bus IC, le bit est dabord plac sur la ligne SDA, puis la ligne SCL est place 1 (donc libre) durant un moment puis force de nouveau 0. Lmission du bit seffectue donc sans aucune correspondance dun flanc dhorloge. Il est lu lors du flanc montant de cette horloge. Notez quil est interdit de modifier la ligne SDA durant un niveau haut de SCL. La drogation cette rgle nest valable que pour les squences dites de condition que vous allons voir plus loin. Voici quoi ressemble lmission et la lecture dun bit :

Vous constatez que le bit prsent sur la ligne SDA doit tre prsent avant la monte du signal SCL, et continuer un certain temps avant sa redescente.

361

22.2.2 Le start-condition Comme pour tout signal synchrone, le protocole ne dfinit pas de start et de stop-bit. Mais il intgre toutefois les notions de start-condition et de stop-condition . Ces squences particulires, obtenues en modifiant la ligne SDA alors que la ligne SCL est positionne ltat haut permettent de dfinir dbut et fin des messages. Nous verrons que ceci est indispensable pour reprer le premier octet du message, qui a un rle particulier. Si on se souvient quau repos, SCL et SDA se trouvent relchs, et donc ltat haut, le start-condition (symbole conventionnel : S) est ralis simplement en forant la ligne SDA 0, tout en laissant la ligne SCL 1.

Il existe un driv de cette condition, appel repeated start condition , qui est utilis lorsquun message en suit directement un autre. Il sagit donc en fait dun second startcondition au sein dun mme message. Nous verrons son intrt plus tard. Sachez ce niveau quun repeated start-condition peut tre considr comme identique un startcondition . 22.2.3 Le stop-condition Nous venons dy faire allusion. Cette condition indique la fin du message en cours. Elle remet les lignes SDA et SCL au repos, mais en respectant la chronologie suivante : La ligne SDA est ramene 1, alors que la ligne SCL se trouve dj 1. Voici ce que cela donne :

362

22.2.4 Lacknowledge Voici de nouveau un nom barbare. Lacknowledge (ACK) est en fait laccus de rception de loctet envoy. Cest donc le rcepteur qui met ce bit pour signaler quil a bien lu loctet envoy par lmetteur. Cet accus de rception est lu comme un bit classique. Il vaudra 0 si laccus de rception signifie OK , et 1 pour toute autre raison (rcepteur dans limpossibilit de rpondre, par exemple). Voici un ACK (OK) :

Et voici une absence daccus de rception, encore dnomme NOACK. En effet, un NOACK quivaut une absence de raction, puisque seul le niveau bas est impos, le niveau haut tant li la libration de la ligne (ou sa non appropriation).

22.2.5 Le bit read/write Il nous faut encore voir un bit particulier. Le bit R/W indique lesclave si les bits de donnes contenus dans la trame sont destins tre cris (R/W = 0) ou lus (R/W = 1) par le matre. Dans le cas dune criture, le matre enverra les donnes lesclave, dans le cas dune lecture, cest lesclave qui enverra ses donnes au matre. Etant donn que ce bit ne prsente aucune particularit, je ne vous donne pas le chronogramme (identique celui dcrit pour le bit ordinaire ).

363

22.3 La notion dadresse Nous avons vu que nous pouvions connecter un grand nombre de composants IC sur le mme bus. Cest dailleurs le but recherch. Mais nous ne disposons daucune ligne de slection, comme nous lavions pour le module SPI (ligne SS). Cest donc de faon logicielle que le destinataire va tre slectionn. Ceci fait naturellement appel la notion dadresse. La premire norme IC limitait la taille des adresses 7 bits. Cependant, au fil de son volution, on a vu apparatre la possibilit dutiliser des adresses codes sur 10 bits. Nous verrons comment cela est possible. Nous pouvons donc dire que seul lesclave dont ladresse correspond celle envoye par le matre va rpondre. Toutes les adresses ne sont pas autorises, certaines sont rserves pour des commandes spcifiques. Parmi celles-ci, nous trouvons : B0000000 : utilise pour adresser simultanment tous les priphriques (general call address). Les octets complmentaires prcisent le type daction souhait (par exemple, reset). B0000001 : utilise pour accder aux composants CBUS (anctre de l IC) B0000010 : rserve pour dautres systmes de bus B0000011 : rserve pour des utilisations futures B00001xx : pour les composants haute-vitesse B11111xx : rserve pour des utilisations futures B11110xy : permet de prciser une adresse sur 10 bits.

Concernant cette dernire adresse, cette squence permet de complter les 2 bits de poids forts xy reus par un octet complmentaire contenant les 8 octets de poids faible. Nous obtenons donc une adresse comprenant 10 bits utiles. Nous verrons ceci en dtails. Souvenez-vous que si vous devez attribuer une adresse votre PIC configur en esclave, il vous faudra viter les adresses prcdentes, sous peine de problmes lors de lutilisation avec certains composants spcifiques. Corollaires de tout ce qui prcde : Seuls les esclaves disposent dadresses Ce sont toujours les matres qui pilotent le transfert Un matre ne peut parler qu un esclave (ou tous les esclaves), jamais un autre matre.

Rien ninterdit cependant quun composant passe du status de matre celui desclave et rciproquement. Le bus IC est donc dune complte souplesse ce niveau. 22.3 Structure dune trame IC

364

Nous avons vu tous les signaux possibles, il nous faut maintenant tudier le protocole de communication des intervenants en IC. Nous savons dj que la transmission commence par un start-condition (S). Vient ensuite ladresse, code sur 7 ou 10 bits, complte par le bit R/W qui prcise si les donnes qui vont suivre seront crites ou lues par le matre. Notez que certains considrent que ce bit fait partie de ladresse, ce qui implique alors que ladresse relle se retrouve multiplie par deux, et que les adresses parires sont destines des critures, et les adresses impaires des lectures. Chaque octet envoy est toujours accompagn dun accus de rception de la part de celui qui reoit. Un octet ncessite donc 8 + 1 = 9 impulsions dhorloge sur la pin SCL. On distingue ds lors 2 cas : soit ladresse est code sur 7, soit sur 10 bits. Voici comment se prsente le dbut dune trame ( partir ce cet instant, dans les chronogrammes concernant lIC, je note en rouge ce qui est transmis par le matre et en bleu ce qui est transmis par lesclave) : Notez donc, et cest logique, que cest toujours le matre qui envoie ladresse, quil soit rcepteur ou metteur pour le reste du message.

Pour coder une adresse sur 10 bits, on utilisera comme premier octet ladresse rserve B11110xy0 qui prcise quun second octet est ncessaire. Voici ce que donne lenvoi dune adresse sur 10 bits :

Remarquez que si vous codez ladresse sur 2 octets, le bit R/W doit toujours imprativement tre gal 0. Vous prcisez donc toujours une criture. Nous verrons plus loin ce que nous devons faire si nous avons besoin dune lecture. De plus, le bit R/W ne se situe que dans le premier octet de ladresse, cela explique pourquoi vous pouvez caser 8 bits dans loctet 2, alors que vous tes limits 7 bits dans le cas dune adresse sur 1 octet. Notez enfin que si vous utilisez une adresse sur 7 bits, elle ne pourra jamais, videmment, commencer par B 11110

365

Afin dtre certain que vous avez tout compris, je vous donne le chronogramme correspondant (jai considr que les flancs des signaux taient verticaux, cest plus simple dessiner cette chelle) :

Vous constaterez en lisant les chronogrammes prsents dans les documentations techniques des composants, quon ne vous donne que la ligne SDA rsultante. Celle-ci dfinit le niveau prsent sur la ligne, mais il ne faut jamais oublier que le matre et lesclave placent des bits tour de rle sur la mme ligne. Vous voyez ici, par exemple, que durant lacknowledge, le matre libre la ligne SDA (niveau 1). A ce moment, cest lesclave qui impose le niveau bas de confirmation. Vous en dduirez ce moment que lorsque le matre crit (en rouge), lesclave lit, lorsque lesclave crit (en bleu), le matre lit. Ceci explique que pour un nophyte, ces chronogrammes ne sont pas toujours aiss comprendre. Nul doute quaprs avoir lu ce chapitre, vous trouverez la norme IC comme simple comprendre. Nous venons de crer le dbut de notre trame, savoir le start-condition (S) envoy par le matre, le premier octet dadresse galement envoy par le matre, suivi par laccus de rception de lesclave (ACK) envoy par lesclave. Suit ventuellement un second octet dadresse (adresse sur 10 bits), complt de nouveau par laccus de rception. Remarquez que, si vous vous tes poss la question, aprs lenvoi du premier octet, dans le cas dune adresse code sur 10 bits, il pourrait trs bien y avoir plusieurs esclaves concerns par ce premier octet (plusieurs esclaves dont ladresse tient sur 10 bits). Il se peut donc quil y ait plusieurs esclaves diffrents qui envoient en mme temps leur accus de rception. Ceci na aucune importance, lors de lenvoi du second octet, il ny aura plus quun seul esclave concern. Si on ne reoit pas un ACK , cest soit que lesclave slectionn nexiste pas, soit quil nest pas prt.

366

A ce stade, nous avons choisi notre esclave, reste savoir ce quon attend de lui. Nous avons ce stade, 2 possibilits : Soit nous allons lui envoyer un ou plusieurs octets de donne Soit nous allons recevoir de lui un ou plusieurs octets de donne

La slection de lun ou lautre cas dpend de la valeur que vous avez attribu votre bit R/W. Si R/W = 0, les octets suivants sont envoys par le matre et lus par lesclave (criture) Si R/W = 1, les octets suivants sont envoys par lesclave et lus par le matre (lecture)

Corollaire : il nest pas possible, en IC, dcrire et de lire en mme temps. Si vous dsirez effectuer une lecture et une criture, ceci ncessitera 2 oprations, donc 2 envois du startsequence. Maintenant, si vous avez suivi, vous devez vous dire : mais alors, puisque le bit R/W du premier octet dadresse dans le cas dune adresse sur 10 bits vaut toujours 0, comment donc effectuer une lecture en mode 10 bits ? . En fait, ceci est un peu plus compliqu. Il faut comprendre que limposition du bit R/W 0 se justifie pour llectronique des circuits concerns. Les esclaves devront en effet lire le second octet dadresse, il sagit donc dune criture (vu du ct du matre). La solution pour la lecture dans les adresses 10 bits est la suivante : Le matre envoie le start-condition Le matre envoie le premier octet dadresse, avec R/W 0 Le matre envoie le second octet dadresse Le matre envoie un repeated start-condition Le matre envoie le premier octet dadresse, avec R/W 1 Le matre commence la rception des donnes.

Vous remarquez que le matre doit reprciser le bit R/W 1 . Comme ce bit est contenu dans le premier octet dadresse, il doit donc renvoyer ce dernier. Et comme il conserve le dialogue (il na pas envoy de stop-condition), il sadresse donc toujours en fait au mme esclave, et na pas besoin de rpter le second octet dadresse. Voici ce que donne lenvoi dune adresse 10 bits pour une lecture :

367

Voyons tout dabord le cas de lcriture. Je parlerai pour faire simple dadresses codes sur 7 bits, pour les adresses sur 10 bits, il suffira dajouter un ou deux octet, vous avez compris la manuvre. La procdure est la suivante : Le matre envoie le start-condition (S) Le matre envoie loctet dadresse, avec le bit R/W 0 (criture) Lesclave rpond par ACK Le matre envoie le premier octet de donne Lesclave rpond par ACK Le matre envoie le dernier octet de donne Lesclave rpond par ACK ou par NOACK Le matre envoie le stop-condition (P)

Notez quil est possible, pour lesclave denvoyer un NOACK (cest--dire un bit ACK 1 ) pour signaler quil dsire que le matre arrte de lui envoyer des donnes (par exemple si sa mmoire est pleine, ou si les octets reus sont incorrects etc.). Le matre dispose de la possibilit de mettre fin au transfert en envoyant le stop-sequence (P). Voici ce que a donne dans le cas o le matre envoie une adresse code sur 7 bits et 2 octets de donnes destination de lesclave.

Le cas de la lecture nest pas plus compliqu. Voici la squence, toujours pour une adresse code sur 7 bits : Le matre envoie le start-condition Le matre envoie le premier octet dadresse, avec le bit R/W 1 (lecture) Lesclave rpond par ACK L esclave envoie son premier octet de donne Le matre rpond par ACK Lesclave envoie le xme octet de donne Le matre rpond NOACK pour signaler la fin du transfert Le matre envoie le stop-sequence (P).

Notez que cest le matre seul qui dcide de mettre fin la rception. Il doit alors commencer par envoyer un NOACK , ce qui signale lesclave quil na plus besoin de transmettre, suivi par un stop-sequence qui met fin la transmission. Comme cest le matre qui envoie le ACK chaque fin de lecture, lesclave na aucun moyen de faire savoir quil dsire que la lecture sarrte. Voici ce que cela donne :

368

Vous voyez que de nouveau, tout est logique. Le dbut de la squence est toujours identique, le matre envoie toujours le start, ladresse, et le bit R/W. Lesclave donne toujours le premier acknowledge. A partir de loctet qui suit lenvoi de ladresse, cest lesclave qui place les donnes sur SDA, et donc le matre qui rpond par lacknowledge. La dernire requte se termine par lenvoi, par le matre, dun NOACK prcdent le stop-condition. Notez cependant que cest toujours le matre qui pilote la ligne SCL. 22.4 Evnements spciaux Il nous reste voir quelques squences particulires qui compltent cette gestion du bus IC. 22.4.1 La libration du bus Nous avons vu que le matre prend le contrle du bus avec le start-condition. A partir de ce moment, les autres matres ventuels doivent attendre que le matre ait termin ses transactions avant de tenter de prendre leur tour le contrle du bus. La libration du bus est effective une fois que 4,7 s se sont coules depuis lenvoi du stop-sequence, aucun nouveau start-sequence nayant t reu. 22.4.2 Le repeated start-condition Si le matre dsire envoyer une seconde trame, au lieu de terminer la trame en cours par un stop-condition, il la terminera par un nouveau start-condition. Ce dernier porte dans ce cas le nom de SR pour repeated start-condition . Sa fonction est strictement identique au startcondition, mais ne libre pas le bus comme risquerait de le faire la combinaison stopsequence/start-sequence. Le matre actuel empche donc un autre matre de saccaparer le bus. Le repeated start-condition marque en mme temps la fin dune transaction, et le dbut dune suivante. 22.4.3 La pause Un autre cas particulier est celui ou, lors dune criture, lesclave demande au matre de patienter durant un certain temps (par exemple le temps mis pour traiter loctet prcdent).

369

La mthode est simple, lesclave bloque la ligne dhorloge SCL ltat bas durant la pause demande. Lorsque le matre essaye denvoyer limpulsion dhorloge, il ny arrive pas, puisquelle est force ltat bas par lesclave. Le matre dispose dune lectronique interne qui lui permet de vrifier ltat de SCL. Lorsquil est de nouveau prt, lesclave libre la ligne SCL, qui prend alors ltat haut. Le matre peut alors poursuivre. Tant que la ligne SCL est bloque, le matre resette son gnrateur de temps interne. Autrement dit, la libration de la ligne SCL, les chronogrammes de gnration dhorloge seront respects. Vous voyez que limpulsion de SCL qui suit la fin de la pause a strictement la mme dure que toutes les autres impulsions.

22.5 Arbitrage du bus Vous trouverez partout, dans les datasheets concernant les composants IC, la notion d arbitration , pour arbitrage. Cest dans cette capacit quont ces composants de grer les conflits que rside toute la puissance du bus IC. Ces mthodes permettent de disposer dun bus comportant plusieurs matres, et permettent des systmes totalement dcentraliss, dans lesquels chacun peut librement prendre la parole. Il faut pour comprendre ce qui suit, avoir bien compris que le niveau 0 est prioritaire sur le niveau 1 . Si un circuit quelconque passe une ligne 0, cette ligne sera force 0. Si par contre le circuit demande que la ligne passe 1 (libration de la ligne), celle-ci ne passera 1 que si aucun autre circuit ne la force 0 . Bien entendu, les circuits ne peuvent pas prendre la parole nimporte quand ni nimporte comment. Il importe, comme lors dune discussion entre humains, de respecter les rgles de courtoisie lmentaires (quoi que certains, au vu de certains forums, semblent sen passer sans problme).
370

La premire question qui se pose est : quand un matre peut-il prendre la parole ? La rponse est simple : quand le bus est libre. Il nous faut alors tablir quelles sont les conditions dans lesquelles le bus peut tre considr comme tel. Tout matre est capable de dtecter automatiquement les start et stop-conditions . Ds lors, ds quun start-condition est dtect, le bus est considr occup jusqu la dtection du stop-condition correspondant. A partir de ce moment, si le matre prcdent ne reprend pas la parole avant 4,7 s, le bus est considr comme libre. Si le nouveau matre vient de se connecter au rseau, il lui suffit de sassurer que la ligne SCL soit ltat haut depuis au moins 4,7s pour vrifier quun autre matre ne dirige pas le bus. Dans la pratique, certains matres nattendent mme pas cette limite de 4,7s pour sassurer que le bus soit libre, a ne pose pas de problme de fonctionnement particulier. Vous allez me dire : cest bien beau, tout a, mais si 2 nouveaux matres prennent le contrle en mme temps ? Et bien, rassurez-vous, cest galement prvu, et gr de faon automatique. Imaginons que le bus soit libre, et que les matres 1 et 2 saccaparent le bus en mme temps. Chaque matre dispose dune lectronique qui vrifie en permanence la correspondance entre les niveaux appliqus sur SDA et SCL, et les niveaux lus en ralit sur ces lignes. Sil y a discordance, cest quun tiers prend le contrle de cette ligne. Nous en avons dj vu un exemple lorsque lesclave imposait une pause via la ligne SCL. Nous savons donc que les 2 matres vont placer leurs donnes sur la ligne SDA et que lesclave lira la donne place sur le flanc montant de SCL. Mais les matres galement vont lire automatiquement la ligne SDA. Si un matre place un niveau 0, le niveau lu sera obligatoirement 0. Si, par contre, le matre place un niveau 1, et quun autre matre a plac la valeur 0, le premier va constater quil y a un autre matre, puisque la ligne SDA vaudra 0 alors quil a plac 1 . Le second matre ne va sapercevoir de rien du tout, puisque la ligne SDA est bien 0. Dans ce cas, le matre qui na pas pu imposer son niveau 1 perd le contrle du bus, et se retire au profit du matre prioritaire . Voici ce que a donne :

371

Dans cet exemple, le matre 2 dsire placer ladresse B0010011, soit D19. Le matre 2 tente de placer ladresse B0010100, soit D20. Vous voyez que le premier matre qui envoie un bit 1 en mme temps que le matre 2 place un 0 est mis hors-jeu. On peut dj en dduire les rgles suivantes : Les adresses les plus faibles sont prioritaires par rapport aux adresses plus leves. Dans la construction dun systme, les esclaves les plus prioritaires se verront donc affecter les adresses les plus basses En cas dadresses identiques, lcriture a priorit sur la lecture (car le bit R/W vaut 0 pour lcriture). Les collisions sont dites non destructives , puisque la donne prioritaire arrive intacte au destinataire.

Corollaire : ce ne sont pas les matres qui sont prioritaires les uns par rapport aux autres, les priorits sont fonctions des trames envoyes. Donc, si vous avez tout compris, si, au mme moment, un matre tente de slectionner lesclave xx, et quun autre matre tente de slectionner lesclave yy, celui qui envoie ladresse la plus leve devra se retirer du bus. Vous allez me rtorquer quil se peut trs bien que les 2 matres accdent au mme esclave. Fort bien, mais cela ne pose de nouveau aucun problme. Nous avons 2 possibilits : Tout dabord, les 2 matres effectuent une lecture. Dans ce cas, il ny a absolument aucun problme. Les 2 matres envoient tous les deux une requte de lecture de lesclave, qui va leur rpondre tous les deux en mme temps. La requte tant la mme, la rponse est donc identique et valide. Si, par contre, les 2 matres effectuent une criture, ladresse va tre complte par les octets de donne. Dans ce cas, si les 2 matres crivent la mme chose, tous les intervenants ny verront que du feu. Lesclave crira la donne, valide puisquelle est identique pour les 2 matres. Si les 2 matres ncrivent pas la mme chose, nous allons nous retrouver avec un conflit du mme type que celui concernant ladresse. A un moment donn, un des matres va tenter

372

dcrire un 1 , et lira 0 sur la ligne SDA. Il se retirera donc de la communication. La priorit sera une fois de plus affecte loctet de donne de valeur la plus faible. Vous voyez qu partir de ce moment, tous les problmes de conflits de bus sont rsolus. Bien entendu, tout ceci est automatique dans les circuits intgrs. Le programmeur reoit donc simplement des indicateurs qui linforment de ltat actuel de la transmission. Et si 2 esclaves diffrents tentent dcrire en mme temps ? Et bien, cest tout simplement impossible, chaque esclave doit, par dfinition, avoir une adresse diffrente, et ne peut rpondre que sil est slectionn. Si vous avez besoin de rendre un matre prioritaire, il suffit que le dlai qui spare la dtection du stop-condition de la prise de contrle du bus soit plus court que pour les autres matres. Autrement dit, vous introduirez un dlai plus ou moins long entre la dtection du stop-condition et le fait de considrer que le bus est libre. Ainsi, le matre qui disposera du dlai le plus court pourra simposer en premier. De cette faon, vous pouvez choisir entre donner priorit un matre donn, ou priorit ladresse de destination la plus petite. Si vous tes attentifs, vous pouvez encore protester, en dmontrant que les 2 matres peuvent tenter de prendre le contrle du bus, mais dune faon lgrement dcale (un des deux ragit un peu plus vite que lautre). Dans ce cas, il faut en revenir au start-condition, dans lequel le matre fait descendre la ligne SDA 0 . Le matre le plus lent verra donc la ligne SDA descendre avant mme quil ne lai positionne. Il se retirera donc du bus avant mme davoir tent la moindre action. Dernire petite question : un matre peut-il parler un autre matre ? En fait, non. Dune part, seuls les esclaves ont une adresse, et seuls les esclaves subissent lhorloge. Par contre, rien ninterdit un matre, lorsquil na rien dire, de se configurer en esclave. Alors il peut changer des informations avec un matre. Jen reste l pour la thorie gnrale, je pourrais vous parler de l IC high-speed , mais si vous avez compris ce qui prcde, et que vous avez besoin dun tel composant, il vous sera ais de le mettre en uvre. Ce que je prfre faire, par contre, cest vous donner un exemple concret de circuit courant utilisant LIC. Jai choisi de vous expliquer la faon dont fonctionne une eeprom de type 24C32, 24C64 ou autre (attention, la 24C16 fait exception). Lisez ceci, car jutiliserai cette eeprom dans le cadre de notre exercice pratique sur LIC. 22.6 Les eeprom IC Avec ce chapitre, je vais vous montrer comment interprter un datasheet de composant IC. Jai reu normment de courrier concernant ces composants et le mode IC, ce qui justifie, ce cours tant destin rendre service au plus grand nombre, que je my attarde.

373

De plus, je suis persuad que si vous devenez un adepte des microcontrleurs, vous aurez un jour ou lautre besoin dun composant de ce type. 22.6.1 Caractristiques gnrales Pour que tout le monde parte sur les mmes bases, je vous fournis le datasheet du 24C64 en format pdf, et en direct de chez Microchip. Tout dabord, un il sur le tableau de la premire page nous montre quil sagit dun circuit qui accepte une frquence dhorloge de maximum 400KHz. Nous sommes donc en plein dans la norme IC seconde gnration. Les tensions sont compatibles avec notre PIC. Notez que la norme IC nimpose pas la valeur de la tension de rappel au niveau haut. Suivant les technologies, vous pourriez vous retrouver avec des composants ncessitant des tensions diffrentes (12V). Dans ce cas, de simples rsistances de protection vous permettraient dinterfacer facilement ces composants avec votre PIC. Un coup dil aux caractristiques vous informe que vos donnes sont labris pour une dure minimale garantie de 200 ans. Si vous perdez vos sauvegardes au bout de 100 ou 150 ans, courez donc vite vous plaindre chez Microchip. 1.000.000 de cycles deffacement/criture sont de plus garantis (vous avez le temps de procder des exprimentations). Microchip vous informe galement sur cette premire page, que la 24C64 (ou 24LC64, ou 24AA64) dispose de 64Kbits de mmoire. Attention, Kbits et non Kbytes. Ceci nous donne en fait 8Kbytes de donnes mmorises. On vous parle galement de random access et de sequential access . La notion de random pour alatoire , signifie que vous pouvez accder nimporte quelle case mmoire, sans avoir parcourir lensemble. Si vous imaginez un livre, cela signifie que vous pouvez ouvrir ce livre nimporte quelle page. Sequential , pour squentiel signifie exactement linverse. Vous pouvez crire ou lire adresse aprs adresse, dans lordre croissant, sans avoir vous proccuper de dfinir chaque fois ladresse. Ceci vous indique donc que les accs offrent beaucoup de souplesse. On vous parle de plus de page-write . Nous verrons que cela permet dcrire toute une srie doctets (sur la mme page de 32) en une seule opration dcriture. Page 2, vous trouvez les contraintes lies aux signaux, contraintes qui restent bien entendu conformes la norme IC. Rien dinquitant pour nous. Le petit tableau 1-1 en haut droite, nous donne la fonction des pins de cette eeprom. On y voit que 3 pins, A0, A1, et A2 sont des pins qui sont utilises par lutilisateur pour dfinir la slection du circuit. Nous y reviendrons. Nous trouvons en plus les 2 lignes dalimentation, ce qui est logique, et les 2 lignes du bus, SCL et SDA, obligatoires pour tous les circuits IC.

374

On notera une dernire pin, WP, qui permet, lorsquelle est place au niveau haut, dinterdire les critures dans la mmoire, tout en autorisant les lectures. Si cette pin est connecte la masse, ou nest pas connecte du tout, les oprations dcritures sont autorises. Nous arrivons au tableau 1-3 de la page 3. Ce tableau poursuit les caractristiques prcdentes. Jetez un il sur la ligne bus free time . Vous voyez quil est indiqu que le bus doit tre libre depuis 4700 ns avant quune nouvelle transmission puisse avoir lieu. Nous retrouvons donc bien nos fameuses 4,7s entre le stop-condition et le moment o les composants considrent que le bus est libre. Encore plus bas dans le tableau, vous voyez input filter . Sachez que tous les composants IC sont munis dun filtre destin liminer les parasites. Notre PIC en est galement pourvu, jen reparlerai au moment voulu. Lavant-dernire ligne nous indique quune opration dcriture ncessite 5ms maximum, ce qui est norme par rapport aux vitesses mises en jeu. Une opration dcriture est lcriture dun seul octet, ou dun groupe doctets (maximum une page). Vous avez donc intrt utiliser les procdures dcriture par page lorsque vous avez plusieurs octets conscutifs crire. Jexpliquerai plus loin comment savoir que lcriture est termine. Dailleurs, le plus loin ne lest pas tant que a. Si vous tournez la page, vous voyez la description des pins dont jai dj parl, et les caractristiques du bus, qui sont bien entendu celles du bus IC. Un encadr est important, il signale que leeprom nenvoie pas son accus de rception (donc le matre lira un NOACK ) si un cycle prcdent dcriture nest pas termin. Voici donc comment savoir si le cycle est oui ou non termin. La page 5 montre comment se passe le transfert, cest--dire de faon strictement conforme au protocole IC (heureusement). 22.6.2 Octet de contrle et adresse esclave On arrive aux choses srieuses avec la page 6. La figure 5-1 vous montre le format de loctet de contrle. Octet de contrle ? Quest-ce que cest que cette nouveaut ? Pas de panique, Il sagit tout simplement de loctet dadressage, donc le premier octet mis par le matre et le premier reu par lesclave, cest--dire leeprom. Vous allez me dire que je nai dit nulle part que leeprom est un composant esclave. Cest vrai, mais il faut faire de temps en temps fonctionner sa tte. En fait, ce nest pas leeprom qui dcide si on la lit ou on lcrit. Ce nest pas non plus leeprom qui dcide quel moment envoyer ou recevoir des donne. De plus, la figure 5-1 nous montre que leeprom a une adresse, et cest le composant esclave qui dispose dune adresse. Tout ceci nous dmontre que leeprom est un circuit IC esclave. Voyons loctet dnomm de contrle par Microchip. Bien entendu, nous, nous savons quil sagit tout simplement de loctet dadresse, envoy directement aprs le start-condition :

375

Microchip a utilis le terme contrle probablement pour ne pas que lutilisateur confonde ladresse de leeprom (contrle) et ladresse de loctet concern dans leeprom. Nous constatons immdiatement quon nous impose les 4 premiers bits de ladresse de leeprom, les 3 bits restants tant notre convenance. Comme toujours, cette adresse se termine par le bit R/W. Nous pouvons en dduire que : - Notre eeprom sera adresse obligatoirement dans la plage : B1010000 B1010111. Cest une adresse assez leve, les eeproms ntant pas considrs comme des composants prioritaires Nous avons 8 possibilits de choix de ladresse, fonctions de A2,A1, et A0. Nous pouvons de ce fait placer, sans artifice, un maximum de 8 eeproms sur notre bus. Nous avons besoin dune possibilit de choisir ces 3 bits. Nous avons affaire un adressage sur 7 bits.

Arrivs ce stade, vous devez savoir que les bus IC sont destins recevoir une foule de composants diffrents. Or, chaque composant doit avoir une adresse distincte. Les constructeurs de circuits intgrs se sont donc mis daccord pour rserver certaines plages dadresses des composants particuliers. Ceci permet dviter les conflits malheureux qui rendraient impossibles certaines cohabitations. Les adresses commenant par 1010 sont en gnral rserves aux eeproms. Il nous reste dfinir les 3 bits dadressage pour notre ou nos eeproms. En fait, cest tout simple, nous avons vu que notre eeprom dispose de 3 broches A0, A1, et A2. Cest le niveau plac sur ces pins qui dfinira ladresse. Par exemple, si vous placez du +5V ( 1 ) sur la pin A1, alors le bit A1 de ladresse vaudra 1 . Si vous reliez cette mme pin la masse ( 0 ), alors le bit A1 de ladresse vaudra 0 . Vous choisissez donc ladresse finale de votre eeprom au moment de la connexion physique de celle-ci sur son circuit. Ladresse est donc configurable au moment de la cration du circuit, et ne peut pas tre modifie par logiciel. La plus petite adresse possible sera : B1010000, soit D80 ou 0x50. La plus grande sera B1010111, soit D87 ou 0x57. Voyons donc comment interfacer nos 8 eeproms sur un circuit, et quelles seront leurs adresses respectives.

376

Je nai reprsent que les lignes +5V et 0V. Les lignes SDA et SCL respectives de chaque circuit devront bien videmment tre relies entre elles et au bus IC. La pin WP sera laisse non connecte, ou relie la masse pour une utilisation normale, et connecte au +5V pour protger leeprom. Bien entendu, il nest pas interdit de connecter ces pins WP une pin de votre PIC, de faon commander la protection en criture par logiciel. Si vous avez besoin de plus de 8 eeproms, vous pouvez galement utiliser des mcanismes de slection de groupes deeprom partir de votre PIC, en laissant ou non passer lhorloge SCL, mais ceci dborde du cadre de ces explications, et du bus IC dans son sens strict.

Vous voyez que dans cet exemple, chacune des 8 eeproms rpondra une adresse unique, comprise entre 0x50 et 0x57. Je nai pas reprsent les interconnexions des lignes SCL et SDA, mais je vous en ai dj parl tout au dbut du chapitre.

377

22.6.3 Slection dune adresse interne Nous venons de dfinir ladresse IC, cest--dire ladresse physique sur le bus de chaque circuit eeprom. Mais, lorsquon opre dans une mmoire, il nous faut encore savoir quelle adresse on lit ou on crit parmi les 8Koctets possibles quelle contient. Il ne faut pas, ce niveau, confondre ladresse du circuit, et ladresse de la donne lire ou crire. Pour slectionner une adresse dans une eeprom, il suffit, directement aprs le premier octet dadressage du circuit, dcrire dans leeprom slectionne ladresse, code sur 2 octets, laquelle devra sinitialiser son pointeur interne. Donc, mme si on doit lire un emplacement mmoire, on doit crire ladresse (R/W = 0). Jinsiste donc une nouvelle fois : ladresse sur laquelle on travaille dans leeprom doit tre crite par le matre. Toutes les oprations sur les eeprom se ralisent en effet sur lemplacement dsign par un pointeur interne. Ceci fonctionne exactement comme pour ladressage indirect des PICs. Avant de pouvoir utiliser la case pointe par INDF , vous deviez en effet initialiser le pointeur FSR , il en est de mme ici. La slection dune adresse seffectue donc fort logiquement de la faon suivante : Le matre envoie le start-condition Le matre envoie le premier octet avec ladresse de leeprom et le bit R/W 0 (criture) Leeprom envoie laccus de rception (ACK) Le matre envoie les 2 octets de ladresse de loctet concern.

Notez que ladresse de loctet doit tenir dans la plage des 8Koctets disponibles, ce qui donne une adresse code sur 13 bits (de B0000000000000 B1111111111111, soit de 0x00 0x1FFF). Comme on envoie 2 octets, cela nous donne 16 bits. Ladresse sera aligne vers la droite, ce qui implique que les 3 bits gauche du premier octet (poids fort) seront inutiliss. On les placera de prfrence 0, pour toute compatibilit future avec une eeprom de capacit suprieure. Autrement dit :

Jai not ladresse de lesclave en majuscules (Ax) et la valeur dinitialisation du pointeur de leeprom (adresse de loctet) en minuscules (ax). Ceci afin de ne pas vous embrouiller avec ces diffrentes notions dadressage. A ce niveau, il faut se souvenir que le premier ACK envoy par lesclave signale si celui-ci est prt. Si lesclave rpond ce moment NOACK (donc ne rpond pas), cest

378

quil na pas termin lopration prcdente, il faut donc recommencer la tentative jusqu ce que la rponse soit ACK . Evidemment, si vous adressez un esclave qui nexiste pas, vous obtiendrez galement un NOACK , puisque personne naura plac la ligne SDA 0 . Dans ce cas, vous risquez dattendre longtemps.

22.6.4 Ecriture dun octet Nous savons maintenant choisir une eeprom, et slectionner une adresse dans cette eeprom. Nous allons voir comment crire une donne lemplacement choisi. La procdure est trs simple, elle consiste initialiser le pointeur dadresse, puis envoyer directement aprs loctet de donne. Leeprom sait parfaitement que loctet suivant devra tre crit lemplacement spcifi. Cest bien entendu le bit R/W qui indique leeprom quil sagit dune criture. Donc, dans lordre : Le matre envoie le start-sequence Le matre envoie ladresse de leeprom avec le bit R/W 0 (criture) Leeprom, si elle est prte, rpond ACK Le matre envoie le poids fort de ladresse concerne Leeprom rpond ACK Le matre envoie le poids faible de ladresse concerne Leeprom rpond ACK Le matre envoie la donne crire en eeprom Leeprom rpond ACK Le matre envoie le stop-sequence. Sous forme de chronogramme simplifi :

Pour faire un peu plus concret, imaginons que nous voulions crire la valeur 0x3D ladresse 0x1A58 de notre EEPROM, sur laquelle les pins A2 et A0 sont connectes la masse et A1 au +5V. Voici ce que nous devons envoyer :

Et voil, rien de plus simple. Vous savez maintenant comment crire un octet dans une eeprom 24c64 ou assimile.

379

22.6.5 Ecriture par page Nous avons vu que nous pouvions crire un octet nimporte quelle adresse de notre eeprom. Mais cette opration dcriture ncessite leeprom un temps de traitement interne de 5ms. Durant ces 5ms, leeprom rpondra NOACK toute nouvelle tentative dcriture. Pour crire nos 8*1024 octets, il nous faudrait, en pratiquant de la sorte, 8 * 1024 * 5ms, soit plus de 40 secondes. Cette mthode nest donc gure pratique pour les critures multiples doctets. Heureusement, nous avons la possibilit dcrire des srie doctets en une seule opration dcriture. Nous parlerons ici du cas de la 24C64, dont chaque page fait 32 octets. Cette taille varie en fonction du type deeprom, une 24C512 dispose de pages de 128 octets, par exemple. Ceci est rendu possible par un mcanisme interne leeprom, qui incrmente automatiquement les 5 bits de poids faible du pointeur dadresse (b0 b4). Attention, il ny a pas de report sur le bit b5 pour la 24C64. (en fait, leeprom utilise un buffer interne, mais cest transparent pour lutilisateur). Il suffit donc de placer les octets de donne les uns la suite des autres, ils seront crits automatiquement dans les adresses successives de leeprom. Donc, nous aurons des trames du style :

Supposons que ladresse dfinie soit 0x1180 : Loctet 0 sera crit : 0x1180 + 0x00 = 0x1180 Loctet 1 sera crit : 0x1180 + 0x01 = 0x1181 .. .. L octet 31 sera crit : 0x1180 + 0x1F = 0x119F Attention cependant au pige. Si vous dfinissez une adresse de base de 0x115E, par exemple : 0x115E = B0001 0001 0101 1110 Loctet 0 sera crit ladresse 0x115E Loctet 1 sera crit ladresse 0x115F A quelle adresse sera crit loctet 2 ? Si vous me rpondez : 0x1160 , vous tes tombs dans le pige. Voyons en binaire : Loctet 2 sera crit ladresse : B0001 0001 0101 1110

380

+ B0000 0000 0000 0010 ----------------------------------= B0001 0001 0100 0000 En effet, je vous ai dit que lincrmentation ne concernait que les 5 bits de poids faible (b4 b0), le bit b5 nest donc pas affect par lincrmentation. Ceci vous prcise que loctet 2 sera crit ladresse 0x1140. Donc, lincrmentation ne seffectue correctement que si lensemble des adresses des octets crits se trouvent dans la mme page de 32. Le cas optimal est obtenu si ladresse passe comme second et troisime octet de la trame est une adresse dont les 5 bits de poids faible sont gaux 0. Dans ce cas, on se trouve en dbut de page, et on peut donc crire 32 octets simultanment. Comme lcriture de ces 32 octets ne ncessite pas plus de temps que lcriture dun seul, le temps ncessaire pour remplir toute notre eeprom sera divise par un facteur de 32, soit moins de 2 secondes. Partant de la, il vous est trs simple de dcouvrir quelle mthode utilise votre programmateur deeprom. Je rsume les 2 mthodes dcriture : Soit vous envoyez ladresse dcriture suivie par loctet crire Soit vous envoyez ladresse de dpart suivie par un maximum de 32 octets, qui seront cris squentiellement dans la mmoire.

Tout ce qui prcde est valable pour la 24C64. Sur une 24C512, par exemple, vous pourrez envoyer 128 octets de data, le bit 7 du pointeur dadresse interne ntant pas affect par lcriture. Renseignez-vous selon le type deeprom que vous utiliserez. 22.6.6 La lecture alatoire Alatoire (random) est prendre ici dans le sens de nimporte o , et non en comme signifiant au hasard . Cette dernire dfinition naurait dailleurs aucun intrt. Pour lire un octet une adresse spcifique, il faut raliser les oprations suivantes : Initialiser le pointeur dadresse de leeprom Lire loctet concern Envoyer un NOACK pour signifier la fin de la transaction.

Donc, vous voyez que ceci ncessite une opration dcriture (le pointeur dadresse), suivie par une opration de lecture (loctet lire).

381

Comme le bit R/W dfinissant le sens de transfert nexiste que dans le premier octet suivant le start-condition, vous voyez dj quil vous faudra envoyer 2 fois ce premier octet, donc 2 fois le start-condition. Vous pouvez procder en une seule tape, ou en 2 tapes successives. Si vous utilisez 2 tapes, vous aurez : Le matre envoie le start-condition (S) Le matre envoie ladresse de leeprom avec le bit R/W 0 (criture) Leeprom envoie laccus de rception (ACK) Le matre envoie loctet fort de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie loctet faible de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie le stop-condition (P) Le matre envoie le start-condition (S) Le matre envoie ladresse de leeprom avec le bit R/W 1 (lecture) Leeprom envoie laccus de rception (ACK) Leeprom envoie loctet lire Le matre envoie un NOACK Le matre envoie le stop-condition (P) Autrement dit :

Si vous utilisez la mthode conseille, cest--dire en une seule tape, vous remplacez simplement le stop-condition suivi du start-condition par un repeated start-condition (SR), ce qui nous donne : Le matre envoie le start-condition (S) Le matre envoie ladresse de leeprom avec le bit R/W 0 (criture) Leeprom envoie laccus de rception (ACK) Le matre envoie loctet fort de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie loctet faible de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie le repeated start-condition (SR) Le matre envoie ladresse de leeprom avec le bit R/W 1 (lecture) Leeprom envoie laccus de rception (ACK) Leeprom envoie loctet lire Le matre envoie un NOACK Le matre envoie le stop-condition (P)
382

Ou, sous forme de chronogramme simplifi :

Donc, pour rsumer, vous crivez ladresse, puis vous lisez la donne. Le passage entre lecture et criture ncessite de devoir renvoyer le start-condition (SR). 22.6.7 La lecture de ladresse courante Vous avez vu que si nous dcidons deffectuer une lecture alatoire en 2 tapes, nous commenons par envoyer ladresse de lecture, puis nous procdons la lecture en elle-mme. En fait, si vous rflchissez, ceci quivaut dire : Jinitialise le pointeur dadresse de leeprom Je lis ladresse courante.

En effet, le pointeur dadresse reste mmoris dans leeprom. A tout moment, vous disposez donc de la possibilit de lire ladresse qui est actuellement pointe par le pointeur dadresse. REMARQUE : La lecture dun octet provoque automatiquement lincrmentation du pointeur dadresse. Donc, la prochaine lecture renverra loctet suivant, et ainsi de suite. Si le pointeur dpasse la capacit de la mmoire, il recommence 0. 22.6.8 La lecture squentielle Et bien, de nouveau, cest tout simple. Cette procdure permet de lire plusieurs octets conscutifs. Ceci fonctionne exactement comme pour lcriture par page, except que nous retrouvons pas ici les contraintes de page. Nous pouvons donc lire lintgralit de la mmoire eeprom en une seule opration, nous ne sommes pas limits 32 octets. Nous procdons comme pour la lecture alatoire, mais au lieu que le matre envoie un NOACK aprs avoir lu loctet, il envoie un ACK , qui signale leeprom quil souhaite lire loctet suivant. Ce nest quau dernier octet que le matre enverra un NOACK .

383

La procdure est la suivante : Le matre envoie le start-condition (S) Le matre envoie ladresse de leeprom avec le bit R/W 0 (criture) Leeprom envoie laccus de rception (ACK) Le matre envoie loctet fort de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie loctet faible de ladresse lire Leeprom envoie laccus de rception (ACK) Le matre envoie le repeated start-condition (SR) Le matre envoie ladresse de leeprom avec R/W 1 (lecture) Leeprom envoie laccus de rception (ACK) Leeprom envoie loctet lire Le matre envoie un ACK Leeprom envoie loctet suivant lire Le matre envoie un ACK Leeprom envoie loctet suivant lire .. .. Leeprom envoie loctet suivant lire Le matre envoie un NOACK Le matre envoie le stop-condition (P) Ce qui nous donne :

22.6.9 Le cas de la 24C16 Bon, juste un mot sur leeprom 24C16, dont je vous ai dit quelle ntait pas compatible. Jen parle pour mviter du courrier, car jai comme une intuition ce sujet. Sur la 24C16, les pins A0, A1, et A2 existent, mais ne sont pas connectes lintrieur de leeprom. Elles ne peuvent donc pas servir pour slectionner ladresse. Premire dduction : on ne peut mettre quune et une seule eeprom 24C16 par bus. Nos bits A2,A1, et A0 du premier octet sont donc libres. Ils sont en fait utiliss comme bits de poids fort de ladresse de loctet concern. Leeprom 2416 contient 16Kbits de mmoire, donc 2Koctets. Ladresse dun octet se code de ce fait sur 11 bits. Donc, si vous me suivez toujours :

384

Sur une 24C64, on a : - start-condition - premier octet = 1010 A2 A1 A0, avec A2/A0 adresse physique de leeprom - second octet = MSB adresse = x x x a12 a11 a10 a9 a8 - troisime octet = LSB adresse = a7 a6 a5 a4 a3 a2 a1 a0 Sur une 24C16, on aura - start-condition - premier octet = 1010 a10 a9 a8, avec a10/a8 = 3 bits de poids fort de ladresse de loctet - second octet = a7 a6 a5 a4 a3 a2 a1 a0 Autrement dit, leeprom 24C16 rpond en fait 8 adresses diffrentes pour un seul circuit. Cest une astuce qui permet de gagner un octet en mission, mais se paye par loccupation de 8 adresses pour un seul circuit. Cest pour a que vous lisez souvent que la 24C16 nest pas compatible avec les autres eeprom (24c32, 24c64). Vous pouvez donc remplacer sans problme dans un montage une 24C32 par une 24C64, mais vous ne pouvez remplacer une 24c16 par une autre 24cxx que si vous modifiez le logiciel du matre en consquence. 22.6.10 Conclusions Nous venons dtudier tout le datasheet de leeprom 24C64. Vu le nombre important de questions que jai reues ce sujet, il ma paru ncessaire deffectuer cette analyse. Ceci vous ouvrira de plus les portes des nombreux autres priphriques IC. Ceci clture notre tude thorique du bus IC. Nous allons maintenant tudier comment ces options sont disponibles sur nos 16F87x.

385

Notes :

386

23. Le module MSSP en mode IC


23.1 Introduction Nous avons vu que le bus IC permettait plusieurs configurations diffrentes, ncessitait la gestion de larbitrage du bus, des diffrents vnements etc. Notre 16F87x est capable de grer tout ceci de faon automatique. Nous allons donc trouver : La gestion des modes esclave, matre, et multi-matre La gnration et la dtection de start et stop-condition La gnration et la reconnaissance des signaux ACK La gnration automatique des NOACK en cas de problme logiciel Les modes dadressage sur 7 et 10 bits Lutilisation de toutes les vitesses de transfert compatibles

De plus, les pins SDA et SCL, lorsque le module est utilis en mode IC, adaptent les flancs des signaux en suivant les spcifications IC. Ceci est rendu possible par lintgration de filtres spcifiques sur les pins concernes. Je ne parlerai pas du fonctionnement de ces filtres, car cela sort du cadre de cet ouvrage et ne vous apporterait rien de plus. Limportant tant que les PICs sont compatibles IC. Pour rester simple, disons quen sortie, ceci permet de respecter les chronologies, et quen entre cela rduit les risques de prise en compte des parasites. En effet, comme le signal SDA, en mission, doit rester prsent un certain temps aprs le retour 0 de la ligne SCL, ce temps tant dpendant de la vitesse du bus IC, il faudra prciser ce paramtre. Les 16F87x sont prvus pour travailler avec des frquences dhorloge SCL de 100 KHz, 400 KHz, et 1MHz. Vous configurerez les filtres en fonction des frquences choisies. Dans le mme esprit, les pins disposent dentres de type trigger de Schmitt , dont nous avons dj parl plusieurs reprises, et qui permettent une dtection plus scurise des signaux parasits. Les pins SDA et SCL sont naturellement multiplexes avec dautres pins, en loccurrence RC4 et RC3. Nous utiliserons dans ce chapitre les registres tudis dans le mode SPI, mais le mode IC ncessite lutilisation de registres supplmentaires. En tout, nous aurons besoin de 6 registres. Sachant que le registre SSPSR contient la donne en cours de transfert et que SSPBUF contient la donne reue ou mettre (voir mode SPI), il nous reste 4 registres examiner.

387

23.2 Le registre SSPSTAT Tout comme pour ltude du mode SPI, je dcris les fonctions des bits des registres tudis uniquement pour le mode IC. Certains bits ont une autre signification suivant le mode, en cas de doute, consultez le datasheet, ou ltude du mode SPI dans cet ouvrage. Il faut noter que les noms de plusieurs bits ont t choisis en fonction de leur rle dans le mode SPI. Comme le mode IC peut utiliser ces bits dans un but compltement diffrent, leur nom na parfois plus aucun rapport avec leur fonction. Dans ce cas, je ne rappellerai pas ce nom, jutiliserai un nom plus appropri. Un exemple est donn par le bit SMP (SaMple bit). Ce bit permettait de choisir linstant dchantillonnage (sample) dans le mode SPI. Dans le mode IC, il sert mettre en service le filtre adquat. Son nom dans ce mode na donc plus aucun rapport avec sa fonction. Le registre SSPSTAT en mode IC b7 : SMP : Slew rate control set bit (1 pour 100 KHz et 1MHz, 0 pour 400 KHz) b6 : CKE : Input levels set bit (0 = bus IC, 1 = bus SMBUS) b5 : D_A : Data / Address bit (0 = adresse, 1 = data) b4 : P : stoP bit b3 : S : Start bit b2 : R_W : Read/Write bit information (0 = write, 1 = read) b1 : UA : Update adress b0 : BF : Buffer Full / data transmit in progress Vous constatez que, dans ce mode, tous les bits sont maintenant utiliss. Nous avons beaucoup plus dindicateurs que dans le mode SPI. Voyons tout ceci en dtail. Le bit SMP prend ici une nouvelle signification. Il dtermine si le filtre doit tre ou non mis en service. Il vous suffit de savoir que si vous travaillez avec une frquence de 100 KHz ou de 1 MHz, vous devez mettre ce bit 1 (hors-service). Par contre, pour une vitesse de 400 KHz, vous le laisserez 0 (en service). Le bit CKE prend galement une signification diffrente. Si vous mettez ce bit 1 , vous travaillerez avec des niveaux dentre compatibles avec le bus SMBUS. Par contre, en le laissant 0 , vous travaillerez avec des signaux compatibles IC (ce qui est notre objectif ici). D_A vous indique si le dernier octet reu ou transmis tait un octet de donne (1) ou un octet dadresse (0). Ceci vous permet de savoir o vous en tes dans le transfert dinformations. Le bit P est galement un indicateur. Il vous informe si le dernier vnement dtect tait un stop-condition (P = 1). Il sefface automatiquement ds quun autre vnement est reu. Il sefface galement lors dun reset, ou si vous mettez le module MSSP hors-service. Le bit S est un indicateur qui procde exactement de la mme faon, mais pour un startcondition. R_W a 2 fonctions distinctes selon quon travaille en IC matre ou en IC esclave :

388

En IC esclave, il vous indique ltat du bit R/W (bit 0 du premier octet qui suit le startcondition). Si R/W vaut 0 , une criture est donc en cours, sil vaut 1 , il sagit dune lecture. Ce bit est valide entre la dtection de la correspondance dadresse (Si cest votre PIC qui est slectionne par le matre) et la fin de la trame en cours, donc jusquau prochain stop-sequence, repeated start-sequence, ou bit NOACK. En IC matre, il vous informe si un transfert est en cours (1) ou non (0).

Le bit UA est un indicateur utilis uniquement en esclave sur adresses 10 bits. Il vous prvient quand vous devez mettre votre adresse jour en mode IC. En effet, vous ne disposez que dun seul registre de 8 bits pour inscrire votre adresse esclave. Comme le mode 10 bits ncessite 2 octets, le positionnement automatique de ce bit UA vous signale que le premier octet dadresse a t trait, et quil est temps pour vous de placer votre second octet dadresse dans le registre concern (et inversment). Nous verrons en temps utile comment cela se passe. Le bit UA force la ligne SCL 0 (pause), et est effac automatiquement par une criture dans SSPADD. Reste maintenant le bit BF, qui indique si le registre SSPBUF est plein (1) ou vide (0). Evidemment, positionn, en rception, il signifie que loctet a t reu, alors quen mission il signale que la transmission de loctet nest pas encore termine. 23.3 Le registre SSPCON De nouveau, nous allons voir que tous les bits de ce registre sont utiliss. SSPCON en mode IC b7 : WCOL : Write COLlision detect bit b6 : SSPOV : SSP OVerflow indicator bit b5 : SSPEN : SSP ENable select bit b4 : CKP : Pause (si 0) b3 : SSPM3 : SSP select bit M3 b2 : SSPM2 : SSP select bit M2 b1 : SSPM1 : SSP select bit M1 b0 : SSPM0 : SSP select bit M0 Voyons ces bits en dtail WCOL est, comme son nom lindique, un indicateur qui signale que nous rencontrons une collision dcriture (WCOL = 1). Il y a 2 cas possibles : En mode master, cette collision arrive si vous tentez dcrire dans le registre SSPBUF alors que ce nest pas le bon moment (bus pas libre, start-condition pas encore envoye). Cette collision a pour consquence que la donne nest pas crite dans le registre SSPBUF (bloqu en criture).

389

En mode slave, ce bit sera positionn si vous tentez dcrire dans le registre SSPBUF alors que le mot prcdent est toujours en cours de transmission. Vous devrez remettre ce flag 0 par logiciel

Lindicateur SSPOV vous informe dune erreur de type overflow . Cest--dire que vous venez de recevoir un octet, alors que vous navez pas encore lu le registre SSPBUF qui contient toujours loctet prcdemment reu. Le registre SSPBUF ne sera pas cras par la nouvelle donne, qui sera donc perdue. Vous devez remettre ce flag 0 par logiciel. Ce bit na donc de signification quen rception. SSPEN permet de mettre tout simplement le module en service. Les pins SCL et SDA passent sous le contrle du module MSSP. Les pins SCL et SDA devront cependant tre configures en entre via TRISC. CKP joue dans ce mode IC un rle particulier. Il permet de mettre lhorloge SCL en service (1), ou de la bloquer ltat bas, afin de gnrer une pause (0). De fait, CKP ne sera utilis que dans le cas de lIC esclave, qui est le seul mode o la pause peut savrer ncessaire. SSPMx sont les bits qui dterminent de quelle faon va fonctionner le module IC. Je vous donne donc seulement les modes relatifs lIC b3 b2 b1 b0 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 Fonctionnement mode IC esclave avec adresse sur 7 bits mode IC esclave avec adresse sur 10 bits mode IC matre : frquence dtermine par le registre SSPADD rserv rserv Mode esclave forc ltat de repos rserv rserv Mode matre avec adresses 7 bits, interruptions sur vnements S et P Mode matre avec adresses 10 bits, interruptions sur vnements S et P

Nous ne parlerons que des 3 premiers modes, les autres ntant pas ncessaires. De plus, je ne sais pas ce que signifie mode matre avec adresse , tant donn quun matre na pas dadresse, et que rien nempche le mme matre dadresser des esclaves sur 7 et 10 bits dadresse. Jai pos la question Microchip, leur rponse a t que les modes IC firmware controlled permettent de prendre la gestion IC en charge par le logiciel. Avouez que, comme prcision, a nous avance beaucoup. Ma question tait pourtant prcise. A mon avis, le technicien nen savait pas plus que moi ce sujet. 23.4 Le registre SSPADD Puisque je viens den parler, autant continuer par celui-ci. Son nom signifie Synchronous Serial Port ADDress register. Il a deux fonctions compltement distinctes, selon que lon travaille en IC matre ou en IC esclave.

390

Commenons par le mode esclave. Cest dans ce registre que vous placerez ladresse laquelle devra rpondre votre PIC. Si vous dcidez de travailler en mode 10 bits, vous placerez les bits de poids fort dans SSPADD. Vous serez alors prvenus par le bit UA du registre SSPSTAT quil est temps dy placer les bits de poids faible afin de complter ladresse, et inversment. En mode matre, ce registre permet de dterminer la frquence de travail du signal dhorloge. Tous les signaux sont synchroniss sur base dune dure unitaire qui dpend de la valeur place dans ce registre. Le pic utilise un registre spcial, quivalant un timer, et quon dnomme BRG pour Baud Rate Generator. Ce dernier se charge avec la valeur contenue dans SSPADD (en fait les seuls 7 bits de poids faible), et dcrmente 2 fois par cycle dinstruction. Une fois arriv 0, il se recharge si ncessaire pour produire un nouvel vnement. Par exemple, pour la lecture dun octet, on aura un temps TBRG durant lequel la ligne SDA est positionne, SCL tant 0, ensuite la ligne SCL est mise 1 durant un temps TBRG. A la fin de ce temps, la ligne SCL redescend et le nouveau bit SDA est positionn. Ce qui nous donne une lecture de bit de la forme :

Remarquez que le positionnement de SDA intervient peu aprs la descente de SCL. Ceci est impratif pour la norme IC, qui impose que SDA ne peut varier tant que SCL se trouve ltat haut. Ce retard est provoqu par les filtres de sortie (flew rate control), ce qui explique leur importance. Nous constatons que la dure dun cycle dhorloge SCL est de 2 fois TBRG. Comme BRG dcrmente 2 fois par cycle dinstructions (donc tous les 2 Tosc), et quil met 2 Tosc supplmentaires pour se recharger, on peut dire que le temps de dure dun bit est de : Dure dun bit = Tosc * 2 * 2 * (SSPADD + 1) La frquence de lhorloge est donc dtermine par la formule :

391

FSCL = Fosc / (4 * (SSPADD + 1)) En gnral, vous connaissez la frquence (100KHz, 400KHz, 1MHz), et vous cherchez dterminer SSPADD. Do : SSPADD = (Fosc / (FSCL * 4)) 1 Avec, bien entendu, Fosc la frquence de votre oscillateur principal. Pour rappel, seuls 7 bits tant transfrs vers BRG, SSPADD ne poura tre suprieur 127. Examinons maintenant quoi sert SSPADD en mode esclave. En ralit, il prend ici le rle que lui confre son nom, autrement dit il contient ladresse laquelle va rpondre le PIC aux requtes du matre. Dans le cas des adresses codes sur 7 bits, il contiendra les 7 bits en question. Mais attention, ladresse en question sera crite dans les bits 7 1 de SSPADD. Le bit 0 devra TOUJOURS tre laiss 0. En fait, tout se passe comme si le PIC comparait le premier octet reu avec loctet mmoris dans SSPADD. Si on a identit, il sagit dune criture dans le PIC. Le pic compare ensuite loctet reu avec SSPADD+1, si on a identit, il sagit dune lecture du PIC. Autrement dit, si ladresse que vous avez choisie pour votre PIC est 0x03, vous devez placer cette adresse dans les bits 7 1, autrement dit dcaler ladresse vers la gauche, et positionner b0 0. Vous inscrirez donc dans SSPADD la valeur 0x06. Adresse : SSPADD : 0000011 00000110 : 0x03 (sur 7 bits) : 0x06

Si ladresse est code sur 10 bits, vous commencez par crire le premier octet dans SSPADD (prcd bien entendu par B11110). Vous attendez le positionnement du bit UA, puis vous placez dans SSPADD votre second octet. En dautres termes : Vous placez : 1 1 1 1 0 A9 A8 0 dans SSPADD Vous attendez le bit UA Vous placez : A7 A6 A5 A4 A3 A2 A1 A0 dans SSPADD

Une fois la transaction termine, le bit UA est de nouveau positionn, vous replacez B1 1 1 1 0 A9 A8 0 dans SSPADD pour tre prt pour le transfert suivant. Tant que le bit UA reste positionn, le bus IC est maintenu en pause automatiquement par votre PIC esclave. Quand vous mettez SSPADD jour, le bit UA est automatiquement effac, et le bus est libr.

392

23.5 Le registre SSPCON2 Voici un registre que nous navons pas encore examin. Voyons donc ce quil contient, le bit 7 ne concerne que le mode esclave, tandis que les autres bits ne concernent que le mode matre : SSPCON2 b7 : GCEN b6 : ACKSTAT b5 : ACKDT b4 : ACKEN b3 : RCEN b2 : PEN b1 : RSEN b0 : SEN : General Call ENable bit (rponse appel gnral) : ACKnowledge STATus bit (tat du bit ACK reu) : ACKnowledge DaTa bit (valeur transmise) : ACKnowledge ENable bit (placer lacknowledge) : ReCeive ENable bit (lance la rception) : stoP-condition ENable bit (gnrer stop-condition) : Repeated Start-condition ENable bit (gnrer SR) : Start-condition ENable bit (gnrer S)

Examinons maintenant ces bits en dtail : GCEN est le seul bit qui concerne le mode esclave. Si vous validez ce bit, le PIC rpondra non seulement ladresse que vous avez place dans SSPADD, mais galement ladresse rserve dappel gnral (0x00). Les octets qui suivront dans ce cas auront des fonctions particulires et rserves (reset par exemple). Si vous dcidez que votre PIC doit rpondre ladresse dappel gnral, il vous appartiendra de grer ces commandes. ACKSTAT vous donne ltat du bit dacknowledge envoy par lesclave lorsque vous crivez des donnes. Si ACKSTAT vaut 0 , cest que lesclave vous a envoy un ACK , sil vaut 1 , cest quil ny a pas eu dacknowledge (NOACK). ACKDT est la valeur de lacknowledge envoyer lesclave lorsque vous procdez une lecture. Ce bit sera envoy lorsque vous positionnerez le bit ACKEN. De nouveau, une valeur 0 signifie que vous envoyez un ACK , une valeur 1 sera place pour un NOACK. ACKEN lance la gnration de lacknowledge. La valeur de ce bit (ACK ou NOACK) est dtermine par la valeur que vous avez place dans ACKDT. Ces 2 bits sont donc lis. RCEN : Lance la rception dun octet en provenance de lesclave. Pour lancer une criture, on place la valeur crire dans SSPBUF, pour lancer une lecture, on met RCEN 1 . PEN : Lance la gnration automatique du stop-condition RSEN : Lance la gnration du repeated start-condition SEN : Lance la gnration du start-condition. Attention de ne pas confondre, au niveau des noms, les bits CREN, RCEN, SREN. Une erreur ce niveau est trs difficile dtecter par relecture du code.

393

23.6 Les collisions Il nous reste voir ce qui se passe en cas de collision sur le bus, si le PIC configur en matre se voit ravir le bus IC durant une opration (mode multi-matre). La rponse est simple : le bit BCIF (Bus Collision Interrupt Flag) du registre PIR2 est positionn, lopration courante est abandonne, et une interruption est ventuellement gnre si elle est configure. 23.7 Le module MSSP en mode IC matre Nous voici maintenant dans lutilisation concrte de notre PIC configure en IC matre. Je ne traite pour linstant que le cas o votre PIC est le seul matre du bus (mode monomatre), cest le cas que vous rencontrerez probablement le plus souvent. Je vais partir dun cas thorique, dans lequel on ralise les oprations suivantes : On crit un octet dans lesclave Puis, sans perdre le contrle du bus, on lit un octet de rponse en provenance de lesclave Ceci se traduira par la squence dvnements suivante : On configure le module MSSP On envoie le start-condition On envoie ladresse de lesclave concern avec b0 = 0 (criture) On envoie la donne crire On envoie le repeated start-condition (SR) On envoie ladresse de lesclave concern avec b0 = 1 (lecture) On lance la lecture On envoie un NOACK pour indiquer la fin de la rception On envoie un stop-condition

Je vais maintenant vous expliquer comment tout ceci sorganise en pratique dans notre PIC. 23.7.1 La configuration du module Avant de pouvoir utiliser notre module, il importe de le configurer. Nous allons imaginer que nous travaillons avec une frquence dhorloge principale de 20MHz (le quartz de notre PIC), et avec une frquence dhorloge IC de 400 KHz. Les tapes seront les suivantes : 1) On configure TRISC pour avoir les pins SCL (RC3) et SDA (RC4) en entre 2) On configure SSPSTAT comme ceci :

394

On positionne SMP (slew rate control) suivant la frquence du bus IC. Dans le cas de 400KHz, ce bit sera mis 0, ce qui met le slew rate en service. On place CKE 0 pour la compatibilit IC

3) On calcule la valeur de recharge du Baud Rate Generator, et on place la valeur obtenue dans SSPADD SSPADD = (Fosc / (FSCL * 4)) 1 Dans notre cas : SSPADD = (20 MHz / (400KHz * 4)) 1 SSPADD = (20.000 / (400 * 4))-1 SSPADD = 11,5. On prendra la valeur 12 (dcimal, faites attention) Pourquoi 12 et pas 11 ? Tout simplement parce que plus SSPADD est petit, plus la frquence est grande. Il vaut donc mieux arrondir en utilisant une frquence infrieure la limite acceptable que suprieure. 4) On configure SSPCON, comme suit : On positionne le bit SSPEN pour mettre le module en service On choisit SSMPx = 1000 pour le mode IC matre Tout ceci nous donne un code du style :
Init BANKSEL TRISC bsf TRISC,3 bsf TRISC,4 movlw B00000000 movwf SSPSTAT movlw D12 movwf SSPADD bcf STATUS,RP0 movlw B00101000 movwf SSPCON ; ; ; ; ; ; ; ; ; ; slectionner banque 1 SCL en entre (mis par dfaut sur un reset) SDA en entre (idem) slew rate control en service, mode IC dans SSPSTAT valeur de recharge du BRG dans registre de recharge passer banque 0 module MSSP en service en mode IC master dans registre de contrle

23.7.2 La vrification de la fin des oprations A ce stade, il faut que vous sachiez quil nest pas autoris, ni dailleurs possible, de gnrer une action si la prcdente nest pas termine. Par exemple, vous ne pouvez pas lancer un start-condition si le prcdent stop-condition nest pas termin, de mme vous ne pouvez pas envoyer un ACK si la rception de loctet nest pas acheve, et ainsi de suite. Vous avez donc 3 solutions pour raliser une squence doprations sur le port IC.

395

Soit vous lancez votre commande et vous attendez quelle soit termine avant de sortir de la sous-routine Ceci se traduit par la squence suivante :

Commande Lancer la commande Commande termine ? Non, attendre la fin de la commande Oui, fin

Soit vous sortez de votre sous-routine sans attendre, mais vous devrez tester si lopration prcdente est termine avant le pouvoir la lancer (puisque vous tes galement dans ce cas sorti de la commande prcdente sans attendre). Ceci se traduit par la squence suivante :

Commande Y a-t-il toujours une commande en cours ? oui, attendre quelle soit termine non, alors lancer la nouvelle commande et fin

Soit vous utilisez les interruptions, mais je verrai ce cas sparment, car la gestion des interruptions dans ce mode nest pas vraiment simple

Dans le premier cas, on connat lvnement dont on dtecte la fin (la commande quon excute), dans le second, on doit tester toutes les possibilits, afin de conserver une sousroutine unique. Il faut de plus savoir que lorsque vous lancez une commande (par exemple un acknowledge), le bit dexcution est effac automatiquement une fois laction termine. Donc, pour lacknowledge, vous placez ACKEN pour lancer la commande, ACKEN est automatiquement effac lorsque laction est termine. Je vous prsente tout dabord la faon de dtecter si une commande quelconque est en cours dexcution. Il faut dterminer tous les cas possibles, les commandes peuvent tre : Transmission en cours (signale par le bit R/W du registre SSPSTAT) Start-condition en cours (signal par le bit SEN du registre SSPCON2) Repeated start-condition en cours (signal par le bit RSEN de SSPCON2) Stop-condition en cours (signal par le bit PEN de SSPCON2) Rception en cours (signal par le bit RCEN de SSPCON2) Acknowledge en cours (signal par le bit ACKEN de SSPCON2)

Voici donc un exemple de sous-routine qui attend que lopration prcdente soit termine :
I2C_idle BANKSEL I2C_idle2 SSPSTAT ; passer en banque 1

396

clrwdt btfsc SSPSTAT,R_W goto I2C_idle2 i2C_idle3 clrwdt movf SSPCON2,w andlw 0x1F btfss STATUS,Z goto I2C_idle3 bcf STATUS,RP0 return

; effacer watchdog ; tester si mission en cours ; oui, attendre ; ; ; ; ; ; ; effacer watchdog charger registre SSPCON2 conserver ACKEN,RCEN,PEN,RSEN,et SEN tester si tous 0 (aucune opration en cours) non, attendre repasser en banque 0 rien en cours, on peut sortir

Bien videmment, si vous navez pas activ le watchdog, vous pouvez vous passer des instructions clrwdt . 23.7.3 La gnration du start-condition Commenons donc par le commencement, savoir la gnration du start-condition. Les tapes ncessaires sont les suivantes : On vrifie si lopration prcdente est termine On lance le start-condition Voici le code correspondant :
I2C_start call I2C_idle BANKSEL SSPCON2 bsf SSPCON2,SEN bcf STATUS,RP0 return ; ; ; ; ; attendre fin de lopration prcdente(voir plus haut) passer en banque 1 lancer start-condition repasser en banque 0 retour

Et son alternative, comme expliqu prcdemment : On lance le start-condition On attend que le start-condition soit termin
; passer en banque 1 ; lancer le start-condition ; ; ; ; ; effacer watchdog start-condition termin ? non, attendre oui, repasser en banque 0 et retour

I2C_start BANKSEL SSPCON2 bsf SSPCON2,SEN I2C_start2 clrwdt btfsc SSPCON2,SEN goto I2C_start2 bcf STATUS,RP0 return

23.7.4 Lenvoi de ladresse de lesclave Nous allons maintenant devoir envoyer ladresse de lesclave. Nous allons construire notre sous-routine en imaginant que ladresse de lesclave est SLAVE . Cette adresse est code sur 7 bits.

397

Nous allons donc raliser les oprations suivantes ; On vrifie si lopration prcdente est termine On place ladresse de lesclave dcale vers la gauche et complte par le bit 0 (R/W) 0 (criture) dans SSPBUF, ce qui lance lmission
; ; ; ; attendre fin de lopration prcdente charger adresse esclave (b7 b1) avec b0 = 0 lancer lmission de ladresse en mode criture et retour

I2C_adress call I2C_idle movlw SLAVE*2 movwf SSPBUF return

et son alternative : On place ladresse de lesclave comme prcdemment On attend la fin de lmission
; charger adresse esclave (b7 b1) avec b0 = 0 ; lancer lmission de ladresse en mode criture ; passer en banque 1 ; ; ; ; ; effacer watchdog tester si mission termine non, attendre oui, repasser banque 0 et retour

I2C_adress movlw SLAVE*2 movwf SSPBUF BANKSEL SSPSTAT I2C_adress2 clrwdt btfsc SSPSTAT,R_W goto I2C_adress2 bcf STATUS,RP0 return

23.7.5 Le test de lACK Nous aurons souvent besoin, aprs avoir envoy ladresse de lesclave, de savoir si ce dernier est prt, et donc a bien envoy laccus de rception ACK . Avant de pouvoir tester ce bit, il faut que lmission soit termine, ce qui est automatiquement le cas si vous avez utilis la seconde mthode, mais nest pas vrai si vous avez prfr la premire. Voici donc le cas correspondant la premire mthode : On vrifie si lopration prcdente est termine On teste le bit ACK et on dcide en consquence
; ; ; ; ; attendre fin de lopration prcdente passer en banque 1 tester ACK reu pas reu, traiter erreur poursuivre ici si OK

I2C_check call I2C_idle BANKSEL SSPCON2 btfsc SSPCON2,ACKSTAT goto error .. .. .. .. .. .. error .. .. .. .. .. ..

; continuer ici si lesclave nest pas prt.

Pour la seconde mthode, cest strictement identique, si ce nest que lappel de la sousroutine I2C_idle nest pas ncessaire (lopration prcdente est forcment termine, puisquon attend quelle soit finie avant de sortir de la sous-routine).
398

Cette procdure ne lance aucune commande, elle ne ncessite donc pas dattendre avant le return . 23.7.6 Lcriture dun octet Nous en sommes arriv lcriture de loctet dans lesclave. Cette procdure est strictement identique lenvoi de ladresse de lesclave. Nous allons donc raliser les oprations suivantes ; On vrifie si lopration prcdente est termine On place loctet envoyer dans SSPBUF, ce qui lance lmission
; ; ; ; attendre fin de lopration prcdente charger octet envoyer lancer lcriture de loctet et retour

I2C_send call I2C_idle movlw OCTET movwf SSPBUF return

et son alternative : On place loctet envoyer dans SSPBUF On attend la fin de lmission
; charger octet envoyer ; lancer lcriture ; passer en banque 1 ; ; ; ; ; effacer watchdog tester si mission termine non, attendre oui, repasser banque 0 et retour

I2C_send movlw OCTET movwf SSPBUF BANKSEL SSPSTAT I2C_send2 clrwdt btfsc SSPSTAT,R_W goto I2C_send2 bcf STATUS,RP0 return

23.7.7 L envoi du repeated start-condition Nous allons devoir procder une lecture. Comme nous tions en mode criture, nous devons renvoyer un nouveau start-condition. Comme nous ne dsirons pas perdre le bus, nous nallons pas terminer lopration prcdente par un stop-condition, nous enverrons donc directement un second start-condition, autrement dit un repeated start-condition. On vrifie si lopration prcdente est termine On lance le repeated start-condition Voici le code correspondant :
I2C_start call I2C_idle BANKSEL SSPCON2 bsf SSPCON2,RSEN bcf STATUS,RP0 ; ; ; ; attendre fin de lopration prcdente passer en banque 1 lancer le repeated start-condition repasser en banque 0

399

return

; retour

Et son alternative, toujours sur le mme principe : On lance le repeated start-condition On attend que le repeated start-condition soit termin
; passer en banque 1 ; lancer le repeated start-condition ; ; ; ; ; effacer watchdog start-condition termin ? non, attendre oui, repasser en banque 0 et retour

I2C_start BANKSEL SSPCON2 bsf SSPCON2,RSEN I2C_start2 clrwdt btfsc SSPCON2,RSEN goto I2C_start2 bcf STATUS,RP0 return

Maintenant, vous allez me dire mais quest-ce qui diffrencie un repeated start-condition dun start-condition ordinaire ? En fait, vous devez vous souvenir quun start-condition part de la ligne au repos (SCL = SDA = 1) et place SDA 0 alors que SCL reste son tat de repos (1). Le stop-condition, lui, remet les lignes dans leur tat de repos en suivant la logique inverse (remonte de SDA alors que SCL est dj remont 1). Vous avez besoin du repeated start-condition lorsque les lignes ne sont pas dans leur tat de repos, puisque SCL est 0, et SDA peut ltre galement (ACK). Donc, avant de pouvoir rgnrer un nouveau start-condition, il faut remettre ces lignes dans leur tat de repos. Cest ce que fait le repeated start-condition. La squence gnre par ce dernier est donc la suivante : Ds le positionnement de SREN, la ligne SDA est place ltat haut Aprs un temps gal TBRG, la ligne SCL est amene galement ltat haut Aprs une nouvelle dure gale TBRG, on repasse SDA 0, ce qui quivaut alors un start-condition TBRG plus tard, le bit SREN est effac, et le bit SSPIF positionn. Ds lors, il vous suffira de placer votre adresse dans SSPBUF. Vous voyez que les 2 premires lignes sont supplmentaires par rapport un startcondition simple. Elles permettent de ramener les 2 lignes ltat haut sans gnrer de stop-condition . Elles ncessitent 2 temps TBRG supplmentaires. Les 2 dernires lignes correspondent videmment au cycle normal dun startcondition . Et hop, un petit chronogramme (mais si, je sais que vous aimez a) :

400

Si vous avez tout compris, vous constatez que rien ne vous empche dutiliser un SR au lieu dun S , mais vous ne pouvez pas utiliser un S en lieu et place dun SR . Dans ce dernier cas, les lignes ntant pas au repos, le start-condition ne serait tout simplement pas gnr. 23.7.8 Lenvoi de ladresse de lesclave Nous allons maintenant devoir envoyer encore une fois ladresse de lesclave. Nous devrons cette fois positionner le bit 0 1 pour indiquer une lecture. Nous allons donc raliser les oprations suivantes ; On vrifie si lopration prcdente est termine On place ladresse de lesclave dcale vers la gauche et complte par le bit 0 (R/W) 1 (lecture) dans SSPBUF, ce qui lance lmission

I2C_adress call I2C_idle ; attendre fin de lopration prcdente movlw (SLAVE*2) |1 ; charger adresse esclave (b7 b1) avec b0 = 1 ; on pouvait crire galement (SLAVE*2)+1 movwf SSPBUF ; lancer lmission de ladresse en mode criture return ; et retour

et son alternative : On place ladresse de lesclave comme prcdemment On attend la fin de lmission

I2C_adress movlw (SLAVE*2)|1 ; charger adresse esclave (b7 b1) avec b0 = 1 ; SLAVE*2 = adresse dcale, |1 = OR 1 401

movwf SSPBUF BANKSEL SSPSTAT I2C_adress2 clrwdt btfsc SSPSTAT,R_W goto I2C_adress2 bcf STATUS,RP0 return

; lancer lmission de ladresse en mode criture ; passer en banque 1 ; ; ; ; ; effacer watchdog tester si mission termine non, attendre oui, repasser banque 0 et retour

23.7.9 La lecture de loctet Nous devons maintenant lire loctet en provenance de lesclave. De nouveau 2 mthodes. Tout dabord : On vrifie si lopration prcdente est termine On lance la lecture
; ; ; ; ; attendre fin de lopration prcdente passer banque 1 lancer la rception repasser banque 0 et retour

I2C_read call I2C_idle BANKSEL SSPCON2 bsf SSPCON2,RCEN bcf STATUS,RP0 return et lalternative : -

On lance la lecture On attend la fin de lopration pour sortir ; passer banque 1 ; lancer la rception ; ; ; ; rception termine ? non, attendre repasser banque 0 et sortir

I2C_read BANKSEL SSPCON2 bsf SSPCON2,RCEN I2C_read2 btfsc SSPCON2,RCEN goto I2C_read2 bcf STATUS,RP0 return

23.7.10 Lenvoi du NOACK Pour clturer une lecture, le matre doit envoyer un NOACK, voici comment oprer : On vrifie si lopration prcdente est termine On lance le NOACK
; ; ; ; ; ; attendre fin de lopration prcdente passer banque 1 le bit qui sera envoy vaudra 1 lancer lacknowledge (= ACKDT = 1 = NOACK) repasser banque 0 et retour

I2C_NOACK call I2C_idle BANKSEL SSPCON2 bsf SSPCON2,ACKDT bsf SSPCON2,ACKEN bcf STATUS,RP0 return

et lalternative :

402

On envoie NOACK On attend que lenvoi soit termin


; passer banque 1 ; le bit qui sera envoy vaudra 1 ; lancer lacknowledge (= ACKDT = 1 = NOACK) ; ; ; ; tester si NOACK termin non, attendre repasser banque 0 et retour

I2C_NOACK BANKSEL SSPCON2 bsf SSPCON2,ACKDT bsf SSPCON2,ACKEN I2C_NOACK2 btfsc SSPCON2,ACKEN goto I2C_NOACK2 bcf STATUS,RP0 return

23.7.11 Lenvoi du stop-condition Il ne reste plus qu envoyer notre stop-condition pour voir si tout sest bien pass. On vrifie si lopration prcdente est termine On lance le stop-condition Voici le code correspondant :
I2C_stop call I2C_idle BANKSEL SSPCON2 bsf SSPCON2,PEN bcf STATUS,RP0 return ; ; ; ; ; attendre fin de lopration prcdente passer en banque 1 lancer le stop-condition repasser en banque 0 retour

Et son alternative, comme expliqu plus haut : On lance le stop-condition On attend que le repeated start-condition soit termin
; passer en banque 1 ; lancer le stop-condition ; ; ; ; ; effacer watchdog stop-condition termin ? non, attendre oui, repasser en banque 0 et retour

I2C_stop BANKSEL SSPCON2 bsf SSPCON2,PEN I2C_start2 clrwdt btfsc SSPCON2,PEN goto I2C_start2 bcf STATUS,RP0 return

Nous avons maintenant termin nos oprations. Remarquez cependant que si vous avez utilis la seconde mthode, le transfert est termin. Par contre, si vous avez prfr la premire, il vous faudra encore attendre la fin du stop-condition avant que le bus ne soit effectivement libr. Remarquez galement que le flag SSPIF est positionn pour chaque opration termine. Vous pouvez donc remplacer le test de fin dexcution par un simple test sur ce flag :

403

Wait bcf STATUS,RP0 bcf PIR1,SSPIF Wait2 btfss PIR1,SSPIF goto Wait2 return

; passer banque 0 ; reset du flag ; flag positionn ? ; non, attendre ;return

23.8 Lutilisation des interruptions Il se peut que vous dsiriez utiliser les interruptions pour traiter tous ces vnements, en particulier si vous dsirez que votre pic continue effectuer dautres tches durant les temps des oprations sur le bus eeprom. Il existe cependant une certaine difficult pour mettre en uvre les interruptions ce niveau. En effet, vous avez un seul flag (SSPIF), alors que linterruption sera gnre pour chacune des oprations. Afin de savoir o vous en tes au moment o vous arrivez dans votre routine dinterruption, il vous faudra utiliser un compteur que vous incrmenterez chaque opration. Vous aurez alors dans votre routine dinterruption SSP, un slecteur dans le style (cmpt est un compteur) :
incf cmpt,f movlw HIGH tablesaut movwf PCLATH movf cmpt,w addlw LOWtablesaut btfsc STATUS,C incf PCLATH,f tablesaut movwf PCL goto I2C_start goto goto goto goto goto I2C_sendadress I2C_testAck I2C_sendByte I2C_testAck I2C_stop ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; incrmenter compteur de position poids fort adresse de la table de saut dans PCLATH charger compteur de position ajouter poids faible adresse table de saut tester si on dborde oui, incrmenter PCLATH provoquer un saut dans la table qui suit : envoyer start-condition (cmpt valait 0 en entrant dans la routine, et vaut 1 maintenant) envoyer adresse esclave (cmpt = 2) tester acknowledge envoyer octet tester acknowledge envoyer stop-condition

Vous voyez que suivant ltat de cmpt, vous allez sauter des routines diffrentes en fonction de lavance de votre squence. Jai illustr une squence pour lcriture. Celle-ci commencera en initialisant cmpt 0. Il sera incrment chaque appel. Ainsi, la premire adresse de saut sera etiquettesaut + 1 , donc le premier goto . Vous crirez bien entendu galement, si vous en avez besoin, une seconde suite de goto correspondant la lecture. Si la premire adresse du goto concernant la lecture vaut, par exemple, 22, vous initialiserez cmpt 21 pour une lecture. Ensuite, vous remarquez ici une astuce de programmation. Je vous avais dit que lorsque vous criez un tableau de saut, vous ne pouviez pas dborder sur 2 pages de 256 octets. Votre tableau tait donc de fait limit.

404

Lastuce utilise ici permet de dpasser cette limite. En effet, au lieu dajouter w PCL directement, on opre laddition dans le registre W . Si on dtecte que le rsultat dbordera sur lautre page, on incrmente tout simplement PCLATH. Ne reste plus qu placer le rsultat dans PCL. De cette faon, non seulement vous pouvez placer votre tableau nimporte quel endroit, mais, en plus, vous pouvez utiliser un compteur de plus de 8 bits, ce qui vous permet de crer des tableaux de sauts de plus de 256 lments. La mme technique est videmment autorise pour les tableaux de retlw . Vous voyez que vous commencez devenir des pros . 23.8 Le module MSSP en mode IC multi-matre Le passage en mode multi-matre signifie tout simplement que votre PIC nest pas le seul matre du bus IC. Il doit donc sassurer de ne pas entrer en conflit avec un autre matre durant les diverses utilisations du bus. 23.8.1 Larbitrage Il y a plusieurs choses prendre en considration lorsque vous travaillez avec plusieurs matres. Tout dabord, vous ne pouvez prendre la parole que si le bus est libre, ce qui implique de vrifier si le bus est libre avant denvoyer le start-condition. En second lieu, lorsque vous prenez la parole, vous devez vous assurer quun autre matre qui aurait pris la parole en mme temps que vous na pas pris la priorit. Auquel cas, vous devez vous retirer du bus. Il y a donc 5 moments durant lesquels vous pouvez perdre le contrle du bus : Durant le start-condition Durant lenvoi de ladresse de lesclave Durant lmission dun NOACK Durant lmission dun octet Durant un repeated start-condition.

23.8.2 La prise de contrle du bus Une fois de plus, Microchip nous a facilit la vie ce niveau. Votre PIC surveille le bus IC en permanence, et vous informe de ltat du bus. En effet, chaque fois quil voit passer un start-condition , il positionne le bit S du registre SSPSTAT 1 et efface le bit P . Chaque fois quun stop-condition est mis, il positionne le bit P du mme registre, et efface le bit S .

405

Il vous suffira alors de tester si le bit S est effac pour vous assurer que le bus est libre. Microchip recommence cependant de tester les bits S et P simultanment pour sassurer de la libration du bus. Quoi que je ne comprenne pas bien lintrt de cette technique, je vous la dcrit cependant. En effet, si P est positionn, alors S est effac. Donc, il est inutile de tester P , cest de la logique combinatoire. Jai interrog Microchip ce sujet, la rponse a t que javais effectivement raison, mais quil tait plus sr de tester comme indiqu. Je souponne donc fortement que cette rponse qui ne veut rien dire ne cache un bug de fonctionnement concernant le fonctionnement de ces bits. Je vous encourage donc procder comme recommand. Si le bit P est positionn, alors le bus est libre Si P et S sont effacs, alors le bus est libre. Ceci nous donne :
I2C_free BANKSEL SSPSTAT btfsc SSPSTAT,P goto busfree btfsc SSPSTAT,S goto I2C_free busfree suite ; ; ; ; ; passer en banque 1 tester P positionn oui, traiter bus libre Tester si P et S libres S positionn, on recommence les tests

; suite du traitement, le bus est libre

23.8.3 La dtection de la perte de contle De nouveau, la dtection de la perte de contrle, quel que soit le cas, ncessite de ne tester quun seul bit. Le bit BCIF (Bus Collision Interrupt Flag) du registre PIR2. La dtection de la perte de contrle est en effet automatique et cbl de faon hardware dans le PIC. Cest donc lui qui soccupe de tout grer ce niveau. Il vous suffit donc, aprs ou avant chaque opration, suivant la mthode retenue, de tester ltat du bit BCIF. Sil est positionn, alors vous avez perdu le contrle du bus et vous devez abandonner le traitement en cours. Vous le reprendrez ds que le bus sera nouveau libre. Si vous utilisez les interruptions, vous pourrez galement gnrer une interruption sur base de BCIF. Il vous suffira alors, par exemple, si une interruption de ce type est gnre, de remettre votre variable cmpt 0, afin de reprendre les oprations au dbut. 23.9 Le module MSSP en mode IC esclave 7 bits Je vais maintenant vous expliquer comment utiliser votre PIC en IC esclave. Vous rencontrerez ce cas lorsque vous voudrez que votre PIC communique avec un autre matre, qui peut tre un circuit programmable spcialis, un microcontrleur, un autre PIC etc. La premire chose comprendre, cest que, dans ce mode, vous pouvez mettre et recevoir, mais ce nest pas vous qui dcidez quand le transfert lieu.

406

Donc, puisque la transmission peut avoir lieu nimporte quel moment, la meilleure mthode dexploitation sera dutiliser les interruptions. Je vous donnerai donc des exemples de routines dans le chapitre correspondant. Nanmoins, en mode esclave, vous tes dchargs de toute une srie dopration, comme la gnration des start et stop-conditions, lenvoi de ladresse etc. Vous disposez de 2 possibilits dutilisation du mode esclave, selon que vous utiliserez une adresse code sur 7 ou sur 10 bits. Nous verrons les diffrences que cela implique. Pour linstant, je me contente dun adressage sur 7 bits. Ce mode correspond une valeur de SSPMx de B0110 23.9.1 Ladressage et linitialisation Nous allons cette fois utiliser notre registre SSPADD pour mmoriser ladresse laquelle rpondra notre PIC. En effet, en mode esclave, nul besoin de gnrer lhorloge, ce qui explique le double rle de ce registre. Ladresse sera mmorise, bien entendu, dans les bits 7 1 du registre SSPADD. Le bit 0 devra tre laiss 0 .
I2C_init BANKSEL SSPADD movlw ADRESSE*2 movwf SSPADD clrf SSPSTAT movlw B10000000 movwf SSPCON2 bcf STATUS,RP0 movlw B00110110 movwf SSPCON ; ; ; ; ; ; ; ; ; passer en banque 1 charger valeur adresse (bits 7 1) dans registre dadresse slew rate ON, compatible I2C appel gnral en service (facultatif) dans registre SSPCON2 repasser banque 0 MSSP en service, mode I2C 7 esclave 7 bits, SCL OK dans registre de contrle

Vous allez voir que bon nombre doprations sont maintenant prises automatiquement en charge par votre PIC. Nous allons imaginer la squence suivante pour traiter tous les cas particuliers : Le matre crit un octet dans votre PIC Ensuite le matre lit un octet de rponse Voici, chronologiquement, tous les vnements qui vont intervenir : 23.9.2 la rception du start-condition Le matre va commencer par envoyer le start-condition. Votre PIC esclave, qui surveille le bus, va positionner le bit S du registre SSPSTAT. Cependant, vous navez mme pas vous en proccuper, le PIC va poursuivre lexamen du bus et sait maintenant que suit ladresse de lesclave concern.

407

23.9.3 La rception de ladresse en mode criture Deux cas peuvent maintenant se produire. Soit ladresse qui vient dtre mise par le matre est celle de votre PIC, soit elle concerne un autre esclave. Dans le second cas, vous ne verrez rien du tout, le PIC naura aucune raction suite une adresse qui ne le concerne pas. Il attend alors le prochain stop-condition pour poursuivre lanalyse du bus. Par contre, si le message est bien destin votre PIC, et si votre PIC est en condition valable pour le recevoir (je vais en parler), les vnements suivants vont se produire : Ladresse sera transfre dans le registre SSPBUF En consquence, le bit BF sera positionn, pour indiquer que le buffer est plein Le bit SSPIF sera positionn, et une interruption aura ventuellement lieu Le bit D_A du registre SSPSTAT sera mis 0 (adresse prsente) Le bit R_W sera mis 0 (criture) Laccus de rception ACK va tre gnr

La premire chose dont il faut se souvenir, cest quil faut toujours lire une donne prsente dans SSPBUF, sinon la donne suivante ne pourra pas tre traite. Ceci, mme si vous navez pas besoin de cette donne. En effet, un octet prsent dans SSPBUF est signal par le positionnement 1 du bit BF (Buffer Full). La lecture du registre SSPBUF provoque leffacement automatique du bit BF. Si vous recevez votre adresse alors que loctet BF tait toujours positionn (vous naviez pas lu la donne prcdente), alors : Ladresse ne sera pas transfre dans SSPBUF Le bit SSPOV sera positionn Laccus de rception ne sera pas transmis (NOACK) indiquant au matre que vous ntes pas prt recevoir. Le bit SSPIF sera positionn.

Le bit SSPOV devra imprativement tre effac par votre programme. Autrement dit, si vous avez des raisons de penser quil est possible que votre programme ne ragisse pas assez vite un vnement, vous devez toujours tester SSPOV. Sil vaut 1 , alors, vous devez leffacer, vous devez lire SSPBUF, et vous savez que vous avez perdu loctet reu. Rassurez-vous, comme vous avez envoy un NOACK , le matre le sait aussi, il peut donc rpter linformation. Lexception tant que sil sagit de ladresse gnrale (0x00), et quun autre esclave qui rpond galement cette adresse gnre le ACK , le matre ne pourra pas savoir que vous navez pas rpondu. Vous savez maintenant que vous devez lire ladresse afin deffacer le bit BF. Mais vous allez me dire que cela ne sert rien, puisque ladresse lue doit forcment tre ladresse de votre esclave, que vous connaissez.

408

En ralit, pas tout fait. Si vous avez positionn le bit CGEN , votre PIC ragira, soit ladresse contenue dans SSPADD, soit ladresse gnrale 0x00. Le test de cette valeur vous indiquera de quelle adresse il est question.
I2C_adress movf SSPBUF,w btfsc STATUS,Z goto adressGen suite.. I2C_adressGen Suite.. ; ; ; ; charger adresse reue (efface BF) tester si 0 (adresse gnrale) oui, traiter adresse gnrale (toujours en criture) non, traiter une commande via ladresse de SSPADD

; ici, il sagit de ladresse gnrale.

23.9.4 La gnration du ACK Comme je viens de vous le dire, elle est automatique. On distingue 2 cas : Soit le buffer tait vide (BF = 0) et le bit SSPOV tait galement 0 lors de la rception de ladresse : dans ce cas, le bit BF sera positionn et un ACK sera automatiquement gnr. Soit le bit BF ou le bit SSPOV ou les deux tait 1 au moment du transfert, et dans ce cas SSPOV et BF seront positionns 1, et un NOACK sera gnr.

En ralit, gnrer un NOACK , je vous le rappelle, quivaut tout simplement ne rien gnrer du tout. Si le bit SSPOV tait positionn et pas le bit BF, alors cest que votre programme nest pas bien crit (vous avez rat un octet et vous ne vous en tes pas aperu, puisque vous avez lu SSPBUF sans effacer SSPOV). Ce cas ne devrait donc jamais arriver. Le bit SSPIF sera positionn dans tous les cas. Une interruption pourra donc toujours avoir lieu mme si la transaction ne sest pas correctement effectue, ce qui vous permettra de traiter lerreur. 23.9.5 La rception dun octet de donne Et bien, la rception dun octet de donne est strictement similaire la rception de ladresse. La seule diffrence est que le bit D_A sera positionn pour indiquer que loctet reu est un octet de donne. Les cas derreurs sur BF et SSPOV fonctionneront de faon identique, je nai donc pas grand-chose dire ici. De nouveau, un ACK sera gnr si tout sest bien pass, et un NOACK dans le cas contraire, mettant fin au transfert.

409

23.9.6 La rception de ladresse en mode lecture Vous allez maintenant recevoir le repeated start-condition, gr en interne par votre PIC. Suit directement aprs ladresse de votre PIC, mais avec le bit R/W mis 1 (lecture). Voici ce qui va en rsulter : Ladresse sera transfre dans le registre SSPBUF, avec R/W 1 DANS CE CAS PARTICULIER, BF NE SERA PAS POSITIONNE Le bit SSPIF sera positionn, et une interruption aura ventuellement lieu Le bit D_A du registre SSPSTAT sera mis 0 (adresse prsente) Le bit R_W sera mis 1 (lecture) Laccus de rception ACK va tre gnr

Il est important de constater que lorsquon reoit une adresse en mode esclave et en lecture, bien que SSPBUF contienne ladresse reue, BF nest pas positionn. Ceci est logique, car SSPBUF contient forcment ladresse mmorise dans SSPADD, nul besoin de la lire, vous connaissez son contenu. En effet, les commandes gnrales sont par dfinition des commandes dcriture. Votre PIC va maintenant gnrer un ACK automatiquement, vous navez donc pas vous en proccuper. 23.9.6 Lmission dun octet Une fois que votre PIC a dtect que le matre avait besoin de recevoir un octet, il force automatiquement le bit CKP 0 , ce qui place la ligne SCL 0 , et donc gnre une pause. Ceci est bien entendu ncessaire pour viter que le matre ne lise votre PIC avant que vous nayez eu le temps dcrire votre octet de donne dans SSPBUF. Ne tranez cependant pas, n oubliez pas que vous bloquez lintgralit du bus IC. Il vous reste donc placer votre octet envoyer dans SSPBUF, puis placer le bit CKP 1, ce qui va librer la ligne SCL et va permettre au matre de recevoir votre donne. Lcriture de SSPBUF provoque le positionnement de BF, qui sera effac une fois loctet effectivement envoy.
I2C_Send movlw DATA movwf SSPBUF bsf SSPCON,CKP ; charger la donne envoyer ; dans le buffer ; met fin la pause, permet le transfert

ou, en alternative, si vous dsirez tester toutes les conditions derreur :


I2C_Send bsf STATUS,RP0 btfsc SSPSTAT,BF goto $-1 bcf STATUS,RP0 I2C_send2 bcf SSPCON,WCOL movlw DATA ; ; ; ; passer en banque 1 envoi prcdent envoy ? non, attendre repasser banque 0

; effacer bit de collision ; charger donne envoyer 410

movwf btfsc goto bsf

SSPBUF SSPCON,WCOL I2C_send2 SSPCON,CKP

; ; ; ;

dans le buffer collision ? oui, on recommence lcriture met fin la pause, permet le transfert

Jai utilis volontairement lexpression goto $-1 , car vous la rencontrerez parfois dans certains programmes. Lexplication est la suivante : $ est une directive qui reprsente ladresse courante. Donc, $-1 ladresse prcdente. Comme dans les PICs 16Fxxx, chaque instruction ncessite un mot de programme, goto $-1 veut simplement dire sauter la ligne prcdente , tout comme goto $-3 signifiera sauter 3 lignes plus haut . De mme goto $+4 indiquera quil faut sauter 4 lignes plus bas. Si vous dcidez dutiliser cette directive, limitez-vous 2 ou 3 lignes au maximum. Evitez les goto 35 qui rendraient votre programme parfaitement illisible. Cest pourquoi jai attendu longtemps avant dintroduire cette notion, afin de vous habituer utiliser des tiquettes. Cette fois, cest le matre qui enverra le ACK sil dsire lire un autre octet, ou un NOACK sil a dcid de terminer. Vous ne disposez daucun moyen direct pour lire ltat du ACK envoy. Votre PIC gre automatiquement ce bit pour savoir sil doit attendre un nouveau start-condition, ou sil doit se prparer lenvoi dune nouvelle donne. Cependant, une astuce vous permet de savoir si un NOACK a t gnr par le matre. En effet, la rception du NOACK provoque le reset du bit R_W. Comme je vais vous lindiquer plus bas, ceci se traduit, en fin de lecture, par la configuration suivante : R_W = 0 (donc on peut croire quon est en criture) SSPIF = 1 (donc loctet a t reu) BF = 0 (et le buffer est vide). Il est vident que si lopration est termine et que le buffer est vide, cest quil sagissait dune lecture, et non dune criture (auquel cas SSPBUF sera plein et BF vaudrait 1 ). Cette anomalie vous permet donc de dtecter la prsence du NOACK si besoin tait. 23.9.7 Le mode esclave 7 bits par les interruptions Je vous ai dit quen mode esclave, le traitement par interruptions tait le plus appropri. Il est temps maintenant de vous expliquer comment procder. De nouveau, vous navez quune seule interruption, savoir SSPIF . Le bit BCIF na aucune raison dtre ici, car seul un matre peut gnrer et dtecter une collision de bus. Or, vous avez plusieurs vnements susceptibles de provoquer cette interruption. Il vous faudra donc une mthode pour distinguer quel vnement vous avez traiter. Tout se base sur ltat des bits suivants, contenus dans le registre SSPSTAT : S : sil vaut 1 , une opration est en cours

411

R_W : 1 indique une lecture en cours, 0 une criture D_A : 1 signale que loctet qui vient darriver est une donne, 0 une adresse BF : 1 signale que SSPBUF est plein Nous allons, en toute logique, rencontrer les cas suivants : Premier cas S=1 R_W = 0 BF = 1 D_A = 0 ; transfert en cours ; il sagit dune criture ; la donne a t reue ; et il sagit de ladresse

Nous voyons donc que le matre dsire crire (nous envoyer des octets). Ladresse de notre PIC ou ladresse gnrale est maintenant dans notre SSPBUF. Nous devons le lire afin deffacer BF. Second cas S=1 R_W = 0 BF = 1 D_A = 1 ; transfert en cours. ; il sagit dune criture ; la donne a t reue ; et il sagit dune donne

Nous venons ici de recevoir une donne, quil nous incombe de ranger dans lemplacement de notre choix. ATTENTION : Si votre bus IC travaille grande vitesse, ou que le traitement de votre interruption est retard (autre interruption), vous pourriez avoir le bit S=0 et le bit P (stop-condition) = 1. Ceci signifie simplement que le stop-condition a dj t reu avant que vous nayez eu le temps de traiter la rception de loctet. Troisime cas S=1 R_W = 1 BF = 0 D_A = 0 ; transfert en cours ; il sagit dune lecture ; Le buffer est vide (ladresse en lecture ninfluence pas BF) ; ladresse a t reue

Votre PIC vient de recevoir son adresse. Comme il sagit de requte de lecture, le bus est maintenu en pause par votre PIC qui a plac le bit CKP automatiquement 0 . Une fois que vous avez plac la premire donne crire dans votre registre SSPBUF (BF passe 1 ), vous remettez CKP 1, ce qui va permettre lmission. Remarquez que, bien que ladresse soit charge dans SSPBUF, le bit BF na pas t positionn automatiquement. Il est donc inutile de lire SSPBUF. Quatrime cas S=1 ; transfert en cours

412

R_W = 1 BF = 0 D_A = 1

; il sagit dune lecture ; loctet a t envoy (le buffer est vide) ; et il sagissait dune donne

Ceci indique que vous devez maintenant placer un octet de donne qui nest pas le premier. Le matre vous en rclame donc dautres. Vous devez donc placer un nouvel octet dans SSPBUF, puis remettre CKP 1 pour librer la ligne SCL. Si, suite une erreur du matre, il vous demandait un octet, alors que vous navez plus rien envoyer, vous naurez dautre solution que de lui envoyer nimporte quoi. Une absence de raction de votre part entranerait le blocage du bus IC. Cinquime cas S=1 R_W = 0 BF = 0 D_A = 1 ; transfert en cours ; il sagit dune criture ; Le buffer est vide ; Le dernier octet envoy tait une donne.

En fait, nous avons ici une impossibilit apparente dont jai dj parl. En effet, si nous sommes dans notre routine dinterruption, cest que lopration a t termine, puisque SSPIF a t positionn. De quelle opration sagit-il ? Et bien, dune criture. Donc, puisque lcriture est termine, la donne se trouve dans SSPBUF. Or le bit BF nest pas positionn, cest donc une situation paradoxale. Lexplication est quun NOACK a t reu lors dune lecture, indiquant que le transfert est termin. Ce NOACK a comme effet de resetter le bit R_W, ce qui nous donne cette configuration paradoxale. Ce cas est donc tout simplement la concrtisation de la rception dun NOACK. Vous savez partir de ce moment que le matre ne vous demandera plus de nouvel octet, vous de traiter ceci ventuellement dans votre programme. La routine dinterruption Nous pouvons maintenant en venir la faon de traiter les interruptions. Nous allons imaginer que nous compltons la sous-routine dinterruption int_ssp. Nous allons considrer que la zone des variables contient les lments suivants : La zone de stockage des octets reus dmarre en bufin La zone de stockage des octets mettre dmarre en bufout ptrin contient un pointeur sur le prochain emplacement libre de bufin ptrout contient un pointeur sur le prochain emplacement libre de bufout flagout est un flag qui est positionn lorsque tous les octets ont t envoys flaggen est un flag qui indique sil sagit dune commande gnrale Le traitement des flags est du ressort du programme principal.

413

Je ne gre pas les conditions derreur (ex : dbordement des buffers) afin de ne pas alourdir les routines, limportant est de comprendre la faon de procder.
;***************************************************************************** ; INTERRUPTION SSP * ;***************************************************************************** ;----------------------------------------------------------------------------; Gestion de linterruption en mode esclave. ; cas 1 : rception de ladresse en mode criture ; cas 2 : rception dune donne ; cas 3 : rception de ladresse en mode lecture ; cas 4 : envoi dune donne en mode lecture ; cas 5 : rception du NOACK de fin de lecture ; erreur : tout autre cas est incorrect, on provoque le reset du PIC par ; dbordement du watchdog ( vous de traiter de faon approprie) ;----------------------------------------------------------------------------intssp ; conserver les bits utiles ; -------------------------BANKSEL SSPSTAT ; passer en banque 1 movf SSPSTAT,w ; charger valeur SSPSTAT andlw B00101101 ; garder D_A, S, R_W, et BF bcf STATUS,RP0 ; repasser en banque 0 movwf ssptemp ; sauvegarder dans variable temporaire ; cas 1 : rception adresse criture ; ---------------------------------xorlw B00001001 ; buffer plein, contient une adresse btfss STATUS,Z ; condition ralise ? goto intssp2 ; non, examiner cas 2 movlw bufin movwf ptrin bcf flaggen movf SSPBUF,w btfsc STATUS,Z bsf flaggen return ; ; ; ; ; ; ; charger adresse du buffer de rception le prochain octet sera le premier du buffer in par dfaut, pas une commande gnrale efface BF, teste ladresse adresse gnrale reue ? oui, positionner le flag fin du traitement

intssp2 movf xorlw andlw btfss goto

; cas 2 : rception dune donne ; ------------------------------ssptemp,w B00101001 B11110111 STATUS,Z intssp3 ; ; ; ; ; ; ; ; ; ; ; charger bits utiles de SSPSTAT buffer plein, contient une donne liminer le bit S (stop-condition dj reu) condition ralise ? non, examiner cas 3 charger pointeur dentre dans pointeur dadresse charger donne reue la placer dans le buffer dentre incrmenter le pointeur dentre fin du traitement

movf ptrin,w movwf FSR movf SSPBUF,w movwf INDF incf ptrin,f return

intssp3 movf

; cas 3 : mission de la premire donne ; --------------------------------------ssptemp,w ; charger bits utiles de SSPSTAT

414

xorlw B00001100 btfss STATUS,Z goto intssp4 movlw bufout+1 movwf ptrout movf bufout,w movwf SSPBUF bsf SSPCON,CKP return

; ; ; ; ; ; ; ; ;

demande denvoi, on vient de recevoir ladresse condition ralise ? non, examiner cas 4 charger adresse du buffer dmission + 1 le prochain octet sera le second du buffer out charger le premier octet du buffer dmission dans le buffer I2C met fin la pause, permet le transfert fin du traitement

intssp4 movf xorlw btfss goto

; cas 4 : mission dune donne quelconque ; ---------------------------------------ssptemp,w B00101100 STATUS,Z intssp5 ; ; ; ; ; ; ; ; ; ; ; charger bits utiles de SSPSTAT demande denvoi qui suit un autre envoi condition ralise ? non, examiner cas 5 charger pointeur buffer de sortie dans pointeur dadresse charger octet envoyer le mettre dans le buffer de sortie libre lhorloge, permet le transfert incrmenter pointeur de sortie fin du traitement

movf ptrout,w movwf FSR movf INDF,w movwf SSPBUF bsf SSPCON,CKP incf ptrout,f return

intssp5 movf xorlw btfss goto

; cas 5 : rception du NOACK ; --------------------------ssptemp,w B00101000 STATUS,Z $ ; ; ; ; charger bits utiles de SSPSTAT NOCAK reu condition ralise ? non, reset PIC par dbordement watchdog

bsf flagout return

; signaler fin de lecture ; et retour

Vous voyez que tout est logique. Vous allez peut-tre penser que cette routine de slection est un peu longue. Mais, de toute faon, je viens de lcrire pour vous, pas vrai? Je vous conseille un excellent exercice si vous comptez utiliser lIC en mode esclave : essayez de rcrire cette routine vous-mme, vous saurez vite si vous avez tout compris ou pas. 23.10 Le module MSSP en mode IC esclave 10 bits Je ne vais pas reprendre lintgralit des fonctions pour ce cas, je vais me contenter de vous expliquer ce qui diffre lorsque vous dcidez de travailler avec des adresses codes sur 10 bits. Nous allons distinguer 2 cas, celui de la lecture et celui de lcriture. Commenons par le second. Les oprations raliser sont les suivantes (pour le cas o on a correspondance dadresse) : Le PIC reoit le start-condition On reoit le premier octet dadresse, avec R/W 0 . On gnre le ACK On reoit le second octet dadresse
415

On gnre le ACK On reoit le premier octet. . Tout ceci est dj connu, il ne reste quune question : comment savoir que lon doit placer le second octet dadresse ? Et bien, tout simplement parce que le bit UA (Update Adress) est automatiquement positionn lorsque vous devez changer loctet qui se trouve dans SSPADD. Ce positionnement de UA saccompagne de la mise en pause automatique du bus, lhorloge tant bloque ltat bas par le PIC esclave. Ds que vous crivez votre autre octet dans SSPADD, le bit UA est automatiquement effac, et la ligne SCL est libre afin de mettre fin la pause. Les tapes sont donc les suivantes : On reoit le premier octet dadresse : Le ACK est gnr automatiquement, le bit UA est positionn ainsi que le bit SSPIF. Lhorloge SCL est maintenue ltat bas, plaant le bus en mode pause On crit le second octet dadresse dans SSPADD : le bit UA est resett, la ligne SCL est libre On reoit le second octet dadresse : Le ACK est gnr automatiquement, le bit UA est positionn ainsi que le bit SSPIF. Le bus est plac en pause. On crit le premier octet dadresse dans SSPADD : on est alors prt pour une prochaine rception, le bit UA est resett automatiquement et la pause prend fin

Nous avons donc reu 2 fois une interruption avec le bit UA positionn. Il suffit donc, en dbut de notre routine dinterruption, de tester UA. Sil est mis 1 , on met jour SSPADD. Sil contenait loctet 1 dadresse, on y place loctet 2, et rciproquement. Voyons maintenant le cas de lcriture : Le PIC reoit le start-condition On reoit le premier octet dadresse, avec R/W 0 . On gnre le ACK On reoit le second octet dadresse On gnre le ACK On reoit le repeated start-condition On reoit le premier octet dadresse, avec R/W 1 On gnre le ACK On envoie le premier octet demand Ceci se traduit, au niveau du PIC : On reoit le premier octet dadresse : Le ACK est gnr automatiquement, le bit UA est positionn ainsi que le bit SSPIF. Lhorloge SCL est maintenue ltat bas, plaant le bus en mode pause

416

On crit le second octet dadresse dans SSPADD : le bit UA est resett, la ligne SCL est libre On reoit le second octet dadresse : Le ACK est gnr automatiquement, le bit UA est positionn ainsi que le bit SSPIF. Le bus est plac en pause. On crit le premier octet dadresse dans SSPADD : on est alors prt pour une prochaine rception, le bit UA est resett automatiquement et la pause prend fin On reoit de nouveau le premier octet dadresse, MAIS UA nest pas positionn (cest une fonction automatique du PIC). On se retrouve donc maintenant dans le cas dune lecture avec des adresses de 7 bits (mmes conditions).

Donc, notre algorithme reste valable : on ne met jour SSPADD que si UA est positionn. Sinon, on poursuit le traitement ordinaire de notre interruption. 23.11 Synthse Nous avons maintenant tudi tous les cas possibles, savoir : Lmission et la rception en IC matre Lmission et la rception en IC multi-matre Lmission et la rception en mode esclave sur adresses 7 bits Lmission et la rception en mode esclave sur adresses 10 bits La gestion des interruptions.

Vous voici devenu des spcialistes de lIC, moins que vous ne ressentiez brutalement un important mal de tte. Rassurez-vous, en prenant une pause et en relisant quelques fois, vous allez voir que cest plus simple comprendre qu expliquer (heureusement pour vous, malheureusement pour moi). Vous constatez par vous-mme quil ne mest gure possible de donner un exemple concret pour chaque mode, cest pourquoi je vous ai donn dans la thorie des exemples de traitement des vnements IC. Je ne raliserai donc quun exercice, que jai choisi volontairement comme un cas des plus courants. Je vous rappelle que vous disposez de plusieurs faons de traiter les vnements (pooling, interruptions). Je vous donne ici les mthodes que je vous prconise : Pour le mode matre ou multimatre: Si vous avez le temps dattendre, je vous conseille dexcuter les commandes et dattendre la fin de leur excution pour sortir de la sous-routine correspondante. Si vous voulez gagner un peu de temps, vous sortez de suite, et vous testez lors de la prochaine sous-routine si la prcdente est termine. Cette mthode est galement plus pratique pour le mode multimatre, car le test du bus libre peut tre intgr dans la routine I2C_free

417

Si vous avez absolument besoin de librer le PIC le plus rapidement possible, vous devrez utiliser les interruptions.

Pour le mode esclave : Je vous conseille dans tous les cas le traitement par interruption, puisque les vnements arrivent de faon asynchrone au droulement de votre programme, sans oublier quun retard de raction provoque le blocage du bus. Si vous ne souhaitez absolument pas utiliser les interruptions, alors noubliez pas de tester toutes les conditions derreurs possibles (SSPOV, BF ) afin dviter un blocage dfinitif du bus IC.

23.12 Exercice pratique : pilotage dune 24c64 Enfin, nous arrivons la pratique. Pour raliser cet exercice, nous allons ajouter une eeprom 24C32 ou 24C64 notre PIC. Nous aurons galement besoin de 8 LEDs, afin de visualiser ce que contient leeprom. Voici le montage :

418

Notez sur ce schma la prsence des 2 rsistances de rappel au +5V indispensables sur les lignes SCL et SDA. Nous allons raliser un programme qui crit 40 valeurs dans leeprom. Ces valeurs vont ensuite tre lues pour provoquer les allumages des LEDs. En somme, le programme va fonctionner comme notre programme lum , mais les donnes seront crites et lues dans notre eeprom, ce qui est bien entendu le but de cet exercice. Commencez par effectuer un copier/coller de votre fichier m16F876.asm et renommez cette copie lum_eep.asm . Construisez un nouveau projet sur base de ce fichier. Comme toujours, on commence par len-tte et la configuration. Jai choisi de mettre le watchdog en service :
;***************************************************************************** ; Exercice sur les accs I2C concrtiss par les changes avec une 24C64. * ; Le programme crit 40 octets dans l'eeprom, puis les lit en boucle et * ; envoie les octets lus intervalle de 0,5 seconde sur le PORTB (LEDs) * ; * ;***************************************************************************** ; * ; NOM: lum_eep * ; Date: 06/07/2002 * ; Version: 1.0 * ; Circuit: Platine d'exprimentation * ; Auteur: Bigonoff * ; * ;***************************************************************************** ; * ; Fichier requis: P16F876.inc * ; * ;***************************************************************************** ; * ; Notes: L'eeprom est connecte sur le bus I2C (SCL + SDA) * ; A2, A1, et A0 de l'eeprom sont connectes la masse * ; 8 LEDs sont connectes sur le PORTB * ; LE quartz travaille 20MHz. * ; * ;***************************************************************************** LIST p=16F876 #include <p16F876.inc> ; Dfinition de processeur ; fichier include

__CONFIG _CP_OFF & _DEBUG_OFF & _WRT_ENABLE_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _PWRTE_ON & _WDT_ON & _HS_OSC ;_CP_OFF ;_DEBUG_OFF ;_WRT_ENABLE_OFF ;_CPD_OFF ;_LVP_OFF ; _BODEN_OFF ;_PWRTE_ON ;_WDT_ON ;_HS_OSC Pas de protection RB6 et RB7 en utilisation normale Le programme ne peut pas crire dans la flash Mmoire EEprom dprotge RB3 en utilisation normale Reset tension hors service Dmarrage temporis Watchdog en service Oscillateur haute vitesse (4Mhz<F<20Mhz)

;*****************************************************************************

419

; ASSIGNATIONS SYSTEME * ;***************************************************************************** ; REGISTRE OPTION_REG (configuration) ; ----------------------------------OPTIONVAL EQUB'10000000' ; RBPU b7 : 1= Rsistance rappel +5V hors service

Ensuite, nos assignations. Nous allons avoir besoin de la valeur de recharge du timer2, calcule comme dans notre programme spimast1.asm . On prendra une valeur de recharge de PR2 de 249, ce qui, associ un nombre de passages de 125 et une combinaison pr et postdiviseurs donnant une division totale de 80, nous donnera un temps total de 0.5S. Comme nous avons cbl notre eeprom avec ses lignes A2,A1, et A0 la masse, son adresse : 1010 A2 A1 A0 R/W nous donnera en pratique : B10100000 (0xA0). Vue sous cette forme, ladresse est dj prte placer dans SSPADD, donc nous naurons pas besoin de la dcaler vers la gauche.
;***************************************************************************** ; ASSIGNATIONS PROGRAMME * ;***************************************************************************** ADRESS EQU B'10100000' ; ; PR2VAL EQU D'249' ; CMPTVAL EQU D'125' ; ; ; adresse eeprom = 1010 A2 A1 A0 R/W(0) l'adresse est dj dcale (0xA0) Valeur de comparaison timer 2 125 passages dans la routine d'interruption dure = Tcy*(PR2+1)*prdiv*postdiv*cmptval = 0,2s * 250 * 16 * 5 * 125 = 0.5s.

Nous allons devoir crire 40 valeurs dans notre mmoire RAM. Afin de rduire un peu notre fichier source (mais pas lexcutable), nous allons crer une petite macro, qui place loctet pass en paramtre dans le buffer, la position prcise comme second paramtre.
;***************************************************************************** ; MACRO * ;***************************************************************************** WBUF macro octet,offset movlw octet movwf buffer+offset endm ; place l'octet "octet" dans buffer+offset ; charger octet ; placer dans le buffer

Maintenant, notre zone de variables, qui contiendra notre buffer de 32 octets, une variable contenant le nombre doctets envoyer, une variable sur 16 bits qui contiendra ladresse envoyer dans le pointeur dadresse de leeprom (ladresse de lecture ou dcriture), et, enfin, notre flag et notre compteur pour linterruption du timer2.
;***************************************************************************** ; VARIABLES BANQUE 0 * ;***************************************************************************** ; Zone de 80 bytes ; ---------------CBLOCK 0x20 buffer : 0x20 ; Dbut de la zone (0x20 0x6F) ; 32 octets de buffer 420

buflen : 1 cmpt : 1 flags : 1 eepa : 2 ENDC

; ; ; ; ; ;

longueur utilise du buffer compteur de passages d'interruption 8 flags d'usage gnral b0 : 0.5s s'est coule valeur pour le pointeur d'adresse eeprom Fin de la zone

La zone de variables communes ne contient que les variables de sauvegarde ncessaires :


;***************************************************************************** ; VARIABLES ZONE COMMUNE * ;***************************************************************************** ; Zone de 16 bytes ; ---------------CBLOCK 0x70 w_temp : 1 status_temp : 1 ENDC ; Dbut de la zone (0x70 0x7F) ; Sauvegarde registre W ; sauvegarde registre STATUS

La routine dinterruption, tout droit tire de notre programme lum , se contente de positionner le flag au bout de 125 passages correspondants 0.5 s.
;***************************************************************************** ; DEMARRAGE SUR RESET * ;***************************************************************************** org 0x000 goto init ; Adresse de dpart aprs reset ; Initialiser

; //////////////////////////////////////////////////////////////////////////// ; I N T E R R U P T I O N S

; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;***************************************************************************** ; ROUTINE INTERRUPTION * ;***************************************************************************** ;----------------------------------------------------------------------------; La routine d'interruption timer 2 est appele toutes les ; (0,2s * 80 * 250) = 4ms. ; au bout de 125 passages, une demi-seconde s'est coule, on positionne ; le flag ;----------------------------------------------------------------------------;sauvegarder registres ;--------------------org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS,w ; swap status avec rsultat dans w movwf status_temp ; sauver status swapp bcf STATUS,RP0 ; passer banque0 bcf STATUS,RP1

421

; interruption timer 2 ; -------------------bcf PIR1,TMR2IF ; effacer le flag d'interruption bsf flags,1 ; 8 ms coules decfsz cmpt,f ; dcrmenter compteur de passages goto restorereg ; pas 0, fin de l'interruption movlw CMPTVAL ; valeur de recharge du compteur movwf cmpt ; recharger compteur bsf flags,0 ; positionner flag ;restaurer registres ;------------------; ; ; ; ; ; ; swap ancien status, rsultat dans w restaurer status Inversion L et H de l'ancien W sans modifier Z Rinversion de L et H dans W W restaur sans modifier status return from interrupt

restorereg swapf status_temp,w movwf STATUS swapf w_temp,f swapf w_temp,w retfie

On en arrive aux initialisations. En premier lieu, les PORTs. Comme au moment de la mise sous tension, les ports sont tous en entre, on ne forcera que les pins qui devront tre configures en sortie. Dans notre cas, le PORTB, qui, pilotera les LEDs. On en profite pour couper les rsistances de rappel au +5V (pullup) du PORTB, qui seront ici inutiles, via le registre doption.
; //////////////////////////////////////////////////////////////////////////// ; P R O G R A M M E ; //////////////////////////////////////////////////////////////////////////// ;***************************************************************************** ; INITIALISATIONS * ;***************************************************************************** init ; initialisation PORTS ; -------------------BANKSEL PORTB ; passer banque 0 clrf PORTB ; sorties PORTB 0 bsf STATUS,RP0 ; passer en banque1 clrf TRISB ; PORTB en sortie, les autres en entre movlw OPTIONVAL ; charger masque movwf OPTION_REG ; initialiser registre option

Vient maintenant notre module IC. Nous allons indiquer que nous mettons le slew-rate control en service et que nous travaillons avec des niveaux compatibles IC. Ceci nous amne ncrire que des 0 dans SSPSTAT. La frquence est dtermine par la valeur place dans SSPSTAT. Nous avons dj calcul cette valeur pour une frquence de quartz de 20MHz et un dbit de 400KBauds. Cette valeur vaut 12. Il ne reste plus qu mettre le module en service et choisir le mode IC matre (tout ceci via SSPCON)

422

clrf movlw movwf bcf movlw movwf

; initialiser I2C ; --------------SSPSTAT ; slew rate control en service, mode IC D'12' ; valeur de recharge du BRG (400 Kbauds) SSPADD ; dans registre de recharge STATUS,RP0 ; passer banque 0 B'00101000' ; module MSSP en service en mode IC master SSPCON ; dans registre de contrle

On initialise ensuite nos variables, ce qui se limite ici resetter le flag et initialiser le compteur de passage pour la premire dure de 0.5 s.
; initialiser variables ; --------------------clrf flags ; reset flags movlw CMPTVAL ; pour 125 passages movwf cmpt ; dans compteur de passages

Reste initialiser notre timer2 (avec pr et postdiviseurs) et de mettre linterruption correspondante en service. Cest du dj-vu.
; initialiser timer 2 ; ------------------movlw PR2VAL ; charger valeur de comparaison BANKSEL PR2 ; passer banque 1 movwf PR2 ; initialiser comparateur movlw B'00100110' ; timer2 on, prdiv = 16, post = 5 bcf STATUS,R