manual instrucciones pic16f84a

219
1 LA PROGRAMACION DE LOS PIC® POR BIGONOFF PRIMERA PARTE – Revisión 34 COMENZAR CON LOS PIC® CON EL PIC16F84

Upload: guillermo-arturo-gomez-guarin

Post on 29-Dec-2015

136 views

Category:

Documents


11 download

TRANSCRIPT

Page 1: Manual Instrucciones PIC16F84A

1

LA PROGRAMACION DE LOS PIC® POR BIGONOFF

PRIMERA PARTE – Revisión 34

COMENZAR CON LOS PIC® CON EL PIC16F84

Page 2: Manual Instrucciones PIC16F84A

2

1. INTRODUCCION.................................................................................................................................. 7

2. LOS SISTEMAS DE NUMERACION................................................................................................. 8

2.1 EL SISTEMA DECIMAL............................................................................................................................... 8 2.2 EL SISTEMA BINARIO................................................................................................................................ 8 2.3EL SISTEMA HEXADECIMAL....................................................................................................................... 10 2.4 LAS OPERACIONES................................................................................................................................... 11 2.5 LOS NUMEROS CON SIGNO........................................................................................................................ 11 2.6 LAS OPERACIONES BOOLEANAS................................................................................................................ 12 2.6.1 El complemento..................................................................................................................................... 12 2.6.2 La función« Y » o « AND »................................................................................................................... 13 2.6.3 La función « O » u « OR ».................................................................................................................... 13 2.6.4 La función « O EXCLUSIVA » o « Exclusif OR » o « XOR ».............................................................. 14 2.7 UNAS PALABRAS SOBRE LAS UNIDADES.................................................................................................... 14

3. COMPOSICION Y FUNCIONAMIENTO DE LOS PIC®........ ........................................................ 17

3.1 ¿ QUE ES UN PIC® ?............................................................................................................................ 17 3.2 LAS DIFERENTES FAMILIAS DE PIC®............................................................................................ 18 3.3 IDENTIFICACION DE UN PIC®......................................................................................................... 18 3.4 ORGANIZACIÓN DEL 16F84............................................................................................................... 19 3.4.1 La memoria de programa..................................................................................................................... 20 3.4.2 La memoria EEPROM.......................................................................................................................... 20 3.4.3 La memoria RAM.................................................................................................................................. 20

4. ORGANIZACION DE LAS INSTRUCCIONES................................................................................. 21

4.1 GENERALIDADES...................................................................................................................................... 21 4.2 LOS TIPOS DE INSTRUCCIONES.................................................................................................................. 21 4.2.1 Las instrucciones« orientadas a octeto »............................................................................................. 21 4.2.2 Las instrucciones « orientadas a bits »................................................................................................. 21 4.2.3 Les instrucciones generales.................................................................................................................. 22 4.2.4 Los saltos y llamada a subrutinas........................................................................................................ 22 4.3 PANORAMICA DE LAS INSTRUCCIONES...................................................................................................... 22 4.4 LOS INDICADORES DE ESTADO................................................................................................................... 24 4.4.1 El indicador de estado « Z ».................................................................................................................. 24 4.4.2 El indicador de estado « C »................................................................................................................. 24

5. LOS COMIENZOS CON MPLAB®....................................................................................................... 26

5.1 PREPARACION PARA UTILIZACION.............................................................................................................. 26 5.2 CREACION DE NUESTRO PRIMER PROYECTO............................................................................................... 26

6. ORGANIZACION DE UN FICHERO « .ASM »................................................................................... 33

6.1 LOS COMENTARIOS..................................................................................................................................... 33 6.2 LAS DIRECTIVAS......................................................................................................................................... 33 6.3 LOS FICHEROS « INCLUDE »....................................................................................................................... 33 6.4 LA DIRECTIVA _CONFIG.......................................................................................................................... 34 6.5 LAS ASIGNACIONES.................................................................................................................................... 35 6.6 LAS DEFINICIONES...................................................................................................................................... 35 6.7 LAS MACROS............................................................................................................................................... 35 6.8 LA ZONE DE VARIABLES............................................................................................................................. 36 6.9 LAS ETIQUETAS........................................................................................................................................... 36 6.10 LA DIRECTIVA « ORG »........................................................................................................................... 37 6.11 LA DIRECTIVA « END » Y EL FIN DE UN PROGRAMA................................................................................ 37

7. REALIZACION DE UN PROGRAMA.................................................................................................... 39

7.1 CREACION DE NUESTRO PRIMER PROGRAMA................................................................................................ 39 7.2 EL ENSAMBLAJE DE UN PROGRAMA................................................................................................. 40

8. LA SIMULACION DE UN PROGRAMA................................................................................................ 43

Page 3: Manual Instrucciones PIC16F84A

3

8.1 LANZAMIENTO Y PARAMETRAJE DEL SIMULADOR......................................................................................... 43

8.2 EXPLICACION DE LOS REGISTROS FUNDAMENTALES...................................................................................... 45 8.2.1 Los registros « PCL » y « PCLATH »........................................................................................................ 46 8.2.2 El registro « W »........................................................................................................................................ 47 8.2.3 El registro « STATUS »............................................................................................................................. 47 8.3 LANZAMIENTO DE LA SIMULACION ................................................................................................................ 48

9. EL JUEGO DE INSTRUCCIONES............................................................................................................. 51

9.1 LA INSTRUCCION « GOTO » (IR A)................................................................................................................. 51 9.2 LA INSTRUCCION « INCF » (INCREMENT FILE)............................................................................................. 52 9.3 LA INSTRUCCION « DECF » (DECREMENT FILE).......................................................................................... 53 9.4 LA INSTRUCCION « MOVLW » (MOVE L ITERAL TO W)............................................................................... 53 9.5 LA INSTRUCCION « MOVF » (MOVE FILE)................................................................................................... 53 9.6 LA INSTRUCCION « MOVWF » (MOVE W TO FILE)...................................................................................... 55 9.7 LA INSTRUCCION « ADDLW » (ADD LITERAL AND W)................................................................................ 55 9.8 LA INSTRUCCION « ADDWF » (ADDW AND F)............................................................................................ 55 9.9 LA INSTRUCCION « SUBLW » (SUBTRACT W FROM LITERAL)..................................................................... 56 9.10 LA INSTRUCCION « SUBWF » (SUBTRACT W FROM F).............................................................................. 58 9.11 LA INSTRUCCION « ANDLW » (AND LITERAL WITH W)............................................................................ 59 9.12 LA INSTRUCCION « ANDWF » (ANDW WITH F)........................................................................................ 59 9.13 LA INSTRUCCION « IORLW » (INCLUSIVE OR LITERAL WITH W)............................................................... 60 9.14 LA INSTRUCCION « IORWF » (INCLUSIVE ORW WITH FILE)...................................................................... 60 9.15 LA INSTRUCCION « XORLW » (EXCLUSIVE OR LITERAL WITH W)............................................................ 61 9.16 LA INSTRUCCION « XORWF » (EXCLUSIVE ORW WITH F)........................................................................ 62 9.17 LA INSTRUCCION « BSF » (BIT SET F)......................................................................................................... 62 9.18 LA INSTRUCCION « BCF » (BIT CLEAR F).................................................................................................... 63 9.19 LA INSTRUCCION « RLF » ( ROTATE LEFT THROUGH CARRY)..................................................................... 63 9.20 LA INSTRUCCION « RRF » ( ROTATE RIGHT THROUGH CARRY)................................................................... 64 9.21 LA INSTRUCCION « BTFSC » (BIT TEST F, SKIP IF CLEAR).......................................................................... 65 9.22 LA INSTRUCCION « BTFSS » (BIT TEST F, SKIP IF SET)............................................................................... 67 9.23 LA INSTRUCCION « DECFSZ » (DECREMENT F, SKIP IF Z)......................................................................... 67 9.24 LA INSTRUCCION « INCFSZ » (INCREMENT F, SKIP IF ZERO)..................................................................... 68 9.25 LA INSTRUCCION « SWAPF » (SWAP NIBBLES IN F).................................................................................. 69 9.26 LA INSTRUCCION « CALL » (CALL SUBROUTINE)...................................................................................... 69 9.27 LA INSTRUCCION « RETURN » (RETURN FROM SUBROUTINE)................................................................. 70 9.28 LA INSTRUCCION « RETLW » (RETURN WITH LITERAL IN W)................................................................... 72 9.29 LA INSTRUCCION « RETFIE » (RETURN FROM INTERRUPT)....................................................................... 73 9.30 LA INSTRUCCION « CLRF » (CLEAR F)....................................................................................................... 73 9.31 LA INSTRUCCION « CLRW » (CLEAR W).................................................................................................... 73 9.32 LA INSTRUCCION « CLRWDT » (CLEARWATCHDOG)................................................................................ 74 9.33 LA INSTRUCCION « COMF » (COMPLEMENT F).......................................................................................... 74 9.34 LA INSTRUCCION « SLEEP » (PUESTA A DORMIR).................................................................................. 75 9. 35 LA INSTRUCCION « NOP » (NO OPERATION).............................................................................................. 75 9.36 LAS INSTRUCCIONES OBSOLETAS.................................................................................................................. 75

10. LOS MODOS DE DIRECCIONAMIENTO............................................................................................... 76

10.1 EL DIRECCIONAMIENTO LITERAL O INMEDIATO............................................................................................... 76 10.2 EL DIRECCIONAMIENTO DIRECTO..................................................................................................................... 76 10.3 EL DIRECCIONAMIENTO INDIRECTO.................................................................................................................. 76 10.3.1 Los registros FSR y INDF.......................................................................................................................... 77 10.4 ALGUNOS EJEMPLOS........................................................................................................................................ 78

11. REALIZACION DE UN PROGRAMA EMBARCADO........... .................................................................. 80

11.1 EL MATERIAL NECESARIO................................................................................................................................. 80 11.2MONTAJE DE LA TARJETA DE EXPERIMENTACION.............................................................................................. 81 11.3 CREACION DEL PROYECTO................................................................................................................................. 82 11.4 EDICION DEL FICHERO FUENTE.......................................................................................................................... 82 11.5 ELECCION DE LA CONFIGURACION..................................................................................................................... 83 11.6 EL REGISTRO OPTION...................................................................................................................................... 84 11.7 EDICION DEL PROGRAMA.................................................................................................................................. 85

Page 4: Manual Instrucciones PIC16F84A

4

11.8 EL REGISTRO PORTA.................................................................................................................................... 87 11.8.1 uncionamiento particular de los PORTS.................................................................................................. 89 11.9 EL REGISTRO TRISA...................................................................................................................................... 90 11.10 LOS REGISTROS PORTB Y TRISB............................................................................................................. 91 11.11 EJEMPLO DE APLICACION............................................................................................................................ 91 11.12 LA RUTINA DE INICIALIZACION ................................................................................................................... 92 11.13 LOS RESULTADOS DEN ENSAMBLADO........................................................................................................ 95 11.14 EL PROGRAMA PRINCIPAL........................................................................................................................... 95 11.15 LA BRUTINA DE TEMPORIZACION................................................................................................................ 96

12. LAS INTERRUPCIONES........................................................................................................................... 101

12.1 ¿QUE ES UNA INTERRUPCION ?..................................................................................................................... 101 12.2MECANISMO GENERAL DE UNA INTERRUPCION.............................................................................................. 101 12.3MECANISMO DE LA INTERRUPCION SOBRE LOS PIC®.................................................................................... 103 12.4 LAS FUENTES DE INTERRUPCION DEL 16F84................................................................................................. 104 12.5 LA PUESTA EN MARCHA DE LOS DISPOSITIVOS.............................................................................................. 105 12.6 EL REGISTRO INTCON (INTERRUPT CONTROL)......................................................................................... 106 12.7 SALVAGUARDIA Y RESTAURACION DEL ENTORNO......................................................................................... 108 12.7.1 Los registros a salvaeguardar................................................................................................................... 108 12.7.2 El método de salvaguardia........................................................................................................................ 109 12.7.3 El método de restauracion........................................................................................................................ 110 12.7.4 OPERACIONES SOBRE EL REGISTRO STATUS........................................................................................... 112 12.7.5Particularidad de la instruccion « RETFIE »........................................................................................... 112 12.8 UTILIZACION DE UNA RUTINA DE INTERRUPCION......................................................................................... 113 12.9 ANALISIS DE LA RUTINA DE INTERRUPCION.................................................................................................. 116 12.10 ADAPTACION DE LA RUTINA DE INTERRUPCION.......................................................................................... 118 12.11 LA INICIALIZACION ................................................................................................................................... 119 12.12 CONSTRUCCION DE UN PROGRAMA PRINCIPAL.......................................................................................... 120 12.13 CONSTRUCCION DE LA RUTINA DE INTERRUPCION...................................................................................... 121 12.14 PASO AL SIMULADOR DE UNA RUTINA DE INTERRUPCION........................................................................... 122 12.15 PRIMERA CORRECCION: RESET DEL FLAG................................................................................................... 126 12.16 PONERSE A LA ESCALA DE TIEMPO DE UN PIC®........................................................................................ 127 12.17 EL PROBLEMA DEL ANTI REBOTE................................................................................................................. 127 12.18 FINALIZACION DE UN PROGRAMA............................................................................................................... 128 12.19 OBSERVACIONES IMPORTANTES.................................................................................................................. 131 12.20 CONCLUSIONES............................................................................................................................................ 132

13. EL TIMER 0................................................................................................................................................... 133

13.1 LOS DIFERENTES MODOS DE FUNCIONAMIENTO............................................................................................... 133 13.2 EL REGISTRO TMR0......................................................................................................................................... 133 13.3 LOS METODOS DE UTILIZACION DEL TIMER0................................................................................................... 133 13.3.1 El modo de lectura simple.......................................................................................................................... 133 13.3.2 El modo de vigilancia del flag................................................................................................................... 134 13.3.3 El modo de interrupcion............................................................................................................................. 134 13.3.4 Los métodos combinados........................................................................................................................... 134 13.4 EL PREDIVISOR.............................................................................................................................................. 135 13.5 APLICACION PRÁCTICA DEL TIMER0.............................................................................................................. 137 13.5.1 Preparaciones............................................................................................................................................ 137 13.5.2 La inicializacion........................................................................................................................................ 139 13.5.3 La rutina de interrupcion.......................................................................................................................... 140 13.6MODIFICACION DE LOS REGISTROS EN EL SIMULADOR................................................................................... 141 13.7PUESTA EN PRACTICA SOBRE LA PLACA.......................................................................................................... 141 13.8 PRIMERA MEJORA DE LA PRECISION.............................................................................................................. 141 13.9 SEGUNDA MEJORA DE LA PRECISION......................................................................................................... 142 13.10 EL METODO DE « ATRAPE »......................................................................................................................... 142 13.11 EL METODO HARDWARE – ADAPTACION DEL RELOJ........................................................................... 143 13.12 EL METODO DE LUXE : EL DOBLE RELOJ................................................................................................ 144 13.13 EJEMPLO DE UTILIZACION DE 2 INTERRUPCIONES........................................................................................ 144 13.13 CONCLUSION............................................................................................................................................... 145

14. EL ACCESO A LA MEMORIA « EEPROM »......................................................................................... 146

Page 5: Manual Instrucciones PIC16F84A

5

14.1 TAMAÑO Y LOCALIZACION DE LA MEMORIA « EEPROM ».............................................................................. 146 14.2 PREPARACION DEL PROGRAMA..................................................................................................................... 146 14.3 INICIALIZACION DE LA ZONA EEPROM........................................................................................................... 148 14.4 EL REGISTRO EEDATA................................................................................................................................ 149 14.5 EL REGISTRO EEADR................................................................................................................................... 149 14.6 EL REGISTRO EECON1................................................................................................................................ 149 14.7 EL REGISTRO EECON2................................................................................................................................. 150 14.8 ACCESO EN LECTURA A LA MEMORIA « EEPROM »........................................................................................ 150 14.9 EL ACCESO EN ESCRITURA A LA ZONA EEPROM.............................................................................................. 151 14.10 UTILIZACION PRÁCTICA DE LA MEMORIA « EEPROM »................................................................................ 153 14.11 SECURIZACION DEL ACCESO A LA MEMORIA « EEPROM »............................................................................ 155 14.12 CONCLUSION............................................................................................................................................... 156

15. EL WATCHDOG........................................................................................................................................ 157

15.1 EL PRINCIPIO DE FUNCIONAMIENTO.............................................................................................................. 157 15.2 EL PRE DIVISOR Y EL WATCHDOG................................................................................................................. 157 15.3 LOS ROLES DEL WATCHDOG.......................................................................................................................... 158 15.4 UTILIZACION CORRECTA DAL WATCHDOG.................................................................................................... 159 15.5 LO QUE NO HACE FALTA HACER.................................................................................................................... 159 15.6 MEDIDA DEL TIEMPO REAL DEL WATCHDOG................................................................................................. 159 15.7 SIMULACION DEL BLOQUEO DE UN PROGRAMA............................................................................................ 161 15.7.1 Correccion con utilizacion del watchdog................................................................................................. 162 15.8 ELECCION DEL VALOR DEL PRE DIVISOR........................................................................................................ 162 15.9 TIEMPOS TIPICO, MINIMO , Y MAXIMO ........................................................................................................... 163 15.10 CONCLUSION................................................................................................................................................ 163

16. EL MODO SLEEP...................................................................................................................................... 164

16.1 PRINCIPIO DE FUNCIONAMIENTO................................................................................................................... 164 16.2 LA SALIDA DEL MODO « SLEEP ».................................................................................................................... 164 16.3 DESPERTAR CON GIE FUERA DE SERVICIO.................................................................................................... 164 16.4 DESPERTAR CON GIE EN SERVICIO............................................................................................................... 165 16.5 PUESTA A DORMIR IMPOSIBLE....................................................................................................................... 165 16.6 UTILIZACION DEL MODO « SLEEP »............................................................................................................... 165 REMARQUE........................................................................................................................................................... 166 16.7 CASOS TIPICOS DE UTILIZACION.................................................................................................................... 166 16.7 PARA UN CONSUMO MINIMO......................................................................................................................... 166 16.8 CONCLUSION................................................................................................................................................ 167

17. EL RESTO DEL DATASHEET.................................................................................................................. 168

17.1 LA ESTRUCTURA INTERNA.............................................................................................................................. 168 17.2 LA SECUENCIA DE DECODIFICACION............................................................................................................. 168 17.3 ORGANIZACION DE LA MEMORIA................................................................................................................... 168 17.4 LOS REGISTROS ESPECIALES......................................................................................................................... 168 17.5 LA ELECTRONICA DE LOS PUERTOS............................................................................................................... 169 17.6 EL REGISTRO DE CONFIGURACION................................................................................................................. 169 17.7 LOS DIFFERENTES TIPOS DE OSCILADORES.................................................................................................... 169 17.7.1 La precision del oscilador......................................................................................................................... 170 17.8 EL RESET....................................................................................................................................................... 171 17.9 LA PUESTA EN TENSION................................................................................................................................. 172 17.10 CARACTERISTICAS ELECTRICAS.................................................................................................................. 173 17.11 PORTABILIDAD DE LOS PROGRAMAS............................................................................................................ 173 17.12 LA PUESTA AL DIA DE LOS COMPONENTES................................................................................................... 173 17.13 CONCLUSION............................................................................................................................................... 174

18. TRUCOS DE PROGRAMACION............................................................................................................... 175

18.1 LAS COMPARACIONES..................................................................................................................................... 175 18.2 SUSTRAER UN VALOR DE W............................................................................................................................. 176 18.3 LAS MULTIPLICACIONES.................................................................................................................................. 176 18.4 MULTIPLICACION POR UNA CONSTANTE........................................................................................................... 179 18.5 DIRECCIONAMIENTO INDIRECTO APUNTANDO A 2 ZONAS DIFERENTES............................................................ 179

Page 6: Manual Instrucciones PIC16F84A

6

18.6 LAS TABLAS EN MEMORIA DE PROGRAMA..................................................................................................... 180 18.7 LAS VARIABLES LOCALES............................................................................................................................. 184 18.7.1 Déterminction de las variables locales.................................................................................................... . 184 18.7.2 Construccion sin variables locales.......................................................................................................... . 184 18.7.3 Construccion con variables locales.......................................................................................................... 185 18.8 DIVISION POR UNA CONSTANTE.................................................................................................................... 185 18.9 CONCLUSION................................................................................................................................................. 185

19. UTILIZACION DE LAS RUTINAS EN UN FICHERO SEPARA DO.................................................. 186

19.1 PREGUNTAS Y PUNTO DE PARTIDA................................................................................................................. 186 19.2 UTILIZACION DIRECTA DE LAS RUTINAS EN EL FICHERO................................................................................ 186 19.3 ENCAPSULACION EN MACROS SIMPLES.......................................................................................................... 189 19.4 MÉTODO FINALIZADO..................................................................................................................................... 190 19.5 CONCLUSION.................................................................................................................................................. 191

20. LA NORMA ISO 7816................................................................................................................................... 192

20.1 ESPECIFICACIONES UTILES DE LA NORMA ISO 7816...................................................................................... 192 20.1.1 Los comandOs ISO 7816............................................................................................................................ 193 20.1.2 El protocolo de intercambio de informaciones.......................................................................................... 193 20.2 LAS CONEXIONES SERIE ASINCRONAS............................................................................................................ 194 20.2.1 El start-bit................................................................................................................................................... 195 20.2.2 Los bits de datos......................................................................................................................................... 195 20.2.3 El bit de paridad........................................................................................................................................ 195 20.2.4 El stop-bit.................................................................................................................................................. 195 20.2.5 Tasa de enviot........................................................................................................................................... 195 20.3 ADQUISICION DE LOS BITS............................................................................................................................. 196 20.4 CARACTERISTICAS DE LAS TARJETAS « STANDARD ».................................................................................... 197 20.5 CREACION E INICIALIZACION DEL PROYECTO................................................................................................. 197 20.6 LA BASE DE TIEMPOS..................................................................................................................................... 198 20.7 RECEPTION DE UN OCTETO............................................................................................................................ 199 20.8 LA EMISION DE UN CARACTER....................................................................................................................... 201 20.9 INITIALIZACION .............................................................................................................................................. 203 20.10 ENVIO DEL ATR.......................................................................................................................................... 203 20.11 EL ENVIO DE STATUS.................................................................................................................................... 205 20.12 RECEPCION DE LA CLASSE............................................................................................................................ 206 20.13 RECEPCION DE INS, P1, P2, Y LEN............................................................................................................ 206 20.14 CONTROL DE LA INSTRUCCION RECIBIDA.................................................................................................... 206 20.15 TRATAMIENTO DE UNA INSTRUCCION......................................................................................................... 207 20.16 LAS VARIABLES............................................................................................................................................ 209 20.17 CONCLUSION................................................................................................................................................ 209

ANNEXE1 : PREGUNTAS FRECUENTES (F.A.Q.)..................................................................................... 210

A1.1 ENCUENTRO QUE 8 SUB PROGRAMAS SON POCOS.......................................................................................... 210 A1.2 NO UTILIZAO MAS QUE 8 ANIDAMIENTOS PERO MI PROGRAMA SE BLOQUEA................................................ 210 A1.3MI PROGRAMA PARECE NO SALIR JAMAS DE LAS INTERRUPCIONES............................................................... 210 A1.4 NO LLEGO A UTILIZAR EL SIMULADOR, LAS OPCIONES NO APARECEN........................................................... 210 A1.5 RECIBO UN MENSAJE DE ERROR EOF ANTES DE LA INSTRUCCION END....................................................... 210 A1.6 COMO DESENSAMBLAR UN FICHERO « .HEX » ?............................................................................................ 211 A1.7 UTILIZACION DE MINUSCULAS YMAYUSCULAS............................................................................................. 211 A1.8 LA ELECCION DE UN PROGRAMADOR............................................................................................................ 211 A1.9 TENGO UN ERROR DE « STACK ».................................................................................................................... 212 A1.10 ¿CUALES SON LAS DIFERENCIAS ENTRE 16F84 Y 16F84A ?........................................................................ 212 A1.11 TENGO UN ERROR 173 DESPUES DEL ENSAMBLADO..................................................................................... 213 A1.12 EL PIC16F84 ESTA OBSOLETO, ¿POR QUE NO UTILIZAR EL 16F628 ?........................................................ 213 A1.13 UTILIZO UNA VERSION DE MPLAB® MAS RECIENTE................................................................................. 214 A1.14 MI PIC VIRGEN NO OSCILA.......................................................................................................................... 214

CONTRIBUCION SOBRE UNA BASE VOLUNTARIA............. ................................................................. 215

UTILIZACION DEL PRESENTE DOCUMENTO................. ...................................................................... 216

Page 7: Manual Instrucciones PIC16F84A

7

1. Introducción

Vamos a empezar un camino juntos para esta gran aventura que es la programación de los PIC®. Voy a inten-tar ser lo más concreto posible, pero, independientemente de todo, una cierta teoría es indispensable para llegar a buen puerto. Voy a empezar este pequeño curso por un recordatorio sobre los sistemas de numeración. Veo que comienza a incomodarle. Pero estoy seguro que comprenderá que es de todo punto de vista imposible programar se-riamente un micro controlador sin saber que es un bit o cómo convertir las notaciones decimales en hexade-cimales. Esté seguro que voy a ser breve y podremos abordar rápidamente el sujeto que tanto nos interesa. Si usted es ya un “pro” se puede saltar el primer capítulo y pasar directamente al siguiente. No dude jamás en hacerme participe de sus sugerencias ni de hacerme saber los errores que se me han esca-pado (www.bigonoff.org). Reproduzca las informaciones que encontrará aquí, traduzca el documento a otros idiomas o a otros formatos. Simplemente, en esos casos, respete los deseos del autor y hágame llegar una copia del trabajo. Esto es para provecho de la mayor cantidad de gente posible ([email protected]). Le llamo la atención sobre el hecho de que este curso, para ser eficaz, debe ser leído en su totalidad realizan-do los ejercicios que se proponen. Las soluciones a los ejercicios son suministradas en forma de ficheros ejemplo anexados al curso. Todo lo que le hará falta es un 16F84(A), un cuarzo de 4 MHz, una pequeña placa de experimentación, un diodo LED, un pulsador y el logicial MPLAB® , puesto graciosamente a su disposición por la sociedad Micro-chip® en la dirección http://www.Microchip.com. En esa misma dirección encontrará el datasheet del 16F84. He pasado numerosos días realizando estos ejercicios. Personalmente los he testado sobre una maqueta uno por uno. Le pediría que intentase realizarlos por usted mismo antes de preguntarme por email cuestiones que hubiesen estado ya respondidas si hubiese hecho ese pequeño esfuerzo. Créame, es en el momento de poner las cosas en practica que nos damos cuenta que finalmente no habíamos comprendido del todo alguna cosa que parecía de todo punto de vista evidente. Siempre respondo a los correos recibidos, pero hace falta decir que me resulta un poco enervante recibir preguntas de gente que me dice que ha asimilado el contenido de esta obra en una hora. ¡Le juro que me llegan! Personalmente he utilizado la versión 6.30 de MPLAB® a partir de la revisión 13ª del curso. Las revisiones precedentes utilizaban la versión 5.20. La versión 6.60 está disponible en la sección de « archives » sobre la pagina de Microchip®. Le pido, para seguir correctamente el curso, que utilice esta versión y, una vez acabado el aprendizaje, cargar la versión más reciente y experimentar por sí mismo las diferencias. El PIC® utilizado aquí es el más simple que existe en esta familia, no se trata de un PIC de los más avanzados. Cuando realice sus propios montajes elija el modelo en función de su cuaderno de cargas. La calavera de la portada no es un símbolo ligado a este curso, es una utilidad histórica. Desde que propuse las primeras versiones de mi curso, no tenia alojamiento web, mis cursos estaban distribuidos en la red (hasta la revisión 6). En consecuencia, en caso de litigio, no tenía ningún medio de probar legalmente que yo era el autor de este curso y podía fácilmente ser “robado”. No deseando desvelar mi verdadera identidad (no tengo necesidad de una cohorte de estudiantes llamando a mi puerta constantemente) quise firmar este documen-to de forma que pudiese probar en todo momento que yo era el autor. Si va a mi web y observa la foto de mi moto, verá que esa calavera no es otra cosa que una parte de su care-nado. Como esa foto está en mi curso y puedo probar que soy propietario de la moto, puedo probar que soy el autor del curso si fuese necesario.

Page 8: Manual Instrucciones PIC16F84A

8

2. Los sistemas de numeración 2.1 El sistema decimal

Estamos habituados desde la infancia a utilizar el sistema de numeración decimal, hasta tal punto, que no somos conscientes de la manera en que este trabaja y llega a ser para nosotros un automatismo. ¿Por qué decimal? Porque utilizamos una numeración con 10 cifras. Diremos que es un sistema en base 10. Como pequeña historia, utilizamos un sistema en base 10 porque nuestros ancestros empezaron a contar usando los diez dedos de las manos, no hace falta buscar más lejos. La posición de las cifras tiene una gran importancia. Las cifras menos significativas se sitúan a la derecha del número y las de mayor importancia tanto más cuanto más a la izquierda del numero estén. En el numero 502, el 5 tiene mayor importancia que el 2. En realidad, cada cifra, que podemos llamar digito, tiene un valor que depende de su rango. ¿Cuál es el multiplicador a aplicar a cada digito en función de su posición (rango)? Sim-plemente la base elevada a la potencia de su rango. Esto tiene el aire de ser complejo, pero es muy simple de comprender. Cuando lo haya entendido, entenderá cualquier sistema de numeración. Retomemos, por ejemplo, el numero 502. ¿Qué significa el 2? Simplemente que su valor equivale a 2 multipli-cado por la base (10) elevada al rango (posición) que ocupa, es decir 0. Un número elevado a 0 siempre vale 1. El 2 representa, pues, 2*1. Observe aquí una cosa muy importante: el rango se cuenta de derecha a izquierda comenzando por 0. Para nuestro número 502 en realidad su valor es: 502 = 2*100 + 0*101 +5*102 Nótese que utilizamos el símbolo * para indicar multiplicación. Y recuerde que 100 = 1, 101 = 10, 102 = 100, etc.

2.2 El sistema binario

¿Entendió lo que precede? Lo siguiente le parecerá simple. Para usted no es ningún problema contar con los dedos, pero para un ordenador no es simple. No saben, en general, más que hacer la distinción entre dos ni-veles (presencia o ausencia de tensión). El sistema de numeración decimal no se les adapta bien. Comprenderá que el único sistema que se les adapta es el sistema de numeración de base 2 también llamado binario. Este sistema trabaja con solo dos cifras, a saber, 0 y 1. Como, además, los primeros ordenadores (y los PIC®) trabajaban con numero de 8 cifras binarias, se les llamó octetos (o bytes en inglés). A la cifra 0 ó 1 se le llama bit (por binary unit). En lo que sigue, se adoptarán las siguientes convenciones: todo numero decimal estará escrito tal cual, o utili-zaremos la notación D’xxx’. Todo numero binario lo escribiremos con la forma B’xxxxxxxx’, donde x vale 0 ó 1. Analicemos un número binario. Sea el octeto B’10010101’. ¿Cuál es su valor decimal? Muy simple. Aplicamos el mismo algoritmo que para el decimal. Partamos desde la derecha hacia la izquierda y tendremos:

B’10010101’ = 1*2° + 0*21 + 1*22 + 0*23 + 1* 24 + 0*25 + 0*26 + 1*27

Page 9: Manual Instrucciones PIC16F84A

9

Evidentemente 0 multiplicado por cualquier cosa es 0, y 1 multiplicado por cualquier cifra da la cifra en cues-tión. El cálculo precedente quedará como:

B ‘10010101’ = 1+4+16+128 = 149 Como ve es muy fácil convertir cualquier número de binario a decimal. ¿Y a la inversa? Igualmente simple. Hace falta conocer la tabla de exponentes de 2. Esta se aprende fácilmente con el uso. Podemos proceder de la forma siguiente (hay otras maneras):

¿Cuál es el más grande exponente de 2 contenido en 149? Respuesta: 7 (27 = 128)

Sabemos que el bit 7 valdrá 1. Quedará 149-128=21 El bit 6 representa 64 que es más grande que 21, luego b6 valdrá 0. El bit 5 representa 32 que es más grande que 21, luego b5 valdrá 0. El bit 4 representa 16, luego b4=1 y quedarán 21-16=5 El bit 3 representa 8 que es más grande que 5, luego b3 valdrá 0. El bit 2 representa 4, luego b2=1 y quedarán 5-4=1 El bit 1 representa 2 que es más grande que 1, luego b1=0. El bit 0 representa 1, que es lo que nos queda, luego b0=1. El número binario obtenido será B’10010101’, que es nuestro octeto de partida. Tenga en cuenta que si en-contramos un número de menos de 8 cifras bastará con completar con 0 por la izquierda hasta tener 8 cifras. En efecto, B’00011111’ = B ‘11111’ de la misma manera que 0502 = 502. Piense siempre en completar los octetos obteniendo 8 bits puesto que esto es imposible de hacer para la ma-yor parte de los ensambladores (veremos qué es esto en las lecciones que seguirán a continuación). Observe que el mayor numero que puede ser representado por un octeto es B’11111111’. Si hace la conver-sión a decimal verá que es 255. Todo número superior a 255 necesita, para ser representado, más de un octe-to. Le doy otro método simple para convertir de decimal a binario, procediendo de manera inversa, es decir, desde la derecha a la izquierda. El método consiste en escribir el resto de ir dividiendo por 2 el número en cuestión. Retomemos nuestro numero decimal 149: 149 / 2 = 74 y queda 1. 74 / 2 = 37 y queda 0. 37 / 2 = 18 y queda 1. 18 / 2 = 9 y queda 0. 9 / 2 = 4 y queda 1. 4 /2 = 2 y queda 0. 2 / 2 = 1 y queda 0. 1 / 2 = 0 y queda 1. Cogiendo todos los restos empezando por el último (o empezando por el primero y rellenando de derecha a izquierda), obtendremos B’10010101’, que es valor que habíamos obtenido anteriormente. En general, este método es más simple para aquellos que no tiene el hábito de jugar con las potencias de 2, pero el primero es más rápido de hacer mentalmente si adquirimos el hábito.

Page 10: Manual Instrucciones PIC16F84A

10

2.3 El sistema hexadecimal La representación de números binario no es fácil de usar, y escribir una sucesión de 1 y 0 es una fuente cons-tante de errores. Hacía falta una solución más práctica para representar números binarios. Decidimos romper cada octeto en dos cuartetos y representar cada parte por una sola cifra. Como un cuarteto puede variar de B’0000’ a B’1111’, constatamos que le valor puede variar de 0 a15. Esto da 16 combinaciones posibles. Las 10 cifras del sistema decimal no son suficientes para codificar estos valores. Mejor que inventar 6 símbolos nuevos se decide utilizar las 6 primeras letras del alfabeto como cifras. Lógi-camente a este sistema de numeración en base 16 lo llamamos hexadecimal. Observe que este sistema es, simplemente, una forma más eficaz de representar números binarios y la con-versión de uno a otro es instantánea. A partir de ahora, representaremos un numero hexadecimal haciéndole comenzar por 0x. Veamos si lo ha entendido:

Tabla de conversión de diferentes (un semi octeto) Binario

Hexadeci-mal

Decimal

B’0000’ 0x0 0 B’0001’ 0x1 1 B’0010’ 0x2 2 B’0011’ 0x3 3 B’0100’ 0x4 4 B’0101’ 0x5 5 B’0110’ 0x6 6 B’0111’ 0x7 7 B’1000’ 0x8 8 B’1001’ 0x9 9 B’1010’ 0xA 10 B’1011’ 0xB 11 B’1100’ 0xC 12 B’1101’ 0xD 13 B’1110’ 0xE 14 B’1111’ 0xF 15

Para representar un octeto, hacen falta 2 dígitos hexadecimales. Por ejemplo, nuestro B’10010101’ se repre-senta por el hexadecimal 0x95. Si hace la conversión de hexadecimal a decimal, utilizará el mismo principio

que hemos estado utilizando, y obtendremos 0x95 = 9*161 + 5*16° = 149. Probemos cuál es en hexadecimal el número más grande que podemos representar. Respuesta: 0xFF, es decir 15*16 + 15 = 255. Si ha entendido todo esto, ahora es capaz de convertir no importa qué número en no importa qué base en otro con base distinta. Encontrará en algunas revistas alusiones al sistema octal, que es el sistema en base 8 y que fue muy utilizado en el pasado pero casi nada actualmente.

Page 11: Manual Instrucciones PIC16F84A

11

2.4 Las operaciones Después de haber convertido números en diferentes formatos, vamos a ver que es muy simple realizar opera-ciones con estos números en no importa que formato. Ejemplo: ¿Qué vale B’1011’ + B ‘1110’? Procederemos de la misma manera que para una operación en de-cimal.

B’1011‘ + B’1110‘ --------------

?

Sumamos las cifras de la derecha y obtenemos: 1+0=1. Escribimos 1. Sumamos 1+1 y obtenemos 10 (2 no existe en binario). Escribimos 0 y llevamos 1. Sumamos 0+1+1 (que traíamos) y obtenemos 10. Escribimos 0 y llevamos 1. Sumamos 1+1+1 (que traíamos) y obtenemos 11. Escribimos 1 y llevamos 1. Queda lo que traemos. Escribimos 1. La respuesta será: B’11001’, es decir 25 en decimal. Los números de partida eran B’1011’ es decir 11, y B’1110 es decir 14. Procedemos de la misma manera pa-ra números hexadecimales, sabiendo que 0xF +0x1 = 0x10, es decir 15 + 1 = 16 (escribimos 0 y llevamos 1).

2.5 Los números con signo

En ciertas aplicaciones es necesario utilizar números negativos. Como los procesadores no entienden el símbolo “-“, y como hace falta limitar el tamaño a 8 bits, el único método encontrado es incluir el signo en el numero. Se ha decidido (y no por azar) utilizar el bit 7 para representar el signo. En los números con signo, un bit 7 a 1 significa número negativo. Si hacemos esto, perdemos un valor posible. En efecto B’10000000’ (-0) será ahora igual que B’00000000’ (0). Es más, por razones de facilidad de cálculo, decidimos utilizar una notación ligeramente diferente. Para tener un número negativo procederemos en 2 etapas:

1. Invertimos la totalidad del número. 2. Añadimos 1.

Así obtendremos lo que llamamos el complemento a 2. Ejemplo: sea el número 5, es decir B’00000101’. ¿Cómo escribiremos -5?

1. Invertimos todo (complemento a 1): B’11111010’ 2. Añadimos 1 (complemento a 2): B’11111011’

Para hacer la conversión inversa, se procede de la misma manera.

1. Invertimos todo (complemento a 1): B’00000100’ 2. Añadimos 1 (complemento a 2): B‘00000101’

Y obtenemos el número de partida.

Page 12: Manual Instrucciones PIC16F84A

12

Para los números con signo tendremos ahora los siguientes límites:

• El valor más grande es B’01111111’, es decir +127.

• El valor más pequeño es B’10000000’, es decir –128. Comprobemos que las operaciones continúan funcionado correctamente. Tomemos -3 +5.

B ’11111101’ (-3) + B ’00000101’ (5) ----------------------------- B’100000010’ (2)

Me puede decir que esto no es 2. Si observa bien verá que hay 9 bits. El procesador no opera más que con 8. El 9º bit es un bit especial que veremos más tarde. En el registro del procesador estarán los primeros 8 bits, es decir, B’00000010’, que representa 2. Si me ha seguido hasta ahora estará a punto de decirme: cuando tenemos B’11111101’ ¿Cómo estoy seguro de que es -3 y no 253? No lo sabremos sin conocer previamente el contexto. Sepa que los números significan lo que el programador ha querido que signifiquen. Si trabaja con números con signo o no, o si el octeto representa otra cosa (un tiempo, un carácter, etc.). Lo único importante es res-pectar el significado que usted ha fijado a la hora de crearlos. Es por lo tanto su responsabilidad decidir la ne-cesidad de que tipo de datos precisa.

2.6 Las operaciones booleanas ¿Qué es esto? Me preguntará. Para simplificarlo, diremos que se trata de operaciones que se realizan bit a bit sobre un octeto determinado. Más que una gran teoría sobre el algebra de Boole (veo que respira mejor), voy a ser más concreto presentándole las operaciones indispensables que hay que conocer para programar los PIC® u otros micro controladores.

2.6.1 El complemento También llamado “inversión” o “NOT” o también complemento a 1. A menudo se señaliza como”!”. Su fun-cionamiento es muy simple y consiste en invertir todos los bits del octeto (0 cambia a 1 y 1 se cambia a 0). Ejemplo: NOT B’10001111’ = B’01110000’ Como ve, para las operaciones booleanas es mucho más fácil trabajar directamente en binario. Traduzca el ejemplo sucesivamente a hexadecimal (podemos decir “hexa” como los “pro”) y después a decimal e intente complementar directamente. Buenos días neuronas. ¿Para qué sirve esta operación? Por ejemplo, para leer un valor cuyos bits han sido invertidos, para calcular números negativos y para otras operaciones que veremos más adelante.

Page 13: Manual Instrucciones PIC16F84A

13

2.6.2 La función « Y » o « AND » Llamada también multiplicación bit a bit, y a menudo, anotada como “&”. Consiste en aplicar una palabra sobre otra palabra y multiplicar cada bit del mismo rango. Para realizar una operación “Y” necesitaremos dos octetos. Las diferentes posibilidades están dadas en la siguiente tabla (se lee en horizontal). Primera línea: 0 AND 0 = 0. Este tipo de tabla se llama “tabla de verdad” o “tabla de Karnaugh”, debido al nombre de su inventor, Maurice Karnaugh.

Vemos que la única posibilidad de obtener un 1 es que los dos bits sean 1. Esto corresponde a una multiplica-ción directa de los bits. Ejemplo: B’11001100’ AND B ‘11110000’ = B’11000000’ ¿Para qué sirve esta operación? Es utilizada para enmascarar (ocultar) los bits que no nos interesan. Tomemos el ejemplo anterior. El segundo octeto contiene 4 bits a 1 y 4 bits a 0. Observe el resultado obteni-do. Los 4 primeros bits del primer octeto se han mantenido como estaban (1100) y los otros 4 han pasado a 0. Por medio de esta operación, podemos forzar a 0 los bits que queramos sin conocer previamente qué valor tenían. Esta operación es conmutativa, es decir, obtendremos el mismo resultado invirtiendo el orden de los octetos (el primero por el segundo y viceversa).

2.6.3 La función « O » u « OR » A menudo anotada como “|” permite, como su nombre indica, posicionar un bit a 1 si el bit1 o el bit2 son 1 (o ambos a la vez). La siguiente tabla de verdad explica su funcionamiento.

Page 14: Manual Instrucciones PIC16F84A

14

Ejemplo: B’10001000’ OR B’11000000’ = B’11001000’ ¿Para qué sirve esta instrucción? Nos permite forzar no importa que bit de un octeto a 1 sin conocer su esta-do precedente. Puede ver que en el ejemplo precedente, los dos primeros bits han sido forzados a 1 independientemente del nivel precedente. Esta operación también es conmutativa. .

2.6.4 La función « O EXCLUSIVA » o« Exclusif OR » o « XOR » Esta es la última operación que vamos a abordar en esta puesta a nivel. Se comporta como una OR con un de-talle añadido. Para obtener un 1 hace falta que el bit1 sea 1 O el bit2 sea 1 EXCLUIDO que ambos sean iguales. Si los dos bits son 1 el resultado será 0.

Por ejemplo: B’10001000‘ XOR B’11000000’ = B’01001000 ‘ ¿Para qué sirve? Simplemente para invertir no importa que bit de un octeto sin tocar el resto de ellos. En el ejemplo precedente puede ver que en la posición de los dos bits a 1 del segundo octeto, en el primer octeto han quedado invertidos y el resto permaneció sin cambios. Con esto se termina el primer capítulo consagrado a los PIC®. Sé que ha sido “espeso” pero, si no domina es-to que acabamos de explicar jamás podrá realizar correctamente sus propios programas.

2.7 Unas palabras acerca de las unidades

Hemos hablado de octetos y de bits. En informática utilizamos corrientemente términos como Byte y bit. En-contrará igualmente el término “palabra” (Word). Una palabra es un múltiplo de bit. El termino palabra debe ser precisado con el tamaño de la misma. Nos podemos referir implícitamente a un tamaño de palabra mani-pulado por el componente (un procesador de 16 bits manipula palabras de 16 bits). Cuando vea este término, compruebe de qué se trata en función del contexto. Los símbolos más corrientes son: Octeto: « B » (por byte) Bit: « b » Los ingleses están obligados a utilizar “B” mayúscula para los octetos con el fin de evitar confusiones con “b” minúscula para los bits. La “traductomania” imperante en Francia hace que sea el único país que ha adoptado el símbolo “o” para octeto. Por otra parte, visto que Francia no ha impuesto traducción para “bit” no hay ries-go de confusiones.

Page 15: Manual Instrucciones PIC16F84A

15

Tenga en cuenta que todo esto es muy relativo dado que le organismo de validación de símbolos (el SI) ha re-usado validar una serie de símbolos por lo que: B: es ilícito porque ya se utiliza para definir el Bel. b: es ilícito porque se utiliza para el bar. O: es ilícito por posible confusión con 0. o: no corresponde a nada para un anglófono y por lo tanto no es oficial. Brevemente, la situación no está clara, tendrá que hacer referencia a los documentos que esté leyendo pero que, en general, siguen las convenciones que le he dado hasta ahora. Aún nos queda hablar de los múltiplos (kilo, mega, etc.) lo que se hace rápidamente. En base 10, nuestra base de todos los días, se decide adoptar múltiplo de la potencia 3. Así por ejemplo:

kilo (k) = 103 (el único símbolo > 1 en minúscula, debido a una posible confusión con Kelvin) Mega (M) = 106

Giga (G) = 109

Tera (T) = 1012

Pero trabajando en base 2, estos valores no caen justos, y 10³ no representa en base 2mas que un valor entre otros muchos (le dejo a usted convertirlo a binario). Hace falta representar múltiplos que sean particulares. Se procedió en una primera instancia, a recuperar pura y simplemente los términos utilizados en base 10 (kilo, mega, etc.) tal como recuperamos las letras para las cifras en el sistema hexadecimal. Se les ha reajustado los valores utilizando los exponentes de 2 como múltiplos de 10. De esta forma hemos definido:

kilo = 210

Mega = 220

Giga = 230

Tera = 240

De esta forma, un kilo octeto en informática vale 210 octetos, es decir 1024 octetos, mientras que un kilogra-mo vale 1000 gramos. La situación empeora para Mega puesto que 1 Mega octeto vale 1048576 octetos mientras que un Mega gramo vale 1000000 gramos. Los vendedores de material de almacenamiento masivo de datos se lo han saltado. Ellos conservan la deno-minación original (legal) en potencias de 10, mientras que los informáticos trabajan en potencias de 2. De tal forma, cuando compramos un disco duro de 100 Mb (100 Mega octetos) tendrá 100.000.000 de octetos. Los sistemas operativos (Windows) señalan el tamaño generalmente en “Megas informáticos” y traducen este tamaño a (100.000.0000 / 220

) = 95,4Megaoctetos. Resumiendo 100 Mega octetos de constructor de discos duros = 95,4 Mega octetos de informático. Está claro que la situación no podía perdurar. Al comienzo de los años 2000 se decidió regular todo esto. Era claro que sería en base a inventar nuevos términos, siendo el primero el término “kibi” que representa el múltiplo de base 2: 210.

Page 16: Manual Instrucciones PIC16F84A

16

Llegamos a las convenciones siguientes:

Kibi (Ki) 2 10 1024 1,024k (retomamos la mayúscula en Ki)

Mébi(Mi) 220 1048576 1,048586 M

Gibi (Gi) 230 1073741824 1,073741824 G

Tébi (Ti) 240 1099511627776 1,099511627776 T

Pebi (Pi) 250 1125899906842624 1,125899906842624 P

Exbi (Ei) 260 1152921504606846976

Zebi (Zi) 270 1180591620717411303424

Yobi (Yi) 280 1208925819614629174706176

Así, en posesión de nuevas convenciones no existirá ninguna duda sobre los términos utilizados. Deberíamos encontrar en informática los términos Ki, Mi, etc. La situación real está muy lejos de ser ni parecida. La gran mayoría de los libros de informática y de los logiciales continúan utilizando los kilo para las potencias de 2, a veces incluso con la k mayúscula (KB). Deberá estar atento a esta situación que tardará bastantes años en re-gularizarse. Así pues, 210

octetos es un KiB para el mundo y es un Kio para Francia. 230

bits son ahora un Gib para el mundo. Su interés será entender a su interlocutor, aunque use términos incorrectos, a la vez que usted emplea los términos correctos. De paso, no confunda Gibi con la famosa marca de Wisky, sería malo para sus neuronas y para sus relaciones con su jefe.

Page 17: Manual Instrucciones PIC16F84A

17

3. Composición y funcionamiento de los PIC®

Por fin algo interesante. Vamos a colgarnos ahora de un PIC®, en particular del PIC16F84. Tenga por segu-ro que todo lo que veamos del 16F84 podrá utilizarlo directamente sobre otros 16Fxxx que no son otra cosa que 16F84 mejorados. Cada dispone de las funcionalidades de los modelos inferiores, aumentadas con nue-vas funciones. En el momento de la revisión 13 de este curso, el modelo que encontrará será probablemente el 16F84A, lo cual no supondrá ningún problema para este curso. Las diferencias son únicamente de hardwa-re. Lo primero que debe hacer es cargar el datasheet del PIC16F84, pues es el documento que vamos a utilizar en el resto de las lecciones. Le aconsejo que lo imprima pues es algo que necesitará constantemente cuando desarrolle sus propios programas. Estos datasheet son mis libros de cabecera. Encuentro más juicioso trabajar mediante la práctica y comentar-los en un orden útil, mejor que traducirlo a lo “bestia”.

3.1 ¿Qué es un PIC®?

Un PIC® no es otra cosa que un micro controlador, es decir, una unidad de tratamiento de información de tipo micro procesador a la que se le han añadido periféricos internos de comunicación con el exterior permi-tiéndonos realizar montajes sin necesidad de añadir componentes externos, o al menos, con un reducido número de componentes externos. La denominación PIC® está bajo el copyright de Microchip® , por lo que otros fabricantes no pueden utili-zar ese término para sus micro controladores. Un PIC® es un micro controlador de la marca Microchip® . Los son componentes RISC (Reduced Instructions Set Computer), componentes con un juego de instruccio-nes reducido. ¿Por qué? Sepa que contra más reducido es el número de instrucciones, más fácil y más rápido es la decodificación y el componente funciona más rápido. Habrá adivinado que en el mercado existen dos familias opuestas, la RISC y la CISC (Complex Instructions Set Computer). En los CISC disponemos de menor velocidad de tratamiento, pero las instrucciones son mucho más complejas, potentes y más numerosas. Se trata de una elección estratégica. Todos los PIC® Mid-Range tienen un juego de 35 instrucciones, almacenan cada instrucción en una sola pa-labra de programa y ejecutan cada instrucción (salvo saltos) en un ciclo. Tendremos grandes velocidades y las instrucciones son fácilmente asimilables. La ejecución en un solo ciclo es típica de los componentes RISC. El reloj suministrado al PIC® está pre dividido por 4 a nivel de este. Es esta base de tiempos la que nos da la duración de un ciclo. Si por ejemplo utilizamos un cuarzo de 4 MHz, tendremos 1000000 ciclos / segundo, o, como el PIC® ejecuta prácticamente 1 instrucción por ciclo, excepto saltos, esto nos dará una potencia del orden de 1 MIPS (1 M illion d’ Instructions Par Seconde). Piense que los PIC® pueden alcanzar más de una docena de MHz según la familia y tipo. Es una velocidad de tratamiento más que honorable.

Page 18: Manual Instrucciones PIC16F84A

18

3.2 Las diferentes familias de PIC® La familia de los PIC® de 8 bits (manipulan datos de 8 bits en una sola operación) está subdividida en el mo-mento de escribir la revisión 1 de este curso en 3 grandes familias. La familia Base-Line, que utiliza palabras de 12 bits para ciertas instrucciones (veremos qué es esto más ade-lante) (12C508) y 14 bits para otros 12F675. La familia Mid-Range, que utiliza palabras de 14 bits (de la que forman parte el 16F84 y el 16F876). La familia High-End, que utiliza palabras de 16 bits y que se describen en la parte 5 de este curso. Después han aparecido otras familias como la Enhanced family, los PIC10F y la cosa no hace más que evo-lucionar. Nos podemos encontrar también los PIC® 16 bits, en los cuales algunos incluyen un DSP (Digital Signal Processor), y los de 32 bits. Por otra parte, para cada familia, nuevos modelos y nuevas funcionalida-des no paran de aparecer por lo que es a usted a quien le incumbe, una vez finalizado este aprendizaje, con-sultar el sitio de Microchip® para elegir el modelo que más le convenga para su aplicación particular. Nosotros nos limitaremos en las primeras 4 partes de esta obra (desde el curso parte 1 al curso parte 4) a la familia Mid-Range. La obra que está a punto de leer concierne a las bases necesarias para utilizar el más simple de los PIC16F, el PIC16F84. El curso parte 5 le dará las pistas para utilizar la familia más potente de 8 bits, la familia High-End. Cuando lo comprenda todo, tendrá las bases suficientes para pasar a otras familias, incluso a otros micros controlado-res. Tenga en cuenta desde ahora que el datasheet del 16F84 no es más que una pequeña parte de la documenta-ción completa. Para tener toda la documentación, necesitará descargar más de 600 páginas de documenta-ción del sitio de Microchip®, los datasheet para la gama Mid-Range. De todas formas, la documentación de base es suficiente para el 99,9% de las aplicaciones, y, además, los da-tasheet Mid-Range están disponibles en el formato de un fichero por capitulo. Por mi parte tengo práctica-mente todo impreso, pero le aconsejo que lo haga tal como lo vaya necesitando. Esos documentos compor-tan informaciones suplementarias puntuales, sobre todo a nivel electrónico. 3.3 Identificación de un PIC® Para identificar un PIC® utilizaremos simplemente su número. Las dos primeras cifras indican la categoría del PIC®, 16 indica PIC® Mid-Range. A continuación la letra L: esta indica que el PIC® puede funcionar con una excursión de tensión de alimenta-ción más tolerante (por ejemplo 3 V). Seguidamente se encontrará con una de estas posibilidades:

• C indica que la memoria de programa es una EPROM o más raramente una EEPROM.

• CR indica una memoria de tipo ROM.

• F indica una memoria de tipo FLASH. A este nivel, solo las memorias de tipo EEPROM o FLASH pueden ser borradas, por lo que no espere poder volver a programar un PIC® de tipo CR.

Page 19: Manual Instrucciones PIC16F84A

19

Para las versiones C lea el datasheet. El 16C84 puede ser reprogramado, se trata de una memoria eeprom. El 12C508, por ejemplo, posee una memoria de programa EPROM que solo se puede borrar por exposición a luz ultravioleta. Este chip posee una ventana transparente para borrar la memoria, que es una versión especial para desarrollo y no la versión que nos encontramos normalmente. Un componente que no se puede reprogramar lo llamamos O.T.P. por One Time Programming: componen-te de programación única. Las últimas cifras identifican precisamente el PIC® (84). Para acabar, verá sobre el encapsulado el sufijo “-XX” en los cuales XX representa la frecuencia máxima de re-loj que soporta. Por ejemplo, -04 indicará que puede soportar hasta 4 MHz. De hecho, parece que este dato es puramente comercial y que todos los PIC® de un mismo modelo soportan trabajar a la velocidad máxima del modelo, velocidad mostrada al comienzo del datasheet. Aparentemente esta información es inútil. De to-das formas, no me hago responsable de este último comentario. Es su responsabilidad juzgar y obrar en con-secuencia. En un proyecto comercial y a fin de evitar cualquier problema, yo seguiría las indicaciones del fa-bricante. Para un proyecto casero o con fines experimentales, juzgue por usted mismo. Por lo tanto, un 16F84-04 es un PIC® Mid-Range (16) con memoria de programa FLASH (F) por lo tanto reprogramable del tipo 84 y capaz de aceptar una frecuencia de reloj de 4MHz en teoría (probablemente 10 MHz para un 16F84 y 20 MHz para un 16F84A). Una última indicación que encontrará es el tipo de encapsulado. Para nuestros experimentos utilizaremos el encapsulado de tipo PDIP, que es un encapsulado DIL de 18 patillas, con una distancia entre patillas de 0,3”. La versión de 4 MHz será más que suficiente. Microchip® evoluciona sus referencias, hay otras mencionadas, le corresponde a usted comprobar a qué co-rresponden. A destacar que hasta el presente, los PIC® son componentes estáticos, es decir, podremos bajar la frecuen-cia del reloj hasta la parada completa sin pérdida de datos y sin disfuncionamientos. Esto es por oposición a los componentes dinámicos (como el micro procesadores de su ordenador o la RAM de estos), en los cuales la frecuencia de reloj debe estar entre límites bien definidos. No pruebe nunca hacer trabajar un PIII/500 a 166 MHz, pues es un componente dinámico. Por el contrario, puede hacer trabajar su PIC16F84-04 a 1 KHz sin ningún problema incluso llegar a pararlo (sin cortar la alimentación). Si va a comprar un para utilizarlo en el curso pida un PIC® 16F84A-xx en encapsulado PDIP. Esta obra se escribió para un 16F84 que actualmente está reemplazado por el 16F84A. He conservado la terminología en esta obra, las diferencias se limitan a algunas mejoras hardware (más rapidez entre otras). Por lo tanto uso el término 16F84 para indicar un 16F84 o un 16F84A indistintamente.

3.4 Organizacion del 16F84

La memoria del 16F84 está dividida en 3 partes. En la página 4 del datasheet encontrará la tabla 1-1 que nos da una vista general de la familia 16F8X. Los números de pagina pueden variar en función de las puestas al día de la documentación por parte de Microchip®. Puede que tenga que buscar un poco, o utilizar el datasheet suministrado con el curso. Para aquellos que lo quieren entender todo, la figura 3-1 de la pagina 8 les muestra la organización interna de un 16F84.

Page 20: Manual Instrucciones PIC16F84A

20

3.4.1 La memoria de programa La memoria de programa está constituida por 1 Ki palabras de 14 bits. Es en esta zona donde vamos a escribir nuestro programa. Esto explica por qué sus ficheros en PC ocupan 2 Kio (Kibi octetos) pues hace falta 2 octe-tos para codificar 14 bits. Esto explica, igualmente, por qué cuando usted lee un virgen PIC®, lee 0x3FFF. Esto es en binario B’0011111111111111’, es decir, 14 bits útiles. Más adelante le explicaré de donde vienen los famosos 14 bits. Advierta que una instrucción está codificada sobre una palabra. 1 Ki de palabras nos da un buen millar de ins-trucciones posibles para su programa (no está nada mal). Cuando vaya a escribir un programa de 1 Ki usted ya será más que autónomo en esto de la programación de aplicaciones.

3.4.2 La memoria Eeprom La memoria EEPROM (Electrical Erasable Programmable Read Only Memory) está constituida por 64 octetos que usted puede leer y escribir desde su programa. La información allí contenida se conserva después de un corte de alimentación y es muy útil para conservar parámetros semi permanentes. Su utilización implica el uso de procedimientos especiales que veremos más adelante, pues no es una RAM sino más bien una ROM un tanto especial. Es más rápida de leer que de escribir. Si ya ha programado este tipo de memorias (2416 por ejemplo) ya habrá constatado este efecto.

3.4.3 La memoria Ram La memoria RAM (Random Access Memory) es la que no vamos a dejar de utilizar constantemente. Todos los datos almacenados aquí se pierden durante un corte de la alimentación. La memoria RAM está organizada en 2 bancos de memoria para el 16F84. La RAM está dividida además en 2 partes. En cada uno de los bancos nos vamos a encontrar “casillas” especiales llamadas “registros especiales” o SFR (Special Function Registers) así como casillas de memoria “libres” que usted podrá utilizar a su con-veniencia. Los SFR sirven para configurar su PIC, mientras que otros emplazamientos sirven para colocar sus variables (veremos de qué se trata más tarde). En el caso del 16F84 dispondrá de 64 octetos libres. La organización de la RAM se muestra en la tabla 4-2 de la pagina 13. Verá la separación vertical de los 2 bancos y abajo del todo verá dos bancos de 68 octetos de RAM. Desgraciadamente, la indicación « mapped in bank 0 » le indica que acceder a esos octetos en el banco 0 o en el banco 1 es indiferente, son lo mismo. Verá en la parte superior el nombre de todos los registros especiales en el PIC®. Veremos todos, se lo asegu-ro. Cada registro provoca un funcionamiento especial del PIC® o la puesta en servicio de una función particular. Verá que ciertos registros son idénticos en ambos bancos (FSR por ejemplo). Esto significa que acceder a ellos en el banco 0 o en el banco 1 no tiene ninguna diferencia. El banco 0 utiliza las direcciones desde la 0x00 a la 0x7F y el banco 1 desde la 0x80 a la 0xFF. Las zonas grises son lugares no utilizados (ni utilizables). El lugar 0x00 es un emplazamiento al que no se puede acceder. Para la gran mayoría de los registros, cada bit tiene una función especial. En la página 14, tabla 4-1, encon-trará los nombres de los bits de estos registros.

Page 21: Manual Instrucciones PIC16F84A

21

4. Organización de las instrucciones 4.1 Generalidades

Vamos con coraje, esto comienza a ser más concreto. Vamos a echar un vistazo al juego de instrucciones de los PIC®. Saltamos directamente a la página 55 del datasheet, al capítulo 9. Y sí, como esta obra no es un manual de referencia técnica sino un aprendizaje, será necesario ver los capítulos del datasheet en desorden. En esa página, encontrará un pequeño recuadro en gris que hace alusión a las antiguas instrucciones que ya no sirven. No nos serviremos de ellas, pues. Por el contrario, encontrará una tabla 9-1 que nos dice como son codificadas las instrucciones en los PIC®. Al fin ve a que corresponden los 14 bits de la memoria de progra-ma.

4.2 Los tipos de instrucciones

Constatará que existen 4 tipos de instrucciones que pasamos a detallar. De todas formas, no tiene por qué memorizar cono están organizadas las instrucciones, se trata simplemente de comprender que es lo que pasa.

. 4.2.1 Las instrucciones « orientadas a octeto »

Son instrucciones que manipulan los datos en forma de octetos. Están codificadas de la siguiente forma: 6 bits para la instrucción: lógico puesto que hay 35 instrucciones, nos harán falta 6 bits para codificar todas ellas. 1 bit de destino (d): 0 indica que el resultado de la operación se colocará en el registro de trabajo W (Work), 1 indica que el resultado se colocará en el operando precisado por los siguientes 7 bits. 7 bits para codificar el operando (File): si d vale 0 (ver arriba), este operando indica a la vez el dato a manipu-lar e igualmente el entorno donde el resultado será almacenado. Primer problema, 7 bits no nos dan acceso a la totalidad de la memoria RAM, he aquí la explicación de la divi-sión de la RAM en dos bancos. Hacen falta 8 bits para acceder a 256 posiciones diferentes. Hará falta una solución para recolocar el bit que nos falta. ¿Ha dicho un bit de los registros? ¡Bravo! Veo que lo ha entendido completamente. Se trata en realidad del bit RP0del registro STATUS. Puede señalarme que también hay un RP1. El 16F876, por ejemplo, dispone de 4 bancos, este bit será utiliza-do en otros PIC® que veremos en la segunda parte. Dejaremos RP1 a 0 para el 16F84, con el fin de poder “portar” sus programas hacia PIC® superiores sin problema.

4.2.2 Las instrucciones « orientadas a bits » Son instrucciones que manipulan los datos en forma de bits. Dicho de otra forma, están destinadas a modifi-car bits precisos en registros especificados. Están codificadas de la siguiente forma: 4 bits para la instrucción (en el espacio dejado libre por las instrucciones precedentes). 3 bits para indicar el número de bit a manipular (bits 0 a 7 posibles), y de nuevo: 7 bits para indicar el operando.

Page 22: Manual Instrucciones PIC16F84A

22

4.2.3 Las instrucciones generales

Son instrucciones que manipulan datos que están codificados en la misma instrucción directamente. Veremos esto más en detalle cuando hablemos del direccionamiento. Se codifican de la siguiente manera: 6 bits para codificar la instrucción. 8 bits para codificar el valor concernido (valor llamado « inmediato » porque se encuentra inmediatamente en la instrucción. El valor puede valer de 0 a 255.

4.2.4 Los saltos y llamada a subrutinas

Son instrucciones que provocan una rotura en la secuencia de desarrollo del programa. Se codifican de la si-guiente forma: 3 bits para codificar la instrucción. 11 bits para codificar la dirección de destino. Podemos ya deducir que los saltos no dan acceso a 2 Ki de memoria de programa (211). Esto no supone ningún problema, el 16F84 no dispone nada más que de 1 Ki de palabras de memoria. Para codificar una dirección de salto en la memoria de programa de un 16F84, hará falta 10 bits (210 = 1024 = 1 Ki). No olvide que la reglamentación oficial quiere que utilicemos el término “kilobinario” o “kibi”, abreviado en “Ki” para expresar 2 a la potencia 10. Desgraciadamente parece que poca gente lo utiliza en la práctica.

4.3 Panorámica de las instrucciones.

Voy a mostrarle cómo funciona la tabla 9-2 de la pagina 56. Esta tabla le permite, de un simple vistazo, infor-marle de la forma en que funciona cada instrucción. La primera columna indica el nemónico y los operandos de cada operación. Los nemónicos son palabras re-servadas (por lo que no podrá usarlas nada más que para este uso, y no para nombrar una variable, por ejemplo), que son comprendidas e interpretadas por el programa de ensamblaje, llamado « Ensamblador ». Observe aquí la confusión del lenguaje común para el término “ensamblador”, que utilizamos a la vez para in-dicar el programa que permite ensamblar el código (programa de ensamblaje) y el lenguaje utilizado en el editor (lenguaje ensamblador). Trate de usar los términos correctos, la situación en informática no es ahora simple para que le añadamos confusiones inútiles. Decir “he programado en ensamblador” no quiere decir absolutamente nada, mismo si el personal le ha entendido. Debería decir “he programado en lenguaje en-samblador”. El ensamblador es el programa que ensambla su programa escrito en lenguaje ensamblador. Va a encontrar en este sitio las instrucciones propiamente dichas que va a poder codificar en su programa. La sintaxis debe ser la siguiente para el ensamblador MPASM® (suministrado con el entorno de desarrollo in-tegrado MPLAB ®), que utilizaremos en la próxima lección.

Page 23: Manual Instrucciones PIC16F84A

23

Tendremos, en orden, para cada línea de código:

• Etiqueta (opcional o pudiendo encontrarse solo en una línea)

• Espacio(s) o tabulación(es)

• Nemónico (en mayúsculas o minúsculas)

• Espacio(s) o tabulación(es)

• Operando o valor

• Coma de separación eventualmente

• Bit de destino W o F o eventualmente número de bit de 0 a 7 si es necesario

• Espacio(s) o tabulación(es) (opcional)

• Comentario precedido de punto y coma (opcional) Observe que el nemónico no puede encontrarse en la primera columna, y que todo lo que sigue a un punto y coma es ignorado por el ensamblador (será la zona de comentarios). La primera columna es la zona reservada para las etiquetas (lugares de llamada). Dispone de la opción de insertar uno o más espacios o tabulaciones a cada lado de la coma. A título de ejemplo, dos líneas validas:

Ma_ligne ; Esto es una etiqueta

MOVF STATUS, W ; carga el registro STATUS en el registro de trabajo

Para terminar con nuestra tabla: La segunda columna de nuestra tabla nos da una breve descripción de la instrucción. La tercera columna nos da el número de ciclos necesarios para ejecutar la instrucción. Observe que todas las instrucciones necesitan 1 ciclo, excepto las instrucciones de salto que necesitan 2 (incluso las operaciones de test con salto, puesto que el resultado del test genera un salto: instrucciones anotadas como 1(2)). La 4ª columna nos da lo que llamamos el OPCODE, es decir, la palabra en binario que MPASM® va a gene-rar por usted a partir del nemónico. No lo hará nunca pero sepa que podría programar directamente el PIC® sin pasar por el ensamblador, construyendo directamente un fichero .hex con los valores obtenidos en esta columna. En ese caso, debería comprender y calcular los saltos. Esto es lo que yo hice en mis comienzos sobre un pro-cesador 6502 dado que no disponía de un ensamblador. Podríamos utilizar esta técnica para construir pro-grama auto modificables debido a la severa limitación de tamaño de memoria. Estas técnicas pertenecen a la Edad Media de la informática. Puede correlacionar estos valores con los de la tabla 9-1 a titulo educativo, si no, olvídese de esta columna.

Page 24: Manual Instrucciones PIC16F84A

24

La 5ª columna, por el contrario, es primordial y nos da los indicadores de estado (Status Flag) afectados (mo-dificados) una vez se ejecute la instrucción. Veremos estos indicadores en detalle puesto que constituyen una llave esencial en la programación. De hecho, estos indicadores de estado son los medios de que disponemos para tomar una decisión en un programa (condiciones). La última columna nos envía a las notas a pie de página. La nota 1 es muy importante, hace alusión al método de “lectura/modificación/escritura” propio de los puertos de entrada/salida (I/O). Volveremos a ello en el momento de tratar los PORTS. La nota 2 indica que una modificación de un timer repone a cero el valor en su pre divisor. Lo trataremos cuando abordemos el timer 0. La tercera nota indica que si nos servimos de la instrucción para modificar el contador del programa (PC), que apunta a la siguiente instrucción a ser ejecutada, habrá un ciclo suplementario. Es lógico puesto que esto equivale a un salto. Veremos que esta técnica es práctica para ir a buscar valores en una tabla construida en la memoria de programa.

4.4 Los indicadores de estado

Estos indicadores son indispensables para la programación. Es absolutamente necesario haber comprendido su funcionamiento (al menos para Z y C). Lea lo que sigue con mucha atención. Todos los indicadores son bits del registro STATUS. Vea la tabla en la pagina 15. Abordaremos aquí los flags Z y C. Los otros los trataremos en el estudio del registro STATUS (capi-tulo 8.2.3)

4.4.1 El indicador de estado « Z » Es el indicador de Zero y funciona de la siguiente manera: Si el resultado de una instrucción que afecta a este bit da un resultado igual a 0, el flag Z se posiciona en 1. Por lo tanto, no se complique la vida. Decir que Z=1 corresponde a decir que el resultado = 0. La tabla 9-2 en la columna 5 indica las instrucciones que modifican Z. Por lo tanto, si realiza una suma con ADDWF y el resultado obtenido es 0, el bit Z se pondrá a 1. Si el resul-tado es !=0 (diferente de 0), el bit Z valdrá 0. En los dos casos será modificado. Por el contrario, si almacena un valor con la instrucción MOVWF , el bit Z no será modificado incluso si el va-lor almacenado es 0. Estas consideraciones son validas para el resto de los flags por lo que no volveré a ello.

4.4.2 El indicador de estado « C » Es el indicador para Carry (acarreo). Si el resultado de una operación entraña desbordamiento, el bit se po-siciona a 1. Se trata, de hecho, del 9º bit de una operación. Por ejemplo: Si sumamos B’11111110’ (254)

+ B’00000011’ (3)

Obtendremos B’100000001’, (257), es decir 9 bits.

Page 25: Manual Instrucciones PIC16F84A

25

Como los registros del PIC® solo tienen 8 bits, obtendremos B’00000001’ (1) y el bit C se posicionará a 1 (de hecho el 9º bit). El resultado final es 256 + 1 = 257. Observe que si ha añadido B’11111110’ a B’00000010’ habría obtenido B’00000000’. En este caso tendría C = 1 y Z = 1, que significa resultado nulo pero con acarreo (por lo que resultado = 256). Atención, este bit sirve también de borrow (préstamo) cuando realiza una sustracción. En este caso C vale 0 si intenta sustraer un número más grande que le numero de partida, indicando un resultado negativo. En caso contrario valdrá 1 indicando un resultado positivo. C funciona de manera inversa en el caso de sustracciones. Estudiaremos en detalle estas instrucciones. Veremos los otros bits del registro en la continuación de esta obra, en el momento en que lo necesitemos.

Page 26: Manual Instrucciones PIC16F84A

26

5. Los comienzos con MPLAB® Vamos a arrancar la gran aventura con nuestro primer y modesto programa. Explicaré las instrucciones y los registros en tanto en cuanto lo vayamos necesitando. A partir de la revisión 13 del curso, trabajamos con MPLAB® 6.x que es la versión que salió después de la 5.x, utilizada en el curso hasta la revisión 12 del mismo.

5.1 Preparación para la utilización La primera cosa a realizar es ir a buscar la versión actual de MPLAB® 6.60 en el sitio de Microchip®: http://www.Microchip.com en la sección « archives ». Puede utilizar una versión más reciente (la 8.x), pero será a su cargo el lidiar con las diferencias. Le aconsejo arrancar con la versión 6.6 y pasar posteriormente a una versión más moderna una vez terminado el aprendizaje, lo que le evitará dificultades añadidas. Atención: MPLAB-X es una versión específica de MPLAB que tiene bastantes diferencias con las versiones clásicas. Esta versión presenta la ventaja de ser compatible con otros sistemas operativos (OS) sobre todo Li-nux. En este curso, las copias de pantalla están realizadas sobre MPLAB® 6.3 pero las diferencias deberían ser mínimas con respecto a la versión 6.6 (más estable). Si utiliza una versión más reciente de y esta le pregunta qué tipo de código quiere producir, respóndale siempre « ABSOLUTE ». Descomprima el fichero y proceda a su instalación. Después de la instalación, tendrá bastantes ventanas ex-plicativas concernientes a diferentes útiles de Microchip®, como el debugger y el simulador. Como presumo que no dispone aún de esos útiles (todavía), cierre todas las ventanas al final de la instalación. Personalmente, no me gusta dejar todos los datos en los subdirectorios de instalación de mis programas, en la instalación principal. Si usted es como yo, cree un directorio en un sitio que le resulte conveniente y lláme-lo, por ejemplo, DataPIC. Copie allí el fichero m16F84.asm suministrado con el curso. Es un fichero que he creado con la finalidad de poder arrancar inmediatamente con un nuevo programa. Le he llamado « m16f84.asm » con una « m » por « maqueta ». Si no tiene ese fichero, es que ha cargado el curso desde un sitio inadecuado, vaya a mi web: www.bigonoff.org para cargar la versión correcta y completa (gratuitamente). Si no desea crear un nuevo directorio, copie el fichero en el directorio de instalación: por defecto c:\program files\MPLAB IDE. Le recuerdo que guardar todos los datos en la partición que contiene todos los programas es una muy mala idea, sobre todo en caso de bloqueo de su sistema operativo (la restauración borraría sus datos). Para cada nuevo programa que vaya a crear, haga una copia de m16F84.asm. Para nuestro primer programa copie/pegue ese fichero y renombre la copia obtenida como Essai1.asm.

5.2 Creación de nuestro primer proyecto Ahora ya puede lanzar MPLAB® IDE a partir del menú de arranque o del icono en su escritorio, si aceptó tenerlo en la instalación. Después de unos instantes, se encontrará con una pantalla vacía con menú y barra de útiles. Si tiene ventanas abiertas en el escritorio de MPLAB® 6, ciérrelas todas, así sabrá como abrirlas y todo el mundo arrancará con la misma configuración. MPLAB® es un logicial que está construido en base a la noción de proyectos. Un proyecto permite memori-zar todo el entorno de trabajo necesario para la construcción de un……… proyecto. Le permitirá abrir todo el entorno de trabajo cuando lo seleccione.

Page 27: Manual Instrucciones PIC16F84A

27

MPLAB® 6 dispone de un « wizard » (asistente) para la creación de proyectos, lo que le permitirá crearlos automáticamente. De todas formas, con el fin de permitirle localizar las principales opciones de un proyecto, no me serviré de él en este curso. Si a continuación usted decide hacerlo, se selecciona con la ayuda del menú menú « project->wizard ». Vaya al menú « Project » y seleccione « new… ». La ventana que se abre le permite introducir el nombre del proyecto y el directorio de trabaja del mismo. Para el directorio, dispone del botón « browser » que le permite apuntar al buen directorio sin riesgo de equivocarse. Entre « essai1 » como nombre de su nuevo proyecto y seleccione el directorio en el que colocó su fichero “maqueta” y su fichero essai1.asm. He nombrado el fichero asm de manera idéntica al proyecto, pero esto no es obligatorio.

Una vez presionado el botón <OK>, una nueva ventana aparecerá en el rincón superior izquierdo del es-critorio del MPLAB® IDE.

Page 28: Manual Instrucciones PIC16F84A

28

En esta ventana, usted ve el nombre del proyecto así como los ficheros asociados a él. Por el momento no tiene ninguno, es normal. Vamos a comenzar a precisar los parámetros importantes de nuestro proyecto, empezando por el tipo de PIC® que vamos a utilizar. Seleccione el menú « configure->select device », aparecerá una ventana que le propondrá la elección de un PIC®. Observe que, por defecto, le propone un de la familia 18F, la promoción de nuevos productos obliga…… Seleccione el PIC16F84 (si elige el PIC16F84A, modifique todas las referen-cias al en todos los ficheros fuente: de todas formas es inútil).

La ventana nos muestra, con la ayuda de leds verdes y rojos, cuales son los útiles soportados por el PIC® se-leccionado. Usted verá que el simulador integrado (MPLAB® SIM) funciona con su PIC®, y así para los otros útiles de desarrollo disponibles en Microchip®. Una vez pulsado <OK> la ventana se cierra.

Page 29: Manual Instrucciones PIC16F84A

29

Vamos ahora a precisar qué lenguaje vamos a utilizar, sabiendo que nosotros trabajamos en lenguaje ensam-blador, pero que diferente ensambladores/compiladores son propuestos por Microchip® y por otras com-pañías. Seleccione el menú « project -> Select langage toolsuite ». En la ventana que se abrirá seleccione en el menú desplegable: « Microchip MPASM® toolsuite ». MPASM® es el ensamblador por defecto de Micro-chip®.

En las ventanas inferiores puede ver el nombre del ejecutable utilizado por MPASM®, no se preocupe. Pulse <OK> para cerrar la ventana. Nos hace falta ahora indicar a MPASM® cuál es nuestro o nuestros fichero(s) fuente. Eso se hace en la ven-tana que se quedó abierta en la esquina superior izquierda. Para añadir un fichero fuente es muy simple: cli-quee con el botón derecho sobre « source files », después seleccione « Add ». Una vez seleccionado el fi-chero, el nombre de este aparecerá en el árbol de ficheros. Observe que MPLAB® apunta por defecto a su directorio de trabajo, anteriormente elegido.

Page 30: Manual Instrucciones PIC16F84A

30

Aquí es importante comprender que el fichero fuente elegido será el que se ensamble (o compile si utiliza otro lenguaje). Dicho de otra forma, si en ese fichero figura una instrucción « include » que incluye otro fiche-ro, usted no deberá seleccionarlo explícitamente, será añadido al proyecto en el momento del ensamblaje. Por el contrario, si desea ensamblar dos ficheros simultáneamente, y su primer fichero no hace referencia al segundo, deberá añadir ese fichero al árbol de ficheros. Todos nuestros proyectos solo necesitarán añadir un solo fichero. Si sus proyectos necesitan añadir uno o más

ficheros suplementarios, la directiva « include » deberá ser añadida en el fichero fuente con el fin de que el ensamblador integre de manera automática esos ficheros. No necesita nada más, los otros elementos del árbol no son necesarios para un proyecto en lenguaje ensam-blador del tipo de los que vamos a hablar. Nos queda un elemento importante por precisar, es el sistema de numeración utilizado por defecto. Seleccio-

ne el menú: « Project -> build options -> project ». Se abrirá una ventana. Seleccione la pestaña « MPASM assembler ».

Page 31: Manual Instrucciones PIC16F84A

31

Como hemos decidido trabajar en hexadecimal bajo la forma 0x, y el binario bajo la forma B’’. Todo número

sin prefijo será considerado decimal. Elija la opción « Decimal ». No toque las otras opciones, las utilizará po-siblemente cuando llegue a ser un “pro” y quiera adaptar ciertos parámetros. En su momento le resultará más claro. Con la finalidad de evitar cualquier olvido, le aconsejo poner un prefijo delante de cualquier número que utili-ce, esto le evitará no pocos sinsabores. Utilice, por ejemplo, la siguiente sintaxis:

• 0x10, H’10’, o 10h: para notaciones hexadecimales.

• B’00010000’: para notaciones binarias.

• D’16 ‘: para notaciones decimales.

• .16: otra notación decimal (no olvide el «.»). Un numero escrito sin prefijo será interpretado según el parámetro que hemos descrito anteriormente y pue-de resultar un problema si después integra este fichero en otro proyecto o, simplemente, y le envía ese fiche-ro a otra persona sin precisarle cual es la notación por defecto. Si a pesar de todo decide utilizar un prefijo por defecto, no olvide señalarlo en los ficheros fuente con la ayu-da de la directiva “radix”, como por ejemplo:

radix dec ; se trabaja en decimal por defecto

Esta directiva suplanta la elección realizada en MPLAB y, por lo tanto, el fichero funcionará correctamente

sea la que sea la configuración de MPLAB y de todos aquellos que vayan a ensamblar el fichero. Esta directiva

no se puede colocar en la primera columna, ponga un espacio o una tabulación antes de la directiva « radix ».

Ahora MPASM® está listo para ensamblar el programa. Usted me dirá: ¡No hay nada en la pantalla!

Efectivamente MPASM® no se preocupa de los ficheros que hay en la pantalla, solo cuentan los ficheros que hay en el árbol de ficheros y los incluidos a partir de ellos.

Puede tener todos los ficheros que quiera en la pantalla, o no tener ninguno. A MPLAB® le da lo mismo. Por el contrario, todos los ficheros abiertos desde la salvaguardia del proyecto serán reabiertos automáticamente en la próxima apertura del proyecto, aunque no sirvan para nada. Esta propiedad es muy práctica para reali-zar copiados/pegados a partir de otro fichero o para ver que hizo otra persona mientras programamos. Vamos a mostrar nuestro fichero fuente para ver que contiene. El método más simple para hacerlo es hacer

doble clic sobre el nombre en el árbol de la ventana essai1.mcw. Hágalo. He aquí su fichero de partida abier-to. Póngale el tamaño adecuado haciéndole ocupar la mitad izquierda de la pantalla si necesita sitio (no nece-sitará la ventana de proyecto por lo que puede ocultarla o reducirla). Para abrir un fichero que no está incluido en el árbol, utilice el menú « file->open » o el icono de apertura en la barra de útiles.

Si quiere que sus pantallas tengan mejor apariencia, cambie las políticas del editor. Para esto, seleccione « Edit -> properties », después la pestaña « text ». Luego pulse el botón que le permitirá elegir la política de

caracteres: « Courier New » de estilo « Standard » e imponer un tamaño « 9 ». Para los que les interese, yo trabajo con un monitor de 22” y mi resolución es de 1680x1050, esto le indica las

proporciones. Verifique igualmente en la pestaña que la longitud está fijada a 4 (tamaño en MPLAB® >7.5). Modifíquelo según necesidad.

Page 32: Manual Instrucciones PIC16F84A

32

Ahora su fichero asm está en la pantalla. Observen aquellos que vienen de MPLAB® 5, que la gestión de las tabulaciones es diferente en MPLAB® 5 y en MPLAB® 6, lo que significa que la puesta a punto de las páginas de un fichero escrito en MPLAB® 5 no se respetarán en MPLAB® 6. A aquellos que dispongan de la versión precedente les interesa usar los nuevos ficheros.

Para aquellos que viene de MPLAB® 5, verán que MPLAB® 6 es más simple de manipular, lo que será todavía más evidente cuando utilicemos el simulador. Vamos a examinar este fichero más detenidamente.

Page 33: Manual Instrucciones PIC16F84A

33

6. Organización de un fichero « .asm »

Para empezar, clique en cualquier sitio del interior de ese fichero. Usted se encuentra en el interior de un simple fichero de texto con sintaxis coloreada. En la esquina inferior izquierda verá un número de línea y de columna. Es la posición actual de su cursor. Me serviré de esa posición para guiarle a través del fiche-ro. No añada por lo tanto ninguna línea, por el momento, para que se conserve la correspondencia con el presente texto. Si no es capaz de ver el numero de línea vaya al menú « edit -> properties » y marque « line numbers » en el submenú « editor » Si no consigue efectuar modificaciones en su fichero y el teclado parece inactivo (ocurrido con MPLAB® 5.x) es que usted ha utilizado un carácter extendido en el nombre del fichero. Al contrario que MPLAB® 5, MPLAB® 6 ahora gestiona todos los caracteres extendidos para los ficheros y para los dosieres.

6.1 Los comentarios

Desde la línea 1 a la 31 usted ve un gran cuadro. Si mira con atención vera que el primer carácter de cada línea es el símbolo « ; ». Todo lo que está a continuación está considerado como zona de comentarios. Us-ted puede poner ahí lo que usted quiera. Procure coger el hábito de comentar siempre sus programas. Esté seguro de que dentro de 6 meses no se acordará en absoluto de que es lo que pretendía hacer. Será entonces cuando los comentarios le serán de muchísima utilidad si decide modificar el programa. Y omito los casos en que, como yo, usted distribuye sus fuentes o trabaja en una empresa donde otros deberán releerlos. Rellenemos el cuadro con las diferentes referencias. Adjunto a esta lección el fichero « essai1.asm » tal como estará al final de la misma.

6.2 Las directivas

En la línea 34 encontramos una directiva destinada al MPASM® para indicarle que tipo de procesador se utiliza en este programa. Las directivas no forman parte del programa, no son traducidas a OPCODES, solo sirven para indicarle al ensamblador de qué manera tiene que trabajar. Son comandos destinados exclusivamente al ensambla-dor. Por el contrario, las instrucciones se traducirán en OPCODES y serán cargados en el PIC®. Es imperativo hacer correctamente la distinción. La línea 36 contiene una directiva de la que ya hemos hablado, que le indica a MPASM que cada vez que omitamos un prefijo para un número, este número lo tiene que considerar como representación decimal.

6.3 Los ficheros « include » La línea 35 le indica al ensamblador, a través de la directiva « #include », que en este preciso punto debe cargar el contenido del fichero P16F84.inc (MPASM efectúa un simple copiar y pegar a su copia de traba-jo interno). ¿Qué contiene este fichero? Simplemente los valores de todas las constantes que vamos a uti-lizar. Para ver que contiene vaya al menú « file ->Open », elija « all source files » en el cuadro inferior y abra p16F84.inc del repertorio.

C:\Program Files\MPLAB IDE\MCHIP_Tools para MPLAB® 6 y C:\Program Files\Microchip\MPASM Suite para MPLAB® 7

Page 34: Manual Instrucciones PIC16F84A

34

Una vez pasada la zona de comentarios, verá unas líneas del estilo:

FSR EQU H'04' Esta línea, incluyendo la directiva « EQU » significa simplemente que FSR es igual a 0x04. Dicho de otra manera, siempre que utilice la palabra FSR en una instrucción, MPASM reemplazará simplemente FSR por H’04’ (0x04). Siendo 04 simplemente la dirección del registro FSR en la memoria RAM del PIC. H’04’ es otro método autorizado para expresar un número en hexadecimal, así como h04. Advierta que el prefijo siempre está precisado, puesto que de otra forma Microchip no sabría que sistema de numeración por defecto está usted utilizando. Si usted coge la tabla 4-2 de la pagina (13), constatará que este es el caso. Este fichero está destinado principalmente a evitar que usted tenga que memorizar todas las direcciones. Un nombre es mucho más fácil de utilizar y de memorizar que una dirección. Cierre ahora el fichero p16F84.inc para no atiborrar la ventana de trabajo.

6.4 La directiva _CONFIG

La línea siguiente comienza por « __CONFIG ». Esta línea contiene los famosos “fusibles”, por utilizar una expresión popular, que fijan el funcionamiento del PIC®. Los valores que están aquí escritos se integraran en el fichero « .hex » para señalar al programador los valores a codificar en las direcciones específicas del PIC®. Posteriormente volveremos al este tema. Sepa que si un fichero « .hex » ha sido creado por un programador atento que ha utilizado esta directiva, usted no tendrá ninguna necesidad de definir estos parámetros en el momento de la programación. El lo-gicial del programador irá normalmente a buscar estos valores en el fichero mismo. Desgraciadamente aún queda un número de personas que creen que esta directiva así como los comen-tarios y otras facilidades están reservados a los principiantes y no se utilizan normalmente. ¡Gran error! Recibo regularmente correos de personas que estiman este procedimiento inútil y que seguidamente in-curren en errores. Si usted no utiliza la directiva « __CONFIG » estará condenado a indicar explícita-mente a cada utilizador de sus ficheros « .hex » de qué forma deben configurar esos bits a nivel de su programador de PIC. Y usted mismo deberá recordarlo cada vez que programe su fichero. Reconózcame que como método es un poco “bárbaro”. He colocado en los ficheros todos los valores posibles de estos parámetros junto con su explicación co-rrespondiente. Es suficiente con reemplazar un valor con el deseado. Por ejemplo, para activar el Code Protect (protección de lectura) reemplace simplemente la línea:

__CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC

por __CONFIG _CP_ON & _WDT_ON & _PWRTE_ON & _HS_OSC

Hágalo. Note que los diferentes valores están unidos por el símbolo « & » (AND) explicado en la lección sobre los sistemas de numeración. Funciona pues poniendo los bits a “0”. Preste atención a precisar todos los valores, incluso aquellos que no vaya a utilizar. Los valores correspondientes se encuentran de nuevo en el fichero « P16F84.INC ». No hay nada de magia, todo se explica reflexionando un poco sobre ello.

Page 35: Manual Instrucciones PIC16F84A

35

6.5 Las asignaciones

En las líneas 58 y 63 encontrará las asignaciones personales que funcionan bajo el mismo principio que el fichero « .inc ». ¿Para qué sirve? Es para facilitar el mantenimiento del programa. Es mucho más fácil recordar en su pro-grama el valor « MASQUE » que manipular el valor 0x5B. En el modo de trabajo, la facilidad para man-tener una aplicación es una de las prioridades principales de un proyecto por la simple razón que este mantenimiento tiene un coste. Las asignaciones se comportan como simples sustituciones de texto. En el momento de ensamblar, cada vez que el ensamblador se encuentra una asignación, la reemplazara automáticamente por su valor. Otra gran ventaja es que si usted cambia el valor de una asignación, este cambio será efectivo para todo el programa. Usted no se arriesga a olvidarse de algún valor a cambiar por el camino. Le aconsejo vivamente a utilizar la asignación así como los otros métodos que veremos más adelante. La sintaxis es simple puesto que se trata de EQU (igual a). El símbolo funciona « = » de la misma manera. Ejemplo de asignación: mivalor EQU 0x05

6.6 Las definiciones

Descendamos un poco más. Descubrimos en las líneas de la 73 a la 75 la utilización de la directiva « #DEFINE ». Las definiciones funcionan como las asignaciones, salvo que reservamos las asignaciones para los valores y las definiciones para sustituir un texto más complejo. Por ejemplo, podríamos utilizar un PORT seguido de un número de bit, o bien una instrucción seguida de sus parámetros. Una definición está construida de la siguiente manera: la directiva #DEFINE seguida del nombre que deseamos utilizar y finalmente la cadena a sustituir. Por ejemplo:

#DEFINE mibit PORTA,1

La utilización de esta definición se realiza utilizando simplemente su nombre en el programa. Por ejem-plo:

bsf monbit ; poner mibit a 1

Será traducido por MPLAB® como :

bsf PORTA,1 ; poner mibit a 1

6.7 Los macros

Más abajo, en las líneas de la 83 a la 86 encontramos un macro.

LIREIN macro comf PORTB,0 andlw 1 endm

La macro se compone de un nombre escrito en la primera columna seguido de la directiva« macro ». El final de la macro está definido por la directiva « endm » (end of macro).

Page 36: Manual Instrucciones PIC16F84A

36

Ya habremos deducido que la finalidad de una macro es reemplazar un bloque de código que utilizamos a menudo. En nuestro ejemplo, cada vez que se encuentre la macro LIREIN esta será reemplazada, en el momento del ensamblaje, por dos líneas:

comf PORTB , 0 andlw 1

La macro simplifica la escritura pero no reduce el tamaño del fichero .hex obtenido puesto que las dos líneas serán efectivamente escritas en el PIC®. Adviértase que se pueden utilizar macros más complejas con paso de parámetros. Es uno de los intereses principales de las macros. No entraremos en estas funcionalidades particulares por ahora. Advierta así mismo que dispone de una ayuda en el menú « help->MPASM Help ». En efecto la ayuda de MPLAB® concierne a la utilización del logicial. Las ayudas concernientes al lenguaje están en MPASM® dado que es un lenguaje que utiliza MPLAB®.

6.8 La zona des variables

Toda zona definida por el utilizador como declarada para variables comienza por la directiva CBLOCK seguida por la dirección de comienzo de la zona. Para situar las variables, que son emplazamientos de memoria a las cuales les hemos asignado un nom-bre, debemos consultar de nuevo la tabla 4-2. Vemos que la zona de RAM de libre utilización comienza en la dirección 0x0C. Nuestra zona de variables contendrá pues la directiva:

CBLOCK 0x00C ; comienzo de la zona de variables

Inmediatamente podrá utilizar 68 emplazamientos de memoria, que responderán a la sintaxis siguiente: nombre de la variable seguido del signo « : » (los espacios son ignorados) seguido del tamaño utilizado. Por ejemplo:

w_temp : 1 ; Zona de 1 byte

Deberá precisar el final de la zona en curso con la ayuda de la directiva ENDC:

ENDC ; Fin de la zona

6.9 Las etiquetas

Usted se encontrará en el programa en la primera columna lo que llamamos etiquetas. Son nombres que escogemos y que no son otra cosa que posiciones para MPASM®, estas posiciones se podrán utilizar di-rectamente en el código fuente. El ensamblador las sustituirá por la dirección correcta del programa en las que estén posicionadas. Esto nos evitará tener que calcular constantemente cuál es la dirección de cada punto concreto del programa. Veremos más adelante el principio de funcionamiento.

Page 37: Manual Instrucciones PIC16F84A

37

6.10 La directiva « ORG »

La directiva ORG, seguida de la dirección, precisa en qué dirección de la memoria del PIC® será co-

locada la siguiente instrucción. Excepto por esta situación, cada instrucción se emplazará en la direc-

ción siguiente a la instrucción precedente.

Hay dos cosas importantes a tener en cuenta:

Después de un reset o de una puesta en tensión, el PIC® arranca siempre desde la dirección 0x00.

El comienzo de su programa se debe situar siempre allí.

La dirección 0x04 es la dirección utilizada por las interrupciones (veremos el principio más tarde).

No queda, pues, mucho espacio para colocar el programa (entre la 0x00 y la 0x03). Empezaremos

pues por un salto hasta el lugar donde esté colocado el programa principal donde tendremos más

espacio. Veamos cómo funciona:

org 0x000 ; Direccion de arranque despues de un reset goto init ; Direccion 0 : inicializar La primera línea es nuestra directiva ORG que indica que la línea siguiente se colocará en la dirección

0x00.

La segunda línea es una instrucción explicada en la página (62) que le indica al PIC® que el programa de-

be saltar a la dirección « init » para seguir con la ejecución del programa. « init » es pues una etiqueta.

Después de un reset, el PIC® ejecuta la instrucción goto init que se encuentra en la dirección 0x00, se-

guido por la ejecución de la instrucción que se encuentra en la dirección init más abajo en el programa

(justo debajo de la etiqueta init).

6.11 La directiva « END » y el final de un programa

Esta directiva precisa el punto en el que acabará el ensamblado de su programa. Es obligatoria en cualquier programa y su omisión generará un error que le señalará que se ha llegado al final del fi-chero (End Of File) sin haber encontrado la directiva END. Todas las instrucciones que se encuentren después de la directiva END serán simplemente ignoradas. No se trata de una instrucción destinada a parar el funcionamiento del PIC sino de una directiva des-tinada a parar el funcionamiento de MPASM® en este punto. Dicho de forma explícita:

END no significa que el PIC® se vaya a parar en este punto. Partiendo de esto, si escribiese un programa que terminase de la siguiente manera:

Instruccion x Instruccion y END

Page 38: Manual Instrucciones PIC16F84A

38

Y nos imaginásemos que Instrucción x se encuentra en la dirección 0x50 (por ejemplo), usted se en-contraría esto en la memoria flash:

Direccion 0x50 : instruccion x ensamblada Direccion 0x51 : instruccion y ensamblada Direccion 0x52 : 0x3FFF (memoria flash vacia = 14 bits a 1) Direccion: 0x53: 0x3FFF (idem) ……

Ninguna señal de vuestro END puesto que se trata de una directiva. Dicho de otra forma, su pic, una vez que se ejecute la instrucción y, va a ejecutar la instrucción correspondiente al código hexadeci-mal 0x3FF. Y esta instrucción existe, se trata de « addlw 0xFF ». De hecho su pic va a ejecutar de forma tonta instrucciones « addlw 0xFF » hasta llegar al final de la memoria de programa. Ni tan si-quiera cuando llegue al final se parará, simplemente incrementará el contador de programa hasta que este desborde y vuelva a empezar por la dirección 0x00. Su programa se volverá a ejecutar una vez más (eventualmente con los registros modificados por la pasada precedente, puesto que no se trata de un reset). Si usted quiere para el programa después de una sola ejecución, hará falta bloquearlo en un bucle. Si no quiere dejar el pic en vigilancia:

bucle goto bucle

O bien dejándolo en vigilancia:

bucle sleep goto bucle

El bucle es ahora una seguridad en caso de despertar el pic debido a un parásito o al desbordamiento del watchgog. Por lo tanto, no olvide nunca que END no quiere decir parar. Me sabe mal ser tan pesado en este punto, pero continúo encontrando programas enviados por in-ternautas, extrañados por un comportamiento curioso de los mismos …. no parando nunca que cre-ían que el pic se pararía después de la directiva END.

Page 39: Manual Instrucciones PIC16F84A

39

7. Realización de un programa

7.1 Creación de nuestro primer programa

Antes de lanzar nuestro primer programa vamos a realizar algunas modificaciones al fichero « es-sai1.asm » con la finalidad de conservar lo que nos interesa. Primero vaya a la línea 105 (el número puede ser diferente en su fichero) y reemplace la línea

goto init por

goto start.

No haga faltas de ortografía.

goto start ; Direccion 0: arrancar

Descienda a la línea 226, encontrará nuestra etiqueta, que es a la que nuestro programa saltará. Bo-rre la línea

clrwdt ; borrar watch dog

Veremos el significado de todo esto más adelante. Se trata de una instrucción que ya hemos visto, la instrucción.

goto start ; bucle

La hoja de datos (el datasheet) en la pagina 62 nos enseña que la instrucción « goto » viene seguida inmediatamente de un valor (es decir de un valor en forma de número) codificado en 11 bits. Re-cuerde que el programa puede ser de hasta 1000 palabras (kW) luego con 11 bits (211=2048) pode-mos saltar a no importa qué posición del programa.

El valor puede a buen seguro ser reemplazado por una etiqueta y MPASM® se encargará de calcular por usted su correcto emplazamiento. La hoja de datos le indica también que se trata de un salto in-condicional, es decir, se realizará siempre, sin ninguna condición. Se indica que un salto requiere 2 ci-clos.

Hagamos sitio debajo de la etiqueta start y añadamos la siguiente línea (atención, jamás en la pri-mera columna) :

clrf mavariable ; borrar mavariable

CLRF es una instrucción detallada en el capítulo dedicado a las instrucciones. Quiere decir que el emplazamiento de memoria indicado detrás de la instrucción (o una variable) se borrará. El bit Z será puesto a 1 ó 0 dependiendo del resultado de la operación, como ya ha sido explicado.

Como el cometido de la operación es poner la variable a 0, el bit Z valdrá siempre 1 después de esta instrucción. Observe que el emplazamiento de memoria se puede situar desde 0 a 127 (0x7F). Es lógico. Si consulta la tabla 4-2 verá que la RAM se detiene en la posición 127 para cada uno de los 2 bancos.

Page 40: Manual Instrucciones PIC16F84A

40

Ponga a continuación una etiqueta (primera columna) a la que llamará. Debajo de esta etiqueta aña-da la instrucción:

boucle INCF mavariable,f

Esta instrucción también será explicada en el capítulo de las instrucciones. Usted verá que esta ins-trucción incrementa (+1) el contenido de la variable mavariable, y que el resultado de la operación es colocado en el emplazamiento d. Para todas las instrucciones d puede valer o bien « f », en este caso el resultado es almacenado en la variable en cuestión, o bien « w » en cuyo caso el resultado es almacenado en el registro de trabajo y la variable NO es modificada. Verá igualmente que el bit Z del registro SFR STATUS está afectado por la operación. Le recuerdo una vez más: si el resultado de la incrementación da 0 entonces Z se pondrá a 1. Se pondrá a 0 en el resto de los casos (en una instrucción que modifique Z evidentemente). Para acabar, reemplace

goto start

Por goto boucle

El programa debe acabar imperativamente por la directiva « END ». Con esto usted deberá tener al-go así como:

start clrf mavariable ; borrar mavariable

boucle incf mavariable , f ; incrementa mavariable goto boucle ; bucle END ; directive fin de programa

7.2 Ensamblado de un programa

El ensamblado de un proyecto se puede realizar de dos maneras. Bien ensamblando únicamente el fichero seleccionado con <F10>, o bien se ensamblan todos los ficheros del árbol pulsando <CTRL> + <F10>. En tanto en cuanto no tengamos nada más que un fichero en el árbol, nosotros utilizare-mos <F10>. Vamos a intentar ensamblar este programa para obtener un fichero .hex. Pulse la tecla « F10 », se abre alguna ventana en MPLAB® que intentará ensamblar nuestro programa. El ensamblado se parará a medio camino apareciendo una barra roja y una nueva ventana («output ») se abre, conteniendo los resultados de salida del comando. Si apareciese un error no previsto (error 173, por ejemplo) échele un vistazo a los anexos al final del curso.

Page 41: Manual Instrucciones PIC16F84A

41

¿Qué ha pasado? Examinemos el informe obtenido. Primero vemos mensajes (warning). Son mensa-jes destinados a llamar su atención, pero que no impiden el ensamblaje del programa. No olvide que hay toda una parte del programa que no sirve para nada pero que está escrita (debajo de la etiqueta start). Usaremos esa parte en los capítulos siguientes. El problema viene, evidentemente, de las líneas « error ». Allí nos dice que el símbolo « mavariable » no ha sido definido por el utilizador. Este error se encuentra en las dos líneas dónde hemos utiliza-do nuestra variable. En efecto, nos hemos olvidado de declararla.. Para remediar este problema, ce-rramos la ventana de mensajes de error y volvemos al editor. Hacemos sitio bajo la línea 97 en la zo-na de variables y añadimos:

mavariable : 1 ; declaración de mi variable

Tendremos:

CBLOCK 0x00C ; comienzo de la zona de variables

w_temp :1 ; Zona de 1 byte status_temp : 1 ; zona de 1 byte mavariable : 1 ; declaración de mi variable ENDC ; Fin de la zona

Relancemos el ensamblado mediante la tecla <F10>. Esta vez todo habrá ido bien. El indicador estará verde y después de los warnings tendremos la frase:

BUILD SUCCEEDED: construcción obtenida con éxito.

Acabamos de construir nuestro primer programa y hemos aprendido 3 de las 35 instrucciones que se pueden usar con el PIC®. ¿Quién dice que es complicado?

Nótese que la declaración de la variable no tiene el sentido que podría tener por caso en lenguajes de alto nivel como C. Realmente esta declaración no reserva nada, no realiza ningún trabajo excepto el de asignar una dirección (valor) a la variable en cuestión. Sabiendo que w _temp se encuentra en la dirección RAM 0x0C (puesto que CBLOCK está asignado a esta dirección) y que hemos declarado 1 octeto, status_temp se encuentra en la dirección 0x0D y mavariable en la dirección 0x0E. Hubiésemos podido escribir, en vez de colocar mavariable en CBLOCK:

mavariable EQU 0x0E

o #DEFINE mavariable 0x0E

Entonces ¿por qué utilizar CBLOCK? Simplemente porque así los cálculos son realizados automática-mente, evitando el riesgo de un mal cálculo después de una modificación del programa. Hay alguna otra razón para los desarrolladores de programas objeto, pero se salen del ámbito de este curso. Les aconsejo encarecidamente utilizar siempre la directiva CBLOCK en vez de divertirse (cada uno con su truco) calculando las direcciones de todas las variables. Ahora, puede dejar la ventana de salida abierta, cerrarla o minimizarla. Salgamos de nuestro progra-ma y atendamos las demandas de salvaguardia.

Page 42: Manual Instrucciones PIC16F84A

42

Le aconsejo realizar la salvaguardia de ficheros de forma regular después de cada modificación con la ayuda del atajo <Ctrl> + « s ». Esto le evitará disgustos por la pérdida de información en caso de una parada no deseada de su ordenador. Vaya ahora al subdirectorio de trabajo y descubrirá 7 nuevos ficheros generados por la aplicación y del tipo « essai1.xxx ». Observe sobre todo la existencia de su primer fichero .hex. El fichero tal como debería ser al final de este capítulo, se encuentra en los ficheros ejemplo suministrados, así como todos los que iremos creando a continuación.

Page 43: Manual Instrucciones PIC16F84A

43

8. La simulación de un programa

Acabamos de crear el capítulo precedente nuestro primer pequeño programa para el 16F84. Estric-tamente, este programa no sirve para nada. De todas formas, vamos a utilizarlo para experimentar con la simulación en MPLAB®. . Una vez visto este capítulo, usted será capaz de:

• Crear y modificar un proyecto.

• Ensamblar su programa

• Hacerlo rodar en el simulador para depurarlo 8.1 Lanzamiento y parametraje del simulador

Comencemos arrancando el « essai1.asm ». Este recuerda el nombre del último proyecto que manejó (essai1.mcp) y lo abre automáticamente. Si posteriormente usted realizó ensayos personales con otros proyectos, cargue nuestro proyecto desde el menú « project->open ». Se encontrará en la ven-tana de la última lección. Recuerde que si tiene algún problema, el fichero está disponible en los ficheros de ejemplo suminis-trados con este curso. Recuerde lanzar el ensamblado con <F10>. El interés de un simulador es el de visualizar el funcionamiento de un programa, luego debemos se-ñalarle a MPLAB® qué es lo que queremos observar. Primero seleccionemos la puesta en servicio del simulador de MPLAB® . Seleccionad el menú: « Debugger -> select tool -> MPLAB® Sim ». Eso es todo. De nuevo apare-cerán útiles en la barra de útiles. Observe la aparición de nuevas opciones en el menú « debugger ». Si todavía no lo ha hecho, coloque la ventana del fichero fuente al fondo a la izquierda. Vamos a hacer aparecer en la pantalla las informaciones a vigilar. Seleccione « View -> Special function re-gisters ». Se abre una nueva ventana. Agrándela y colóquela a la derecha de la ventana del fichero fuente (o más allá si le falta sitio). Ahora ve en esta ventana todos los registros que había descubierto en la tabla 4-2 de la pagina 13. En el capitulo siguiente vamos a utilizarlos todos de forma progresiva. En nuestro pequeño programa utilizamos una variable. Vamos a hacerla aparecer. Vaya al menú « View -> watch ». Se abre una nueva ventana. Dispone de 4 posiciones para colocar 4 series de varia-bles, es muy práctico para depurar. Para visualizar una variable dispone de varios métodos:

• Haga un cliqueo doble sobre la casilla situada justo debajo de « symbol name » y escriba allí el nombre de la variable. Si la variable no existe aparecerá la leyenda « not found » en la co-lumna « value ». En caso contrario, es el valor actual de la variable lo que aparecerá.

• Haga un cliqueo doble en la casilla debajo de la columna « Add.. » (adresse) e introduzca manualmente la dirección (en este caso 0x0E luego entre el valor E).

• Seleccione el nombre en el fichero fuente, cliquee una vez sobre la selección y arrastre la va-riable hasta la ventana « Watch » sin soltar el botón. Una vez allí, suelte el botón del ratón (« Drag and drop »).

Para eliminar una variable simplemente use la tecla <Del>.

Page 44: Manual Instrucciones PIC16F84A

44

El entorno se parecerá a lo siguiente:

Observe que en el arranque MPLAB® supone que el valor de la variable es 0, lo cual no ocurre necesariamente en la realidad, puesto que la RAM toma un valor aleatorio después de una puesta en tensión. Esto puede ocasionar graves sorpresas para aquellos que se olvidan de inicializar sus variables. En efecto, el programa puede funcionar perfectamente en el simulador (variable iniciali-zada a 0) pero no así en la realidad (variable inicializada de forma aleatoria). Seleccione con el botón derecho del ratón sobre para indicarle el estilo de visualización de los valo-res, 8 bits y formato hexadecimal, aunque esto debería estar elegido por defecto.

Page 45: Manual Instrucciones PIC16F84A

45

Pulse sobre <OK> para cerrar la ventana.

8.2 Explicación de los registros fundamentales.

Estamos preparados para lanzar la simulación. ¿Pero de qué serviría esto si no comprende los cam-bios que se van a producir en los registros especiales? Vamos pues a comenzar explicando los regis-tros de base necesarios para la comprensión del proceso.

Page 46: Manual Instrucciones PIC16F84A

46

8.2.1 Los registros « PCL » y « PCLATH »

Un procesador, en el ámbito de este curso, es un componente que ejecuta secuencialmente una serie de instrucciones organizadas según un ensamblado llamado programa.

Existe pues en el procesador un secuenciador, es decir, un contador que permite apuntar a la próxima instrucción a ejecutar. Este contador es llamado “contador ordinal” en los procesadores o “puntero de programa”. En el caso de los PIC®, se llama oficialmente PC, por Program Counter. Observe que le PC es un registro que no es accesible directamente por parte del utilizador.

El principio de base es siempre el mismo. En los PIC 16F, los registros solo tienen 8 bits por lo que no pueden almacenar más de 255. Harán falta 2 registros para acceder a una dirección. Estos PIC® tie-nen un funcionamiento un poco particular a este respecto. Nos encontramos primero con un registro que contiene la dirección base del PC, es decir, los 8 bits menos significativos. Este registro es accesible en lectura y escritura. Se trata del PCL (Program Counter Low). Existe otro registro con 5 bits útiles solamente y que participa en el funcionamiento del secuenciador. Se llama PCLATH (Program Counter LAT ch High). Tambien es accesible en lectura y escritura. El PC completo está codificado sobre 13 bits por lo que tendremos que completar el PCL con 5 bits más para poder apuntar a una dirección completa de la memoria del programa. Existen dos casos po-sibles:

• Después de un salto, el contenido del PC se carga con los 11 bits de destino contenidos en la instrucción en sí misma. Los 2 bits que faltan son extraídos del PCLATH a través de sus bits 3 y 4. Estos bits, que deben ser posicionados por el utilizador, son posicionados directamente en los bits 11 y 12 del PC a fin de completar la dirección de destino. Como el 16F84 solo ges-tiona 1 kW de memoria de programa, no tendremos necesidad de este registro en el caso de saltos. Recuerde que 16F84 solo gestiona 10 bits de los 13 del PC. Preste atención a que este no será el caso cuando tratemos del 16F876, por ejemplo (ver parte 2 del curso).

• En caso de modificación del PCL directamente por el utilizador, como para un registro ordina-rio, PCL se carga directamente en el PC y se completa con los 5 bits del registro PCLATH . Como el 16F84 solo gestiona 1 kW de memoria de programa, los bits b2,b3 y b4 de PCLATH serán inutilizados. Los bits 0 y 1 serán los que completen a los 8 bits de PCL para conformar una dirección de 01 bits.

Nótese que el límite de PC es 13 bits lo que implica que los PIC® de la familia de rango medio tendrán una capacidad de programa máxima de 8 kW (es decir 213). Es importante acordarse siempre que el PC apunta a la dirección siguiente, es decir, la instrucción que todavía no está siendo ejecutada. Es importante comprender esto para analizar los programas que están siendo simulados.

Page 47: Manual Instrucciones PIC16F84A

47

8.2.2 El registro « W »

Este registro es un registro utilizado por los para realizar toda suerte de cálculos. Recuerde que el destino de un resultado (d) puede en general ser una dirección RAM (f) o el registro de trabajo (w). Es un registro fundamental. En los 16F este registro no es accesible directamente. Esto no es así para otros PIC como los PIC18F (parte 5 del curso).

8.2.3 El registro « STATUS »

Este es un registro donde cada bit tiene un significado particular. Principalmente es utilizado para to-do lo que tiene que ver con comprobaciones. Es así mismo de una importancia fundamental. Esta descrito en la tabla de la pagina 15. He aquí los diferentes bits que lo componen, comenzando por el bit0 (b0), el bit más a la derecha o bit menos significativo. Alguna vez utilizamos el término LSB o bit menos significativo.

b0: C Carry. Este bit es, de hecho, el 9º bit de una operación.

Por ejemplo, si una adición de 2 octetos da un valor >255 (0xFF) este bit se pondrá a 1. Después de una sustracción se posicionará si el resultado no es negativo.

b1: DC Digit Carry. Este bit es utilizado principalmente después de un trabajo con números codifica-dos BCD: indica un acarreo del bit 3 al 4. Como información, un numero BCD es un numero donde cada cuarteto representa un digito decimal. No abordaremos este principio aquí.

b2: Z Zero. Este bit se pone a 1 si el resultado de la última operación fue 0. Recuerde, de todas for-mas, que estas banderas o flags son posicionadas solo por las instrucciones que así lo precisan (Status bit affected).

b3: PD Power Down. Indica que evento fue el último en causar una parada del PIC® (instrucción sle-ep o desborde del watchdog). En realidad PD tiene una barrita encima del nombre: activo en estado bajo. 0 = bit valido.

b4: TO Time-out. Este bit (si está a 0) indica que el arranque después de una parada se debió a un

desbordamiento del tiempo o de una puesta en sleep. En este caso PD hace la distinción.

b5: RP0 Register Bank Select0. Permite indicar en qué banco de la RAM se trabaja. 0=banco 0.

B6: RP1 Register Bank Select1. Permite seleccionar los bancos de la RAM 2 y 3. Inutilizado en los 16F84, debe ser dejado en 0 para garantizar la compatibilidad ascendente (portabilidad del progra-ma).

b7: IRP Indirect RP. Permite decidir qué bando direccionamos en caso de direccionamiento indirecto

(que veremos más tarde).

Page 48: Manual Instrucciones PIC16F84A

48

8.3 Lanzamiento de la simulacion

Bien, ya conocemos 4 de los registros. Vamos a poder comenzar a simular nuestro programa. Contrariamente en MPLAB® 5, no necesitamos seleccionar la ventana fuente para los comandos del simulador que están siempre activos independientemente de la ventana seleccionada en el interior de MPLAB. Si esto no fuese así, presione para ensamblar. Después presione <F10>, la línea:

goto start ; Adresse 0: inicialización

estará ahora indicada mediante una flecha verde. La flecha verde indica la próxima línea que va a ser ejecutada por el simulador. De hecho, lo que hemos provocado es un reset del programa. En vez de utilizar las teclas del teclado (más práctico), podríamos haber utilizado las herramientas de la barra de herramientas o las opciones del menú « debugger ». La correspondencia es la siguiente:

<F6> : reset <F7> : step into (avance de un paso en el programa) <F8> : step over (idem, pero se ejecuta un subprogram interior si lo hubiese).

Recuerde que el reset provocará el arranque en la dirección 0x00. Verifiquemos que:

• La línea seleccionada contendrá la directiva « ORG 0x00 » que nos indica que la línea si-guiente a ejecutar es la dirección 0x00: primer buen signo.

• Examinemos PCL y PCLATCH, los dos a 0. La próxima dirección a ejecutar será la 0x00. Todo correcto.

Examinemos la línea en cuestión. Nos indica que la próxima instrucción, después de su ejecución, será aquella situada en la dirección « start ». Pulse <F7>. Su programa estará posicionado ahora sobre la línea que sigue a la etiqueta « start ». Se ha efectuado un salto o lo que es lo mismo, una ruptura de secuencia.

start clrf mavariable ; effacer mavariable

En este punto, la instrucción todavía no se ha ejecutado. PCL vale ahora 34 52 00110100, es decir 0x34 ó 52 en decimal ó B’00110100 MPASM® ha calculado él solito en qué lugar se sitúa la etiqueta « start ». Si usted hubiese querido indicárselo, hubiese tenido que contar todas las líneas precedentes para ver dónde estaba. Es más, después de cada modificación que realizase, huiese tenido que recalcularlo. Por suerte MPASM® lo hace por nosotros. Pulse de nuevo<F7> para ejecutar esta instrucción: borrar mavariable. Como mavariable vale y 0 (en el simulador), aparentemente no ha ocurrido nada.

Page 49: Manual Instrucciones PIC16F84A

49

La nueva línea apuntada deberá ser:

Incf mavariable,f ; incrémenter mavariable

Y PCL ahora valdrá 0x35 D’53’ B’00110101’ Es la dirección siguiente. Ausencia de salto, ausencia de ruptura de secuencia. Es simplemente la di-rección siguiente. Aprovechemos para echarle una ojeada a la ventana .Verá nuestra variable « mavariable ». Su valor es 0x00 (ó H’00), y su dirección es 0x0E. Ya hemos explicado por qué anteriormente. Pulse ahora <F7>. La variable « mavariable » vale ahora « 0x01 » puesto que la operación de incre-mento se ha ejecutado. Supongo que ahora ya lo ha entendido, la ejecución de la siguiente instruc-ción le llevará a la línea que sigue a la etiqueta « boucle ». Diviértase ahora pulsando <F7> repetidas veces y viendo cómo evoluciona la variable. En este momento se preguntará qué va a ocurrir cuando la variable llegue a tener el valor 0xFF. No va a tener que presionar la tecla 500 veces. Podemos acelerar el proceso. Para hacer esto nada más fácil que clicar dos veces en la casilla de valor de la variable e introducir allí un nuevo valor, por ejemplo « ff ». Clica en cualquier parte fuera de la casilla para validar la entrada. Pulse <F7> para que la siguiente línea se ejecute:

incf mavariable,f ; incrementar mavariable Se habrá seleccionado la siguiente línea:

goto boucle ; boucle

Examinemos que ha pasado. La variable « mavariable » ha pasado a valer 0. Lógico puesto que 0xFF + 1 = 0x00. O lo que es lo mismo 0x100 codificado en 9 bits. Se obtiene 00 codificado en 8 bits. Examinemos ahora el registro STATUS. ¿Qué apreciamos en él? Los bits 2,3 y 4 están a 1. Los otros bits están a 0. Los bits 3 y 4 son activos a nivel bajo (0). Como están a 1 quiere decir que están inactivos. Queda el bit 2. Un vistazo a la tabla 4-3 nos indica que se trata del bit Z (Zero). Lógico porque el resul-tado de la operación ha sido 0.

Si a estas alturas se pregunta por qué el bit C (Carry) no se ha puesto a 1, bravo, hace buenas pregun-tas. Si consulta el datasheet en la pagina 62 verá que para la instrucción « incf » el único bit afectado es Z y excluye la afectación de C: « Status affected : Z ». Al final es lógico. Si utiliza la instrucción incf y obtiene un resultado 0 es absolutamente necesario que haya habido un desbordamiento por lo que una redundancia de la información sería inútil. Ahora vamos a navegar por los otros métodos de ejecución del simulador. Pulse <F6> para llevar el programa a reset. En el menú « Debugger->run » encontrará todos los métodos posible. Pruébelos. Pulse <F9> para lanzar rápidamente el programa sin visualización. Pulse <F5> para pararlo.

Page 50: Manual Instrucciones PIC16F84A

50

El menú debugger -> animate » nos da una ejecución animada. Más lenta en ejecución pero que nos permite seguir visualmente el desarrollo de la misma. El avance paso a paso se realiza a través de la tecla <F8> y nos permite realizar lo mismo que con <F7> excepto que una subrutina será ejecutada de un solo golpe, como si se tratase de una sola ins-trucción. Y otro método más. Vaya a la línea

goto boucle ; boucle

Posicione el ratón al comienzo de la línea y presionad el botón derecho. Aparecerá un menú. Usted puede poner puntos de parada a lo largo del programa. También puede pedirle al programa que arranque hasta un punto específico (run to cursor) así como otras opciones. La ventana « trace » del menú view->trace le da información suplementaria sobre el camino del programa. El menú « de-bugger » le permite otras configuraciones. Lea la ayuda del simulador para más información concer-niente al mismo. El sub-menú « debugger->stimulus controler » le permite posicionar puntos de pa-rada condicionales. Un doble clic sobre una línea colocará un punto de parada que se representará con el símbolo « B » y que aparecerá a la derecha de la línea. Un segundo doble clic eliminará el punto de parada. Ponga un punto de parada en el programa. Pulse <F6> y después <F9>, el programa se ejecutará has-ta la línea roja, después se parará. He aquí otro método practico para depurar el programa. Existe otro método aún más rápido para depurar: la traza del programa. Quite el punto de parada, pulse <F6>, después seleccione « view -> simulator trace ». Se abrirá una nueva ventana con un mensaje de « no items to display ». Pulse <F9> y algunas fracciones de segun-do después pulse <F5>. En la ventana «trace » tendrá el resumen de todas las instrucciones ejecutadas por su programa des-de el arranque hasta la parada. Esto se revela muy útil sobre todo después de paradas inesperadas por ejemplo. Hay todavía muchas más opciones concernientes a la depuración, no pudo explicarlas todas en el en-cuadre de este curso. Las irá descubriendo en el curso de sus exploraciones. No dude en experimen-tar con todos los menús. A día de hoy hay, de todas formas, menos opciones que en la versión 5 de MPLAB® . De todas for-mas pienso que es debido a la juventud del producto. Microchip® ya ha adjuntado funciones que existían en la versión 5 y que no las había en la 6.00. Para los habituales de MPLAB® 5 hará falta un poco de paciencia para recuperarlas todas.

Page 51: Manual Instrucciones PIC16F84A

51

9. El juego de instrucciones

Las instrucciones están detalladas a partir de la página 57 del datasheet. Puede parecer inútil reto-marlas en este curso. Pero al final he optado por una explicación práctica de las mismas. He escogido explicarlas a través de pequeños ejemplos concretos más que simplemente traducir el datsheet y en-viároslo. Bien entendido que usted podrá experimentar con estas instrucciones con MPLAB® y su simulador insertando estas instrucciones detrás de la etiqueta « start » de su programa. Voy a explicarle estas instrucciones en un orden que me facilite la explicación, empezando por las ya descritas, con el fin de tener en un mismo documento todas ellas.

9.1 La instrucción « GOTO » (ir a)

Esta instrucción efectúa lo que denominamos un salto incondicional, también llamado ruptura de se-cuencia síncrona incondicional. Ruptura de secuencia porque el programa no se va a desarrollar en el mismo orden que el de las instrucciones, síncrona porque sabemos en qué instante del programa se produce la ruptura e incondicional porque el salto se produce no importa qué circunstancias. Recuerde que la instrucción goto contiene en si misma los 11 bits del emplazamiento de memoria al que se efectúa el salto. Los 2 bits restantes son cargados con posterioridad en el PCLATCH. Solo po-dremos saltar al interior de una misma página de 211 es decir 2048 palabras. Esto no plantea ningún problema en el caso del 16F84 que solo dispone de 1 kW de memoria de programa, pero deberá ser tenido en cuenta con de más capacidad (16F876). Volveremos a hablar de ello en la parte 2 del curso. He aquí un resumen del funcionamiento de goto:

• La dirección de salto de 11 bits se carga en PC.

• Los 2 bits que faltan se cargarán después de PCLATH (b3 y b4): no para el 16F84.

• El resultado da la dirección en 13 bits (10 bits para el 16F84).

• La continuación del programa se efectúa desde la nueva dirección.

Recuerde que para el 16F84 dirección de salto = dirección real. No debe preocuparse de nada. Para el resto de casos, MPLAB® os lo señalará. Sintaxis goto etiqueta Ejemplo

Start goto maslejos ; salta a la instruccio despues de etiqueta maslejo s xxxxx xxx

maslejos xxxxxxxx ; instruccion ejecutada despues del salto

Puede saltar hacia delante o hacia atrás. Goto necesita 2 ciclos de reloj, como todos los saltos.

Page 52: Manual Instrucciones PIC16F84A

52

9.2 La instrucción « INCF » (INCrement File)

Esta instrucción provoca un incremento del valor contenido en el emplazamiento especificado (lla-mado file).

Sintaxis

incf f,d

Como para todas las instrucciones, « f » representa « File », es decir el emplazamiento de memoria concernido por la operación, representa el destino « d ». « f » es pues una dirección o el símbolo de una dirección.

incf variable,f

No confunda en este caso la « f » reemplazada por la « variable » con la f situada después de la co-ma que constituye el destino. Salvo especificación en contra, d valdrá siempre:

• f (la letra f) o 1 (la cifra): en este caso el resultado se almacenará en el emplazamiento de memoria especificado por f (variable en nuestro caso).

• W (la letra) o 0 (la cifra): en este caso el resultado de la operación se almacena en el registro de trabajo y el contenido del emplazamiento de memoria nos es modificado. Este registro de trabajo es llamado comúnmente acumulador.

La formula será (f) + 1 � (d) : los paréntesis significan “el contenido de”. Es decir, de forma verbal : El contenido del emplazamiento especificado es incrementado en 1 y el resultado es almacenado en el emplazamiento especificado por d. Emplazamiento que podrá ser, bien el emplazamiento especifica-do por « f », o bien el acumulador quedando en este caso (f) invariado.

Bit afectado del registro STATUS El único bit afectado es el bit Z. Recuerde que la única forma de obtener 0 incrementando un octeto es partiendo del valor 0xFF pa-sar a 0x00. Por lo tanto, si después de un incremento, obtenemos Z=1 es que se ha desbordado. Z valdrá 1 si (f) antes de la ejecución valía 0xFF y no hay otra posibilidad. Ejemplo

incf mavariable , f ; el contenido de mavariable se incrementa en 1 ; el resultado se almacena en mavariable ; W no cambia

incf mavariable , w ; El contenido de mavariable se carga en W ; W se in crementa en 1 ; El contenido de mavariable no cambia

Page 53: Manual Instrucciones PIC16F84A

53

9.3 La instrucción « DECF » (DECRement File)

Decrementa el contenido de un emplazamiento especificado. El funcionamiento es estrictamente igual al de la instrucción precedente excepto que esta vez se decrementa. Sintaxis

decf f , d ; (f) – 1 -> (d)

Bit afectado del registro STATUS El único bit afectado es el bit Z. Si antes de ejecutar la instrucción (f) vale 1, Z valdrá 1 despues de la ejecución (1-1=0). Ejemplo

decf mavariable , f ; decrementa mavariable y lo almacena en mavariable decf mavariable , w ; toma (mavariable)-1 y lo almacena en W

9.4 La instrucción « MOVLW » (MOVe Literal to W)

Esta instrucción carga el valor especificado en el acumulador. El valor precisado se dice literal o in-mediato pues no depende de ningún contenido en un emplazamiento de memoria. Sintaxis

movlw k ; k-> w : k representa un valor desde 0x00 a 0xFF.

Bit afectado del registro STATUS Ninguno (incluso si carga un valor 0. Ejemplo

movlw 0x25 ; carga el valor 0x25 en el acumulador

9.5 La instrucción « MOVF » (MOVe File)

Carga el contenido del emplazamiento especificado en el destino. Sintaxis

movf f , d ; (f) -> (d)

Bit afectado del registro STATUS El único bit afectado es el bit Z. Si (f) vale 0, Z vale 1.

Page 54: Manual Instrucciones PIC16F84A

54

Ejemplo En esta instrucción voy a ser más específico. Comprenderá por qué.

movf mavariable,w ; carga el contenido de mavariable en W

¡ATENCION! Aquí es imperativo hacer la distinción entre movlw k y movf f,w. En el primer caso es el valor el que es cargado en w, en el segundo caso es el valor del contenido del emplazamiento el que es cargado. Si nos acordamos de la lección precedente, mavariable represen-taba el emplazamiento de memoria de dirección 0x0E de nuestra RAM. Supongamos que el empla-zamiento de memoria RAM 0x0E contiene el valor 0x05 (decimos que mavariable contiene 0x05). Si ejecutamos la instrucción:

movlw mavariable

El ensamblador va a traducirla reemplazando mavariable por su valor. Atención ¡ el contenido de una variable no es su valor aunque el lenguaje corriente nos permita presuponerlo. El ensamblador va a realizar simplemente la siguiente sustitución de texto:

movlw 0x0E

Después de la ejecución de la instrucción, el registro W contendrá el valor 0x0E y hablaremos en este caso de un direccionamiento inmediato. El desarrollo es del tipo f �(d) Si por el contrario ejecutamos la instrucción:

movf mavariable , w

El ensamblador va a trabajar efectuando una sustitución (el ensamblador siempre trabaja de esta manera) y obtendremos:

movf 0x0E , w

Lo que significa: cargar el contenido que se encuentre en el emplazamiento 0x0E en el acumulador W. Hablaremos aquí de un direccionamiento directo. Le recomiendo no confundirse porque si no no comprenderemos absolutamente nada del desarrollo de un programa. Después de esta instrucción W contendrá 0x05 que es el valor que contenía el emplazamiento de memoria 0x0E. Es decir el desa-rrollo es del tipo (f) � (d) Ejemplo 2

movf mavariable , f

¿Qué hace esta instrucción? Si ha entendido todo lo anterior verá que esta instrucción coloca el con-tenido de mavariable en mavariable. Decir que esto no sirve para nada sería un poco prematuro. Si bien el contenido de mavariable permanece invariable, el bit Z se posicionará de acuerdo a ello. Esto permite determinar si mavariable contiene 0.

Page 55: Manual Instrucciones PIC16F84A

55

9.6 La instrucción « MOVWF » (MOVe W to File)

Permite guardar el contenido del registro de trabajo W en un emplazamiento de memoria. Es la ope-ración inversa a movf f,d

Sintaxis

movwf f ; (W) -> (f)

Bit afectado del registro STATUS Ninguno. Ejemplo

movlw 0x50 ; carga 0x50 en W movwf mavariable ; mavariable contine ahora 0x50.

9.7 La instrucción « ADDLW » (ADD Literal and W)

Esta operación permite añadir un valor literal al registro W. Estamos de nuevo en el caso de un direc-cionamiento inmediato. Sintaxis

addlw k ; (W) + k -> (W)

Bit afectado del registro STATUS Z : si el resultado de la operación vale 0 , Z valdrá 1 C : Si el resultado de la operación es mayor que 0xFF (255) , C valdrá 1. DC : si el resultado de la operación comporta un trasvase del bit 3 al 4 , DC valdrá 1 No se preocupe mucho por el bit DC, solo se utilizará para números codificados BCD. Ejemplo

movlw 253 ; carga 253 en decimal en W addlw 4 ; añade 4. W contendrá 1, Z valdrá 0, C valdrá 1(de sborde) addlw 255 ; añade 255. W valdrá 0, C valdrá 1, Z valdrá 1

9.8 La instrucción « ADDWF » (ADD W and F)

El contenido del registro W se añade al contenido del registro F. No se confunda con la instrucción precedente. Se trata ahora de un direccionamiento directo. Sintaxis

addwf f , d ; (w) + (f) -> (d)

Page 56: Manual Instrucciones PIC16F84A

56

Bit afectado del registro STATUS

C, DC, et Z Ejemplo

movlw 12 ; carga 12 en W movwf mavariable ; mavariable contiene ahora 12 movlw 25 ; carga 25 en W addwf mavariable , f ;(W) + (mavariable), es decir 25+12=37

; se carga en mavariable

9.9 La instrucción « SUBLW » (SUBtract W from Literal)

Atención, aquí hay una trampa. La instrucción debería llamarse SUBWL. Se sustrae W del valor literal y no a la inversa. Como su nombre indica, se trata de un direccionamiento literal o inmediato.

Sintaxis

sublw k ; k – (W) -> (W)

Bit afectado del registro STATUS

C, DC, et Z

C funciona de manera inversa que para la adición. Es común para la mayoría de los procesadores del mercado. Algún otro utiliza alguna vez un bit específico para la sustracción, comúnmente llamado « borrow ». Si el resultado es positivo no existirá desbordamiento: C=1. Si hay desbordamiento, C se verá forzado a 0. Es lógico y se explica realizando una sustracción de forma manual. El bit C representa el 9º bit puesto inicialmente a 1 de oficio. Si efectuamos una sustracción manualmente resultando <0, se obtendrá un valor final de 8 bits con el bit C sustraído. Si el resultado es >0 no habrá cambio en ese bit 9º, lue-go C permanecerá a 1. La formula de la sustracción será: k precedido de un 9º bit a 1 – contenido de W = resultado en W en forma 8 bits con el 9º bit en C. Ejemplo

movlw 0x01 ; carga 0x01 en W sublw 0x02 ; resta W de 2

; résultado : 2 – (W) = 2-1 = 1 ; Z = 0, C = 1, luego resultado positivo

Page 57: Manual Instrucciones PIC16F84A

57

Hagamos las operaciones manualmente:

¿Cómo proceder? Como en una operación en decimal. Comenzaremos por los bits de la derecha: 0-1 será 1 y arrastro 1. El siguiente bit (b1) será 1-(0+1) = 1-1 = 0 y no hay arrastre. Así hasta alcanzar el bit 9º, que como vemos no se verá afectado dando lugar a un resultado positivo (C=1).

Ejemplo 2

movlw 0x02 ; carga 0x02 en W sublw 0x02 ; resta 2 – (w) = 2 –2 = 0

; Z = 1 , C = 1 : resultado 0

Ejemplo 3

movlw 0x03 ; crga 0x03 en W sublw 0x02 ; resta 2 – (W) = 2 – 3 = -1

Procedamos de la misma manera y obtendremos B’11111111, con el bit C a 0. El que el bit C sea 0 nos indica que estamos ante un número negativo, concretamente 0xFF o 255 en decimal. La interpretación será 2-3 = 255 con C=0 � negativo � -255.

Page 58: Manual Instrucciones PIC16F84A

58

Evidentemente NO. Recuerde el valor absoluto de un número negativo en binario. Deberemos tomar el complemento a 2 del número binario. Por lo tanto:

• Complemento a 1: B’11111111 + 1 = B’00000000

• Complemento a 2 = complemento a 1 + 1 = B’00000000 + 1 = B’00000001

Luego el resultado de la anterior operación será el valor absoluto con el signo negativo, es decir B’00000001 con signo menos = -1. Un último detalle: para efectuar la resta de un numero, usted puede hacerlo mediante la suma de su complementario a 2. El resultado será estrictamente el mismo a condición de la correcta interpretación de los bits Z y C. Este argucia es practica puesto que si disponemos de SUBLW para efectuar k – (w), utilizar el com-plemento a 2 vía ADDLW nos permite realizar la operación inversa (w) – k. Como MPASM sabe como calcular perfectamente los complementos a 2 por sí mismo, ayudándose de una constante, podemos escribir:

sublw 8; efectuará 8 – (w) addlw -8 : efectuará(w) – 8

Observe de nuevo como el número de instrucciones no dice gran cosa de la potencia real de un micro si no estudiamos el contexto de una forma precisa y no hacemos funcionar nuestro cerebro. Por nuestra parte, no hemos echado de menos en absoluto la ausencia de una instrucción que efectúe (w)-8 puesto que en la realidad disponemos de la misma vía la adición de un literal. Un buen progra-mador es aquel que sabe ver más lejos de lo que parece evidente para explotar todas las posibilida-des visibles y escondidas.

9.10 La instrucción « SUBWF » (SUBtract W from F)

Esta instrucción resta el contenido de W del contenido de F. Seguimos en el entorno de las sustrac-ciones, pero en este caso se trata de un direccionamiento directo. Sintaxis

subwf f , d ; (f) – (W) -> (d)

Bit afectado del registro STATUS

C, DC, et Z Ejemplo

movlw 0x20 ; carga 0x20 en w movwf mavariable ; pone w en (mavariable) (0x20) movlw 0x1F ; carga 0x1F en w subwf mavariable,w ; (mavariable) - (w) -> (w)

; 0x20 – 0x1F = 0x01 ; resultado en w, C=1, Z=0

movwf autrevariable ; salva 0x01 en otra variable

Page 59: Manual Instrucciones PIC16F84A

59

Le remito a la instrucción precedente para que sepa cómo se desarrolla la resta y la gestión de los bits C y Z.

9.11 La instrucción « ANDLW » (AND Literal with W)

Esta instrucción realiza un Y lógico bit a bit entre el contenido de W y el valor literal. Se trata de un direccionamiento literal o inmediato, Sintaxis

andlw k ; (w) AND k -> (w)

Bit afectado del registro STATUS

Z Ejemplo

movlw B’11001101’ ; caarga w andlw B’11110000’ ; efectua un ‘and’(&)

Recuerde que se efectúa un Y lógico entre cada bit del mismo rango. El resultado es uno cuando los dos bits del mismo rango son 1. Al efectuar un AND con el valor B’11110000 se enmascaran los bits de 0 a 3 y subsisten los bits de 4 a 7. Aprovecho para indicar que AND es una instrucción conmutati-va. Le aconsejo que traduzca a binario todas las instrucciones que conciernen a bits.

9.12 La instrucción « ANDWF » (AND W with F)

Realiza un Y lógico entre el contenido del registro W y el contenido de F. Es la misma operación que la precedente excepto en que es un direccionamiento directo. Voy pues, a acelerar las explicaciones. Sintaxis

andwf f , d ; (f) AND (w) -> (d)

Bit afectado del registro STATUS

Z

Page 60: Manual Instrucciones PIC16F84A

60

Ejemplo

movlw 0xC8 ; carga 0XC8 en w movwf mavariable ; salva en mavariable movlw 0xF0 ; carga la mascara andwf mavariable , f ; (mavariable) = 0xC0 (eliminamos el cuarteto de me nor peso)

9.13 La instrucción « IORLW » (Inclusive OR Literal with W)

Realiza un OR inclusivo lógico bit a bit entre el contenido del registro W y el valor inmediato precisa-do. La noción de inclusividad de la que ya hemos hablado, precisa que el resultado de la operación para un rango de bits dado valdrá 1 si uno de los dos bits vale 1, incluyendo el caso en el que los dos valgan 1. Se trata de un direccionamiento literal o inmediato. Sintaxis

iorlw k ; (w) OR k -> (w)

Bit afectado del registro STATUS

Z Ejemplo

movlw 0xC3 ; carga 0xC3 en W iorlw 0x0F ; FUERZA los bits 0 a 3 : résultado : (w) = 0xCF

Luego con un OR inclusive podemos forzar a 1 cualquier bit (recuerde que con AND podemos forzar a 0 cualquier bit).

9.14 La instrucción « IORWF » (Inclusive OR W with File)

Efectúa un OR inclusive con direccionamiento directo. Sintaxis

iorwf f , d ; (w) OR (f) -> (d)

Bit afectado del registro STATUS

Z

Page 61: Manual Instrucciones PIC16F84A

61

9.15 La instrucción « XORLW » (eXclusive OR Literal with W)

Realiza un OR exclusivo lógico bit a bit entre el contenido del registro W y el valor literal precisado. La noción de exclusividad precisa que el resultado de la operación para un rango de bits dado valdrá 1 si uno de los dos bits vale 1, excluyendo el caso en el que los dos valgan 1. Se trata de un direcciona-miento literal o inmediato. Observe que si aplicamos un bit 0 a un bit cualquiera mediante XOR obtendremos como resultado el bit sin cambio ninguno. Por el contrario, si lo aplicamos con un 1 obtendremos el inverso del mismo. En efecto 0 xor 1 da 1 pero 1 xor 1 da 0 por efecto de la exclusividad. Esta instrucción nos permitirá invertir no importa que bit de un octeto. Sintaxis

xorlw k ; (w) xor k -> (w)

Bit afectado del registro STATUS

Z Ejemplo

movlw B’11000101’ ; carga W xorlw B’00001111’ ; xor con el valor

; resultado : B ‘11001010’ ; se han invertido los 4 bits menos significativos

Observe que los bits del octeto inicial han sido invertidos por cada bit del segundo operando que val-ían 1. Con lo que ya hemos visto podemos forzar un bit a 1 (OR), forzarlo a 0 (AND) o invertirlo (XOR). Por lo tanto ya sabe manipular los bits como usted quiera.

Page 62: Manual Instrucciones PIC16F84A

62

9.16 La instrucción « XORWF » (eXclusive OR W with F)

Efectúa un OR exclusivo bit a bit en tre el contenido del registro W y el contenido de F. Se trata de la misma operación que la anterior pero con direccionamiento directo.

Sintaxis

xorwf f , d ; (w) xor (f) -> (d)

Bit afectado del registro STATUS

Z

9.17 La instrucción « BSF » (Bit Set F)

Fuerza a 1 el bit de rango especificado en el contenido de F. Dicho de otra forma pone a 1 un número de bit de un registro preciso. Se trata de un direccionamiento directo.

Sintaxis

bsf f , b ; (f).b = 1

El bit nº b es posicionado a 1 en la caja de memoria (f). Evidentemente b vale entre 0 y 7. Bit afectado del registro STATUS

Ninguno. Ejemplo

bsf STATUS , C ; pone a 1 el bit C en el registro STATUS bsf mavariable , 2 ; pone a 1 el bit 2 de (mavariable)

Atención ¡ Aunque usted manipule solo un bit en particular, el PIC no actúa directamente sobre este úni-co bit. De hecho, realiza un ciclo llamado de lectura/modificación/escritura. Si tomamos el primer ejemplo, el PIC va a cargar STATUS (los 8 bits), modificará el bit C y después rees-cribirá STATUS en su emplazamiento original. Podría decirme que ese es un problema del PIC y que a nosotros no nos concierne. De hecho sí y no. Sí en cuanto al ejemplo en cuestión y no si se tratase de un puerto de salidas. Lo veremos más adelante. Esta observación es válida para todas las instrucciones de manipulación de un bit en particular, y por lo tanto también lo será para BCF.

Page 63: Manual Instrucciones PIC16F84A

63

9.18 La instrucción « BCF » (Bit Clear F)

Fuerza a 0 el bit especificado del contenido del emplazamiento F. Dicho de otra forma, pone a 0 un número de bit de un registro precisado. Sintaxis

bsf f , b ; (f).b = 0

El bit nº b es posicionado a 0 en la caja de memoria (f). Evidentemente b vale entre 0 y 7. Bit afectado del registro STATUS

Ninguno. Ejemplo

bcf STATUS , C ; pone a 0 el C en el registro STATUS bcf mavariable , 2 ; pone a 0 b2 de (mavariable)

9.19 La instrucción « RLF » ( Rotate Left through Carry)

Rotación hacia la izquierda del registro F utilizando el carry. Se trata de una instrucción de direcciona-miento directo. Las operaciones de rotación son operaciones muy utilizadas. Los tienen la particularidad de tener solo una instrucción de rotación sobre 9 bits. Vamos a ver que con estas instrucciones es muy fácil realizar de-calajes. La palabra que va a sufrir la rotación está constituida por los 8 bits de un registro especificado, completada por el bit C (carry) del registro STATUS. La operación de rotación realiza lo siguiente: el bit C es memorizado. Inmediatamente cada bit del octeto se desplaza hacia la izquierda. El bit 7 sale del octeto por la izquierda y se convierte en el nuevo bit C. El nuevo bit 0 tomará el valor del anciano carry que fue memorizado inicialmente. Se trata pues de una ro-tación sobre 9 bits.

Sintaxis

rlf f , d ; (f) rotacion a la izquierda con carry-> (d )

El bit nº b es posicionado a 0 en la caja de memoria (f). Evidentemente b vale entre 0 y 7. Bit afectado del registro STATUS

C Ejemplo Un pequeño ejemplo vale más que un largo discurso.

bsf STATUS , C ; pone el carry a 1 movlw B’00010111’ ; carga el valor en w movwf mavariable ; inicializa mavariable rlf mavariable , f ; rotacion a la izquierda

Page 64: Manual Instrucciones PIC16F84A

64

Como ve todos los bits se han decalado hacia la izquierda y C se ha realimentado al bit 0. El resultado queda sobre 9 bits.

Ejemplo 2

bcf STATUS,C ; positionne le carry à 0 movlw b’00010111’ ; charge la valeur dans w movwf mavariable ; initialise mavariable rlf mavariable,f ; rotation vers la gauche

Si lo entendió todo, el resultado será B’00101110 con el carry a 0. ¿Qué ocurre si hacemos esta operación en decimal? Cojamos el número 125 y rotémosle a la izquier-da. Nos dará el numero 1250. Habremos simplemente multiplicado el número por su base (deci-mal=base 10). En binario es exactamente igual. Cojamos el numero B’00010111 (23 en decimal). Rotémosle a la izquierda y obtendremos B’00101110 (46 en decimal). Hemos pues efectuado una multiplicación por 2 (binario = base 2). Partiendo de un número binario entre 0 y 255, rotando tendremos un numero comprendido entre 2 y 510 (siendo el carry el bit 8).

9.20 La instrucción « RRF » ( Rotate Right through Carry)

Rotación a la derecha del registro F utilizando el carry. Se trata de una instrucción de direccionamien-to directo. La operación de rotación a la derecha efectúa la siguiente operación: el bit C es memorizado. Inme-diatamente cada bit del octeto se desplaza a la derecha. El antiguo bit 0 sale del octeto por la dere-cha y pasa a ser el nuevo carry. El antiguo carry pasa a ser el nuevo bit 7. Se trata pues de una rota-ción sobre 9 bits. Sintaxis

rrf f , d ; (f) rotacion con carry -> (d)

Bit afectado del registro STATUS

C Ejemplo 1

bsf STATUS,C ; pone el carry a 1 movlw B’00010111’ ; carga el valor en w movwf mavariable ; inicializa mavariable rrf mavariable,f ; rotación a la derecha

Page 65: Manual Instrucciones PIC16F84A

65

Como ve todos los bits se decalaron a la derecha. C se reintrodujo al bit 7. El resultado es sobre 9 bits. Ejemplo 2

bcf STATUS,C ; pone el carry a 0 movlw b’00010111’ ; carga el valor en w movwf mavariable ; inicializa mavariable rrf mavariable,f ; rotación a la derecha

Si lo comprendió bien, verá que el resultado será B’00001011 con el carry a 1. Si el carry está a 0 al comienzo, simplemente se efectúa una rotación a la derecha. ¿Qué ocurrió? Si nuestro número de partida es 23 en decimal, al final tenemos 11 en decimal. El ca-rry representa el bit “-1”, es decir la mitad del bit 0, luego ½. En efecto, en decimal, la cifra 1 después de las unidades representa 1/base, es decir 1/10. En binario será ½. Si miramos ahora los 9 bits veremos 11 ½. Hemos dividido el número entre 2 (base). Acuérdese de esto, le será muy útil más adelante. Un decalaje a la derecha equivale a dividir por 2. Partiendo de un número comprendido entre 0 y 255 obtendremos un número comprendido entre 0 y 127.5 (siendo el carry el bit de rango -1)

9.21 La instrucción « BTFSC » (Bit Test F, Skip if Clear)

Comprueba el bit precisado del emplazamiento F y salta si es igual a 0. Se trata del primer salto con-dicional (ligado a una condición), o ruptura de secuencia síncrona condicional. En efecto, no habrá salto si la condición no se cumple. Las instrucciones condicionales son la base de todo lo que es la toma de decisiones en un programa. Observe que en este caso, la instrucción utilizará 2 ciclos de reloj, si no solo utilizaría 1. Es más, habrá que recordar que este tipo de saltos no saltan más que la instrucción siguiente (la instrucción no con-tiene dirección de salto).

Sintaxis

btfsc f , b ; on mira el bit b de la memoria (f). ; si es 0 salta la instruccion siguiente ; si es 1 ejecuta la instruccion siguiente

Instruccion x ; se ejecuta solamente si f.b vale 1 Instruccion y ; se ejecuta solamente si f.b vale 0

Page 66: Manual Instrucciones PIC16F84A

66

Observe que para pasar de la instrucción btfsc a la instrucción y nos harán falta 2 ciclos de reloj. En efecto:

• Si f.b se cumple y saltamos x, un salto dura 2 ciclos

• Si f.b no se cumple, btfsc durará 1 ciclo pero para llegar a y tardaremos 1 ciclo más en ejecu-tar x dando un total de 2 ciclos. Esto se cumple siempre y cuando la instrucción x no sea una instrucción de salto como en el ejemplo siguiente.

Ejemplo

btfsc f, b ; mira el bit b de la memoria (f).

; si vale 0 salta la siguiente ; si vale 1 ejecuta la siguiente

goto plusloin ; si b vale 1 salta a plusloin Instruction y ; se ejecuta si b vale 0 … …

plusloin : ; tratamos aquí el caso b vale 1

instruccion z

Esta forma de proceder se utiliza muy a menudo cuando hay muchas instrucciones ejecutar en un ca-so y en otro. El goto se puede, eventualmente reemplazar por un call si el desarrollo del programa requiere que se ejecute la instrucción y en todos los casos. Esta forma de proceder es aplicable a otras instrucciones de “skip”, hablaremos de ello más adelante. Bit afectado del registro STATUS

Ninguno Ejemplo 1 He aquí un ejemplo en el que se debe ejecutar un sola instrucción suplementaria si el bit vale 1.

btfsc STATUS,C ; mira si el bit C de STATUS vale 0 bsf mavariable,2 ; no (C=1), bit 2 de mavariable pu esto a 1 xxxx ; continuación del programa

Ejemplo 2 ¿Qué hacer si el tratamiento del programa necesita efectuar más operaciones? Como ya hemos di-cho, mezclaremos saltos condicionales e incondicionales.

movlw 0x12 ; carga 12 en w subwf mavariable,f ; resta 0x12 de mavariable btfsc STATUSC ;,mira si el resultado es negativo (C =0) goto positif ; salta al tratamiento de resultado po sitivo xxxx ; tratamiento resultado negativo ... positif: ; aquí realizamos el tratamiento si positi vo

Page 67: Manual Instrucciones PIC16F84A

67

9.22 La instrucción « BTFSS » (Bit Test F, Skip if Set)

Comprueba el bit precisado del emplazamiento de memoria F y salta si vale 1. El funcionamiento es estrictamente similar al precedente. Sintaxis

btfss f , b ; mira el bit b de la memoria (f). ; si vale 1 salta la instruccion siguinete ; si vale 0 ejecuta la instruccion siguiente

xxxx ; si el bit vale 1 no será ejecutada (skip) xxxx ; el programa continua aqui

Bit afectado del registro STATUS

Ninguno. Ejemplo

btfss STATUS,C ; mira si el bit C de STATUS vale 1 bsf mavariable,2 ; no (C=0), bit 2 de mavariable pu esto a 1 xxxx ; continuación del programa en los dos casos

9.23 La instrucción « DECFSZ » (DECrement F, Skip if Z) Decremento del contenido del emplazamiento F y salta la instrucción siguiente si el resultado fue 0. Esta instrucción es muy utilizada para crear bucles. Sintaxis

decfsz f, d ; (f) –1 -> (d). Salto si (d) = 0

Bit afectado del registro STATUS

Ninguno. Ejemplo

movlw 3 ; cargar 3 en w movwf compteur ; inicializar compteur movlw 0x5 ; cargar 5 en w boucle ; etiqueta addwfmavariable ,f ; añadir 5 a variable decfsz compteur , f ; decrementar y comprobar goto boucle ; si compteur no es 0, ir a boucle movf mavariable , w ; carga el valor obtenido en w

Page 68: Manual Instrucciones PIC16F84A

68

¿Cómo hemos escrito este programa?

• Inicializamos el contador de bucles

• Ponemos una etiqueta de comienzo de bucle

• Escribimos las instrucciones que se han de ejecutar varias veces

• La instrucción decfsz permite determinar el final del bucle

• La instrucción goto permite localizar el comienzo del bucle

ATENCION

Si puso:

decfsz compteur , w ; decrementar contador y compro bar

el bucle no acabará jamás, puesto que la variable contador jamás será modificada. Esto le permitirá, utilizando su cerebro probar una nueva utilidad de esta instrucción. TRUCO: precisando w como destino, puede comprobar si una variable vale 1 sin modificarla. Observe que un movff variable,f le permite comprobar si una variable era nula, mientras que ahora un decfsz variable,w le permite comprobar si vale 1. Otra vez comprobamos que el número de ins-trucciones no resume las capacidades de un micro utilizado. Volviendo a nuestro ejemplo, si ponemos un 0 en el contador de bucles, este será ejecutado 256 ve-ces. Si desea que solo sea ejecutado bajo determinadas circunstancias deberá añadir una comproba-ción antes de la ejecución del primer bucle como se hace a continuación. Exemple 2

movf compteur , f ; posiciona Z btfsc STATUS , Z ; salta si Z = 0, es decir si compteur >0 goto suite ; compteur = 0, no ejecutar el bucle

boucle ; etiqueta de comienzo del bucle addwf mavariable , f ; añade 5 a mavariable decfsz compteur , f ; decrement y comprueba goto boucle ; si compteur no es 0, vamos a boucle

suite ; saltamos aqui si compteur = 0 movf mavariable , w ; cargamos el valor obtenido en w

9.24 La instrucción « INCFSZ » (INCrement F, Skip if Zero) Incrementa el contenido del emplazamiento F y salta la siguiente instrucción si el resultado fue 0. Igual que la anterior excepto que incrementamos en vez de decrementar.

Sintaxis

incfsz f , d ; (f) + 1 -> (d) : saut si (d) = 0

Bit afectado del registro STATUS

Ninguno. TRUCO: precisando w como destino, puede comprobar si una variable contiene 0xFF sin modificar-la.

Page 69: Manual Instrucciones PIC16F84A

69

9.25 La instrucción « SWAPF » (SWAP nibbles in F)

Invierte los dos cuartetos del contenido del emplazamiento F. Esta operación simplemente invierte el cuarteto (semi-octeto) de peso débil con el de peso fuerte. Esta operación es útil cuando maneja co-dificación BCD. Sintaxis

swapf f , d ; inversion de b0/b3 de (f) por b4/b7 - > (d)

Bit afectado del registro STATUS

Ninguno. Esta particularidad nos resultará muy útil cuando veamos las interrupciones. Ejemplo

movlw 0xC5 ; carga 0xC5 en w movwf mavariable ; mueve a mavariable swapf mavariable , f ; (mavariable) = 0x5C

9.26 La instrucción « CALL » (CALL subroutine)

Llama a una subrutina. Esta instrucción efectúa un salto incondicional a un sub-programa. Se trata de una rotura de secuencia incondicional síncrona con memorización de la dirección de retorno. ¿Qué es un subprograma? Se trata de una porción de programa que puede ser llamado desde mu-chos sitios de un programa llamado “principal” y que presenta la particularidad de reenviar al final de su ejecución a la instrucción siguiente a la desde la que fue llamado. Sintaxis

call sousroutine ; (pc)->(pile), despues sousroutine + PCLATH -> (pc

Mecanismo Después de la ejecución de la instrucción, el contenido del PC es guardado en una pila. Una pila fun-ciona como un apilamiento de asientos: el último asiento puesto en la pila será el primero en poder ser recuperado. O, como ya lo hemos explicado, el PC contiene siempre la dirección de la próxima instrucción a ejecutar. Una vez el PC está guardado, su contenido se reemplaza por los 11 bits del valor de la dirección de salto (subrutina), completado (para los PIC de más de 2 kW de memoria de programa) por los 2 bits de peso fuerte de PCLATH. El programa saltara al emplazamiento subrutina como si fuese un goto. En el sitio de la subrutina se van a encontrar una serie de instrucciones, el subprograma, debiendo este, imperativamente, acabar en una instrucción de retorno al programa “principal” desde el que fue llamada (por ejemplo la instrucción return). Una vez encontrada esta instrucción, el valor intro-ducido en la pila será cargado en el PC, que mandará el programa a la siguiente instrucción a la que hizo la llamada vía call.

Page 70: Manual Instrucciones PIC16F84A

70

Observe que si el subprograma llamase a otro subprograma, la dirección sería guardada en la pila de la misma manera. Es por esto por lo que debe ser una pila, para que la dirección de retorno sea siempre la última que fue almacenada. ATENCION ¡ Esta pila tiene un tamaño limitado de 8 emplazamientos. No existe ninguna forma de comprobar la pila por lo que deberá ser usted mismo quien gestiones los subprogramas de tal for-ma que no pasemos de 8 posiciones, si no, el programa se bloqueará. Tenga en cuenta que cuando un subprograma retorna libera un espacio de la pila. No se trata, pues, de limitar el número de veces que llamamos a subprogramas, sino del número de veces que un sub-programa llama a otro. Bit afectado del registro STATUS

Ninguno. 9.27 La instrucción « RETURN » (RETURN from subroutine)

Retorno de subrutina. Va siempre emparejada a una instrucción call. Esta instrucción indica el final de la porción de programa que constituye una subrutina (SR). Recuerde que por cada instrucción call deberá encontrar una instrucción return. Sintaxis

return ; (pila) -> PC

Bit afectado del registro STATUS

Ninguno.

Ejemplo

Imaginemos un programa que necesita una pequeña temporización (como cada instrucción toma su tiempo en ser ejecutada, nos podemos arreglar para hacer perder el tiempo voluntariamente al pro-grama con el fin de retardar su funcionamiento). Escribámoslo:

movlw 0xCA ; valor del compteur movwf compteur ; inicializar bucles

boucle decfsz compteur,f ;dec.compteur,salto si 0 goto boucle ; bucle si no 0 xxx ; continua programa

Imaginemos que esta pequeña temporización sea llamada regularmente por nuestro programa prin-cipal.

Page 71: Manual Instrucciones PIC16F84A

71

Veamos qué aspecto tendría:

xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera

tempo ; aqui necesitamos una temporizacion xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera

tempo ; aquí tambien xxx ; instrucción cualquiera xxx ; instrucción cualquiera

tempo ; y también aqui xxx ; instrucción cualquiera

La primera cosa que se nos viene a la cabeza es la de copiar y pegar nuestra temporización. Esto, además de no ser elegante, nos obligaría a, si tenemos que modificar el tiempo de temporización, realizar los cambios en todos los trozos que hemos copiado y pegado sin olvidarnos de ninguno de ellos. Además ocuparía mucha memoria de programa. Igualmente podríamos utilizar una “macro” que, recuerde, efectúa una sustitución en el momento del ensamblaje. Es bien cierto que ahora, en caso de modificación, solo debemos ocuparnos de un si-tio, pero en nuestro PIC®, el código será escrito en realidad con el consiguiente desperdicio de me-moria de programa, sobre todo, si el código es muy extenso o se hacen muchas utilizaciones del mismo. Es para resolver esto para lo que vamos a utilizar la técnica de los subprogramas. Primera etapa, mo-dificamos nuestra temporización para hacer de ella una subrutina:

tempo ; etiqueta de comienzo de subrutina movlw 0xCA ; valor de compteur movwf compteur ; inicializa el contador de bucles

boucle decfsz compteur ,f ; decrement y salto si 0 goto boucle ; no es 0 , salta aboucle return ; final de la subrutina

Segunda etapa, modificamos nuestro programa principal para que cada vez que lo necesitemos, lla-memos al subprograma. Tendremos:

xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera call tempo ; llamada al subprograma xxx ; instrucción cualquiera, el programa continua aquí xxx ; instrucción cualquiera xxx ; instrucción cualquiera call tempo ; llamada al subprograma xxx ; instrucción cualquiera, el programa continua aquí xxx ; instrucción cualquiera call tempo ; llamada al subprograma xxx; instrucción cualquiera, el programa continua a quí

En este caso, la rutina de temporización solo está presente una vez en la memoria de programa.

Page 72: Manual Instrucciones PIC16F84A

72

Aún podemos mejorarlo más. Supongamos que necesitemos una temporización de duración variable. Solo nos hará falta modificar la subrutina suprimiendo el valor de inicialización y situamos este en el programa principal antes de la llamada a la subrutina. Llamamos a esto una subrutina con paso de parámetros. En nuestro ejemplo sería:

tempo ; etiqueta de comienzo de subrutina movwf compteur ; inicializa el contador de bucles

boucle decfsz compteur , f ; decrement y salto si 0 goto boucle ; no es 0 , salta aboucle return ; final de la subrutina

Y nuestro programa principal quedaría:

xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera xxx ; instrucción cualquiera movlw 0x25 ; carga w con 0x25 call tempo ; llamada al subprograma xxx ; instrucción cualquiera, el programa continua aquí xxx ; instrucción cualquiera xxx ; instrucción cualquiera movlw 0x50 ; carga w con 0x50 call tempo ; llamada al subprograma xxx ; instrucción cualquiera, el programa continua aquí xxx ; instrucción cualquiera movlw 0x10 ; carga w con 0x10 call tempo ; llamada al subprograma xxx; instrucción cualquiera, el programa continua a quí

Y tendremos una temporización variable sin tener que escribir nuestra rutina de temporización cada vez que necesitemos un tiempo diferente. Ahora ya sabe que es una subrutina. ¿Un juego de niños, no?

9.28 La instrucción « RETLW » (RETurn with Literal in W)

Retorno de subrutina con el valor literal en el registro W. Es una instrucción muy simple. Es equiva-lente al return visto antes pero saliendo con un valor especificado en W. Sintaxis

retlw k ; k -> (w) puis (pile)->(pc)

Bit afectado del registro STATUS

Ninguno. Ejemplo

test ; etiqueta de comienzo de subrutina btfss mavariable,0 ; mira el bit 0 de mavariable retlw 0 ; si vale 0, fin fin de subprograma con (w) =0 retlw 1 ; si no, salimos con (w) = 1

Page 73: Manual Instrucciones PIC16F84A

73

El programa que llamó a la subrutina conoce el resultado de la operación por medio de la lectura del registro w. El interés del ejemplo es muy limitado, pero le muestra cómo proceder con esta instruc-ción.

9.29 La instrucción « RETFIE » (RETurn From IntErru pt)

Retorno de interrupción. Trataremos en un capitulo separado qué son las interrupciones. Sería idiota en-cajar todo un capitulo en la explicación de una instrucción. Sepa de todas formas, que esta instrucción efectúa un retorno de una interrupción de la misma manera que return lo hace desde un subprograma, salvo que, dado que retfie relanza automáticamente las interrupciones, habrá una operación suplemen-taria realizada.

Sintaxis

retfie ; retour d’interruption

Bit afectado del registro STATUS

Ninguno.

9.30 La instrucción « CLRF » (CLeaR F)

Borra el contenido del emplazamiento F. Simplemente pone a 0 el valor del emplazamiento.

Sintaxis

clrf f ; (f) = 0

Bit afectado del registro STATUS

Z vale siempre 1 después de esta instrucción.

Ejemplo

Clrf mavariable ; (mavariable) = 0

9.31 La instrucción « CLRW » (CLeaR W)

Borra el contenido del registro W. Dicho de otra forma, pone a 0 el acumulador.

Sintaxis

clrw ; (w) = 0

Es una instrucción que no sería indispensable puesto que podríamos hacer « movlw , 0 ». La diferencia estriba en que con CLRW se posiciona a 1 el bit Z.

Bit afectado del registro STATUS

Z vale siempre 1 después de esta instrucción.

Page 74: Manual Instrucciones PIC16F84A

74

9.32 La instrucción « CLRWDT » (CLeaR WatchDog)

Rearma el perro guardián (watchdog) de su programa. Trataremos la puesta en marcha de esta parte con posterioridad. Sepa que este es un mecanismo muy práctico que nos permite resetear el PIC® en caso de bloqueo del programa (un parásito por ejemplo). El mecanismo es de todas formas muy simple de entender: se trata para su programa de enviar esta ins-trucción a intervalos regulares. Si el comando no es recibido en un tiempo determinado (programable), el PIC® re arranca desde la dirección 0x00. Es como el mecanismo llamado “de hombre muerto” utilizado por los conductores de trenes que deben pulsar un botón a intervalos regulares. Se detecta así si el con-ductor está siempre en el debido estado de atención, y por lo tanto vivo.

Sintaxis

clrwdt ; (wdt) = 0

Bit afectado del registro STATUS

Ninguno.

9.33 La instrucción « COMF » (COMplement F)

Efectúa el complemento a 1 de un emplazamiento de memoria especificado. Dicho de forma más eviden-te: invierte todos los bits de un emplazamiento.

Sintaxis

comf f , d ; NOT (f) -> (d)

Bit afectado del registro STATUS

Z

Ejemplo

movlw B’11001010’ ; carga el valor en W movwf mavariable ; inicializa mavariable comf mavariable,f ; invierte el valor de los bits

; (mavariable) = B’00110101’

Observe que si precisa w como destino, posicionará Z sin modificar la variable. TRUCO: si precisamos w como destino podremos chequear si la variable vale 0xFF sin modificarla.

Page 75: Manual Instrucciones PIC16F84A

75

9.34 La instrucción « SLEEP » (Dormir)

Sitúa el PIC® en modo dormido. Para la ejecución del programa y disminuye el consumo eléctrico. Solo se despertará bajo ciertas condiciones especiales que veremos más tarde.

Sintaxis

sleep ; Silencio, estoy durmiendo !

Bit afectado del registro STATUS

T0 , PD : estos bits serán descritos en el estudio de los modos de reset del PIC.

9. 35 La instrucción « NOP » (No Operation)

Ninguna operación. Como ambos debemos estar cansados a estas alturas, le presento la operación que no hace nada, no posiciona nada y que no modifica nada. Parecería que no sirve para nada. De hecho se utiliza mayoritariamente para perder tiempo.

Sintaxis

nop ; Parecer que se trabaja ya es un trabajo !

Y aquí acaba el análisis de las 35 instrucciones utilizadas normalmente en los PIC® de rango medio. Le puede parecer arduo pero practicando un poco conocerá rápidamente todas estas instrucciones intuiti-vamente. Para su consuelo, piense que ciertos procesadores de tecnología CISC disponen de varios centena-res de instrucciones. 9.36 Las instrucciones obsoletas

Quedan aún 2 instrucciones que se utilizaron en versiones precedentes de PIC16F®. Todavía son reco-nocidas por el PIC16F84® pero su utilización está desaconsejada por Microchip®. Su compatibilidad futura no está garantizada. Se trata de la instrucción OPTION que sitúa el contenido del registro W en el registro OPTION_REG, y de la instrucción TRIS que sitúa el contenido del registro W en el registro TRISA o TRISB dependiendo de si utilizamos TRIS PORTA o TRIS PORTB. Estas instrucciones ya no son necesarias puesto que estos registros ya son accesibles directamente me-diante métodos clásicos. Le aconsejo encarecidamente el evitar utilizarlas, so pena de encontrar problemas con futuras versiones de los PIC® de Microchip® . Se las he presentado para que sea capaz de comprender un programa que eventualmente las haya utili-zado.

Page 76: Manual Instrucciones PIC16F84A

76

10. Los modos de direccionamiento

Todas las instrucciones usan una manera particular de acceder a las informaciones que manipulan. Es a estos métodos a los que llamaremos “modos de direccionamiento”. Hemos hablado de diferentes modos de direc-cionamiento en el estudio de las instrucciones, debería a estas alturas saber que es un direccionamiento lite-ral o inmediato o un direccionamiento directo. Voy a comenzar simplemente dando un ejemplo concreto de cada modo de direccionamiento. Supongamos que desea embolsarse cierta cantidad de dinero.

10.1 El direccionamiento literal o inmediato

Con el direccionamiento inmediato podremos decir:

“Yo me embolso 100€” El valor forma parte de la frase de forma inmediata. He dicho literalmente la cantidad de dinero de la que hablamos. No tengo necesidad alguna de más información para saber el dinero que voy a tener en el bolsillo. Ejemplo

movlw 0x55 ; cargar el valor 0x55 en W

10.2 El direccionamiento directo

Con el direccionamiento directo podremos ir al banco y decir:

“Quiero embolsarme el contenido de la cuenta nº 123456” Aquí la dirección donde se encuentra el dinero está indicada directamente en la frase. A pesar de ello, ten-dremos que ir a ver a la cuenta nº 123456 para saber cuánto dinero voy a embolsarme. Efectivamente, voy a obtener el dinero que se encuentre dentro de la cuenta nº 123456 y no el montante de 123456€ (lo sentimos mucho). No nos meteremos en el bolsillo el número de la cuenta sino lo que esta con-tenga. Ejemplo

movf 0x10 , W ; cargar el contenido del emplazamiento 0x10 en W

10.3 El direccionamiento indirecto

Ahora podemos imaginarnos que nos vamos a embolsar el dinero que tiene un amigo mio en su cuenta. Po-demos decir:

“Mi amigo me va a decir su número de cuenta y yo me voy a embolsar su contenido”

Tenemos que realizar varias operaciones para tener el dinero en nuestro bolsillo.

• Primero, pedir el número de cuenta de nuestro amigo (simpático el chaval).

Page 77: Manual Instrucciones PIC16F84A

77

• Mirar que hay en la cuenta.

• Ahora ya si sabemos que nos vamos a meter al bolsillo.

Es lo mismo que decir que vamos a obtener el número de cuenta de forma indirecta con la inestimable ayuda de nuestro amigo. Usted no se mete en el bolsillo ni a su amigo (el pobre) ni el número de cuenta a pesar de que ese número se lo ha suministrado su amigo. El camino de acceso al dinero es mucho más indirecto que previamente.

Amigo ���� cuenta ���� bolsillo Como resumen:

Direccionamiento literal: dinero ���� bolsillo Direccionamiento directo: (cuenta) ���� bolsillo Direccionamiento indirecto: (amigo( ���� (cuenta) ���� bolsillo

Si ha entendido esto, ha entendido todo. Como es necesario un elemento de “indirección” (su amigo) esto se traduce en los PIC en la presencia de una serie de registros particulares. Examinémosles.

10.3.1 Los registros FSR et INDF

INDF significa INDirect File. ¿Lo ve? Es el famoso registro de la dirección 0x00. En realidad este registro no existe verdaderamente, es simplemente una forma particular de acceder a FSR utilizada por los PIC® por ra-zones de facilidad de construcción electrónica interna. En ciertos micros el modo de direccionamiento indirecto está indicado en la propia instrucción. En los PIC16F, el modo de direccionamiento indirecto utiliza un pseudo-registro para acceder. De nuevo un modo presente no listado en la lista de instrucciones explicitas. El registro FSR en sí mismo, se encuentra en la dirección 0x04 en los 2 bancos. No es necesario pues, cargar el banco para acceder a él. FSR sí es un verdadero registro. En el ejemplo precedente, su amigo está representado por FSR. El direccionamiento indirecto es un poco par-ticular en los PIC® puesto que es siempre la misma dirección donde encontraremos la dirección de destino. Como resumen, podemos decir que usted solo tiene un único amigo. Afortunadamente su único amigo dispo-ne de numerosas cuentas. ¿Cómo se desarrolla esto en la práctica? Primero tenemos que escribir el puntero de la dirección (el número de cuenta) en el registro FSR. A continua-ción accederemos a este puntero de dirección mediante el registro INDF. Podemos decir que INDF es de hecho el registro FSR utilizado para acceder a la caja de memoria. Por lo tanto, cuando queremos modificar la caja de memoria punteada, modificamos FSR. Cuando queremos conocer la di-rección de la caja de memoria accedemos igualmente a FSR. Si queremos acceder al contenido de la caja ac-cedemos vía INDF. Veremos todo esto con un pequeño ejemplo más adelante. ATENCION El contenido del registro FSR apunta a una dirección de 8 bits. O, en ciertos PIC®, la zona de RAM dispone de 4 bancos (16F876) por lo tanto 512 posiciones. La dirección completa es una dirección de 9 bits.

Page 78: Manual Instrucciones PIC16F84A

78

Esta dirección completa se obtiene, en direccionamiento directo, añadiendo los bits 7 y 8 bajo la forma de RP0 y RP1. Por contar, en el direccionamiento indirecto, la dirección se completa añadiendo el bit 8 bajo la forma de IRP. No se confunda. En el caso del 16F84(A), la RAM dispone de 256 posiciones por lo que puede ser codificada con 8 bits. RP1 e IRP no se utilizan. Es conveniente dejarlos a 0. EJEMPLO

movlw 0x50 ; cargamos un valor cualquiera movwf mavariable ; y lo ponemos en « mavariable » movlw mavariable ; cargamos la DIRECCION de mavariable en W

;por ejemplo en las lecciones precedentes era 0x0E movwf FSR ; ponemos la direccion en FSR.

; decimos que FSR apunta a mavariable movf INDF , w ; cragamos el CONTENIDO de INDF en W.

El contenido de INDF es traducido por el PIC® como el contenido del emplazamiento de memoria apun-tada por FS.

10.4 Algunos ejemplos

Me voy a repetir pero, los modos de direccionamiento deben de ser comprendidos de forma imperativa. Mul-tiplicando los ejemplos espero que todo el mundo lo entienda. Para los ya habituados a diversos procesado-res, les pido perdón por las repeticiones. Consideraremos los registros inicializados por el ejemplo precedente.

movlw mavariable

Este es un direccionamiento inmediato o literal, por lo tanto cargamos el valor de mavariable que en realidad corresponde a su dirección. Por lo tanto se colocará el valor 0x0E en el acumulador w. Esto se puede recono-cer por la “l” de movlw. Atención, el valor de mavariable no es su contenido es su dirección.

movf mavariable , w

Ahora tenemos un direccionamiento directo por lo tanto iremos a la dirección mavariable a ver que contiene en su interior. Allí recogeremos el contenido y lo cargaremos en w por lo que (w) = 0x50 en nuestro ejemplo. Para el ensamblador, “mavariable” será reemplazada por “0x0E” por lo que tendremos movf 0x0e,w

movf INDF , w

Ahora tenemos el direccionamiento indirecto. Este modo se reconoce inmediatamente por la utilización del registro INDF. El irá a ver el registro FSR, leerá la dirección que contiene, en este caso 0x0E. A continuación se va a la dirección que ha leído y lee el contenido de ese emplazamiento. Por lo tanto, w ahora tendrá el conte-nido de la dirección 0x0E, es decir 0x50.

movf FSR , w

He aquí una trampa. En efecto se trata de un direccionamiento directo. Se colocará en w el contenido del re-gistro FSR, por lo tanto 0x0E será colocado en (w).

Page 79: Manual Instrucciones PIC16F84A

79

Voy a terminar con unas pequeñas palabras para los especialistas en microprocesadores que lean este curso como una puesta a punto. Se habrá dado cuenta que no hablamos en absoluto de modos de direccionamiento indexado (post o pre, con o sin offset). Es simplemente porque este modo de direccionamiento no existe en los PIC® del tipo 16F (al contrario que en otras familias de PIC).

Page 80: Manual Instrucciones PIC16F84A

80

11. Realización de un programa embarcado

Designamos, generalmente, como programa o logicial embarcado a un programa destinado a ejecutarse lo-calmente en una carta electrónica con funcionalidades hardware especificas, por oposición a un programa destinado a ejecutarse en un PC generalista. Por otra parte, y vista la actual evolución, la lógica embarcada comienza a cubrir muy diversas realidades, desde las tablet a los smartphones, o incluso cartas equipadas con micros sobre las que se hace correr un sistema operativo (OS). Se tiende a asociar la expresión embarcado a la expresión móvil, en todo caso todos estos límites comienzan a estar muy difuminados. En lo que a nosotros concierne, usaremos el termino embarcado designando a una tarjeta electrónica equipada con un PIC®. Vamos, pues, a comenzar con la parte divertida. Vamos a crear pequeños programas sobre una tarjeta equi-pada con un.

11.1 El material necesario

Utilice una tarjeta de pruebas constituida por pequeños agujeros unidos por filas. Las uniones se realizan por medio de cables al aire. Podrá encontrar estas tarjetas en cualquier comercio de material electrónico. Conec-te el cuarzo lo más cerca posible del PIC sin cables adicionales. Para poner en práctica estas lecciones le hará falta el siguiente material (recuperable y poco oneroso):

• 1 PIC® 16F84 ó 16F84A en encapsulado PDIP y de cualquier frecuencia.

• 1 cuarzo de 4 Mhz

• 2 condensadores de 27 pF

• 1 LED rojo

• 1 resistencia de 330 Ohms

• 1 pulsador N.O. (normalmente abierto)

• 1 poco de cable rígido para las conexiones en la tarjeta de pruebas.

• 1 alimentación estabilizada de 5 V continua. Si no dispone de una fuente de alimentación de 5 V dc puede utilizar una pila de petaca de 4,5 V o construir la pequeña fuente de alimentación cuyo esquema se suministra más adelante. En este caso, además le hará falta el siguiente material:

• 1 alimentador de tensión continua de entre 9 a15 V (no es crítico).

• 1 condensador de 10 a100 µF/35V.

• 1condensadores de 0.1 µF.

• 1 diodo 1N4004 o equivalente que permita una corriente de 1 A (no es crítico).

• 1 regulador del tipo 7805

Page 81: Manual Instrucciones PIC16F84A

81

Es un montaje que no le arruinará. Es más, podrá recuperar los componentes para próximas realizaciones. Verá que los PIC® pueden ser utilizados en toneladas de aplicaciones de la vida cotidiana. De todas formas, comprenda que deberá construirse o comprar un programador de (grabador o tostadora). Vea el anexo A1.8 para más detalles.

11.2 Montaje de la placa de pruebas

Inserte el zócalo. Inserte el resto de componentes en la tarjeta y proceda a las conexiones según el esquema adjunto. Verifique todo antes de conectar la alimentación. Antes de insertar el PIC® en el zócalo compruebe todo una vez más. Conecte la alimentación y compruebe en el zócalo la presencia de las alimentaciones necesarias para el PIC®. Si todo está correcto, desconecte la alimentación, inserte el PIC® y vuelva a conectar la alimentación.

Esquema de conexión del PIC® (no olvide conectar MCLR a Vdd)

Esquema de la fuente de alimentación si fuese necesaria.

Page 82: Manual Instrucciones PIC16F84A

82

11.3 Creación de un proyecto

Ahora está usted preparado para empezar con los experimentos. Efectúe una copia de su fichero m16F84.asm y renómbrelo como Led_clic.asm. Arranque MPLAB® y res-ponda NO si le pregunta si quiere cargar Essai1. En MPLAB® usted sabe ya como crear un proyecto. Cree el proyecto Led_cli en su directorio de trabajo (project->new project). Si tiene una ventana abierta llamada « untitled » ciérrela preferiblemente. Si tiene una versión más reciente de MPLAB® que le pregunta acerca de qué tipo de ejecutable desea, elija « Absolute ». No se olvide de abrir el fichero « Led_cli.asm ». De nuevo, este fichero está disponible como anexo en su versión terminada.

11.4 Edición del fichero fuente

Complete la cabecera siguiente como desee. Le indico aquí abajo un ejemplo. Adquiera el hábito de docu-mentar siempre sus programas. No es un lujo, es imperativo para un mantenimiento eficaz en el tiempo.

;************************************************** ************************ ; PROGRAMA DE INTERMITENCIA DE UN LED CONECTADO EN EL PORTA.2 * ; DE UN PIC 16F84. PROGRAMA DE ENTRENAMIENTO EN EL FUNCIONAMIENTO * ; DE LOS PICS. * ;************************************************** ************************ ; * ; NOMBRE: LED-CLI * ; Fecha: 09/02/2001 * ; Version: 1.0 * ; Circuito: Placa de pruebas * ; Autor: Bigonoff * ;************************************************** ************************ ; * ; Fichier requerido: P16F84.inc * ; * ;************************************************** ************************ ; * ; Notas: Este programa permite hacer parpadear un L ED * ; sobre el port A.2 a una frecuencia de 1 Hz * ; Este programa forma parte de la lección 6 del cur so * ; * ;************************************************** ************************

Page 83: Manual Instrucciones PIC16F84A

83

11.5 Elección de la configuración.

Más abajo en el fichero encontrará lo siguiente: __CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC ; '__CONFIG' precisa los parámetros codificados en el procesador en el momento ; de la programación. Las definiciones están en el fichero include. ; He aquí los valores y sus definiciones: ;_CP_ON Code protection ON : imposible releer ;_CP_OFF Code protection OFF

;_PWRTE_ON Timer reset a la conexión en servicio ;_PWRTE_OFF Timer reset fuera de servicio ;_WDT_ON Watch-dog en servicio ;_WDT_OFF Watch-dog fuera de servicio

;_LP_OSC oscilador de cuarzo de bajo consumo ;_XT_OSC oscilador de cuarzo de velocidad media ext erno ;_HS_OSC oscilador de cuarzo de gran velocidad ;_RC_OSC oscilador red RC

He incluido los comentarios en el fichero de forma que sea rápidamente modificable sin tener que recurrir al datasheet. Note que efectuamos un “Y lógico” (&) entre los diferentes valores, los niveles activos son, pues, niveles 0 da-do que un “AND” solo nos permite imponer valores 0. Deberá precisar todos los valores que no utilice sin los cuales no podría forzar los 1 correspondientes. El primer parámetro precisa que su PIC® estará protegido o no contra la lectura una vez programado. Deje este parámetro en CP_OFF = no protegido. El segundo parámetro precisa si el perro guardián (watchdog) está puesto en servicio o no. En un primer mo-mento reemplace WDT_ON por WDT_OFF para ponerlo fuera de servicio. Deje PWRTE_ON para precisar que va a utilizar un método de reset securizado, con un tiempo de retardo an-tes del arranque. Esto le protege en caso de alimentaciones un poco lentas en arrancar. Explicaré esto más tarde. Al final viene el tipo de oscilador que se va a utilizar. La tabla 8-1 de la pagina 40 da los valores recomendados en función de las frecuencias utilizadas para un PIC® de 10 Mhz. Retenga que el valor _HS_OSC es conveniente con frecuencias elevadas, a partir de 4 Mhz. Observe que a esta frecuencia el modo _HS_OSC también funcionaria. Es importante no utilizar el modo _RC_OSC si utilizamos un cuarzo. Este parámetro está reservado para un funcionamiento con una red RC como la dibujada en la figura 8-7 de la pagina 41. El hecho de utilizar el parámetro RC con un reloj externo puede entrañar la destrucción del PIC®. Aunque los PIC® son componentes muy sólidos en la práctica, evite equivocarse a este nivel. La línea debería quedar así:

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

Page 84: Manual Instrucciones PIC16F84A

84

11.6 El registro OPTION

Si observa la tabla 4-2 comprobará que este registro se encuentra en la dirección de memoria 0x81, es decir en el banco 1. En los ficheros « include » de MPLAB® este registro está declarado con el nombre de OP-TION_REG . Es este nombre el que usted debe utilizar. Vamos a dar aquí los detalles. Este registro es un registro de bits, es decir que cada bit tiene un cometido particular. La tabla de la pagina 16 representa el contenido de este regis-tro: b7 : RBPU Cuando este bit está a 0 (activo a nivel bajo) una resistencia referida a +5V se conectará sobre cada pin del PORTB. Veremos en esta lección el funcionamiento del PORTB. Si observa nuestro esquema verá que el pul-sador conectado a RB2 pone este pin a masa cuando se le pulsa. No existe en nuestro esquema ninguna posibilidad de enviar +5V. Validando esta opción, la resistencia interna fuerza el pin RB2 a 1 cuando el botón no está pulsado. Tenga en cuenta que esta opción válida las resistencias para todo el PORTB. No es posible elegir ciertas resis-tencias en particular. De hecho esta opción solo existe para el PORTB. Habrá observado que todos los puertos no son iguales: hay que pensar en ello en el momento de la concep-ción de un circuito. En nuestro caso pondremos las resistencias en servicio con b7=0. Recuerde que durante el desarrollo de aplicaciones con PICS debería pensar simultáneamente en el esquema y en el programa. Es una costumbre a adquirir. b6 : INTEDG Da, en el caso de utilización de las interrupciones sobre RB0, el sentido de desencadenamiento de la interrup-ción. Si b6=1 la interrupción se dispara cuando RB0 pasa de 0 a 1 (flanco ascendente). Si b6=0 la interrupción se dispara cuando RB0 pasa de 1 a 0 (flanco descendente). Como no vamos a utilizar interrupciones en esta lección, podemos dejar b6 a 0. b5 : TOCS Este bit determina simplemente el funcionamiento del temporizador 0 (timer0). Recuerde que el timer0 se in-crementa bien en función del reloj interno con b5=0 o bien contando los impulsos recibidos por el pin RA4 en el caso de b5=1. Como este último caso necesita un circuito de generación de impulsos externos, nosotros vamos a utilizar el reloj interno por lo que b5=0. b4 : TOSE Da, en el caso de tener el bit 5 a 1 (impulso externo) el sentido de transición que determinará el contaje del timer0. Si b4=1 se contará si la señal pasa de 5V a 0V, si b4=0 a la inversa. Como hemos situado b5=0, b4 estará inutilizado para nosotros por lo que lo dejaremos a 0. b3 : PSA En el PIC® tenemos un pre divisor. ¿Qué es esto? Indica el número de impulso que deberemos recibir para provocar un incremento del timer0. Ya volveremos a ello más tarde. Por ahora basta conocer que este pre di-visor nos puede servir para una de las dos funciones siguientes (y no para las dos).

Page 85: Manual Instrucciones PIC16F84A

85

O bien efectúa una pre división del temporizador del perro guardián (b3=1) o bien efectúa una pre división a nivel del timer0 (b3=0). En nuestro caso pondremos b3=1 (veremos por qué más abajo).

b2, b1,b0 : PS2,PS1,PS0

Estos tres bits determinan el valor de la pre división vista anteriormente. Hay 8 valores posibles mostrados en la tablita de la pagina 16. Nótese que los valores son diferentes para el watchdog o para el timer0. En efecto, no hay divisor por 1 para este último registro. Si no desea utilizar pre divisor de ninguna manera, el único método es poner b3=1 (pre divisor en el watch-dog) y PS2, PS1 y PS0 a 0. En este caso, no hay pre divisor sobre timer0 y hay pre divisor por 1 sobre el watch-dog, lo que corresponde a o tener pre divisor. Nosotros pondremos B2=b1=b0=0.

11.7 Edición del programa

Vamos a utilizar el valor B’00001000 en nuestro programa, es decir el valor 0x08. Tengo la costumbre de no colocar los valores fijos a lo largo del programa a fin de facilitar el mantenimiento. Coloco los valores en el comienzo del programa utilizando asignaciones o definiciones. La asignación ya ha sido creada más abajo en el programa. He creado una constante que llamo OPTION-VAL y que contendrá el valor a colocar más tarde en el registro OPTION_REG. Le recuerdo que las cons-tantes no ocupan espacio en el PIC®, simplemente son reemplazadas por el ensamblador en el momento del ensamblado. Sirven para facilitar la lectura del programa. Busque más abajo en el programa después de las asignaciones y reemplace el valor afectado a OPTIONVAL por aquel que hemos determinado y añada comentarios. Suprima la asignación INTERMASK dado que no vamos a utilizar interrupciones en este primer programa. En la zona de asignaciones se tendrá que ver:

;************************************************** ******************* ; ASIGNACIONES * ;************************************************** ******************* OPTIONVAL EQU H'08 ; Valor del registro OPTION

; Resistencias pull-up ON ; Sin pre divisor

Bajemos hasta la zona de definiciones. Vamos a dar un nombre a nuestro botón y a nuestro LED. Las instrucciones bcf y bsf que vamos a utilizar para poner o leer a 1 o a 0 los registros, tienen la siguiente sin-taxis: « bsf f , n » y los registros de acceso se llaman PORTA (para el puerto A) y PORTB (para el puerto B). Vamos a utilizar la directiva #DEFINE para integrar f y n al mismo tiempo. Vemos en el esquema que el LED está conectado al bit 2 del puerto A. El botón está conectado al bit 2 del puerto B. Borraremos las definiciones de ejemplo y escribiremos las nuestras, quedando de la siguiente for-ma:

;************************************************** ******************* ; DEFINE * ;************************************************** ******************* #DEFINE LED PORTA,2 ; Led rojo #DEFINE BOUTON PORTB,2 ; boton

Page 86: Manual Instrucciones PIC16F84A

86

LED y BOUTON son dos nombres que hemos elegido libremente, a condición de que no se traten de algún nombre reservado. Nada de utilizar STATUS o MOVLW.

¿Para qué sirven las definiciones? Supongamos que decide conectar el LED sobre el PORTB bit1 (RB1) por ejemplo. No tendremos necesidad de buscar a lo largo de todo el programa, bastará simplemente con cambiar la zona DEFINE. Además, cuando lea el programa el término LED será mucho más explicito que el de PORTA,2. Recuerde priorizar la legibilidad y mantenibilidad de un programa excepto cuando tenga la necesidad de recurrir a diversas optimizaciones. Descendemos un poco más y llegamos a la zona de macros. No tenemos verdaderamente necesidad de utili-zarlos, pero vamos a hacerlo a título de ejemplo. Borremos la macro de ejemplo y escribamos las nuestras:

;************************************************** ******************* ; MACROS * ;************************************************** ******************* LEDON macro

bsf LED endm

LEDOFF macro bcf LED endm

La primera columna indica los nombres de las macros (aquí, 2 macros, LEDON y LEDOFF). La directiva macro significa “comienzo de la macro” y la directiva endm significa “fin del a macro”. Observe que las macros pue-den estar constituidas por multitud de líneas de código. Observe también, que una macro puede utilizar una definición (LED) toda vez que se trata de un simple tra-tamiento de textos. Tomemos nuestro ejemplo: cuando utilicemos la línea siguiente en nuestro programa (atención ¡ no poner el nombre de la macro en la primera columna cuando se utilice):

LEDON

En el momento del ensamblado, nuestro ensamblador reemplazará LEDON por:

bsf LED

Y reemplazará LED por:

bsf PORTA , 2

Acabamos de utilizar una facilidad de escritura y mantenimiento de los programas. Recuerde siempre que las macros son simples sustituciones de texto. Si su macro se compone de 50 líneas de código, cada vez que utili-ce la macro se añadirán al programa 50 líneas de código más. Llegamos a la zona de las variables. Añadiremos variables en la medida de su necesidad. Borre las 2 variables presentes puesto que no vamos a utilizar interrupciones.

Page 87: Manual Instrucciones PIC16F84A

87

;************************************************** ******************* ; DECLARACION DE VARIABLES * ;************************************************** ******************* CBLOCK 0x00C ; comienzo de la zona de variables ENDC ; Fin de la zona

Después de la directiva ORG 0x00, dejamos la llamada a la rutina de inicialización. Todo programa comporta una etapa de inicialización de variables y registros. Coja la costumbre de separar esta inicialización del resto del programa. Como no vamos a utilizar las interrupciones, suprima todo lo que sigue justo hasta la rutina de inicialización, obteniendo:

*************************************************** ******************* ; ARRANQUE DESPUES DEL RESET * ;************************************************** *******************

org 0x000 ; Dirección de arranque después de reset goto init ; Directions 0: inicializar

;************************************************** ******************* ; INITIALISATIONS * ;************************************************** ******************* init:

continuacion del programa

Antes de seguir, primero vamos a estudiar los registros que vamos a utilizar: 11.8 El registro PORTA

Este registro es un poco particular puesto que da acceso directamente al mundo exterior. Es un registro que representa la imagen numérica (0 y 1) de los pines de RA0 a RA4, es decir, 5 pines. Si me ha seguido, es el re-gistro que nos servirá para encender el LED. Este registro se sitúa en la dirección 05H del banco 0. Cada bit del registro representa un pin, por lo que solo 5 bits son utilizados. Para escribir sobre un pin de salida ponemos a 1 o a 0 el bit correspondiente según el nivel de salida deseado. Por ejemplo:

bsf PORTA , 1 ; enviar nivel 1 sobre RA1

Pone un nivel +5V (+Vdd para ser precisos) en el pin RA1. Hace falta para esto que el pin esté configurado co-mo salida (ver TRISA). Para comprobar una entrada, podremos, por ejemplo, utilizar:

btfss PORTA,3 ; comprueba RA3 et salta si vale 1 (> 2.5V)

Para los electrónicos, tienen el esquema interno de los bits de RA0 a RA3 en la figura 5-1 de la pagina 21. Pueden ver que la salida puede ser puesta a nivel alto o bajo gracias a dos transistores de salida (en montaje push-pull). Cuando estos pines están programados como entradas, son sensibles a niveles 0/5V. El nivel de transición depende del modelo de PIC®, pero se sitúa generalmente en la mitad de la tensión de alimenta-ción. Una entrada al aire es vista como nivel 0. De todas formas, evítelo por problemas de sensibilidad a pará-sitos.

Page 88: Manual Instrucciones PIC16F84A

88

Para el pin RA4, ver figura 5-2, en salida la configuración es del tipo drenador abierto y en entrada se compor-ta como un trigger-Schmitt. Si no tiene los conocimientos electrónicos necesarios, sepa simplemente que este pin en salida puede imponer un nivel 0 pero no un nivel 1. Si conecta aquí un led con respecto a masa jamás podrá encenderlo. Los puertos disponen de un diodo de protección hacia el 0V y hacia el 5V. De tal forma, con una simple resis-tencia en serie podrá enviar señales hacia estos pines fuera de la gama de tensión Vss/Vdd. ATENCION: Si envía una tensión tal que la corriente drenada por los diodos de limitación hacia Vdd llega a ser superior a la corriente absorbida por el PIC, esta corriente va a “alimentar” su alimentación que se verá solici-tada a absorberla. Si su alimentación no está concebida para regular una corriente inversa, el riesgo es que Vdd aumente de tal forma que provoque la destrucción del PIC. Por lo tanto, no utilice este truco de conexionado si carece de los conocimientos en electrónica suficientes pa-ra prevenir las consecuencias. De nuevo, el pin RA4 es una excepción a la regla puesto que no dispone de diodo hacia masa. Como resumen, los pines RA0 a RA3 pueden ser utilizados como entradas de niveles 0/5V o como salidas en-viando 0V y 5 V. En cuanto al pin RA4 como salida, no puede enviar 5V. Tenga en cuenta estas especificaciones cuando vaya a construir su montaje. Por ejemplo, si hemos situado nuestro led entre RA4 y Vss, jamás podremos encenderlo. A partir de la página 75 del datasheet, capitulo 11, tiene las características máximas de funcionamiento del PIC®. Puede recordar simplemente de limitarse a un máximo de 20 mA por pin de salida, con un máximo de 80 mA por el PORTA y 150 mA por el PORTB. Si va a utilizar características muy cercanas a estos límites, analice las tablas con más detenimiento teniendo en cuenta los límites de potencia disipada y temperatura así como las inyecciones de tensión. Nosotros no tendremos estos problemas a lo largo de nuestros ejercicios. El consumo de nuestro led sobre RA2 responde a la formula aproximada siguiente: INTENSIDAD: (5V – Tension Led) / 330 Ohms = (5V – 1,75V)/330 = 9,85 mA Observe, y es muy común en un gran número de microprocesadores, que es preferible manejar una carga so-bre el nivel 0 (carga conectada entre el pin y Vdd) que sobre el nivel 1 (carga conectada entre el pin y Vss co-mo en nuestro montaje). Esto es así debido a que las sobretensiones para enviar un 1 son mayores que al enviar un 0 debido a la tecno-logía utilizada. Si hemos adoptado la “mala” forma de hacerlo es debido a que, procediendo de la mejor manera, habría que invertir todo el razonamiento: enviar un 0 para encender y enviar un 1 para apagar. Tratándose de un curso didáctico para aprender a programar, he tratado de ser lo más intuitivo en la medida de lo posible.

Page 89: Manual Instrucciones PIC16F84A

89

11.8.1 Funcionamiento particular de los PORTS

Acuérdese que toda operación sobre un bit especifico comporta el principio leer/modificar/escribir. Esto es todavía más evidente en las operaciones concernientes a los puertos, tal como el encendido y apagado de un led. Por ejemplo, si escribimos bsf PORTA,1, el PIC® procederá de la siguiente manera:

1. El PORTA es leído en su totalidad (pines de entrada y salida) 2. El bit 1 es puesto a 1 3. Todo el PORTA es reescrito

Consecuencias importantes ¡Atención! Lo que viene a continuación es importante. Es la explicación al famoso problema del pin RA4 con-figurado como salida y que parece cambiar de valor por sí mismo. Pongamos por ejemplo que el pin RA4 sea una salida y puesto a 1 por programa:

bsf PORTA , 4 ; libera la linea RA4 Como es de drenador abierto, supongamos que la electrónica externa le fuerza eventualmente a 0: por ejem-plo conectando el pin RA4 de 2 PIC diferentes para obtener una especie de OR lógico. O el caso clásico de uti-lizar RA4 para pilotar el reloj de un bus I²C. Si ahora realizamos la siguiente operación:

bsf PORTA , 1 ; poner RA1 a 1

El PIC procederá de la siguiente manera:

1. Lectura del PORTA: el PIC constata que RA4 está a 0 (aunque el programa lo puso a 1). 2. Pone el bit 1 a 1. 3. Escribe el PORTA completo, RA1=1 y RA4=0.

Moraleja, es ahora su propio programa (su PIC) quien bloquea la línea RA4 a 0 (aunque haya escrito bsf POR-TA,4). Si la línea es liberada por el otro componente esta quedará retenida por su PIC sin esperanza de ser li-berada. Es un mecanismo a tener muy en cuenta. Peor si escribe simplemente lo siguiente, admitiendo que RB1 y RB4 están como salidas:

bsf PORTB , 1 ; on met RB1 à 1 bsf PORTB , 4 ; on met RB4 à 1

El resultado parece evidente, puesto que RB1 y RB4 son puestos a 1 los dos. De nuevo hay una trampa. Imagi-nemos que hemos puesto una carga capacitiva muy grande en RB1. La salida, constituida por la resistencia in-terna de RB1 y el condensador de carga, forman una red RC. Esta red tiene una cierta constante de tiempo. Hace falta un cierto tiempo para alcanzar Vdd/2 y por lo tanto ser considerado un 1. ¿Comprende qué va a ocurrir?

Page 90: Manual Instrucciones PIC16F84A

90

En el instante del bsf PORTB,4 sucederá:

1. El PIC lee el PORTB pero RB1 no ha llegado a la tensión suficiente y aparece como un 0. 2. El bit 4 es forzado a 1 3. El PIC reescribe PORTB y pone RB1 a 0

Moraleja, RB1 no pasará a 1 y se quedará (en este caso preciso) a 0. Si estudiamos el datasheet a nivel de ca-racterísticas eléctricas, nos damos cuenta que podemos encontrarnos con este caso aunque se respete el da-tasheet. Por el contrario, siempre respetando el datasheet, el tiempo de establecimiento de la señal perma-necerá inferior a un tiempo de ciclo de instrucción. Moraleja, para evitar el problema nos hará falta añadir una instrucción “nop”:

Bsf ; PORTB , 1 ponemos RB1 a 1 nop ; esperamos a estabilizacion de la tension bsf PORTB , 4 ; ponemos RB4 a 1

Luego, excepto si ha calculado perfectamente los tiempos de subida de las tensiones de salida en función de las cargas, de la frecuencia del reloj y de los ciclos internos de las instrucciones:

Ponga siempre una “nop” entre dos instrucciones sucesivas de manipulación de pines situados sobre el mismo puerto.

Por el contrario, en RA4 “nop” o no, nada cambiará, tendrá que tener en cuenta esta eventualidad y conser-var en memoria el estado que su programa cree tener impuesto a RA4. Sobre otros PIC, como los 18F, este problema no existe ya.

11.9 El registro TRISA

Este registro está situado en la misma dirección que PORTA pero en el banco 1. Su dirección completa en 8 bits es 0x85. Es un registro de funcionamiento muy simple ligado al funcionamiento de PORTA. Cada bit posicionado como 1 configura el pin correspondiente como entrada. Cada bit posicionado como 0 configura el pin correspondiente como salida. Después de un reset del PIC®, todos los pines son configurados como entradas con la finalidad de no enviar señales no deseadas por los pines. Los bits de TRISA se pondrán todos a 1 después de cada reset. Como solo 5 pines son usados en PORTA, solo los bits de b0 a b4 son utilizados por TRISA. Ejemplo de utilización. Tomemos el esquema de nuestro pequeño circuito: ¿Qué tenemos que hacer para encender el led? (PD.: no introduzca por ahora estas instrucciones en nuestro proyecto.) Primero deberemos configurar TRISA y poner el bit 2 (RA2) como salida. Como el registro TRISA se encuentra en el banco 1, y el direccionamiento directo solo utiliza 7 bits, deberemos poner el bit RP0 del registro STATUS a 1 (ver capítulos precedentes).

Page 91: Manual Instrucciones PIC16F84A

91

A continuación podremos enviar un 1 por el PORTA correspondiente a 5V sobre el pin RA2. La secuencia sería:

bsf STATUS , RP0 ; pasamos al banco 1 bcf TRISA , 2 ; bit 2 de TRISA a 0 = salida para RA2 bcf STATUS , RP0 ; pasamos al banco 0 bsf PORTA , 2 ; enviamos 5V por RA2 : el LED se enciende . . . bcf PORTA , 2 ; 0V sobre RA2, el LED se apaga

Observe que como RA2 quedará como salida durante todo el programa (según el esquema de nuestra aplica-ción), colocaremos el valor de TRISA en la rutina de inicialización.

11.10 Los registros PORTB y TRISB

Estos registros funcionan exactamente de la misma manera que PORTA y TRISA salvo que conciernen a los 8 pines RB. En este caso utilizamos todos los bits. Veamos ahora las particularidades del PORTB. Ya hemos visto una, puesto que las entradas del PORTB pueden ser conectadas a una resistencia referida a +5V de forma interna. La selección se realizará por el bit 7 del registro OPTION. El esquema interno en las figuras 5-3 y 5-4 de la página 23 del datasheet nos muestra que los bits b0 y b4/b7 pueden ser utilizados como fuente de interrup-ción, el bit 0 puede, además, puede ser utilizado de forma autónoma para gestionar otro tipo de interrupción. Veremos el funcionamiento de las interrupciones en otro capítulo. De todas formas, tenga en cuenta a partir de ya que los esquemas que conciba en un futuro deberán tener en cuenta todas las particularidades de los PORTs. NOTA Después de un reset se preguntará cual es el estado de tal o cual registro. Encontrará la explicación en la tabla de la pagina 14. Puede ver, que después de un reset, el registro OP-TION_REG verá todos sus bits puestos a 1. Deberá, pues, especificar el borrado del bit7 para validar las resis-tencias de referencia a +5V.

11.11 Ejemplo de aplicación

Siempre partiendo de nuestro esquema, deseamos encender el led cuando presionemos el botón, y apagarlo cuando soltemos. He aquí un ejemplo de programa que necesitaríamos (ponga atención a que el nivel sobre RB2 pasa a 0 cuando el botón está apretado, conexión a masa).

bsf STATUS , RP0 ; pasamos a banco 1 bcf OPTION_REG, NOT_RBPU ; Resistencia de referencia en servicio bcf TRISA , 2 ; bit 2 de TRISA a 0 = RA2 como salida

; no es necesario confirurar TRISB ya está en ent rada ; por defecto edspues del reset del PIC

bcf STATUS , RP0 ; pasamos al banco 0 boucle

btfss PORTB , 2 ; mira RB2, salta si vale 1 bsf PORTA , 2 ; RB2 vale 0, encendemos el LED btfsc PORTB , 2 ; mira RB2, salta si vale 0 bcf PORTA , 2 ; RB2 vale 1, apagamos el led goto boucle ; volvemos a empezar

Page 92: Manual Instrucciones PIC16F84A

92

11.12 La rutina de inicialización

Examinemos las instrucciones a partir de la etiqueta « init ».

Las 2 primeras líneas son:

clrf PORTA ; Salidas portA a 0 clrf PORTB ; Salidas portB a 0

Preparan el forzado de las salidas por los puertos a 0. Con esto, una vez configurados los puertos como sali-das, tendrán por defecto el nivel 0V. Si su electrónica requiere otros niveles queda a su cargo el cambiarlos. La línea siguiente permite conectarnos al banco 1. Hasta nueva orden las instrucciones siguientes utilizarán los registros situados en el banco 1.

bsf STATUS,RP0 ; pasa al banco 1

En seguida nos encontramos con:

movlw OPTIONVAL ; cargar mascara movwf OPTION_REG ; inicializar el registro OPTION

Recuerde que OPTIONVAL es una constante. Su valor está definido anteriormente por nosotros a 0x80. Este valor será enviado al registro OPTION (OPTION_REG). Recuerde también que OPTION_REG es el nombre de-clarado en MPLAB® para el registro OPTION. Sigue la pequeña rutina destinada a borrar la RAM. Recuerde que es importante saber que a la puesta en ten-sión la RAM contiene valores aleatorios. Para evitar malas sorpresas, he integrado esta rutina en cada re arranque que asegura que la RAM no contiene otra cosa que ceros. Esta rutina no es estrictamente necesaria si usted inicializa los valores de las variables correctamente antes de utilizarlas. Se trata de una precaución. Pero esta precaución cuesta muy poco, tan solo un pequeño espacio de memoria de programa ¿Por qué pri-varse de ella? Alguien podría argumentar que existe una pérdida de tiempo en la ejecución del programa, pe-ro esta pérdida ocurre solamente en el momento del arranque, por lo que no afecta en absoluto a la veloci-dad de ejecución del programa como tal. Efecto secundario: en tanto en cuanto que forzamos a 0 todos los valores de la RAM tenemos una perfecta correspondencia entre el simulador y aquello que tendremos en el PIC: potenciales errores más fáciles de depurar.

; ------ Borrar la RAM ------

movlw 0x0c ; inicializa el puntero movwf FSR ; punter direccionamiento indirecto

init1 clrf INDF ; borrado ram apuntada por FSR incf FSR,f ; punter siguiente btfss FSR,6 ; mira si llegamos al final de la zona (>=0x40) goto init1 ; no, bucle btfss FSR,4 ; mira si llegamos al final de la zona (>=0x50) gotoinit1 ; no, bucle xxxxx ; aquí se encuentra la continuación del programa

He aquí un ejemplo concreto de utilización del direccionamiento indirecto (observe la utilización de INDF que caracteriza este tipo de direccionamiento).

Page 93: Manual Instrucciones PIC16F84A

93

En primer lugar, inicializamos el puntero apuntando a la zona a manipular (aquí la zona RAM usuario). Esta zona comienza en la dirección 0x0C (ver tabla 4-2) y termina en la dirección 0x4F inclusive. La zona situada en el banco 1 no existe para este PIC® (imagen del banco 0) y no necesita ser inicializada. Después encontramos la instrucción clrf INDF que significa borrar la posición de memoria apuntada por FSR. Borramos, pues, la memoria situada en 0x0C. A continuación incrementamos FSR que ahora apuntará a 0x0D. Ahora encontramos 2 comprobaciones. Para salir de esta rutina y alcanzar el punto marcado como “aquí se encuentra la continuación del programa” será necesario evitar las 2 líneas goto. Caemos en la primera línea goto cuando el bit 6 de FSR valga 0, si no la saltaremos. Para saltar este “goto” (skip), FSR deberá valer, al menos, B’01000000, o 0x40. En ese momento las direcciones desde 0x0C hasta 0x3F habrán sido puestas a 0. Llegamos a la segunda comprobación. El siguiente goto se ejecutará solamente si el bit 4 de FSR vale 0. En ca-so contrario se saltará. Llegaremos a la línea “aquí …” únicamente cuando los bits 4 y 6 de FSR sean iguales a 1. Es decir en B’01010000, o lo que es lo mismo 0x50. Podrimos igualmente haber usado un contador de bucles y habernos servido de la instrucción decfsz. Pero es-to habría hecho necesaria la utilización de una variable. Esta variable habría sido borrada por la propia rutina con el consiguiente bloqueo del programa lo que hubiese hecho necesarias precauciones adicionales. El método presentado aquí es el más simple y eficaz. Borre las siguientes líneas que no nos interesan para nuestro programa.

bcf TRISA,0 ; Bit PORTA.0 en sortie (exemple) bcf STATUS,RP0 ; Sélectionner banque 0 movlw INTERMASK ; masque interruption movwf INTCON ; charger interrupt control

Deberemos inicializar RA2 como salida para poder utilizarla con el led. La instrucción será (recuerde que es-tamos sobre el banco 1 aún):

bcf TRISA , 2

Habíamos declarado en las definiciones que LED era un alias de PORTA,2 para permitir cambiar fácilmente el led a otro pin. ¿Qué pasará si escribimos bcf LED ? Algunos dirán que “apagamos el LED”. En absoluto. Para aquellos que ya hayan visto el truco, felicitaciones. De hecho, esta instrucción será reemplazada ciegamente por el ensamblador por la siguiente:

bcf PORTA , 2

Es más, después de una nueva sustitución del ensamblador debida a la declaración en nuestro fichero p16F84.inc quedará:

bcf 0x05 , 2

Como RP0 está posicionado a 1 cuando se ejecuta esta línea, estaremos apuntando constantemente al banco 1. Si miramos que hay en la dirección 0x05 del banco 1, es decir en la dirección 0x85, encontrará que está TRI-SA, por lo que la operación lo que hace es poner RA2 como salida.

Page 94: Manual Instrucciones PIC16F84A

94

Cambiando simplemente la definición LED al principio del programa podremos cambiar todas las referencias a RA2 en el programa, incluso para TRISA. Corolario: no es debido a la utilización de #define por lo que todo se resuelve como por arte de magia la selección del banco correcto, hay que mantenerse siempre atento. Ponga un pequeño comentario en la cabecera y tendrá:

; inicializaciones especificas ; --------------------------- bcf LED ; LED en salida (banco 1)

Vaya al banco 0 antes de abandonar la inicialización. Es una buena práctica puesto que muchos errores vienen de olvidos en el cambio de bancos. Añada:

bcf STATUS , RP0 ; pasar al banco 0

Y termine con la línea

goto start

Que le envía al programa principal. Por el momento puede parecer inútil puesto que lo que sigue a continua-ción es el programa principal, pero más adelante vamos a añadir subprogramas y los vamos a intercalar aquí. He aquí a lo que deberá parecerse su rutina de inicialización, que debería comprenderla en su totalidad. Esto al principio tiene el aspecto de ser muy duro pero una vez asimilado verá que es realmente simple. Es más, ya ha visto todo lo necesario para realizar un programa sin rutinas de interrupción.

;************************************************** ******************* ; INICIALIZACIONES * ;************************************************** *******************

init clrf PORTA ; salidas portA a 0 clrf PORTB ; salidas portB a à 0 clrf EEADR ; permite disminuir el consumo bsf STATUS , RP0 ; selecciona banco 1 movlw OPTIONVAL ; carga mascara movwf OPTION_REG ; inicializa registro OPTION ; Borrar RAM ; ------------ movlw 0x0c ; inicializa puntero movwf FSR ; punter direccionamiento indirecto

init1 clrf INDF ; borrar ram incfFSR,f ; apuntar siguiente btfss FSR,6 ; mirar si en fin zona (>=0x40) goto init1 ; no, bucle btfss FSR,4 ; mirar si en fin zona (>=0x50) goto init1 ; no, bucle ; inicializaciones especificas ; --------------------------- bcf LED ; LED en sortie (banque1) bcf STATUS , RP0 ; pasar a banco 0 goto start ; saltar al programa principal

Descendamos en el programa principal y borremos la línea:

clrwdt ; borrar watchdog

Page 95: Manual Instrucciones PIC16F84A

95

11.13 Los resultados del ensamblado

Lance el ensamblado mediante la tecla <F10>. Recuerde que llamamos ensamblado a la traducción (simple) de un programa escrito en lenguaje ensambla-dor en un ejecutable. Por el contrario, llamamos compilación a la conversión (compleja) de un código fuente en lenguaje de alto nivel en un ejecutable.

En la ventana de resultados del ensamblado deberá tener algo así como:

Message[302] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.AS M 101 : Register in operand not in bank 0. Ensure that bank bits are correct.

Es un mensaje de advertencia (warning) que le señala que en la línea 101 (en mi caso, en el suyo puede ser otro numero de línea diferente) ha accedido a un registro que no está en el banco 0. Vaya en el editor a la línea que le ha generado el warning (101 en mi caso). Allí encontrará:

movwf OPTION_REG ; inicializar registro OPTION

Un vistazo a la tabla 4-2 le indica que el registro OPTION está en el banco 1. Pero como nuestro RP0 está a 1 en este momento, no hay ningún problema. Cuando realice grandes programas verá que va a obtener multi-tud de mensajes de advertencia de este tipo. Si quiere evitar tener este tipo de mensajes de banco, añada la directiva

Errorlevel –302

Directamente bajo la línea #include de su fichero. El “-“ significa que quitará este mensaje de los mensajes activos, y el 302 es el numero de warning a evitar. Este número está indicado entre corchetes en el mensaje enviado: Message[302]. Atención, eliminar mensajes de warning significa que MPASM dejará de vigilarlos por usted. Puede volver a poner los mensajes en activo utilizando el signo “+” en cualquier momento de su códi-go en vez de el signo ”-“. Una buena práctica consiste en verificar una zona de su programa con todas las alertas para después desactivarlas.

11.14 El programa principal Queremos hacer parpadear un led a una frecuencia de 1 Hz (1 clic por segundo). A pesar de que, hace ya mu-chos años, decidí escribir la primera versión de este curso en capítulos separados en un fórum, estaba lejos de pensar que “la intermitencia de un led” llegaría a ser un gran clásico del aprendizaje de micro controladores. Brevemente, vamos a crear un programa de la forma:

debut Enciendo el LED Espero ½ sesgundo Apago el LED Espero ½ sesgundo Vuelvo al inicio

Bien, atención a una gran trampa: para hacer parpadear un led a 1 Hz hace falta temporizar 500 ms y no 1 s. Podemos ver que utilizaremos 2 veces la temporización de ½ segundo. Vamos a crear un subprograma que llamaremos « tempo ».

Page 96: Manual Instrucciones PIC16F84A

96

Nuestro programa principal tendrá el siguiente aspecto:

start bsf LED ; encender el LED call tempo ; llamar temporizador de 0.5s bcf LED ; apagar LED call tempo ; llamar temporizador de 0.5s goto start ; boucle

O podríamos escribir también (usando macros):

start

LEDON ; encender el LED call tempo ; llamar temporizador de 0.5s LEDOFF ; apagar LED call tempo ; llamar temporizador de 0.5s goto start ; boucle

Elija el método que prefiera. Lo importante es haberlos comprendido. Le recomiendo el segundo por ser más limpio y más fácil de mantener aunque parezca demandar un esfuerzo de codificación suplementario.

11.15 La subrutina de temporización

Aún no hemos visto el timer0, ni las interrupciones. El objetivo aquí es hacerle entender el funcionamiento del 16F84. Para realizar una temporización, en nuestro caso, es suficiente con hacerle perder tiempo a nues-tro 16F84 entre cada inversión del led. Debemos perder aproximadamente 0.5s. Los segundos no son apropiados para los PIC® que trabajan a una velocidad mucho más elevada. Nosotros vamos a utilizar unidades en la escala de tiempos de los PIC®. Esta noción de escala de tiempos es muy importante en programación, hace falta constantemente imaginarse los eventos que ocurren como si usted fuese el PIC. Esperar 1 segundo equivale entonces a una eternidad y ciertos sucesos que nos parecerían indetectables pasan a ser perceptibles, hablaremos más de esto. Nuestro PIC® está ciclado a la frecuencia de nuestro cuarzo, es decir 4 Mhz. El PIC® ejecuta un ciclo de instrucción cada 4 ciclo de reloj principal. El ejecutara 1.000.000 / 4 = 1 millón de ciclos de instrucción por se-gundo. Recuerde que su simple PIC16F84A puede funcionar perfectamente con un cuarzo de 20 Mhz. Esta-mos trabajando a una quinta parte de las posibilidades. Este curso ha sido escrito inicialmente para PICs trabajando a 4 Mhz, esta es la razón de la elección de este va-lor y así no es necesario modificar todos los ejercicios. Es inútil hacer trabajar al PIC mas allá de lo necesario, de hecho, el hacer parpadear un led a 1 Hz es ya de por sí perfectamente inútil (estamos en un entorno edu-cativo y no en el de la optimización o fabricación a gran escala). Existen otros modelos de PIC de 8 bits que pueden trabajar a velocidades mucho más importantes. El consumo de su PIC aumenta con la velocidad a la que se le hace trabajar. Pensad en ello si realiza montajes alimentados con baterías o pilas. La mayor parte de las instrucciones (excepto los saltos) se ejecutan en un ciclo, lo cual nos da una velocidad de ejecución aproximada a 1 millón de instrucciones por segundo. Verán a veces la denominación de MIPS pa-ra este concepto (Millones de Instrucciones Por Segundo). Nuestro PIC® con este cuarzo tiene una potencia de tratamiento de 1 MIPS.

Page 97: Manual Instrucciones PIC16F84A

97

Cada ciclo de instrucción dura una millonésima parte de segundo, escrito de otra forma 1 µs. He aquí, pues, la unidad de tiempo para trabajar con nuestro PIC®. Nuestra temporización ha de ser de 0.5s, es decir, 500.00 microsegundos. Deberemos no hacer nada durante 500.000 de ciclos de instrucción en nuestra rutina de temporización. Vemos, por lo tanto, que nuestro PIC en esta aplicación se va a pasar la mayor parte de su tiempo sin hacer nada: 1 instrucción útil (encender o apagar el led) por cada 500.000 inútiles. La primera idea que se nos viene a la cabeza es la de realizar un bucle que incremente o decremente una va-riable. Hagámoslo. Empecemos por declarar nuestra variable en la zona de RAM. Añadiendo la declaración obten-dremos:

;************************************************** ******************* ; DECLARACION DE VARIABLES * ;************************************************** ******************* CBLOCK 0x00C ; comienzo de la zona de variables cmpt1 : 1 ; contador de bucles 1 ENDC ; Fin de la zona

Creemos ahora el esqueleto de nuestra subrutina que situaremos entre la rutina de inicialización y el progra-ma principal (de hecho usted podría ponerla en cualquier sitio, se trata de un ejemplo). No olvide usar co-mentarios:

;************************************************** ******************* ; SUBRUTINA DE TEMPORIZACION * ;************************************************** ******************* ;-------------------------------------------------- ------------------- ; Esta subrutina introduce un retardo de 500.000 µs. ; No recibe ni retorna ningún parametro ;-------------------------------------------------- ------------------- tempo

; pondremos aquí el código return ; retorno de la subrutina

Hagamos ahora el bucle.

tempo clrf cmpt1 ; borra el contador 1 boucle1 decfsz cmpt1 ; decrementa el contador 1 goto boucle1 ; si no 0, bucle return ; retorno de la subrutina

Lanzamos el ensamblado con <F10> y obtenemos en la ventana de resultados una línea suplementaria con el siguiente aspecto:

Message[305] D:\DOCUME~1\LESSONS\DATAPIC\LED_CLI.AS M 134 : Using default destination of 1 (file).

Page 98: Manual Instrucciones PIC16F84A

98

El número de línea puede variar en su código fuente. Como el ensamblado se ha realizado correctamente, nuevamente se trata de una línea de warning. Posiciónese en el editor en la línea reseñada (134 en mi caso). Estará sobre la línea:

decfsz cmpt1 ; decrementa el contador 1

¿Qué nos dice el mensaje? Simplemente que no hemos precisado el destino de la instrucción y que ha usado por defecto el valor « 1 » para « file »,, luego se ha usado « ,f » por defecto. En efecto, la instrucción decfsz es de la forma « decfsz f , d ». Nos hemos olvidado de indicar el destino de la operación (el « ,d »). Preste atención, si hubiese querido obtener el resultado en w, el programa sería erróneo. Corrija el código cuando obtenga advertencias de este tipo. Modifique el comando añadiendo ‘,f’:

decfsz cmpt1 , f ; decrementa el contador 1 Ahora vamos a calcular la duración de este tiempo.

tempo clrfcmpt1 ; 1 ciclo boucle1 decfsz cmpt1 , f ; 1 ciclo si no hay salto, 2 con salto

; no salto 255 veces y salto 1 vez goto boucle1 ; 2 ciclos multiplicado por 255 pasadas return ; 2 ciclos

Ya lo hemos visto antes, un tiempo es equivalente a un número de ciclos de instrucción. Podemos medir el tiempo contando el número de ciclos de instrucción. El tiempo total será:

• 2 ciclos para la llamada (call tempo)

• 1 ciclo para el reset de la variable

• 257 ciclos para los 256 decrementos

• 510 ciclos para los 255 gotos

• 2 ciclos para el retorno

Esto hace un total de 772 ciclos que está muy lejos de los 500.000 necesarios. Para los siguientes cálculos, vamos a desechar los 2 ciclos de llamada y los 2 ciclos el return (comparados con 500.000 tienen un efecto irrisorio). Vamos a alargar nuestra rutina realizando un segundo bucle que forzará al primero a realizarse 256 veces. Comencemos declarando una segunda variable cmpt2:

cmpt1 : 1 ; contador de bucles 1 cmpt2 : 1 ; contador de bucles 2

Page 99: Manual Instrucciones PIC16F84A

99

Escribamos los dos bucles imbricados:

tempo clrf cmpt2 ; borra el contador 2

boucle2 clrf cmpt1 ; borra el contador 1

boucle1 decfsz cmpt1 , f ; decrementa contador 1 goto boucle1 ; si no 0, boucler 1 decfsz cmpt2 , f ; si 0, decrementa contador 2 goto boucle2 ; si cmpt2 no 0, volver a empezar bucle 1 return ; retorno de la subrutina

Vemos que nuestros primer bucle está todavía aquí, pero en lugar de efectuar un retorno cuando termina, volvemos a comenzar el bucle en tanto en cuanto cmtp2 no sea igual a 0. Vamos a ejecutar nuestro bucle 1 256 veces. ¿Cuál es la temporización obtenida? Calculémosla de una forma aproximada: Duración del bucle 1: 257 ciclos + 510 ciclos + 1 ciclo (clrf cmpt1) = 768 ciclos Este bucle se va a ejecutar 256 veces luego 768 * 256 = 196.608 ciclos a los que hay que añadir algunos ciclos de inicialización, etc. Pero deseamos tener 500.000 ciclos. Deberemos utilizar entonces sete doble bucle 500.000/196.608=2,54 ve-ces. No sabemos hacer medio bucle. Efectuaremos 2 dobles bucles y deberemos arreglar el doble bucle de forma que dure 250.000 ciclos (500.000/2). Cada instrucción que añadamos al bucle 1 será ejecutada 256*256 veces. Cada instrucción añadida al bucle 2 será ejecutada 256 veces. Cada instrucción añadida al exterior será ejecutada 1 vez. Tenemos, pues, posibili-dades de realizar temporizaciones muy precisas. Esto no es necesario aquí. De todas formas vamos a mejorar la precisión. Si añadimos 1 ciclo inútil al bucle 1 añadiremos 256*256 = 65.536 ciclos. Deberíamos añadir 250.000 – 196.608 = 53.392 ciclos. Eso nos dará un error de 12.000 ciclos, es decir, 12 milésimas de segundo (12ms). La precisión sería más que suficiente para nuestra aplicación, pero le recuerdo que podemos afinar más nuestra precisión realizando cálculos más precisos. Le recomiendo que lo haga como ejercicio. Puede verificar sus re-sultados con un cronometro (midiendo un gran número de intermitencias). Para perder tiempo, añadiremos una instrucción NOP que no hace nada (excepto perder tiempo). Queda por realizar el tercer bucle para ejecutar el doble bucle 2 veces. Crearemos una tercera variable cmpt3 y un tercer bucle (recuerde que habría métodos más simple, pero se trata de un ejemplo didáctico cuya fina-lidad es entender los conceptos mostrados).

;************************************************** ******************* ; DECLARACION DE VARIABLES * ;************************************************** ******************* CBLOCK 0x00C ; comienzo de la zona de variables cmpt1 : 1 ; contador de bucles 1 cmpt2 : 1 ; contador de bucles 2 cmpt3 : 1 ; contador de bucles 3 ENDC ; Fin de la zona de variables

Page 100: Manual Instrucciones PIC16F84A

100

He aquí el código final:

;************************************************** ******************* ; SUBRUTINA DE TEMPORIZACION * ;************************************************** ******************* ;-------------------------------------------------- ------------------- ; Esta subrutina introduce un retardo de 500.000 µs. ; No recibe ni retorna ningún parametro ;-------------------------------------------------- ------------------- tempo

movlw 2 ; para 2 bucles movwf cmpt3 ; inicializa compteur3

boucle3 clrf cmpt2 ; borra compteur2

boucle2 clrf cmpt1 ; borra compteur1

boucle1 nop ; pierde 1 cycle *256 *256 *2 decfsz cmpt1 , f ; decrementa compteur1 goto boucle1 ; si no 0, boucler decfsz cmpt2 , f ; si 0, decrementa compteur 2 goto boucle2 ; si cmpt2 no 0, volver a empezar bucle1 decfsz cmpt3 , f ; si 0, decrementa compteur 3 gotoboucle3 ; si cmpt3 no 0, volver a empezar boucle2 return ; retorno de la subrutina

Lance el ensamblaje con <F10>. Si todo se desarrolló correctamente habrá obtenido en el directorio de traba-jo el fichero « Led_cli.hex ». Envíeselo al PIC® con la ayuda del programador (grabador). Coloque el PIC® programado en la tarjeta con la alimentación desconectada. Conecte la alimentación y observe que pasa.

EL LED PARPADEA A LA FRECUENCIA DE 1 HZ Felicitaciones, acaba de realizar su primer programa embarcado. La puerta se ha abierto a toneladas de nue-vas aplicaciones. CONSEJO IMPORTANTE Si en este momento usted salta hasta el techo gritando “!! Esto funciona!!” no nos precipitemos corriendo a enseñárselo a su mujer. Dudo mucho que ella participe de su entusiasmo por “esta pequeña lucecita que hace intermitencias”. El fichero de trabajo que tendríamos al final de esta lección está disponible en los ficheros adjuntos.

Page 101: Manual Instrucciones PIC16F84A

101

12. Las interrupciones

He aquí un capítulo que puede atemorizar un poco a los futuros programadores. Algunos hablan de esto co-mo reservado para “frikis” o que se trata de secretos de magia negra destinados a hacer huir a todo aquel desgraciado novato que intente aproximarse. Otros intentan rehuirlo haciendo uso de un lenguaje de alto ni-vel mágico que haría desaparecer el aspecto material por otra parte indispensable. El mecanismo de las interrupciones es muy fácil de entender, y por lo tanto de ponerlo en práctica, si lo ma-nejamos de una forma limpia y estructurada.

12.1 ¿Qué es una interrupción?

Imagine una conversación normal:

• Cada interlocutor toma la palabra cuando es su turno.

• De repente algo exterior ocurre que requiere un tratamiento urgente. Por ejemplo, un piano cae des-de el tercer piso a los pies de los que están discutiendo.

• Se imaginará que su interlocutor no va a esperar a que usted termine su frase para señalarle el peli-gro. Él le va a interrumpir su discurso para hacerle notar la situación de peligro.

• Los interlocutores retomarán la conversación cuando el peligro desaparezca (siempre y cuando el piano no hubiese acabado con ellos).

Si usted ha entendido lo anterior ya entiende lo que es una interrupción. En efecto, para los programas es exactamente el mismo principio. Su programa se desarrolla normalmente. De repente ocurre un evento específico e imprevisible. El programa principal se interrumpe (interrupción) y va a tratar el evento antes de retomar el programa principal en el punto en el que lo había dejado. La interrupción es una rotura de secuencia asíncrona, es decir, no sincronizada con el desarrollo normal del programa. Dicho de otro modo, que puede ocurrir en no importa qué momento del desarrollo del programa y de una forma impredecible. Pude ver la diferencia con las roturas de secuencia síncronas provocadas por el propio programa (goto, call , btfss …) y que ocurren en un entorno perfectamente previsto.

12.2 Mecanismo general de una interrupción

Podemos decir, sin equivocarnos mucho, que una rutina de interrupción es un subprograma particular, dispa-rado por la aparición de un evento específico. Esto puede parecer un poco ardua pero va a ver que es muy sencillo. Veamos cómo funciona:

• El programa se ejecuta normalmente.

• Aparece el evento

• El programa archiva la instrucción en curso.

• El programa salta a la dirección de tratamiento de la interrupción (0x04).

• El programa trata la interrupción.

• El programa termina el tratamiento de la interrupción y salta a la instrucción que sigue a la última que fue ejecutada en el programa principal.

Page 102: Manual Instrucciones PIC16F84A

102

Es bien seguro que no se disparará una interrupción no importa cual evento. Hace falta que se cumplan 2 condiciones generales:

1. El evento tiene que figurar en la lista de eventos susceptibles de provocar una interrupción sobre el procesador con el que se trabaja.

2. El usuario debe haber autorizado la interrupción, es decir, debe haber señalado que el evento en

cuestión debe generar una interrupción.

Veamos el organigrama general de la ejecución de una interrupción:

¿Qué podemos decir viendo este organigrama? De entrada podemos decir que el programa principal no sabe cuándo va a ser interrumpido, ni tan siquiera que lo ha sido. Es por lo tanto crucial reponer los registros en el estado en que estaban antes de ser interrumpido. Supongamos que la instrucción xxx había puesto un Flag (por ejemplo el bit Z). Si por desgracia la rutina de interrupción modifica ese Flag, a la vuelta, el programa principal no podrá hacer uso del mismo de una forma correcta y continuar normalmente.

Page 103: Manual Instrucciones PIC16F84A

103

Vemos también que la instrucción xxx termina su ejecución antes de ramificarse a la rutina de interrupción. Una instrucción empezada no se verá interrumpida jamás. Nota: en ciertas familias de micro controladores existe una dirección de interrupción por cada tipo por lo que no tendría sentido en ellos la comprobación de qué tipo generó la interrupción.

12.3 Mecanismo de interrupción de los PIC®

Los PIC responden en general al procedimiento hasta aquí descrito, pero también tienen sus propias particu-laridades. Vamos a ver los principios sobre la familia de los PIC16F®.

• Por principio, la dirección de comienzo de cualquier interrupción es fija y se trata siempre de la direc-ción 0x04.

• Corolario: toda interrupción provocara el salto del programa hacia esta dirección. Todas las fuentes de interrupción llegan a esta dirección. Si el programador usa varias fuentes de interrupción, hará fal-ta que él mismo determine cuál es la fuente que está a punto de ser tratada.

• Los PIC16F® cuando se conectan a esta dirección no guardan nada excepto el contenido del PC, que servirá para conocer la dirección de retorno de la interrupción. Es pues, el usuario el encargado de salvaguardar y posteriormente recuperar la información.

• El contenido del PC se salva sobre la pila interna (8 niveles). Luego, si utiliza las interrupciones, solo dispone de 7 niveles de imbricación para sus sub programas. Menos si usted utiliza sub programas en sus interrupciones.

• El tiempo de reacción de una interrupción está calculado de la siguiente forma: o El ciclo de la instrucción actual se termina. o El Flag de interrupción es leído al comienzo del ciclo siguiente. o Este es archivado. El procesador se para un ciclo para cargar la dirección 0x04 en el PC. o El procesador se conecta a la dirección 0x04 donde le hará falta un ciclo suplementario para

leer la siguiente instrucción a ejecutar.

El tiempo muerto total estará entre 3 y 4 ciclos. Si queremos ser precisos, la lógica interna hace que toda inte-rrupción que ocurre antes del primer cuarto de la instrucción en curso será tenida en cuenta como si la ins-trucción no hubiese comenzado. Dicho de otra forma, el tiempo muerto estará comprendido entre 3 y 3,75 ciclos. Observe igualmente que si la interrupción es síncrona con el oscilador del PIC (interrupción interna, generalmente el timer), no podrá aparecer en mitad de un ciclo, el tiempo muerto será entonces obligatoria-mente de 3 ciclos. A remarcar:

• Una interrupción no puede ser interrumpida por otra interrupción. Las interrupciones se invalidan de forma automática mediante el borrado del bit GIE (que veremos) después de un salto a la posi-ción 0x04.

• Las interrupciones son devueltas a servicio automáticamente después del retorno de interrupción.

La instrucción de retorno sobre un PIC16F, « RETFIE » se trata exactamente como de una ins-trucción RETURN excepto en que la primera reposiciona el bit GIE al mismo tiempo.

Page 104: Manual Instrucciones PIC16F84A

104

Esto nos lleva al siguiente organigrama para las interrupciones en el PIC16F®:

12.4 Las fuentes de interrupción en el 16F84

El PIC16F84® es muy pobre a este nivel dado que solo dispone de 4 fuentes de interrupción (frente a las 13 por ejemplo del PIC16F876®). Los sucesos posibles para generar una interrupción son:

• TMR0: Desbordamiento del timer0 (tmr0). Una vez que el contenido del tmr0 pasa de 0xFF a 0x00 se puede generar una interrupción. Utilizaremos esta propiedad en el capítulo sobre el timer 0.

• EEPROM: Esta interrupción se puede generar una vez completada la escritura sobre una posición de la zona de memoria EEPROM.

• RB0/INT: Se puede generar esta interrupción estando el pin RB0, también llamado INTerrupt pin, con-figurado como entrada y se ha modificado el nivel aplicado a él.

• PORTB: De la misma forma, se puede generar una interrupción si el nivel de uno de los pines de RB4 a RB7 cambiase. No es posible limitar la interrupción a uno de los pines en particular. La interrupción será efectiva para los 4 o para ninguno.

Page 105: Manual Instrucciones PIC16F84A

105

12.5 Puesta en marcha de los dispositivos

¿Cómo impedir o autorizar las interrupciones? ¿Cómo detectar cuál es el evento que disparó la interrupción? ¿Cómo gestionarlas? Primero vamos a abordar el tema de una forma simbólica para ayudar a su visualización. Imaginemos un hotel. El responsable de servicio representa nuestro programa. Este hotel tiene 4 cuartos (nuestras 4 fuentes de interrupción), y cada cuarto está equipado con un botón pulsador. Cada botón sirve para llamar al responsable y está conectado a una bombilla en la recepción del hotel. Cada bombilla tiene la posibilidad de hacer sonar un timbre (desencadenar una interrupción):

• Si el interruptor general del timbre está en posición ON.

• Y si el interruptor particular de cada bombilla está en ON.

He aquí el esquema que obtenemos:

Cuando entienda bien este pequeño esquema eléctrico equivalente, habrá comprendido el principio de las in-terrupciones. Puede ver enseguida que hay dos formas por las que el responsable del servicio puede enterar-se que un cuarto pide atención:

O bien a través de la bombilla o bien a través del timbre. - Mediante el primer método, el responsable deberá mirar las bombillas regularmente para verificar que

alguien llama. Deberá escrutar el tablero de señalización. Es el responsable el que decide cuando mira y por lo tanto cuando reaccionar ante una demanda.

Page 106: Manual Instrucciones PIC16F84A

106

- Con el timbre, el responsable es interrumpido de su trabajo. No tiene necesidad de ir a mirar el panel de señalización inútilmente, pero será interrumpido en su trabajo. Este es el método de las interrupciones. El responsable no es el que decide cuando reaccionará a la demanda de un cliente, lo hará cuándo suene el timbre (si no, lo despediremos). Observe los siguientes puntos:

• El huésped no puede decidir que método va a utilizar el responsable del servicio, es él quien decidirá validar o no los timbrazos.

• El responsable puede impedir todas las posibilidades de que suene el timbre de una vez a través del interruptor general o decidir qué habitación puede hacerla sonar y cual no (interruptores asociados a cada habitación).

• Los interruptores de validación no trabajan sobre las bombillas.

• Si el responsable es interrumpido durante su trabajo por el timbre, tiene de todas formas que ir al panel de señalización para ver quien le está llamando, salvo que solo haya autorizado a una habita-ción.

• Lo que no aparece en este esquema simplificado, pero que hay que tener en cuenta, es que una vez que una bombilla se encendió, esta se quedará encendida (el botón queda memorizado). Es el res-ponsable de servicio quien la apagará manualmente.

Pongamos todo esto en práctica para el 16F84. Habrá deducido ya, que para el 16F84 botones y lámparas se tratan de bits de registros particulares. Veamos el registro principal de gestión de interrupciones del 16F84.

12.6 El registro INTCON (INTerrupt CONtrol)

Este registro está situado en la dirección 0x0B de los dos bancos. Está siempre accesible. Está detallado en la tabla 4-5 de la pagina 17. Es un registro de bits por lo que cada bit tendrá un significado particular. He aquí el detalle de cada bit:

b7 : GIE Global Interrupt Enable bit. Permite validar o no todas las interrupciones a la vez. Corresponde al interruptor de validación general de nuestro esquema. b6 : EEIE EEprom write complete Interrupt Enable bit. Este bit permite validar la interrupción de fin de escritura en ee-prom (estudiaremos más tarde el mecanismo de escritura en la eeprom). Se trata de uno de los interruptores de las habitaciones. b5 : T0IE

Tmr0 Interrupt Enable bit: Valida la interrupción generada por el desbordamiento del timer0. b4 : INTE

INTerrupt pin Enable bit: Valida la interrupción en el caso de variación de nivel en RB0. ATENCION: recuerde el bit 6 del registro OPTION el cual determina qué sentido de variación provocará la interrupción. Podrá elegir de 0 � 1 ó de 1�0 pero no ambos a la vez.

b3 : RBIE

RB Interrupt Enable bit: Valida las interrupciones en el caso de cambio en los bits de RB4 a RB7.

Page 107: Manual Instrucciones PIC16F84A

107

b2 : T0IF Tmr0 Interrupt Flag bit. Es un Flag, es decir, una señal. Este es el de desbordamiento del timer0. Todos los bits acabados en F son flags (banderas, en el sentido se señalización). Corresponden a las lámparas de nuestro es-quema. b1 : INTF INTerrupt pin Flag bit: señala una transición del pin RB0 en el sentido determinado por INTEDG del registro OPTION (b6). b0 : RBIF

RB Interrupt Flag bit: señala que una de las entradas de RB4 a RB7 ha sido modificada.

Observacion

Recuerde que los flags no se reposicionan a 0 por si mismos. Si estos flags están ligados a una interrupción au-torizada, su programa los debe reponer a 0 en el tratamiento de la interrupción, bajo pena de no poder jamás salir de la misma. En efecto, cuando salga de la interrupción, las condiciones normales serán reposicionadas y la interrupción será inmediatamente vuelta a llamar. Diremos que estos flags son remanentes.

Observación

Tenga en cuenta que el reset de RBIF debe estar precedido de una lectura o de una escritura del PORTB con el fin de registrar (automáticamente) el ultimo valor del PORTB utilizado. De lo contrario, desde el momento del reset de RBIF, sería inmediatamente reposicionado por el PIC al reconocer una modificación de lPORTB desde el último valor almacenado en la última lectura del PORTB. Todos los bits cuyo nombre acaba en E (Enable) son de hecho conmutadores de validación (son los interrupto-res de nuestro esquema simbólico). Los bits cuyo nombre termina por F (Flags) (son las lámparas de nuestro esquema). Tendremos 5 interruptores y 4 lámparas. Pero solo tenemos 8 bits. ¿Qué nos falta? Si observa atentamente, nos falta el bit EEIF. Pero, se lo aseguro, este bit existe. Simplemente está en otro re-gistro, se trata del EECON1 que veremos en la lección sobre el acceso a la EEPROM. Veamos el esquema para el PIC® 16F84.

Page 108: Manual Instrucciones PIC16F84A

108

Después de estas detalladas explicaciones, demasiado dirán los habituados a los procesadores, deberá haber comprendido el funcionamiento de las interrupciones en el 16F84. Analicemos ahora algunos detalles de la rutina de interrupción en sí misma. Para ser concretos, vamos a mezclar teoría y práctica.

12.7 Guardado y restauración del entorno

Si observa el nuevo organigrama de la rutina de interrupción, constatará que debe proceder a la salvaguardia y restauración del entorno de su programa. ¿En qué consiste esto? Como ya hemos dicho, el programa interrumpido no sabe qué ha pasado. Deberá reponer los registros en el estado en que se encontraban cuando ocurrió la interrupción para permitir que el programa continúe el fun-cionamiento después de haber tratado la interrupción. Pequeño ejemplo: Supongamos que el programa fue interrumpido entre las 2 instrucciones siguientes:

movf mavariable , w ; cargar mavariable y posicionar Z -> interruption ici btfss STATUS , Z ; test Z y salto si vale 1

Es más que probable que la rutina de interrupción va a utilizar una instrucción que modifique el bit Z. Deberá restaurar el registro STATUS a la posición en que se encontraba antes de la interrupción. Si no fuese así, el test no se realizará sobre el valor de mavariable, sino sobre el valor de Z modificado por la rutina de interrup-ción. Es más, será casi cierto que el registro « w » va a ser modificado por lo que hará falta salvarlo igualmente.

12.7.1 Los registros a salvaguardar Comencemos por una generalidad: en nuestra rutina de interrupción deberemos salvar

• Todo registro modificado por la rutina

• Si nuestro programa principal utiliza ese registro

• Y si nuestro programa principal no espera una modificación de ese registro.

Dicho de otra forma, usted deberá salvar (y restaurar evidentemente) todo aquello que, siendo modificado por la rutina de interrupción, induciría un comportamiento erróneo del programa principal. Evidentemente, hay registros que, casi con toda certeza, deberán ser salvados atendiendo a las razones pre-cedentes. Para empezar, el más simple: el PC. Si no fuese salvado sería imposible volver al entorno del programa princi-pal cuando fue interrumpido. Deberá ser salvado en el entorno de la misma interrupción. Es imposible hacerlo de forma manual, por lo que esta salvaguardia forma parte del funcionamiento automático de las interrup-ciones. Usted no se debe preocupar de hacerlo. A continuación, el registro STATUS. Contiene todos los flags condicionales. Es prácticamente inconcebible una rutina de interrupción que no utilice ninguna instrucción que modifique el STATUS (o entonces la interrupción no hace gran cosa).

Page 109: Manual Instrucciones PIC16F84A

109

Como el programa principal tiene todas las oportunidades del mundo de utilizar una instrucción condicional o de cambio de banco, la salvaguardia de STATUS es prácticamente obligatoria. El mismo razonamiento para el registro W. Los registros a salvaguardar y restaurar de oficio en sus interrupciones serán al 99,99% de posibilidades: STA-TUS y W. En lo que se refiere a otras salvaguardas eventuales, deberá realizarlas en función del contexto. Por ejemplo, si su programa principal utiliza el direccionamiento indexado y su rutina de interrupción también, será casi seguro necesario salvaguardar y restaurar el registro FSR. Se podría pensar en guardar las variables utilizadas en la rutina de interrupción, pero entonces sería necesario un sitio para guardar (otra variable) cada variable, lo que no tendría sentido. Moraleja, si utiliza variables en sus rutinas de interrupción, estas deberán ser variables reservadas para este uso y no ser utilizadas en el pro-grama principal (salvo si las modificaciones realizadas en la rutina de interrupción son útiles para el programa principal).

12.7.2 El método de salvaguarda

Para guardar los registros W y STATUS se utiliza un método ya considerado clásico. Comporta comenzar pri-mero por salvar W y después STATUS dado que la salvaguarda de STATUS conlleva cargarlo primero en el re-gistro W lo que induciría la perdida de W. La salvaguardia se resume en:

movwf w_temp ; salva W en un sitio adecuado movf STATUS,w ; transfiere STATUS a W movwf status_temp ; salva STATUS

Observe que el datasheet y los documentos de referencia, como el “mid-range reference manual”, indican el siguiente método:

movwf w_temp ; salva W en un sitio adecuado swapf STATUS,w ; transfiere STATUS intercambiado en W movwf status_temp ; salva STATUS

En lugar de un movf, Microchip recomienda la utilización de un swapf (que no modifica ningún bit de STATUS) en la maniobra. Evidentemente el octeto cargado está “swapeado” por lo que será necesario otro swap en la reposición. No hay ningún problema en que se modifique STATUS cuando se carga en W, puesto que es W (el antiguo contenido de STATUS) el que será salvado. Después de consultar sobre este tema especifico, no he obtenido, por parte de Microchip, la razón exacta de esta recomendación por su parte (¿Podría ser un antiguo “bug” concerniente a la manipulación de STATUS?). Parece incluso que este método ha desaparecido de las recomendaciones oficiales de Microchip. De todas formas, las primeras versiones de este curso han utilizado el método recomendado, por lo que lo encontrará en los fuentes y documentos. Sepa simplemente, que puede utilizar el método que más le apetez-ca y no cambiará absolutamente nada. Evidentemente, si utiliza un swap para salvar, deberá utilizar un swap para restaurar, si no, el valor restaurado quedará invertido frente al original. ¿Por qué hacerlo simple cuando se puede hacer complicado?

Page 110: Manual Instrucciones PIC16F84A

110

ATENCION: esto vale exclusivamente para el registro STATUS. En lo que concierne a la restauración de W, las observaciones que vienen a continuación permanecen de aplicación.

12.7.3 El método de restauración

Usted me dirá: ¿Qué más hay que decir al respecto? En efecto, parece lógico simplemente proceder a la in-versa.

movf status_temp,w ; carga el STATUS salvaguardado movwf STATUS ; restaura STATUS movf w_temp,w ; restaura W

Todo parece simple y sin embargo se esconde una gran trampa. En efecto, la ultima « movf » instrucción modifica el bit Z de STATUS. Y si restaura primero W, lo destruirá al recuperar STATUS. Esto es inaceptable. Necesitaremos cambiar el método. El truco será restaurar W con una instrucción que no modifique ningún bit de STATUS. Un vistazo a las instrucciones disponibles nos permite ver que la instrucción « swapf » lleva la variable a W sin modificar ningún bit de STATUS. De todas formas, esta instrucción presenta el inconveniente de invertir los 4 bits de peso fuerte con los 4 bits de peso débil del octeto designado, lo que conduciría a una restaura-ción incorrecta. Será necesario anular esta inversión y utilizar 2 instrucciones swapf consecutivas lo que pondrá al octeto en orden correcto. La rutina modificada presenta ahora el siguiente aspecto:

movfstatus_temp,w ; carga el STATUS salvaguardado movwf STATUS ; restaura STATUS swapf w_temp,f ; swapea el sitio guardado swapf w_temp,w ; re-swapea y lleva el resultado a W

De esta forma W estará bien y bellamente restaurado sin haber modificado el registro STATUS anteriormente restaurado. Veamos los dos métodos que son estrictamente equivalentes. METODO INTUITIVO

Salvaguardia movwf w_temp ; salva W en un sitio adecuado movf STATUS,w ; transfiere STATUS a W movwf status_temp ; salvaguarda STATUS

… … … Restauracion movf status_temp,w ; carga el STATUS salvaguardado movwf STATUS ; restaura STATUS swapf w_temp,f ; invierte el sitio de salvaguardia de W swapf w_temp,w ; re-invierte y lleva el resultado a W

Page 111: Manual Instrucciones PIC16F84A

111

METODO PRECONIZADO ORIGINALMENTE

Salvaguardia movwf w_temp ; salva W en un sitio adecuado swapf STATUS,w ; transfiere STATUS swapeado a W movwf status_temp ; salvaguarda STATUS

… … … Restauracion swapf status_temp,w ; carga el STATUS salvaguardado movwf STATUS ; restaura STATUS swapf w_temp,f ; invierte el sitio de salvaguardia de W swapf w_temp,w ; re-invierte y lleva el resultado a W

He aquí la estructura de base de una rutina de interrupción:

;************************************************** ******************** ; ROUTINE INTERRUPTION * ;************************************************** ******************** ;salvaguardar registros ;--------------------- ORG 0x004 ; direccion de interrupcion movwf w_temp ; salvar W swapf STATUS,w ; swap STATUS con resultado en w movwf status_temp ; salvar STATUS swapeado ; switch hacia las diferentes interrupciones ; ---------------------------------- ; test de que interrupcion se trata ; tratamiento interrupcion ; ----------------------------- ; tratamiento interrpcion despues de borrar su flag ;restaurar registros ;------------------- swapf status_temp, w ; swap antiguo STATUS, resultado en w movwf STATUS ; restaura STATUS swapf w_temp,f ; Inversion L y H del antiguo W sin modificar Z swapf w_temp,w ; Re-inversion de L y H en W sin modificar Z retfie ; return desde interrupcion

Este es el esqueleto de la rutina de interrupción. ¿No es complicado, verdad? Solo resta completarla puesto que el fichero m16F84.asm contiene las rutinas de salvaguardia y restauración (que acabamos de explicar) y los test del tipo de interrupción (que vamos a detallar). Como soy partidario de la programación estructurada, la rutina de switch nos conecta, en realidad, con subru-tinas separadas. En efecto, nada nos impide el utilizar subrutinas en una rutina de interrupción. Solo hay que prestar atención al límite de pila disponible.

Page 112: Manual Instrucciones PIC16F84A

112

12.7.4 Operaciones sobre el registro STATUS

No me gustaría terminar esta parte sin prestar atención a un punto en particular, a saber, instrucciones en las cuales el destino es el registro STATUS. Toda instrucción en la que el resultado es susceptible de modificar uno o varios bits del registro STATUS, y en la que la meta es el registro STATUS en sí mismo, induce que cada bit susceptible de ser modificado por la ins-trucción se encuentra en lectura solamente. Algunos ejemplos para ser más explicito:

movwf STATUS ; ningun problema, movwf no modifica ningún flag movf STATUS , w ; ningun problema, STATUS no es el destino andwf STATUS ; el bit Z se posicionará en función del resultado addwf STATUS ; idem para C, DC, et Z . clrf STATUS ; modifica Z, y no da el resultado esperado

Si toma por ejemplo la ultima instrucción, esperará obtener STATUS=0. Parecería que todo pasase como si, modificando el bit Z por clrf, este no estuviese afectado por la instrucción. Al final, inmediatamente después de limpiar STATUS, el resultado valdrá 0, el bit Z se reposionará inmediatamente y STATUS no valdrá jamás 0. Sea pues muy prudente y si el destino de la instrucción es el registro STATUS, utilice de preferencia un movwf o un swapf o las instrucciones de manipulación de bits (bcf y bsf).

12.7.5 Particularidad de la instrucción « RETFIE »

A este nivel de la exposición, un remarque pertinente es el siguiente: ¿Por qué existe una instrucción RETFIE puesto que podríamos utilizar una instrucción RETURN precedida de una instrucción GIE? Para empezar, la misma estructura de nuestra rutina impide que una interrupción pueda ser interrumpida por otra. Si este fuese el casi, la salvaguardia de los registros W y STATUS seria destrozada por una segunda ope-ración (esto es posible en otros microprocesadores incluso en otros PIC®). Diremos que nuestra rutina no es re-entrante. Para tener en cuenta este problema, el PIC fuerza el bit GIE a 0 desde la misma entrada a la rutina de inte-rrupción. Para que una nueva interrupción pueda tener lugar después de terminada la interrupción en curso, hace falta volver a poner GIE a 1. Esto es ejecutado automáticamente por RETFIE. Me podría decir ¿Y si hiciésemos de la siguiente manera?

bsf INTCON , GIE ; reponer GIE a 1 return ; y salir de la rutina de interrupcion

Y bien, es exactamente lo que hace RETFIE excepto por un pequeño e importante detalle. RETFIE es una sola instrucción en sí misma por lo que no puede ser interrumpida por una interrupción. Diremos que es una ins-trucción “atómica” (no puede ser dividida). En el caso del uso de las dos instrucciones precedentes, una vez puesto a 1 GIE, si uno de los flags de inte-rrupción está a 1 (otra interrupción o la misma que se reproduce otra vez), el programa se reconectará a la ru-tina de interrupción antes de haber ejecutado RETURN, es decir, antes de haber restaurado el PC. Continuaremos ocupando un emplazamiento en la pila. Sabiendo que la pila tiene un tamaño limitado a 8 emplazamientos, tendremos muchísimas oportunidades de bloqueo del programa por desbordamiento de pi-la.

Page 113: Manual Instrucciones PIC16F84A

113

Sería imposible calcular el tamaño de pila utilizado por el programa puesto que ignoramos el número de en-tradas a la rutina de interrupción. Todas las condiciones para un bloqueo aleatorio estarán cumplidas. Por lo tanto, utilice siempre la instrucción RETFIE para salir de una rutina de interrupción, salvo que quiera que no se vuelvan a producir ninguna interrupción después de la primera que se ejecute (caso particular).

12.8 Utilización de una rutina de interrupción

Efectúe una copia de su fichero m16f84.asm. Renómbrelo « myinter.asm ». Cree un nuevo proyecto MPLAB® con el nombre « myinter » según el procedimiento habitual. Vamos a construir un programa que invierta el encendido de un LED a cada presión sobre un pulsador. Modi-fique su placa de experimentación conectando el pulsador sobre la entrada RB0 (lo siento, cuando diseñé el esquema pensaba explicar las interrupciones con el timer).

Esto le muestra que la electrónica y la programación con micro controladores son muy dependientes una de otra. Cuando usted construye un circuito ya debe de pensar en la manera en la que va a programarlo. A la in-versa, si ya dispone de un circuito esto le va a imponer numerosas restricciones a su programación. Por ejem-plo, sería imposible manejar el pulsador por medio de interrupciones si estuviese cableado a la entrada RB2.

La resistencia es interna en el PIC®, se trata de una resistencia de carga a +5V (pull-up) que podemos activar por lógica. Como de costumbre, rellenaremos el encabezamiento y suprimiremos las líneas que no vamos a utilizar en las variables, macro y DEFINE. Atención!! No suprima las variables w_temp y status_temp.

Page 114: Manual Instrucciones PIC16F84A

114

#include <p16F84.inc> ; Definicion de constantes

;************************************************** ******************** ; Este es un programa didáctico destinado a mostrar * ; el funcionamiento de las rutinas de interrupción * ;************************************************** ******************** ; * ; NOMBRE: Interrupcion por pulsador sobre RB0 * ; Fecha: 13/02/2001 * ; Version: 1.0 * ; Circuito: Placa de experimentación * ; Auteur: Bigonoff * ; * ;************************************************** ******************** ; * ; Fichero requerido: P16F84.inc * ; * ; * ; * ;************************************************** ******************** ; Notas: Este programa transforma un pulsador en te leruptor. * ; Una pulsación enciende el LED y otra lo apaga. * ;************************************************** ********************

Me puede decir que exagero al hacerle poner comentarios por todos los sitios. Créame, los comentarios nos permitirán hacer un mantenimiento del programa. Le hará ganar mucho más tiempo del que ahora pierde en escribirlos. Siento en ser tan insistente pero recibo regularmente programas de otros internautas desprovistos de todo comentario. Si me los envían, es porque no encuentran el error que contienen. Estos internautas deberían mostrarse más modestos al estimar que “los comentarios están muy bien para los otros”. De todas formas, en el momento que recibo un programa desprovisto de comentarios este va directamente a la papelera. Mi tiempo vale tanto o más que el de estos programadores “ahorradores”. Modifique la configuración para suprimir el funcionamiento del watch-dog:

__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

Calculemos el valor a enviar al registro OPTION - b7 a 0 , necesitamos la resistencia de carga a +5V para el pulsador. - b6 a 0, queremos interrupción cuando pulsemos, nivel de 1 a 0 (flanco descendente). - b5/b0 a 0, ninguna necesidad en esta aplicación Por lo tanto pondremos la asignación B’00000000’:

;************************************************** *******************

; ASSIGNATIONS *

;************************************************** ******************* OPTIONVAL EQU H'00' ; valor del registrp OPTION ; Resistencias pull-up en ON ; Interrupcion flanco descendante en RB0

Page 115: Manual Instrucciones PIC16F84A

115

Para el registro INTCON deberemos tener: b7 a 1: valida las interrupciones b6 a 0: no interrupción EE b5 a 0: no interrupción tmr0 b4 a 1: interrupción RB0 en servicio b3 a 0: no interrupción RB4/RB7 b2/b0 a 0: borrar los flags esto nos da B’10010000’, es decir 0x90 Asignemos una constante para este valor;

INTERMASK EQU H'90' ; Mascara de interrupciones ; interrupciones sobre RB0

Inmediatamente, en la zona de los DEFINE, definiremos nuestro LED y nuestro pulsador:

;************************************************** ******************* ; DEFINE ************************************* *********** ;********************** #DEFINE Bouton PORTB , 0 ; pulsador #DEFINE LED PORTA , 2 ; LED

En la zona de macros, podemos escribir 2 macros que utilizaremos a menudo. Son las instrucciones para pasar del banco 0 al banco 1. Las incluiremos en el nuevo fichero « m16f84.asm » renombrado « m16f84_new.asm »como. Al final de esta lección, suprima el antiguo fichero y renombre « m16f84 » como « m16F84_new ».

;************************************************** ******************* ; MACRO * ;************************************************** ******************* BANK0 macro

bcf STATUS , RP0 ; passer en banque 0 endm

BANK1macro bsf STATUS , RP0 ; passer en banque1 endm

Vamos a la zona de variables. Debemos conservar w_temp y status_temp, puesto que estas variables son utilizadas en la rutina de interrupción para salvar los registros W y STATUS. Vamos a recuperar también la pequeña rutina de tiempo de nuestro fichero « led_cli.asm ». Necesitaremos, pues, las variables utilizadas en esta rutina. Todo lo anterior nos dará una zona de variables por el momento como sigue:

;************************************************** ******************* ; DECLARATIONS DE VARIABLES * ;************************************************** *******************

CBLOCK 0x00C ; comienzo de la zona de variables w_temp :1 ; salvaguardia de W en las interrupciones status_temp : 1 ; salvaguardia de STATUS en las int errupciones cmpt1 : 1 ; contador de bucles 1 en tempo cmpt2 : 1 ; contador de bucles 2 en tempo cmpt3 : 1 ; contador de bucles 3 en tempo ENDC ; Fin de la zona

Page 116: Manual Instrucciones PIC16F84A

116

12.9 Análisis de la rutina de interrupción

Ya hemos visto la primera parte que es la salvaguardia de los registros utilizados.

;************************************************** ******************** ; ROUTINE INTERRUPTION * ;************************************************** ******************** ;sauvegarder registres ;--------------------- org 0x004 ; adresse d'interruption movwf w_temp ; sauver registre W swapf STATUS , w S ; swap status avec résultat dans w movwf status_temp ; sauver status swappé

A continuación deberemos determinar cuál es el origen de la interrupción en curso. En el caso presente, se trata obligatoriamente de la interrupción RB0/INT, puesto que es la única que tenemos autorizada. Podríamos pasar de este test, pero no lo haremos puesto que el objetivo es explicar los métodos a utilizar. Vamos a des-arrollar la totalidad de las explicaciones de forma que pueda utilizar cualquier combinación. Examinemos, pues, esta parte:

; switch hacia las diferentes interrupciones ; inversión del orden para modificar prioridades ;---------------------------------------- btfsc INTCON , T0IE ; test si interrupcion timer autorizada btfss INTCON , T0IF ; SI , test si interrupcion timer en curso goto intsw1 ; NO, test siguiente call inttimer ; SI, tratar interrupcion timer goto restorereg ; y fin de la interrupcion ; SUPRIMIR ESTA LINEA PARA ; TRATAR VARIAS INTERRUPCIONES ; DE UNA SOLA VEZ

Las 2 primeras instrucciones examinan si tenemos algún asunto con la interrupción tmr0. Me podría decir, ¿Por qué utilizar 2 líneas? Sería suficiente examinar TOIF. No es así de simple. Imaginemos que el tmr0, que nosotros no utilizamos, se ha desbordado. El bit TOIF se habrá puesto a 1 y no habrá generado ninguna interrupción dado que TOIE está a 0. De la misma manera, si hubiésemos aceptado las interrupciones timer y tuviésemos una interrupción debida a otras causas, tendría-mos TOIE a 1 y TOIF a 0. Deberemos tratar la interrupción solo si esta está permitida (en servicio) y el flag está posicionado; de esto el doble test. Examinemos este doble test y observemos el funcionamiento de btfsc y btfss: Si TOIE vale 0 saltamos directamente al test siguiente por medio de la línea goto, si vale 1 chequeamos TOIF. Si este vale 0, llegamos a la línea goto que pasará al test siguiente. Si valiese 1, llamamos a la rutina de trata-miento de la interrupción tmr0. La última línea nos permite saltar al final de la rutina de interrupciones, por lo tanto restaurar los registros y salir. En el caso de tener dos fuentes de interrupción simultáneas, solo una sería tratada a la vez. Si por el con-trario suprimimos esta última línea, la siguiente interrupción seria tratada al acabar el tratamiento de la inte-rrupción en curso. Yo he estudiado este esqueleto para dejarle todas las variantes posibles a disposición y di-rectamente utilizable.

Page 117: Manual Instrucciones PIC16F84A

117

A RESALTAR

- Si no se utiliza jamás la interrupciontmr0, podemos suprimir esta parte del código.

- Si la interrupción tmr0 está puesta en servicio a lo largo de todo el programa, podríamos suprimir el test de TOIE (estaría permanentemente a 1).

- En el caso de 2 interrupciones simultaneas de 2 eventos distintos, la primera interrupción tratada será

aquella que será chequeada primero. El orden de los test modifica, pues, la prioridad de las interrup-ciones.

A continuación, nos encontraremos el mismo procedimiento para las interrupciones del tipo RM0 (las que nos interesan en nuestro caso) y las del tipo RB4/RB7. Nótese el emplazamiento previsto para añadir el test de in-terrupciones de escritura de la eeprom. Completaremos m16F84.asm en este nivel estudiando los procedimientos de escritura de eeprom. Todas es-tas modificaciones se han incluido en el fichero « m16f84_new.asm ».

intsw1 btfsc INTCON , INTE ; test si interrupcion RB0 auto rizada btfss , INTF ; SI, test si interrupcion RB0 en curs o INTCON goto intsw2 ; NO, saltar a test siguiente call intrb0 ; SI, tratar interrupcion RB0 bcf INTCON,INTF ; borrar flag interupcion RB0 goto restorereg ; fin de la interrupcion ; SUPRIMIR ESTA LINEA PARA ; TRATAR VARIAS INTERRUPCIONES ; DE UNA SOLA VEZ

intsw2 btfsc INTCON,RBIE ; test si interrupcion RB4/7 auto rizada btfss INTCON,RBIF ; SI, test si interrupcion RB4/7 en curso goto intsw3 ; NO, saltar call intrb4 ; SI, tartar la interrupcion RB4/7 bcf INTCON,RBIF ; borrar el flag interupcion RB4/7 goto restorereg ; fin de la interrupcion

intsw3

Aquí estará el sitio para la interrupción eeprom. Finalmente nos encontramos con la parte que sirve para la restauración de los registros salvaguardados. Ya hemos visto este procedimiento:

;restaurar registros ;------------------- restorereg

swapf status_temp , w ; swap antiguo status, result ado en w movwf STATUS ; restaurar status swapf w_temp , f ; Inversion L y H del antiguo W si n modificar Z swapf w_temp , w ; Re-inversion de L y H en W

; W restaurado sin modificar status retfie ; retorno desde interrupcion Ç

Page 118: Manual Instrucciones PIC16F84A

118

12.10 Adaptación de la rutina de interrupción

Vamos ahora a modificar esta rutina de interrupción para adaptarla a nuestro caso. Solo tenemos una fuente de interrupción valida, que será para tratar INT/RB0. Suprimamos pues los test. Nos quedará:

;************************************************** ******************** ; RUTINA INTERRUPCION * ;************************************************** ******************** ;salvaguarda registros ;--------------------- org 0x004 ; dirección de interrupcion movwf w_temp ; salvar registro W swapf STATUS , w ; swap status con resultado en w movwf status_temp ; salvar status swapeado call intrb0 ; tartar interrupcion RB0 ;restaurar registros ;------------------- swapf status_temp , w ; swap antiguo status, result ado en w movwf STATUS ; restaurar status swapf w_temp , f ; Inversion L y H del antiguo W si n modificar Z swapf w_temp , w ; Re-inversion de L y H en W

; W restaurado sin modificar status retfie ; retorno desde la interrupcion

Podríamos, igualmente, pasar de la línea call intrb0 y poner directamente el tratamiento en su lugar. Conservemos de todas formas esta llamada puesto que no se va a tratar de un par de instrucciones ni esta-mos cerca de desbordar la pila. En caso de que necesitase optimizar la aplicación al máximo, podría pensar en ello. En caso contrario, le recomiendo dar prioridad a la visibilidad del programa (legibilidad continuo insis-tiendo). Vayamos a la continuación del programa. Vamos a encontrarnos con las 3 rutinas de tratamiento de interrup-ciones llamadas en origen por nuestra rutina de interrupción. Como hemos suprimido 2 llamadas es inútil conservar las subrutinas correspondientes. La única que necesitaremos será:

;************************************************** ******************** ; INTERRUPCION RB0/INT * ;************************************************** ********************

intrb0 return ; fin de la interrupcion RB0/INT

; puede ser reemplazada por ; retlw para retornar código de error

No contiene más que el retorno de la subrutina correspondiente a la llamada call intrb0. Atención, en este nivel no utilizaremos RETFIE puesto que no salimos de la rutina de interrupción, sino que salimos de una subrutina llamada por la rutina de interrupción. Si utilizásemos RETFIE en este momento volveríamos a poner las interrupciones en servicio antes de salir y el programa se bloquearía. Nótese que podríamos utilizar para retornar un valor predefinido a tratar por la rutina de interrupción. Por ejemplo retlw 0 si quisiésemos tratar otras interrupciones y retlw 1 si quisiésemos salir. En ese caso, la línea goto restorereg (fin de interrupción) podría estar precedida de un test de w retornado por la subrutina.

Page 119: Manual Instrucciones PIC16F84A

119

12.11 La inicialización Sigamos. Nos encontramos con la rutina de inicialización que ya fue explicada en la lección precedente. Nos contentaremos con reemplazar los cambios de banco por los macros que hemos escrito, y configurar RA2 co-mo salida para el LED.

Obtendremos:

;************************************************** ******************* ; INICIALIZACIONES * ;************************************************** ******************* init

clrf PORTA ; Salidas portA a 0 clrf PORTB ; Salidas portB a 0

consommation clrf EEADR ; permite disminuir BANK1 ; pasar a banco1 movlw OPTIONVAL ; carga mascara movwf OPTION_REG ; inicializa registro option

; Effacer RAM ; ----------------

movlw 0x0c ; inicializacion puntero movwf FSR ; punter direccionamiento indirecto

init1 clrf INDF ; borrar ram incf FSR,f ; punter en siguiente btfss FSR , 6 ; test si fin zona esperada (>=0x40) goto init1 ; NO, bucle btfss FSR , 4 ; test si fin zona esperada (>=0x50) goto init1 ; NO, bucle

; configurar PORTS ; ---------------

bcf LED ; RA2 como salida (TRISA) BANK0 ; pasar a banco 0 movlw INTERMASK ; mascara de interrupcion movwf INTCON ; cargar control interrupcion goto start ; saltar a programa principal

Solo nos queda suprimir la línea:

clrwdt ; effacer watch dog

del programa principal dado que hemos desactivado el watchdog. ADVERTENCIA Una gran parte de los errores de los programas está en el hecho de errores en la selección de banco (sobre todo en los PIC® de 4 bancos). Le aconsejo como convención, entrar siempre en una rutina con el banco 0, y asegurarse de pasar al banco 0 antes de salir. En caso de no hacerlo así, indicarlo claramente en el enca-bezado de la subrutina.

Lance el ensamblado para comprobar que no hay errores. Deberia obtener algo similar a:

Message[302] D:\DOCUME~1\LESSONS\DATAPIC\MYINTER.AS M 143 : Register in operand not in bank 0. Ensure that bank bits are correct. Build completed.

No volveremos a explicar los warnings.

Page 120: Manual Instrucciones PIC16F84A

120

12.12 Construcción del programa principal

Vamos ahora a realizar un tele ruptor. ¿Qué significa eso? Bien, vamos a construir la siguiente función: una presión sobre el pulsador encenderá el LED, una segunda presión lo apagará. Le voy a guiar paso a paso en es-ta realización, intentando mostrarle los problemas prácticos que nos encontraremos en las realizaciones de este tipo. Le recuerdo que es un programa didáctico. Realizaremos este tele ruptor de una forma un poco más elegante en la lección sobre el timer0. Dado que queremos utilizar las interrupciones, la inversión en el encendido del LED se hará en la rutina de in-terrupción del pulsador. En un primer momento podemos pensar que el programa principal no debe ocuparse de nada. Advertencia muy importante En ningún caso puede dejar que su programa principal se deslice fuera de la zona de programa. Si el pro-grama no debe hacer nada, tendrá, en cualquier manera hacer que se bucle sobre sí mismo. De esa forma el programa no se parará jamás (excepto cuando entre en el modo sleep que veremos más adelante). Nuestro programa principal podrá ser de la forma:

start goto start ; bucle

Pero para facilitar la visualización sobre el emulador le pediré que añada algunas instrucciones NOP inútiles:

;************************************************** ******************* ; PROGRAMA PRINCIPAL * ;************************************************** ******************* start

nop ; instruccion inutil nop ; instruccion inutil nop ; instruccion inutil nop ; instruccion inutil nop ; instruccion inutil goto start ; bucle END ; directiva fin de programa

Page 121: Manual Instrucciones PIC16F84A

121

12.13 Construcción de la rutina de interrupción

Hemos programado el PIC® de forma que una interrupción sobre el flanco descendente de INT/RB0 provo-cará una interrupción. No es suficiente con ejecutar en esta rutina de interrupción la inversión a nivel del LED. ¿Hemos dicho invertir un bit? Esto le debería llevar a pensar en la operación lógica <<OR exclusivo>>. Podre-mos escribir:

;************************************************** ******************** ; INTERRUPCION RB0/INT * ;************************************************** ******************** ;-------------------------------------------------- -------------------- ; invierte el nivel de RA2 en cada pasada ;-------------------------------------------------- -------------------- int

rb0 movlw B'00000100' ; bit posicionado = bit a invertir BANK0 ; puesto que no sabemos en que banco estamos ; estamos en una interrupción (el programa ; principal puede haber cambiado el banco). Aqui ; no es el caso, pero es una sabia precaucion xorwf PORTA , f ; invertir RA2 return ; fin de la interrupcion RB0/IN

Observe que en este momento nuestro LED está definido por la línea

#DEFINE LED PORTA , 2 ; LED

Nos bastará solo con cambiar esta línea para situar nuestro led sobre otro pin diferente sin tener que interve-nir en nuestro programa. Por el contrario, esto no funcionará a nivel de la inversión de la salida. Lo ideal es utilizar igualmente un define para la máscara en el comienzo del programa:

#DEFINE LED PORTA , 2 ; LED #DEFINE LED_MASK B’00000100’ ; Mascara de inversión del LED

Y seguidamente reemplazar la línea

movlw B'00000100' ; bit posicionado = bit a invertir por

movlw LED_MASK ; bit posicionado = bit a invertir

De esta forma podremos efectivamente cambiar el pin del LED sin intervenir en el programa excepto a nivel de los dos defines. Nuestra primera prueba está terminada. Pasamos el programa al simulador. Antes le doy el flujo grama del programa que acabamos de realizar. Cuando sus programas se vayan haciendo más complejos, le aconsejo que recurra a los diagramas de flujo antes de escribirlos, salvo que recurra al pseudocódigo (escribirlos en es-pañol).

Page 122: Manual Instrucciones PIC16F84A

122

Flujo grama 1 (versión teóricamente funcional):

Como puede ver, esta rutina de interrupción no puede ser más simple, y se corresponde a lo que podríamos imaginarnos desde un principio. Vamos a comenzar a pasarla por el simulador.

12.14 Pasaje al simulador de una rutina de interrupción

No olvide que el simulador debe estar activado. Si no es así, hágalo en este momento (ver el capítulo sobre el debugger). Seleccione la ventana « Special Functions Registers» si aún no está hecho. Lancemos el ensamblado con <F10>. Inmediatamente haga <F6> para resetear el programa, después haga <F7>. Repita <F7> para seguir la evolución del programa. No se olvide que el programa va a efectuar 68 bu-cles para borrar la RAM, sea paciente. Aproveche para observar el funcionamiento de FSR en el direcciona-miento indirecto. También puede presionar <F9> y seguidamente <F5> durante unos instantes. Una vez llegado al programa principal, este se bucla de forma infinita. En efecto, es necesario un evento exte-rior (pulsador) para provocar el paso a la rutina de interrupción. Vamos ahora a explicar cómo simular un evento exterior en el simulador. Estas explicaciones varían de una versión a otra de MPLAB® . Una vez ter-minado el aprendizaje, le corresponde a usted adaptarlo a las diferentes funcionalidades, esto no es nada complicado. Vaya al menú « debugger -> stimulus ». Si por azar tuviese mensajes de error concernientes al fichero, ignó-relos. Seleccione la pestaña « pin stimulus ».

Page 123: Manual Instrucciones PIC16F84A

123

Cliquee sobre « add Row » para obtener un botón de acción. Se creará una nueva línea en la ventana, con un botón titulado « Fire ». Cliquee una vez sobre la columna « pin » justo al lado del botón. Rellenar las casillas « pin » y « action ». Ensanche las columnas con el ratón para verlas mejor. Vamos ahora a precisar la acción de nuestro botón. En la casilla « type » seleccionamos « asynch » en lugar de «synchrone ». En efecto, el evento podrá ocurrir en cualquier momento de la ejecución del programa. No pulse el botón « fire » por el momento. Con la ayuda del menú desplegable de la casilla « pin » vamos a determinar que pin será estimulado por la acción sobre el botón que hemos creado. Como nuestro botón está conectado sobre el pin RB0, selecciona-mos este pin. Si observa en la casilla « action » verá que ha accedido a un modo de funcionamiento del botón. Tiene la po-sibilidad de elegir entre Pulse (genera un impulso), Low (pone un nivel 0), High (pone un nivel 1) o Toggle (in-versión del nivel a cada pulsación). Elegiremos la menos práctica para este ejemplo, pero la más explícita. Elija « Low ». Ahora sí está ya configurado el botón de estimulación. Debería haber obtenido una ventana como esta:

Page 124: Manual Instrucciones PIC16F84A

124

Cree una nueva línea con « add row » y cree un segundo botón.

Elija « High » en la casilla de acción. Puede poner comentarios en las casillas « comments ». No se corte. Examinemos el registro PORTB en la ventana de registros especiales. Verá que todos los bits están a 0. En efecto MPLAB®, no puede conocer la electrónica que ha conectado a los pines. Es su responsabilidad indi-carle el nivel a los que están conectados los pines. Para aquellos que no hayan comprendido el funcionamien-to de la resistencia de referencia a+5V, he aquí una vez más el esquema equivalente:

Page 125: Manual Instrucciones PIC16F84A

125

Vemos que cuando el pulsador no está activado, tenemos un nivel 1 en RB0 provocado por la resistencia de referencia que pusimos en servicio. Presionemos el segundo botón una vez y vayamos al editor. Presionemos<F7> para avanzar un paso y vali-dar la modificación del nivel. Examinemos PORTB : RB0 ahora ha pasado a 1. Nuestro pulsador no está pre-sionado. Pulse <F7> algunas veces para verificar que no ocurre nada. Vamos ahora a simular la pulsación del pulsador:

• Presione primero el botón para enviar un 0 a RB0.

• Vuelva al editor y pulse una sola vez<F7>. La instrucción que sigue al evento es ahora la instrucción situada en la dirección 0x04 puesto que el paso de 1 a 0 de RB0 ha provocado nuestra interrupción.

• Avance lentamente pulsando en la rutina de interrupción. Examine el efecto de las diferentes instruc-ciones. Una vez la línea:

xorwf PORTA , f ; inverser RA2

Ha sido ejecutada constatará que le LED se enciende (RA2 pasa a valer 1 en el registro PORTA).

Avance lentamente hasta que la línea

retfie ; return from interrupt

Se seleccione y no pulse más <F7>. En este momento nos hemos encontrado el retorno de la rutina de interrupción para volver al programa prin-cipal. Pulse <F7> un sola vez. ¿Qué ha pasado? En lugar de volver al programa principal volvemos a comenzar de nuevo en la rutina de inte-rrupción. Para provocar una interrupción, hace falta que el bit enable y el bit de flag de una de las fuentes de interrup-ción esté a 1. O solo hay un bit enable a 1 y este es INTE.

Page 126: Manual Instrucciones PIC16F84A

126

Examinemos INTF (es el bit 1 de INTCON). Este bit está siempre a 1, es por esto la nueva interrupción. Hemos cometido un error clásico. Nos hemos olvidado de borrar el flag al final del tratamiento de la rutina de interrupción. Observe que ese borrado está integrado en la parte « switch » de de la rutina de interrupción del fichero m16f84.asm. Hemos borrado esta línea por error al suprimir los diferentes test. De hecho lo he realizado de forma volunta-ria siguiendo la regla de que “memorizamos mejor las cosas después de un error”.

12.15 Primera corrección: reset del flag

Nos hace falta añadir la línea siguiente en nuestra subrutina intrb0

bcf INTCON , INTF ; borrar el flag INT/RB0

Tendremos

rb0 movlw B'00000100' ; bit posicionado = bit a invertir BANK0 ; puesto que no sabemos en que banco estamos ; estamos en una interrupción (el programa ; principal puede haber cambiado el banco). Aqui ; no es el caso, pero es una sabia precaucion xorwf PORTA , f ; invertir RA2 bcf INTCON , INTF ; borra el flag INT/RB0 return ; fin de la interrupcion RB0/IN

Ensamblemos de Nuevo el programa con <F10>, después <F6> y recomencemos todo el procedimiento que acabamos de ver. Verifique que la rutina de interrupción termina ahora volviendo al programa principal. El LED está ahora encendido (RA2=1). Presione ahora el segundo botón para simular el soltar el pulsador. Presione <F7> varias veces y después pul-se el botón <RB0 (L)> para simular una segunda pulsación del pulsador. Siga la rutina de interrupción y constate que esta vez el LED se apaga. Obtenemos en el simulador la siguiente funcionalidad:

• Una presión sobre el pulsador enciende el LED.

• Otra pulsación lo apaga.

• Así de continuo. Hemos obtenido el resultado deseado. Coloque el PIC® en el programador y envíele el fichero « Myinter.hex ». Coloque el PIC® sobre la placa de experimentación (modificada) y pulse el pulsador varias veces. El LED no funciona de ninguna manera como habíamos previsto. Reacciona de una forma aleatoria. ¿Qué está pasando?

Page 127: Manual Instrucciones PIC16F84A

127

12.16 Ponerse en la escala de tiempos del PIC®

Acuérdese, ya le llamé la atención sobre este punto en concreto. Convirtámonos en PIC y examinemos que vemos. De hecho la escala de tiempos está relacionada con la escala de tamaños, vemos un enorme pulsador, con un contacto eléctrico del tamaño de un rail de tren. Cuando accionamos el pulsador es como una enorme barra que viene a cortocircuitar 2 contactos. Esta barra es elástica. ¿Qué ve el PIC®? Ve esa enorme barra metálica que cae sobre los 2 contactos desde una altura enorme. Una vez que establece el contacto rebota unas cuantas veces antes de inmovilizarse. A cada pulsa-ción sobre el pulsador, el PIC® ve una serie de aperturas y cierres del contacto en lugar de una sola, en nues-tra escala de tiempos. El PIC® es mucho más rápido que nuestro pulsador.

12.17 El problema del anti rebote ¿Cómo remediar el problema? Fácilmente, ralentizando el funcionamiento de nuestro PIC, y por lo tanto, es-perando un tiempo más largo que el necesario para los rebotes antes de autorizar una nueva interrupción so-bre RB0. Vamos a utilizar nuestros conocimientos actuales para solucionar el problema. Conviene diseñar un flujo gra-ma (ya se lo dije) de lo que vamos a hacer:

Page 128: Manual Instrucciones PIC16F84A

128

Explicación

El programa principal ejecuta sus inicializaciones de forma normal, después chequea si una petición de tiem-

po ha ocurrido (posicionado del flag « tempo »). Si el flag no está activado, entramos en un bucle sin fin. Si pulsamos el pulsador, se genera una interrupción, se invierte RA2, la rutina posiciona el flag tempo a 1 e impide cualquier nueva interrupción. Cualquier otra nueva acción sobre RB0 quedará sin efecto (rebotes in-cluidos). Observe que nada impide que una rutina de interrupción se prohíba a sí misma. La rutina de interrupción acaba. Retornamos al programa principal, el cual chequea ahora si hay una demanda de tiempo mediante el posicionado del flag tempo. Este acaba de ser posicionado por la rutina de interrup-ción. El programa principal llama ahora a una rutina de temporización (que ya habíamos visto en la lección principal). Después de la espera de tiempo necesaria para la desaparición de los rebotes, el flag tempo es reseteado (para no entrar en un bucle sin fin) y las interrupciones son nuevamente autorizadas con la finalidad de per-mitir tener en cuenta una nueva pulsación del pulsador. Aquí puede constatar que a veces es interesante prohibir y permitir las interrupciones en diferentes momen-tos específicos del programa.

12.18 Finalización de un programa

En principio nos hace falta una rutina de temporización. Abra el fichero « Led_cli.asm » y efectúe un co-piar/pegar de la rutina de temporización.

;************************************************** ******************* ; SUBRUTINA DE TEMPORIZACION * ;************************************************** ******************* ;-------------------------------------------------- ------------------- ; Esta subrutina introduce un retardo de 500.000 µs. ; No recibe ni retorna ningún parametro ;-------------------------------------------------- ------ tempo

movlw 2 ; para 2 bucles movwf cmp ; inicializa comp

boucle3 clrf cmpt2 ; borrar compteur2

boucle2 clrf cmpt1 ; borrar compteur1

boucle1 nop ; pierde 1 ciclo decfsz cmpt1 , f ; decrementa compteur1 goto boucle1 ; si no 0, bucle decfsz cmpt2 , f ; si 0, decrementa compteur2 goto boucle2 ; si cmpt2 no 0, recomienza bucle2 decfsz cmpt3 , f ; si 0, decrementa compteur3 goto boucle3 ; si cmpt3 no 0, recomienza boucle2 return ; retorno de la subrutina

Vamos a modificar ligeramente esta subrutina. Podemos eliminar el bucle exterior puesto que 500 ms es mu-cho más que el tiempo de rebotes del pulsador. Quitemos igualmente la instrucción NOP. Obtendremos:

Page 129: Manual Instrucciones PIC16F84A

129

;************************************************** ******************* ; SUBRUTINA DE TEMPORIZACION * ;************************************************** ******************* ;-------------------------------------------------- ------------------- ; Esta subrutina introduce un retardo ; No recibe ni retorna ningún parametro ;-------------------------------------------------- ------ tempo

clrf cmpt2 ; borrar compteur2 boucle2

clrf cmpt1 ; borrar compteur1 boucle1

decfsz cmpt1 , f ; decrementa compteur1 goto boucle1 ; si no 0, bucle decfsz cmpt2 , f ; si 0, decrementa compteur2 goto boucle2 ; si cmpt2 no 0, recomienza bucle2 return ; retorno de la subrutina

No utilizaremos la variable cmpt3. Podemos suprimirla de la zona de variables. Dado que estamos allí, necesi-taremos un flag, es decir un bit. Creémoslo en esta zona.

;************************************************** ******************* ; DECLARACION DE VARIABLES * ;************************************************** ******************* CBLOCK 0x00C ; comienzo de la zona de variables w_temp :1 ; salvaguarda de W en la interrupcion status_temp : 1 ; salvaguarda de STATUS en la inter rupcion cmpt1 : 1 ; contador de bucles 1 en tempo cmpt2 : 1 ; contador de bucles 2 en tempo flags : 1 ; un octeto para 8 flags

; reservamos b0 para el flag tempo ; b1 : libre ; b2 : libre ; b3 : libre ; b4 : libre ; b5 : libre ; b6 : libre ; b7 : libre

ENDC ; Fin de la zona #DEFINE tempoF flags, 0 ; Definicion del flag tempo

Modifiquemos el programa principal siguiendo nuestro flujo grama y obtendremos:

;************************************************** ******************* ; PROGRAMME PRINCIPAL ;************************************************** ******************* start

btfss tempoF ; test si tempo flag esta puesto goto start ; No, esperamos a que este puesto call tempo ; SI, ejecutamos tempo bcf tempoF ; borramos el flag tempo bsf INTCON , INTE ; vuelve a poner interrupciones I NT en servicio goto start ; bucle END ; directiva de fin de programa

Page 130: Manual Instrucciones PIC16F84A

130

No nos queda más que modificar nuestra rutina de interrupción en función de nuestro flujo grama, obtenien-do:

;************************************************** ******************** ; INTERRUPCION RB0/INT * ;************************************************** ******************** ;-------------------------------------------------- -------------------- ; invierte el nivel de RA2 en cada pasada ; prohíbe toda nueva interrupcion ; valida el flag tempo ;-------------------------------------------------- -------------------- intrb0

movlw B'00000100' ; bit posicionado = bit a invertir BANK0 ; puesto que no sabemos en que banco estamos

; estamos en una interrupción (el programa ; principal puede haber cambiado el banco). Aqui ; no es el caso, pero es una sabia precaucion

xorwf PORTA , f ; invertir RA2 bcf INTCON , INTF ; borrar flag INT/RB0 bcf INTCON , INTE ; impide otra interrupcion de RB0 bsf tempoF ; posiciona el flag tempo return ; fin de la interrupcion RB0/IN

Ensamblemos nuestro programa y cambiemos el nuevo fichero .hex en nuestro PIC®. Conectemos la alimen-tación. Esto sigue sin funcionar. ¿Qué está pasando? Reflexionemos. Una vez que la rutina de interrupción termina, las interrupciones son invalidadas. El pulsador rebota sin cau-sar ninguna llamada de interrupción, pero su flag se posiciona a causa de los rebotes. Recuerde que los bits de enable no afectan a los flags. Por lo tanto, cuando el programa general pone de nuevo las interrupciones en servicio, se genera una nueva interrupción nuevamente puesto que INTF estaba posicionado durante los re-botes. Usted debe pensar en todo cuando maneje interrupciones. Si un programa funciona en el simulador pero no lo hace en la vida real, piense inmediatamente en problemas de tiempo. Corolario

Que un programa funcione en el simulador no significa que sea correcto. Bastará con insertar una instrucción de reseteado del flag INTF antes de volver a permitir las interrupciones.

Bcf INTCON , INTF ; borrar flag INT

Podría decir que es inútil borrar este flag en la rutina de interrupción. Es lógico, pero más vale ir cogiendo buenos hábitos. Dejemos pues esta instrucción. Ensamble el programa, recárguelo en el PIC. Alimente el montaje. Esta vez funciona perfectamente. He aquí un montaje muy práctico. Si reemplaza el LED por un rele o por un triac opto acoplado, estará en posesión de un tele ruptor.

Page 131: Manual Instrucciones PIC16F84A

131

A título de plus, he aquí el esquema de un tele ruptor completamente operacional con nuestro programa:

Observaciones sobre nuestro programa. Hemos utilizado una temporización de un valor cualquiera. Esta temporización inhibe la acción del pulsador durante unos 250 ms. Por lo tanto, podría accionar el pulsador unas 4 veces por segundo como máximo. Pero ¿cuál es la duración real de los rebotes? Depende del pulsador, tecnología y tamaño. Para conocer el tiempo de rebote de nuestro pulsador, decremente poco a poco el valor de la temporización hasta que apa-rezcan problemas de rebote. Generalmente será del orden de algún ms.

Observaciones sobre nuestro programa.

Recuerde que una vez que utiliza interrupciones en su programa, no podrá saber el tiempo que separa a dos instrucciones consecutivas. Puede ocurrir que entre dos instrucciones consecutivas se cuele una interrupción, desbaratando nuestro cal-culo de tiempo. En consecuencia, no podrá utilizar el cálculo de tiempo calculando el número de instrucciones en una parte de código en la que pueda ocurrir una interrupción. Para toda aquella secuencia en la que el tiempo sea crítico y no pueda ser interrumpido, usted deberá inhibir las interrupciones. Quedará a su cargo volver a poder permitirlas. Una vez que utilice interrupciones, su programa deberá afrontar sucesos que ocurrirán de forma asíncrona. Por lo tanto, no podrá chequear jamás todas las eventualidades posibles. Desgraciadamente, no podrá estar jamás seguro por medio de la simulación que su programa está libre de errores. Esto le explica por qué la gestión de procesos críticos (naves espaciales) en tiempo real utilizan más de un or-denador. En estos ordenadores, estando de sincronizados, la aparición de un error sobre uno de ellos tiene poca probabilidad de que aparezca en el segundo. Para saber quien ha tenido el problema, basta con tener un tercero. La mayoría los lleva.

Page 132: Manual Instrucciones PIC16F84A

132

Tenga esto en cuenta si algún día está llamado a realizar un programa del que dependa la seguridad de per-sonas o bienes. Conclusiones Al término de este capítulo, usted habrá aprendido los mecanismos de las interrupciones y podrá utilizarlas. Vamos a utilizar todavía esta posibilidad en la lección sobre el timer. Espero haber desmitificado este concepto, demasiado a menudo imaginado como “el método de los profe-sionales”. En realidad, una vez más, nada de magia. Es simplemente una explotación, a veces compleja, de procedimientos simples y fáciles de entender. Simplemente tenga en cuenta que abandona el mundo seguro y previsible de lo síncrono para entrar en el mundo de lo asíncrono, mucho más difícil de simular de una forma totalmente eficiente. La utilización de las interrupciones impone, de hecho, la perfecta comprensión de los mecanismos puestos en juego, y nos obliga a prever todas las posibilidades de ocurrencia de un evento exterior. Esto es especialmen-te cierto para los programas con diversas fuentes de interrupción. Le aconsejo encarecidamente, antes de encarar la realización de un programa de cierta complejidad, comen-zar por escribir un algoritmo general o la utilización de un flujo grama.

Page 133: Manual Instrucciones PIC16F84A

133

13.El Timer 0 En este capítulo vamos a hablar de las temporizaciones y de los conteos. El 16F84 solo dispone de un tempo-rizador de 8 bits, al contrario que otros PIC® de la familia (como el 16F876). Si examinamos atentamente el funcionamiento del timer0 veremos que se trata, de hecho, de un contador.

13.1 Los diferentes modos de funcionamiento

Hemos visto que el timer0 es un contador. ¿Pero qué es lo que cuenta este timer? Bien, tenemos dos posibili-dades:

• En primer lugar podemos contar los impulsos recibidos sobre el pin RA4/TOKI . Diremos en este ca-so que estamos en modo contador.

• Podemos también contar los ciclos de reloj del mismo PIC®. En este caso, como el reloj está fijo, contaremos en realidad tiempo. Estaremos en modo temporizador.

La selección de uno u otro modo de funcionamiento se efectúa a través del bit 5 del registro OPTION:T0CS llamado así por Tmr0 Clock Source select bit (de nuevo la cifra 0 no la letra O). T0CS=1 : funcionamiento en modo contador. T0CS=0 : funcionamiento en modo timer. En el caso de que decidamos trabajar en modo contador, deberemos también decidir cuál es el modo de tran-sición del impulso para que se efectúe el contaje. Esto se hace gracias al bit 4 del registro OPTION : T0SE por Timer0 Source Edge select bit.

13.2 El registro tmr0

Este registro, que está localizado en la dirección 0x01 del banco 0, contiene simplemente el valor actual del timer0. Podrá escribir o leer tmr0. Si por ejemplo ha configurado tmr0 como contador, la lectura del registro tmr0 le dará el numero de eventos que han llegado al pin RA4/TOKI.

13.3 Los métodos de utilización del timer0

¿Cómo utilizar el timer0 y cuál es la oferta de posibilidades a este nivel? He aquí de lo que vamos a hablar.

13.3.1 El modo de lectura simple

El primer método que nos viene a la cabeza es el siguiente: leer el registro tmr0 para ver que contiene. El va-lor leído es el reflejo del número de eventos ocurridos, teniendo en cuenta el hecho de que tmr0 no puede contar más que hasta 255. En caso de sobrepasar este valor el tmr0 comienza desde 0 de nuevo. Es su come-tido el gestionar esta posibilidad. Pequeño ejemplo:

clrf tmr0 ; comienzo de contaje 2 ciclos más lejos ; (ver recuadro más adelante)

xxx ; aquí un cierto numero de instrucciones movf tmr0 , w ; cargar el valor de contaje movwf mavariable ; salvar para tratamiento posterior

Page 134: Manual Instrucciones PIC16F84A

134

13.3.2 El modo de vigilancia del flag

Debemos saber a estas alturas, que todo desbordamiento del timer0 (pasaje de 0xFF a 0x00) conlleva el posi-cionamiento del flag T0IF del registro INTCON. Podemos utilizar este flag para determinar si hemos tenido un desbordamiento del timer0 o, en otras palabras, si el tiempo programado ha sido alcanzado. Ejemplo:

clrf tmr0 ; ; comienzo de contaje 2 ciclos más lejos ; (ver recuadro más adelante)

bcf INTCON , T0IF ; borrado del flag loop

btfss INTCON , T0IF ; test si contador desbordado goto loop ; NO, esperar desbordamiento xxx ; proseguir : 256 eventos alcanzados

Usted podría decir que no desea específicamente espera 256 incrementos del tmr0. Supongamos que desea-mos esperar 100 incrementos. Sería suficiente con colocar en tmr0 un valor tal que 100 incrementos más tar-de se provoque un desborde del tmr0. Ejemplo:

movlw 256-100 ; cargar 256 – 100 movwf tmr0 ; inicializar tmr0 bcf INTCON , T0IF ; borrado del flag

loop btfss INTCON , T0IF ; test si contador desbordado goto loop ; NO, esperar desbordamiento xxx ; proseguir : 100 eventos alcanzados

Esto podría parecer correcto a primera vista, pero: Toda modificación del TMR0 conlleva una parada del contaje correspondiente a 2 ciclos de instrucción. Si el ejemplo precedente no utiliza pre divisor, habrá que tener en cuenta esta pérdida colocando « 256-98 » y no « 256-100 » en el timer.

movlw 256-98 ; desbordamiento en 100 ciclos

13.3.3 El modo interrupción

Evidentemente es el modo principal de utilización del timer0. Puesto que T0IF se ha posicionado en el registro INTCON, cada vez que el flag T0IF pasa a 1, se genera una interrupción. El procedimiento a seguir ya se ha vis-to en la lección sobre interrupciones.

13.3.4 Los métodos combinados

Supongamos que usted quiera, por ejemplo, medir el tiempo entre 2 impulsos sobre el pin RB0. Supongamos que este tiempo sea tal que comporte el desbordamiento de tmr0 varias veces. Un método simple de medi-ción de este tiempo sería el siguiente:

• Con el primer impulso sobre RB0 lanzar el timer0 en modo interrupción.

• A cada interrupción de tmr0 incrementamos una variable.

• Con la segunda interrupción en RB0 leemos tmr0 y paramos las interrupciones.

• El tiempo total será (256 * variable) + tmr0 Habremos utilizado las interrupciones para los múltiplos de 255 y la lectura directa de tmr0 para las unidades.

Page 135: Manual Instrucciones PIC16F84A

135

13.4 El pre divisor

Supongamos ahora que trabajamos con un cuarzo de 4 MHz Tenemos en este caso (4000000/4)=1.000.000 ci-clos por segundo. Cada ciclo de reloj durará 1/1000000 segundos, es decir, 1 µs. Si decidimos utilizar el timer0 en su modo de funcionamiento de interrupción tendremos una interrupción ca-da 256 µs es decir, aproximadamente cada cuarto de milisegundo. Si deseamos realizar un LED intermitente parpadeando a una frecuencia de +- 1Hz necesitaremos una tempo-rización de 500 ms, es decir, 2000 veces mayor. Esto no es práctico. Pero, disponemos de un pre divisor para disminuir la base de tiempos del timer. ¿Qué es esto? Simplemente un divisor de eventos situado antes de la entrada de contaje del timer0. Podemos decidir, por ejemplo, tener un incremento del tmr0 cada 2 eventos, o incluso cada 64. Consecuencias:

• Nuestro contador avanza más lentamente, lo que evitará interrupciones inútiles.

• Nuestra precisión se ve limitada a un múltiplo del valor de pre división. Observe la tabla de la página 9 del datasheet. Puede ver la tabla de los bits PS0 a PS2 del registro OPTION que determinan el valor del pre divisor.

Estos valores varían, para el timer0, entre 2 y 256. El bit PSA determina si el pre divisor está asociado al tiemr0 o al watchdog. He aquí una tabla explicando todas las posibilidades de estos bits.

Page 136: Manual Instrucciones PIC16F84A

136

• PSA a PS0 : bits de configuración del pre divisor.

• /tmr0 : valor resultante del pre divisor sobre el timer0.

• /WD : valor resultante del pre divisor sobre el watchdog.

• Temps tmr0 : numero de ciclos de instrucciones que causaran un desbordamiento.

• Temps watchdog : indica el tiempo típico disponible entre 2 resets del watchdog. El valor entre paréntesis indica el tiempo mínimo, que es el que hay que utilizar para hacer frente a todas las cir-cunstancias.

Observe que el tiempo de desbordamiento depende a la vez del número de ciclos contados entre dos desbor-damientos así como del tiempo necesario para ejecutar un ciclo de instrucción (1µs a 4Mhz). Por el contra-rio, para el watchdog, este tiempo está fijado y no depende de la duración del ciclo de instrucción, ni por lo tanto de la frecuencia del reloj.

Observaciones importantes:

• Solo hay un pre divisor, que puede ser elegido para el timer del watchdog (que veremos más tarde) o

para el tmr0. No puede afectar a los dos al mismo tiempo.

• No existe un pre divisor por 1 para el timer0. Si no quiere pre dividir el timer0 deberá, imperativa-mente, seleccionar el pre divisor para el watchdog con un valor 1.

• El valor contenido en el pre divisor no está accesible. Por ejemplo, si selecciona una pre división por 64 y en un momento dado ya han ocurrido 30 eventos, no tendremos ningún medio directo de saber-lo. El pre divisor limita pues la precisión en caso de lectura directa puesto que solo podremos obtener como resultado un múltiplo entero del pre divisor.

• La escritura en el registro tmr0 borra el contenido del pre divisor. Los eventos que ocurrieron hasta la escritura se perderán irremisiblemente.

• Toda modificación de TMR0 (borrado, incremento, operación…) entrañará una parada del contaje del timer correspondiente, lo que nos dará un defecto de 2 unidades (2 unidades de menos) en el contaje previsto.

Truco 1 Si debe, por una razón u otra, detener el timer, será suficiente con pasarlo a modo de contaje externo con la condición de que no tenga electrónica conectada al pin correspondiente (RA4/T0CKI). Le consejo, en ese caso, forzar un nivel bajo en ese pin con el objetivo de evitar cualquier conteo debido a una perturbación de origen electromagnético y, evidentemente, ajustar T0SE a 0 (conteo por flanco ascendente, de 0 a 1).

Page 137: Manual Instrucciones PIC16F84A

137

Truco 2 Como ya hemos comentado, en caso de lectura del timer los impulsos ya contados en el pre divisor, y no ac-cesibles, se pierden. De hecho, existe un truco para tener una precisión completa incluso con un pre divisor. Imaginemos que deseamos, por ejemplo, contar los impulsos recibidos en T0CKI entre dos sucesos dados. Imaginemos que el sistema está preparado para que en ausencia de impulso el nivel sobre el pin sea alto (con una resistencia de carga por ejemplo). Solo se necesitará desactivar la electrónica de conteo (otro pin del PIC, por ejemplo, a menos que, simplemente, no reciba más ningún evento). Imaginemos, además, para nuestro ejemplo, que ya ha recibido 53 eventos y que el pre divisor está configurado en 16. En este momento se en-cuentra en la siguiente situación:

• Tmr0 contiene 3 (parte entera de 53/16)

• El contador del pre divisor contiene 5 (53 – 3 * 16) Usted sabe que ha contado entre 48 y 63 eventos, pero no sabe el valor exacto. El truco va a consistir en bas-cular RA4 como salida y enviarle vía programa cambios de nivel alto a bajo. Esto va a motivar conteo de ele-mentos que usted mismo habrá efectuado. Será suficiente con ir contando estos eventos y vigilando el mo-mento en el que el timer pasa del valor 3 al 4. Una vez que llegue al valor 4, habrá contabilizado exactamente 64 eventos. Será suficiente ahora con restar los eventos que usted mismo generó, y obtendrá el número exacto de eventos que habíamos contabilizado realmente. Pensando un poco, podremos resolver cualquier tipo de situación.

13.5 Aplicación práctica del timer0

Vamos a poner en marcha nuestro tmr0 con una aplicación práctica. Retomemos nuestro primer ejercicio, a saber, hacer parpadear un LED a una frecuencia aproximada de 1 Hz.

13.5.1 Preparaciones

Haga un copiar/pegar de su nuevo fichero m16f84.asm y renómbrelo como « Led_tmr.asm ». Relance MPLAB® y cree un nuevo proyecto titulado « Led_tmr.pjt ». Añada su Led_tmr.asm ». Cree la cabecera (vuelvo a insistir).

;************************************************** ******************** ; * ; Hacer parpadear un LED a una frecuencia aproximad a de 1 Hz * ; * ;************************************************** ******************** ; * ; NOMBRE: LED PARPADEANTE CON TIMER0 * ; Fecha: 17/02/2001 * ; Version: 1.0 * ; Circuito: Tarjeta de pruebas * ; Auteur: Bigonoff * ; * ;************************************************** ******************** ; * ; Fichero requerido: P16F84.inc * ; * ;************************************************** ******************** ; * ; Notas: Utilizacion didáctica del tmr0 en modo int errupción * ;************************************************** ********************

Page 138: Manual Instrucciones PIC16F84A

138

Definamos a continuación la configuración poniendo el watchdog fuera de servicio. Ahora ya sabe cómo hacerlo. Calculemos el número de desbordamientos de tmr0 necesarios. Necesitamos una temporización de 500 ms, es decir 500.000 µs o 500.000 de veces un ciclo de instrucción. El timer0 genera, sin pre división, una interrupción cada 256 ciclos. Vamos, pues, a utilizar pre divisor. Si to-mamos el mayor valor disponible, 256, tendremos una interrupción cada 256 * 256 = 65536 tiempos de ciclo o lo que es lo mismo, cada 65536 µs (a 4 MHz). Deberemos pasar 500.000 / 65536 = 7,63 veces por nuestra rutina de interrupción. Como no podemos pasar un número decimal de veces elegiremos 7 u 8 veces en función del error que queramos aceptar en un sentido u otro. Observe que si pasa 7 veces habrá contado poco tiempo y siempre será posible alargarlo. En caso contrario (8 veces) habrá pasado demasiado tiempo y no habrá corrección posible. Es evidente que la aceptación de un error estará en función de la aplicación. Si lo que desea hacer parpadear es una guirnalda de Navidad, el error será irrisorio. Si lo que desea construir es un cronometro, el error sería inaceptable. Comencemos ignorando el error. Decidimos utilizar un pre divisor por 256 con 7 pasadas por la rutina de interrupción. El tiempo obtenido será, en realidad, de 256 * 256 * 7 = 458752 µs en lugar de los 500.000 µs teóricos. Retomando nuestra tabla el contenido del registro OPTION con el que deberemos inicializar será B’10000111’, es decir, 0x87. Resistencia de carga fuera de servicio (no la necesitamos), fuente del tmr0 in-terna y pre divisor de 256. Por lo tanto, pondremos:

OPTIONVAL EQU H'87' ; Valor registro option ; Resistencia pull-up OFF ; Préscaler timer a 256

A continuación deberemos determinar el valor a poner en el registro INTCON para obtener las interrupciones sobre el tmr0. Esto será B’10100000’, es decir, 0xA0.

INTERMASK EQU H'A0' ; Interrupciones sobre tmr0

A continuación definimos:

;************************************************** ******************* ; DEFINE * ;************************************************** ******************* #DEFINE LED PORTA,2 ; LED

No tocamos nuestra rutina de interrupción principal dado que tenemos suficiente espacio de memoria para conservar nuestros tests. Escribamos nuestra rutina de interrupción del timer. Vemos inmediatamente que debemos contar las pasadas de tmr0, por lo tanto, necesitaremos una variable. Declarémosla en la zona 0x0C.

cmpt : 1 ; contador de pasadas

Page 139: Manual Instrucciones PIC16F84A

139

13.5.2 La inicialización Como es más fácil detectar un valor igual a 0 que igual a 7, vamos a ir decrementando nuestra variable de 7 a 0. Invertiremos el LED una vez alcanzado el valor 0. Deberemos inicializar nuestra variable a 7 para una prime-ra pasada. Haremos nuestra inicialización antes del goto start. Aprovecharemos para poner nuestro puerto LED en mo-do salidas.

;************************************************** ******************* ; INITIALISATIONS * ;************************************************** ******************* init

clrf PORTA ; Salidas portA a 0 clrf PORTB ; Salidas portB a 0 clrf EEADR ; permite disminuir el consumo BANK1 ; pasar a banco 1 movlw OPTIONVAL ; cargar mascara movwf OPTION_REG ; inicializar el registro OPTION

; Borrar la RAM ; ------------

movlw 0x0c ; inicializacion del puntero movwf FSR ; punter de direccionamiento indirecto

init1 clrf INDF ; borrar ram incf FSR,f ; punter en siguiente btfss FSR,6 ; test si fin zona esperada (>=0x40) goto init1 ; NO, bucle btfss FSR,4 ; test si fin zona esperada (>=0x50) goto init1 ; NO, bucle

; Inicializar puertos ; -------------------

bcf LED ; poner LED como salida BANK0 ; pasar a banco 0 movlw INTERMASK ; mascara de interrupciones movwf INTCON ; cargar control de interrupciones

; Inicializacion de variables ; ---------------------------

movlw 7 ; carga 7 movwf cmpt ; inicializa contador de pasadas goto start ; salta al programa principal

Page 140: Manual Instrucciones PIC16F84A

140

13.5.3 La rutina de interrupción

Realicemos ahora nuestra rutina de interrupción. Lo primero, decrementaremos nuestro contador de pasadas. Si no es nulo no haremos nada en esta pasada.

decfsz cmpt , f ; decrement el contador de pasadas return ; No es 0, no hacemos nada

Si el resultado es nulo deberemos invertir el estado del LED y recargar con 7 el contador de pasadas.

;************************************************** ******************** ; INTERRUPCION TIMER 0 * ;************************************************** ******************** inttimer

decfsz cmpt , f ; decrement el contador de pasadas return ; No es 0, no hacemos nada BANK0 ; por precaucion movlw b'00000100' ; seleccionar el bit a invertir xorwf PORTA , f ; invertir LED movlw 7 ; para 7 nuevas pasadas movwf cmpt ; cargar el contador de pasadas return ; fin de la interrupcion timer

Solo nos queda borra la línea

clrwdt ; borra watch dog

Del programa principal, puesto que el watchdog está fuera de servicio. Ensamble el programa. Lo pasamos al simulador. No olviden poner el simulador en servicio y abrir la ventana de registros especiales. Avance paso a paso su programa hasta que llegue al programa principal. Observaciones

• El último registro que hay en la ventana de registros especiales « T0pre » es un registro que no es accesible realmente, se trata del contador de nuestro pre divisor. Si los puede ver es porque MPLAB® es quien cuenta las redivisiones necesarias para la simulación. Este registro no aparece más en las versiones de MPLAB® superiores a 5.x para los PIC16F® (sí para los 18F). Puede que vuelva a aparecer en versiones superiores. Es por esto por lo que se lo explico.

• Cada vez que « T0pre » llega al valor de la pre división, tmr0 se incrementa en 1. No es hasta que no desborde que tendremos una interrupción.

• En el programa principal, se incrementa en 2 unidades cada vez que presionamos <F7>. Esto es nor-mal puesto que el programa principal comporta un goto y esto ocupa 2 ciclos de instrucción.

Page 141: Manual Instrucciones PIC16F84A

141

13.6 Modificación de los registros en el simulador

Como no nos queremos pasar horas simulando este programa, vamos a modificar los registros durante la si-mulación. Esto es muy fácil de realizar en MPLAB®6, puesto que bastará con hacer doble clic sobre el valor a modificar en la ventana « Special Function ». Vamos a servirnos de esta posibilidad. Primero, suprimimos el pre divisor. Para hacerlo escribiremos B’10001000’, es decir, 0x88. Hacemos doble clic en la casilla « hex » de la línea « OPTION_REG ». Ahora cada presión de <F7> incrementará tmr0 (no el pre divisor). Abra a continuación la visualización de las variables con« view >watch». Coloque la variable « cmpt »como se explicó en capítulos precedentes. Continúe presionando <F7> y constate que el desbordamiento de tmr0 provoca una interrupción y que esta provoca el decremento de cmpt. Para no perder mucho tiempo modifi-que en la ventana fenêtre « modify » el valor de cmpt y póngalo a 1. Siga con la simulación. Constatará que la próxima interrupción provoca la modificación del estado de RA2.

13.7 Colocación sobre la placa de pruebas

Cargue en el PIC® el fichero .hex obtenido y alimente la placa. Cuente los encendidos del LED que obtene-mos en 1 minuto. Deberá obtener entre 65 y 66 pulsos por minuto. Esto le muestra la precisión que hemos obtenido. En realidad tendrá un encendido cada (256*256*7*2) = 917504 µs . En un minuto, deberá obtener: 60.000.000 / 917504 = 65,3 encendidos. La teoría refrenda la práctica. El fichero es suministrado con el nombre de « led_tmr1.asm ».

13.8 Primera mejora de la precisión Vamos a intentar mejorar la precisión de nuestro programa. Podemos comenzar modificando nuestro pre di-visor. Probemos con valores sucesivos.

• Por 1 : da 500.000 / 256 = 1953,125 pasadas. No es práctico.

• Por 2 : da 500.000 / 512 = 976,5625 pasadas. No es práctico.

• Por 4 : da 500.000 / 1024 = 488,28125 pasadas. No es práctico.

• Por 8 : da 500.000 / 2048 = 244,140625 pasadas. En este caso solo necesitamos un contador puesto que el número de pasadas es inferior a 256.

¿Cuál será la precisión obtenida? Si inicializamos cmpt a 244 con una pre división de 8, la duración que obten-dremos será de 256*8*244 = 499712 µs es decir de 999424µs por encendido. En un minuto tendremos 60000000/999424 = 60,034 encendidos. Tenemos una precisión netamente mejorada. Usted ya puede mejo-rar el programa según estas indicaciones. Verá que debe modificar el valor de 7 a 244 en dos sitios diferentes. Esto no es práctico. Añadamos una asig-nación:

TIMEBASE EQU D’244’ ; base de tiempos = 244 decimal

Page 142: Manual Instrucciones PIC16F84A

142

Si tiene algún problema, el fichero funcional de este ejercicio está disponible bajo la denominación « Led_tmr.asm ». Ventaja obtenida: Una precisión más grande. Inconveniente: más interrupciones generadas, más tiempo perdido por el programa principal. En nuestro caso esto no es importante dado que el programa no hace nada más. No siempre será así.

13.9 Segunda mejora de la precisión

Podemos mejorar aún más la precisión de nuestro programa. En efecto, podemos no utilizar el pre divisor y utilizar más contadores para contar 1953,125 pasadas. Al final de la pasada 1953 podremos añadir un último temporizador añadiendo un valor a tmr0. Por ejemplo: Detectamos 1953 pasadas con la ayuda de varios contadores. Al final de la pasada 1953 estaremos a 1953*256 = 499968 µs por lo que nos faltaran 500.000 - 499.968 = 32 µs. Deberemos añadir 256-32 = 224 al tmr0 pero teniendo en cuenta los 2 ciclos perdidos después de cada mo-dificación de TMR0 añadiremos 256-30:

movlw 226 ; desborde en 30 + 2 ciclos addwf tmr0

Teniendo en cuenta que 32µs es un tiempo muy corto para realizar todo deberemos optimizar al máximo nuestra rutina de interrupción. Supresión de los test y subprogramas, etc. Esto es generalmente posible. No trataremos este procedimiento aquí dado que no presenta mayor interés en tanto que las interrupciones van a ocupar la mayoría del tiempo de la CPU.

13.10 El método llamado « de atrape »

Existe un método muy astuto que garantiza que dado un tiempo usted tendrá el número correcto de inte-rrupciones y por lo tanto el encendido del LED en nuestro caso. Por el contrario, este método no garantiza un tiempo constante entre dos interrupciones sucesivas. El truco consiste en dividir el tiempo deseado entre un numero entero de interrupciones. A continuación te-nemos en cuenta la parte fraccionaria que se ha perdido. Si tomamos nuestro ejemplo en 13.8, tendremos que nos harán falta 244,… interrupciones para tener medio segundo. Tomamos 244 interrupciones. Vistos los parámetros utilizados, tendremos una interrupción cada 256 * 8 = 2048 µs. En realidad, para obtener 500 ms después de 244 interrupciones, nos hará falta un tiempo de 500.000 / 244 = 2049,180327868852459016393442623 µs. Nos falta 1,1803… µs en cada interrupción pa-ra tener la duración correcta. En vez de ir rectificando el tiempo, vamos a ir memorizando el tiempo que perdemos e ir acumulándolo en varias variables. Contra más precisión queramos más cifras significativas necesitaremos memorizar. Limité-monos a los 4 primeros decimales. No vamos, evidentemente a trabajar con decimales, por lo que converti-remos todo a diezmilésimas de µs.

Page 143: Manual Instrucciones PIC16F84A

143

Después de una pasada, vamos a añadir el tiempo perdido, es decir 11803 en nuestro contador de tiempo perdido. Una vez que este contador alcance o sobrepase la duración de una interrupción, es decir 20480000 (hace falta una variable sobre varios octetos evidentemente), será suficiente con:

1. Tener en cuenta un interrupción suplementaria (245 en lugar de 244).

2. Restar este tiempo atrapado 20480000 de nuestro contador de tiempo perdido. Así tendremos un tiempo medio (en nuestro caso) de 2049,1803 µs y por lo tanto una temporización media de 2049,1803 * 244 = 499999,9932 µs. Obtendremos un número medio de parpadeos de 60,0000008 en-cendidos por minuto. Observe el extraordinario aumento de la precisión. El único inconveniente es que entre dos conmutaciones sucesivas de nuestro LED podemos tener una diferencia 2048 µs de tiempo de, es decir 2 ms, lo que es abso-lutamente imperceptible. Podemos también disminuir el número de cálculos de la siguiente forma: nos hacen falta 244,140625 inte-rrupciones apara tener un retraso de 500 ms. A cada inversión del LED (cada 244 interrupciones) habremos perdido 0,140625 de interrupción. Nos hará falta contabilizar 140625 (millonésimas) cada 244 interrupciones, y añadir una interrupción, 245 en lugar de 244, cada vez que alcancemos el valor 1000000 (una interrupción entera). El método es el mismo, solo cambia la forma de tener en cuenta el tiempo perdido. He aquí un método muy potente para obtener tiempos medios muy precisos. Encontrará una aplicación real de reloj basado en este principio sobre mi pagina web, pagina « trucs et astuces », a través de la excelente aplicación de reloj en tiempo real d’Erio, que explica perfectamente este método y que aprovecho para agra-decer.

13.11 El método hardware – Adaptación del reloj Supongamos que deseamos construir un cronometro. La precisión es el dato más importante en este caso. Nos vamos a arreglar de tal forma que la desmultiplicación caiga sobre múltiplos enteros ¿Cómo? Simplemen-te cambiando el tiempo de instrucción, es decir, la frecuencia del cuarzo del PIC®. Ejemplo Como no podemos acelerar un PIC® por encima de su velocidad máxima (utilizamos un PIC® 4 MHz), solo podemos enlentecerlo. Partimos de una base de tiempos demasiado rápida. Tomemos nuestro caso de partida: pre divisor a 256, contador de pasadas a 7. Duración con un cuarzo de 4 MHz : 256*256*7 por temporización, 256*256*7*2 por encendido, es decir 917504 µs. Nosotros deseamos 1000000 µs. Procedamos a calcular a la inversa. ¿Cuánto debe durar una instrucción? 1000000/(256*256*7*2) = 1,089913504 µs. Esto nos da una frecuencia de 1/1,089913504µs = 0,917504 MHz Como la frecuencia de los ciclos internos es igual a la frecuencia del cuarzo dividida por 4, necesitaremos un cuarzo de 0,917504 * 4 = 3,670016 MHz El único problema es saber si existe en el mercado cuarzos de esta frecuencia a nuestra disposición. En saco contrario, realizaremos los cálculos con otros valores de pre divisor y contador. Esté seguro que acabará encontrando un cuarzo que responda a la necesidad por el mero hecho de que multitud de relojes de cuarzo están basados en micro controlador y han tenido el mismo problema. Si en-cuentra un cuarzo de la frecuencia apropiada, obtendrá un reloj con la precisión del cuarzo.

Page 144: Manual Instrucciones PIC16F84A

144

13.12 El método de lujo – el doble reloj

El método precedente presenta el problema de hacer más lento el PIC®. ¿Qué podemos hacer si queremos una velocidad máxima y una precisión máxima? Ningún problema. Alimente el PIC® con su cuarzo y cree otro oscilador externo con su cuarzo especial de timing. Aplicamos la señal obtenida sobre RA4/TOKI y configuraremos el timer0 en modo contador. El trabajará a la velocidad máxima y las interrupciones timer0 estarán generadas por una base de tiempos más adaptada a la medida de nuestros eventos.

13.13 Ejemplo de utilización de 2 interrupciones

En este pequeño ejemplo vamos a utilizar 2 fuentes de interrupción con el fin de mostrar un ejemplo concre-to de este tipo de utilización. Vamos a recrear nuestro programa de tele ruptor pero reemplazando la tempo-rización por una interrupción tmr0.

.

Recuerde que nuestro programa principal no hace nada. Podremos utilizar otras funcionalidades sobre esta placa sin perturbar el funcionamiento del tele ruptor. Tendremos una interrupción para RB0 y otra para tmr0. Arriba tiene el flujo grama que vamos a utilizar. Efectúe una copia de m16f84.asm y renómbrelo como « telerupt.asm ». Cree un nuevo proyecto « tele-rupt.pjt » y edítelo con ya hicimos anteriormente: corte del watchdog, posicionado del LED como salida, puesta en servicio de las interrupciones RB0/INT.

Page 145: Manual Instrucciones PIC16F84A

145

Cree su rutina de interrupción timer0 cada 260 ms, pre divisor a 256 y 4 pasadas. Pruebe a realizar el programa por sí mismo. Cárguelo en el PIC® y láncelo. Observe que el flujo grama no contiene el contador de pasadas en tmr0. Le dejo el trabajo de reflexionar sobre ello. Una pulsación sobre el pulsador enciende el LED, otra lo apaga. Si no lo hace así, busque el error o sírvase del simulador. Yo le he suministrado el fichero funcional con los otros ficheros del curso para el caso en que usted se quede bloqueado. Observación Es muy importante comprender bien que hay que borrar tmr0 antes de borrar el flag T0IF y volver a lanzar las interrupciones tmr0. Si hace lo contrario, se arriesga a que tmr0 desborde entre el momento del borrado del flag y el momento del borrado de tmr0. En este caso el flag volverá a ser posicionado inmediatamente después de ser borrado y su programa podrá fallar intermitentemente. Hace falta no olvidarse que toda modificación de TMR0 entraña el no contaje de los 2 próximos incrementos previstos.

13.13 Conclusión

Ahora ya sabe explotar el timer0. Los métodos explicados aquí son una base de trabajo para cosas más serias. Le aconsejo realizar todas las manipulaciones que hemos ido describiendo. Incluso los errores le serán de mu-cho provecho.

Page 146: Manual Instrucciones PIC16F84A

146

14. El acceso a la memoria « eeprom »

En este capítulo le voy a hablar de los procedimientos para acceder a la eeprom interna del PIC®. Conviene no confundirse con la escritura en una memoria eeprom externa del tipo 2416. Para ese tipo de eeprom basta con seguir las directivas del datasheet del componente. Hablaremos de ello más adelante en este curso en la 2ª parte. Se trata de una memoria un tanto particular: no forma parte de la memoria de programa dado que no puede ejecutar instrucciones y sus direcciones de acceso no se parecen del todo a las de acceso a la RAM. Contiene elementos no volátiles pudiendo ser programada al mismo tiempo que programamos el PIC y estando accesi-ble al programa en ejecución.

14.1 Tamaño y localización de la memoria « eeprom »

La dirección física de la memoria eeprom comienza, para los PIC® de rango medio, en la dirección 0x2100.

Esta dirección se sitúa fuera del espacio de direccionamiento de la memoria de programa de los PIC16F® (recuerde, 8 K palabras, dirección máxima 0x1FFF), y fuera del espacio de direccionamiento de la RAM (4 ban-cos de 256 elementos). Podemos desde este momento deducir que nos hará falta utilizar procedimientos es-peciales para acceder a la eeprom. Observe que aunque estos emplazamientos no son accesibles directamente por el programa, por el contrario sí lo son en el momento de la programación. Podremos inicializar la eeprom en el momento de la programa-ción del componente. Estos es igualmente verdad para los registros especiales del PIC®. Por ejemplo, la dirección 0x2007 contiene los parámetros que escribirá en _CONFIG. Podrá, pues, reemplazar la directiva por una inicialización dire-cta de la dirección 0x2007. De todas formas se lo desaconsejo por un tema de portabilidad y evolución rápida de una familia a otra de PIC®. Es más, ¿por qué hacerlo complicado si podemos hacerlo simple? Así mismo, la dirección 0x2006 contiene la identificación del componente. Es así como un programador evo-lucionado puede hacer la distinción entre un 16F84 y un 16F84A, puesto que la información del constructor difiere de uno a otro. El 16F84 dispone de 64 emplazamientos de memoria eeprom disponibles para su uso libremente. Vamos a ver cómo utilizarlos.

14.2 Preparación del programa Comencemos por hacer un copiar/pegar del fichero « Led_tmr1.asm » y renombrémoslos como « eep_test.asm ». Construya su nuevo proyecto en MPLAB® con el mismo nombre y añada allí el programa desnudo. Edite el encabezamiento del programa.

Page 147: Manual Instrucciones PIC16F84A

147

;************************************************** ********************** ; * ; Parpadeo LED a una frecuencia determinada por un emplazamiento eeprom * ; * ;************************************************** ********************** ; * ; NOMBRE: LED PARPADEANTE CON TIMER0 y utilización de la eeprom * ; Fecha: 18/02/2001 * ; Version: 1.0 * ; Circuito: Tarjeta de experimentos * ; Autor: Bigonoff * ; * ;************************************************** ********************** ; * ; Fichero requerido: P16F84.inc * ; * ;************************************************** ********************** ; * ; Notas: Demostracion de la utilización de datos en eeprom * ; La base de tiempos de parpadeo contenida en eepro m * ; * ;************************************************** **********************

Añadimos una variable en la zona de variables. Contendrá el valor a cargar en el contador.

reload : 1 ; valor a recargar en el contador

En nuestro programa inicial, cada vez que el contador de pasadas en el timer llegaba a 0, lo recargábamos con el valor 0x07. Ahora lo recargaremos con el valor contenido en la variable « reload ». El procedimiento es el siguiente:

• Inicializamos un emplazamiento eeprom « eereload » con el valor 0x07 en el momento de la progra-mación del dispositivo.

• En el arranque, leemos « eereload » y ponemos su contenido en la variable « reload ». • El contenido de reload se utilizará para recargar cmpt una vez que llegue a 0.

La ventaja del procedimiento es que si modificamos la base de tiempos en la eeprom, esta modificación no se perderá en el momento que dejemos de alimentar la eeprom. Le doy el flujo grama que vamos a implementar en primera instancia.

Page 148: Manual Instrucciones PIC16F84A

148

Podría decirme que parece un poco inútil leer la eeprom y copiar su contenido en reload. ¿Por qué no utilizar directamente el valor de la eeprom en el resto del programa? La respuesta es simple. El procedimiento de lec-tura de la eeprom es mucho más complejo que una simple lectura de la RAM. Es necesario limitar los accesos a la eeprom al máximo. Comencemos por modificar nuestra rutina de interrupción. La única línea a modificar es aquella que cargaba de oficio el valor 7 en W. Ahora cargaremos el contenido de reload. Tendremos:

inttimer decfsz cmpt , f ; decrement el contador de pasadas return ; NO 0, no hace nada BANK0 ; por precaucion movlw b'00000100' ; selccion del bit a invertir xorwf PORTA , f ; invertir LED movf reload , w ; carga el valor contenido en reloa d movwf cmpt ; en el contador de pasadas return ; fin de la interrupción timer

14.3 Inicialización de la zona eeprom

Vemos en el flujo grama que leemos nuestra eeprom con la finalidad de colocar el contenido en nuestra va-riable. Pero también necesitamos inicializar esta eeprom en el momento de la programación del PIC®. Debe considerar que no serviría de nada inicializar la eeprom a cada arranque del PIC®, si no, ¿qué interés tendría utilizar una zona de memoria que resiste cuando nos está alimentada? Inicializaremos esta zona directamente en el momento de la programación del dispositivo. Esto se efectúa con la ayuda de la directiva « DE » (Data Eeprom) puesta en la zona de datos eeprom, es decir en 0x2100. Creemos una zona eeprom a continuación de la zona de variables.

;************************************************** ******************* ; DECLARACIONES DE LA ZONA EEPROM * ;************************************************** ******************* org 0x2100 ; dirección comienzo zona eeprom DE 0x07 ; valor de recarga del contador

Lance el ensamblado del programa. ¿Querrá, sin duda, verificar que la eeprom contiene el valor 0x07? Nada más simple, lance « EEPROM memory » en el menú « Windows » y verifique el valor. Usted me dirá que el valor está en la dirección 0x00 en vez de en la dirección 0x2100. En efecto, hay que distinguir 2 direcciones. La dirección física de este emplazamiento vista por el programa-dor, que es 0x2100. Esta dirección es únicamente accesible en modo programación. Por el contrario, el programa accederá a estos emplazamientos a partir de un procedimiento especial y con una dirección relativa. La dirección vista por el programa comienza por la dirección 0x00. No hay ninguna po-sibilidad de equivocarse con una dirección de la RAM ni con una dirección de instrucción dado que el acceso se hace por procedimiento particular y no mediante un direccionamiento clásico.

Page 149: Manual Instrucciones PIC16F84A

149

Como resumen:

• Para programar un emplazamiento Eeprom via dispositivo programador, utilizaremos las direcciones empezando por la 0x2100 via un ORG 0x2100. Ningún riesgo de confusión con una dirección concer-niente a la memoria de programa.

• Para acceder a un emplazamiento Eeprom via programa, utilizaremos direcciones comenzando por 0x00 a través de un procedimiento particular que vamos a describir. Ningún riesgo de confusión.

Bien entendido que también podemos asignar un nombre a estas direcciones como hacemos para las varia-bles. Utilicemos, pues, el nombre eereload para designar el valor de reload contenido en la eeprom en la di-rección 0x00. Añadamos simplemente un #define en la zona de inicialización de la eeprom.

#DEFINE eereload 0x00 ; direccion eeprom de eereloa d

O bien

eereload EQU 0x00 En nuestro flujo grama, tenemos la necesidad de leer la eeprom. Usamos 4 registros para acceder a la ee-prom. Vamos a examinarlos.

14.4 El registro EEDATA

Es en este registro donde va a transitar el valor a escribir o leer de la eeprom. Este registro está situado en la dirección 0x08 del banco 0.

14.5 El registro EEADR

En este registro, situado en la dirección 0x09 del banco 0, vamos a indicar en 8 bits la dirección concernida por la operación de lectura o escritura de la eeprom. Vemos que para esta familia de PIC® no podemos sobre-pasar 256 emplazamientos de eeprom. Para el 16F84 la zona admisible va desde la 0x00 a la 0x3F, es decir, 64 posiciones.

14.6 El registro EECON1

Este registro situado en la dirección 0x88 del banco 1, contiene 5 bits que indicaran el funcionamiento de los ciclos de lectura/escritura de la eeprom. Veamos el contenido.

bits 7/6/5

No usados.

bit 4 : EEIF

Por EEprom write operation Interrupt Flag bit. Este flag, está ligado con la interrupción EE-PROM. Pasa a 1 una vez que la operación de escritura ha terminado. Si el bit EEIE de INTCON está a 1 se generará una interrupción.

bit 3 : WRERR

WRite ERRor. Es un bit de error. Pasa a1 si una operación de escritura en eeprom ha sido inte-rrumpida, por ejemplo, por un reset.

Page 150: Manual Instrucciones PIC16F84A

150

bit 2 : WREN

WRite ENable. Autorizacion de arranque del ciclo de escritura. Es una especie de cerrojo de se-guridad.

bit 1 : WR

WRite. Arranque del ciclo de escritura. Se pone a 0 automáticamente cuando la escritura ha ter-minado.

bit 0 : RD

ReaD. Arranque de un ciclo de lectura. Se queda en 1 durante un ciclo, después es puesto a 0 au-tomáticamente. Observación: en el caso de que el ciclo de escritura sea interrumpido por un desborde del watchdog o por un reset, usted puede leer el bit WRERR que lo señalará. Los registros y permane-cen sin cambios y usted puede relanzar el ciclo de escritura. Esto no funciona para un corte de co-rriente. Para este caso le explicaré mi método personal al final de este capítulo.

14.7 El registro EECON2

Nos volvemos a ver con un registro “fantasma” puesto que este registro no existe físicamente o, al menos, no es realmente accesible. Se trata de una dirección 0x89 del bando 1 que sirve para enviarle comandos al PIC® concernientes a los procedimientos para la eeprom. Solo lo podemos utilizar sirviéndonos de las instrucciones que vamos a ver a continuación.

14.8 Acceso en lectura de la memoria « eeprom »

Para leer un dato en eeprom es suficiente con poner la dirección a leer en el registro EEADR. A continuación posicione el bit RD a 1. Inmediatamente podrá recuperar el valor leído en el registro EEDATA . Es necesario no olvidarse de los correspondientes cambios de banco. Como este procedimiento es corto y siempre es el mismo, vamos a crear una macro para ello. Como la macro debe contener la dirección de lectura, vamos a crear una macro con paso de parámetros. Nota: el término “paso de parámetros” es un poco abusivo puesto que en realidad no pasamos nada a la ma-cro. Es más bien el ensamblador el que reemplaza, en el momento del ensamblaje, todos los términos “pará-metros” por el valor especificado sobre la línea de ejecución de la macro. Una vez más, se trata de un editor de texto, todo es conocido en el momento de ensamblar (al contrario que en una subrutina) pero así nos hacemos la ilusión de que disponemos de una rutina capaz de todo a la que le podemos pasar parámetros. Esta es la macro a añadir. Debemos estar en el banco 0 para llamar a esta macro y nos retorna el valor leído en el registro W.

READEE macro adeeprom ; macro con 1 parametro (argumento) movlw adeeprom ; carga la direccion eeprom (argumento recibido) movwf EEADR ; direccion a leer en el registro EEADR bsf STATUS , RP0 ; pasa a banco 1 bsf EECON1 , RD ; lanzar la lectura EEPROM bcf STATUS , RP0 ; pasa a banco 0 movf EEDATA , w ; carga el valor leido en W endm ; fin de la macro

Page 151: Manual Instrucciones PIC16F84A

151

Observe que especificamos un argumento para la macro. Debemos designar estos argumentos después de la directiva macro. Hemos utilizado aquí el argumento adeeprom para indicar la dirección de la eeprom. Cada utilización de adeeprom en nuestra macro será simplemente reemplazado por el argumento “recibido” en el momento de la llamada de la macro. Para utilizar esta macro deberemos, pues, especificar el argumento. Ejemplo:

READEE eereload ; lectura de la direccion eereload de la eeprom

Observe que en el momento del ensamblado, MPASM® toma conocimiento del valor de eereload y reem-plaza este valor directamente en el cuerpo de la rutina. En la memoria del programa, después del ensambla-do, no verá ninguna traza de un eventual parámetro. Esta macro efectuará entonces: lectura de la eeprom en la dirección eereload, es decir, en la dirección 0x00. Esta macro, así como todas las modificaciones principales, serán añadidas al fichero m16f84.asm. Yo se lo he suministrado bajo la denominación « m16f84_n2.asm ». A partir de la siguiente lección, reemplazará a su fichero « m16F84.asm » actual. Volvamos a nuestro flujo grama. Debemos añadir la lectura de la eeprom en la inicialización y colocar este va-lor leído en reload y en cmpt. He aquí la rutina modificada:

; inicializacion de variables ; ---------------------------- READEE eereload ; leer posicion eeprom 0x00 movwf reload ; colocar en reload movwf cmpt ; inicializar contador de pasadas goto start ; saltar al programa principal

Ensamble el programa y cárguelo en el PIC®. El LED deberá parpadear a una frecuencia de 1 Hz. Si no funcio-na, verifique el programa o compárelo con el fichero « eep_test1.asm » suministrado con esta lección.

14.9 El acceso en escritura a la zona eeprom

Ahora me dirá con razón que no sirve de nada leer la eeprom si no somos capaces de escribir en ella. Nuestro programa no presenta ninguna ventaja sobre el que teníamos anteriormente. La observación está plenamen-te justificada. Así que vamos a ver el modo de escribir en la eeprom. Como no dudará, este método utiliza los mismos registros. El procedimiento a seguir consiste en colocar primero el valor en el registro EEDATA y la dirección en el re-gistro EEADR. A continuación se debe enviar una secuencia especifica al PIC® de la que no tenemos nada que comprender, viene impuesta por el constructor. Observaciones

• Microchip® recomienda que este procedimiento especifico no sea interrumpido por una interrup-ción por lo que cancelaremos las interrupciones durante esta fase.

• Al final del procedimiento de escritura, el dato aún no está escrito en la eeprom. Lo estará aproxima-damente 10 ms más tarde (lo que representa unos 10.000 ciclos de instrucción). Por lo tanto no podrá volver a escribir un nuevo valor en la eeprom o leer este valor antes de haber verificado el fin del ci-clo de escritura precedente.

• El fin de la escritura puede ser monitorizado por la generación de una interrupción (bit EEIE posicio-nado) o por la lectura del flag EEIF (si lo había posicionado a 0 antes de la escritura) o por la consulta del bit WR que estará a 1 durante todo el ciclo de escritura.

Page 152: Manual Instrucciones PIC16F84A

152

Vamos a escribir una macro de escritura en la eeprom. Esta vez deberemos pasar 2 parámetros, a saber, el va-lor a escribir (que supondremos colocado en W) y la dirección de la eeprom donde debe ser escrito.

WRITEE macro addwrite ; el dato se encuentra en W LOCAL loop ; etiqueta local

movwf EEDATA ; colocar (w) en el registro movlw addwrite ; cargar la direccion de escritura vwf EEADR ; colocar en el registro

moloop bcf INTCON , GIE ; impeder interrupciones btfsc INTCON , GIE ; test si GIE esta a 0 (inutil) goto loop ; NO, volver a empezar (inutil) bsf STATUS , RP0 ; pasa a banco 1 bcf EECON1 , EEIF ; borra flag de fin de escritura bsf EECON1 , WREN ; autorizar acceso escritura movlw 0x55 ; cargar 0x55 movwf EECON2 ; enviar comando movlw 0xAA ; cargar 0xAA movwf EECON2 ; enviar comando bsf EECON1 , WR ; lanzar ciclo de escritura bcf EECON1 , WREN ; colocar cerrojo proxima escritu ra bsf INTCON , GIE ; autorizar interrupciones bcf STATUS , RP0 ; pasar a banco 0 endm

Observaciones

• He utilizado 3 instrucciones para poner GIE a0. Esto era necesario a causa de un error en el 16C84 que explicaré más adelante, pero ya fue corregido en el 16F84 (y siguientes modelos). El test de verifica-ción ya no es necesario. Pero esto me da la oportunidad de explicar el tema de etiquetas locales. Dejémoslo pues como ejemplo y en el futuro usaremos solo una línea: bcf INTCON,GIE .

• La directiva LOCAL precisa que el símbolo utilizado en esta macro no existirá más que en su interior. Sin esta directiva, si solo usase una llamada a la macro en todo su programa, no habría ningún pro-blema. Si usase la macro 2 veces o más, para cada llamada MPASM® reemplazaría WRITEE por toda la lista de instrucciones contenidas hasta la directiva endm que contiene la etiqueta “loop”. Obtendría un programa real con la etiqueta “loop” repetida en emplazamientos diferentes. Esto generaría un error. La directiva LOCAL informa a MPASM® que cada llamada a la macro trabaja con una etiqueta “loop” diferente por lo que no habrá empleo múltiple.

• La secuencia está impuesta por Microchip® para la escritura de eeprom. No hay nada que compren-der a nivel de esas 4 líneas, simplemente disparan secuencias hardware internas (especie de instruc-ciones).

• El procedimiento de escritura en eeprom es relativamente largo pero además requiere mucho tiem-po. Piense que para rellenar la zona completamente (64 octetos) necesitará 0,6 s. Este tiempo varía de un modelo a otro. Verifíquelo en el datasheet correspondiente.

• El número de ciclos de escritura de la eeprom está limitado. La vida de la eeprom es del entorno de 10 millones de ciclos. Si su programa comporta un error tal que escribe la eeprom sin pararse, el PIC® estará fuera de servicio en 28 horas. Vigile su programa. Nuevamente, el número de ciclos (ga-rantizado y típico) de escritura varía de un modelo a otro. Compruebe los datasheets.

• Antes de escribir en la eeprom debe verificar que no hay otro ciclo de escritura en curso. Utilice el bit WR del registro EECON1. Si vale 0 no hay ningún ciclo de escritura en curso. Observe que hay dos formas de proceder para verificar esta condición:

o Esperar a la finalización del ciclo de escritura en curso: bloqueamos el programa hasta que el ciclo acabe.

o Salir inmediatamente de la rutina verificando siempre, antes de arrancarla, que no existe un ciclo de escritura en curso.

Elija el método en función de la aplicación pero, en todo caso, efectúe esta verificación. Puede incluir la verifi-cación directamente en la macro.

Page 153: Manual Instrucciones PIC16F84A

153

14.10 Utilización práctica de la memoria « eeprom »

Ahora vamos a modificar de nuevo nuestro programa para escribir en la eeprom. Vamos a incrementar la du-ración del parpadeo del LED cada 16 parpadeos (es decir, cada 32 pasadas por la rutina de interrupción) y salvaguardaremos este valor en la eeprom. No necesitaremos interrupciones eeprom para esto pero debemos comprender el principio de las interrup-ciones suficientemente bien para poder servirnos de ellas en caso de necesidad. Vamos a proceder a la modificación de nuestra rutina de interrupción. Debemos añadir un segundo contador (cmpt2) en la zona de RAM por lo que declararemos esta variable.

cmpt2 : 1 ; contador de pasadas 2

Si, ya sé que hay formas de optimizarlo para no tener necesidad más que de un solo contador. No es el obje-tivo de este ejercicio. Quiero ser claro y centrado en el objetivo de este capítulo. La modificación es muy sim-ple en realidad. El la rutina de interrupción timer incrementaremos el contador numero 2 el cual utilizaremos en el programa principal. Unas palabras sobre el “bug” del bloqueo de interrupciones en el 16C84 En el 16C84, el fin de la autorización de interrupciones no se toma en cuenta hasta el final de la instrucción si-guiente. Entre « bcf INTCON, GIE » y la siguiente instrucción puede llegar una interrupción. Como « retfie » repone el bit GIE a 1 de forma automática, la interrupción se pondrá en servicio a pesar del programador. Será necesario, pues, después de poner GIE a 0, comprobar que efectivamente GIE está a 0. En caso contrario, significará que una interrupción a ocurrido en este momento y hará falta recomenzar la operación. A partir del 16F84, las interrupciones se prohíben desde la instrucción ejecutada y antes de la instrucción si-guiente. El “bug” ya no existe. Se lo explico para el caso en que se encuentre delante de una de las muy nu-merosas aplicaciones para este viejo PIC que fue referencia en su día, evolucionado después al 16F84 y poste-riormente al 16F84A. El nuevo flujo grama será:

Page 154: Manual Instrucciones PIC16F84A

154

;************************************************** ******************** ; INTERRUPCION TIMER 0 ;************************************************** ******************** inttimer

; test contado de pasadas ; -------------------------- dcfsz cmpt , f ; decrementa el contador de pasadas return ; NO 0, no hacemos nada ; inviertir LED ;--------------------------- BANK0 ; por precaucion movlw b'00000100' ; selecciona el bit a invertir xorwf PORTA , f ; invertir LED ; recarga el contador de pasadas ; ------------------------------ movf reload , w ; carga el valor contenido en relo ad movwf cmpt ; en el contador de pasadas ; incrementar contador de pasadas 2 ; ---------------------------------- incf cmpt2 , f ; incrementar el contador de pasada s 2 return ; fin de la interrupcion

Vamos a escribir en la eeprom desde nuestro programa principal. ¿Por qué no desde la rutina de interrup-ción? Es necesario, en la medida de lo posible, salir cuanto antes de una rutina de interrupción. Están reservadas para tratamiento urgente de la información y no deben ser interrumpibles por otras interrupciones. Observe que si su programa no comporta nada más que una fuente de interrupciones, pudiera ser que no tenga ninguna importancia alargar la duración de la interrupción que la maneja. De todas formas, conviene ir aprendiendo buenos hábitos. Observe que la macro corta y relanza las interrupciones. Evidentemente usted no puede relanzar las interrup-ciones desde dentro de una interrupción. Si decidiese utilizar la macro dentro de una interrupción, debería suprimir la instrucción de puesta en servicio (y de corte) de GIE a nivel de la macro. De la misma manera, nuestra macro, relanza de oficio las interrupciones. Es así mismo evidente que, si utiliza la macro en un programa sin interrupciones, deberá así mismo suprimir la línea en cuestión. Por el contrario, en el caso más rebuscado, en el que las interrupciones estén habilitadas y cortadas de forma intermitente, deberá añadir en la macro un test para no volver a habilitar las interrupciones salvo en el caso de que hubie-sen estado de que estuviesen habilitadas a la hora de entrar en la macro. Lo más simple es salvar el estado de GIE en un bit de la RAM (flag) y reponer GIE en servicio si este flag estuviese posicionado. Veamos el programa principal.

Page 155: Manual Instrucciones PIC16F84A

155

;************************************************** ************* ; PROGRAMA PRINCIPAL ;************************************************** ************* start

; test si 16 inversiones de LED ; ----------------------------- btfsscmpt2 , 5 ; test si 32 pasadas goto star ; NO, esperar clrf cmpt2 ; SI, borrar contador 2 ; incrementar reload ; ------------------ incf reload , f ; incrementa reload incf reload , f ; incrementar 2 veces es mas visible

; Test si la escritura precedente de la eeprom acab ada ; ------------------------------------------------- ---

; esto es a voluntad dado que el tiempo usado ; es suficientemente largo

BANK1 ; pasar a banco 1 wait

btfsc EECON1 , WR ; test si escritura en curso goto wait ; SI, esperar BANK0 ; pasar a banco 0 ; escribir contenido de reload en eeprom ; -------------------------------------- movf reload , w ; cargar reload WRITEE eereload ; escribir en direccion 0x00 goto start ; bucle END ; directive de fin de programa

Ensamble el programa y cárguelo en el PIC®. Construya el montaje en la placa de experimentación. Observe que el LED parpadea a una frecuencia de 1 Hz. Cuando se encienda la 17ª vez, la frecuencia de parpadeo dis-minuirá ligeramente. Así cada 16 pasadas. Estime la frecuencia actual de parpadeo y corte la alimentación del PIC®. Espere unos segundos y vuelva a conectar la alimentación. El LED parpadeará a la frecuencia precedente, indicativo de que el parámetro fue guardado en la eeprom. El fichero, tal como debería estar al final de esta lección se encuentra disponible bajo la denominación «eep_test.asm».

14.11 Securización del acceso a la memoria eeprom. Nuestro programa precedente adolece de un defecto grave. Admitamos que cortamos la alimentación justo durante una operación de escritura en la eeprom. En este caso, los adtos presentes en la eeprom pueden es-tar corrompidos. Tenemos diferentes maneras de prevenir este problema:

• Puede prever una reserva de alimentación (condensador) para el PIC y notificarle que la tensión prin-cipal ha sido cortada. Así, podemos hacer que el PIC no comience ninguna operación de escritura en eeprom. La reserva de alimentación debería ser lo suficientemente grande para poder acabar una eventual operación de escritura en curso.

Page 156: Manual Instrucciones PIC16F84A

156

• Puede hacer que sea el propio PIC el que gestione la alimentación, sobre todo si se trata de una ali-mentación salvaguardada (baterías, pilas, etc.). En este caso, después de pulsar un botón (por ejem-plo), el PIC terminará sus operaciones en curso, y después se cortará la alimentación a sí mismo, pilo-tando la alimentación a través de un pin (tal como los PC se gestionan a sí mismos).

• También puede no preocuparse por nada en el apagado y, en el encendido, verificar que los datos de la eeprom son válidos. Hará falta utilizar 2 juegos de valores para asegurarse siempre de tener uno válido.

Vamos a hablar del último método propuesto, los otros métodos son, básicamente, una cuestión de hardwa-re. Veamos un procedimiento que nos permite verificar los datos:

1. Ponga una cabecera en el primer o los primeros octetos de la eeprom. Por ejemplo pondremos 0x55 en el octeto 0x00.

2. Para empezar la escritura de nuevos valores empezaremos borrando el encabezamiento. 3. Después escribiremos los valores. 4. Para acabar, reescribiremos el encabezamiento.

Después de un arranque del PIC®, si el encabezamiento está presente, significará que los valores de la ee-prom son validos. En caso contrario, el ciclo de escritura fue interrumpido. En su mano reaccionar en conse-cuencia, por ejemplo, reescribir en la eeprom los valores por defecto. Para aumentar la seguridad del sistema en caso de información crítica, pude encabezar con más octetos. Para no tener que volver a los valores por defecto, puede utilizar 2 juegos de datos, cada uno comenzando por su propio encabezamiento: así, si la escritura en un bloque se interrumpió, usted podrá utilizar los valores del otro bloque.

14.12 Conclusión

He aquí una nueva etapa superada en su conocimiento del 16F84. Ahora puede utilizar la zona eeprom de su PIC® para almacenar allí su datos remanentes. Si tiene un riesgo de escritura de múltiples datos a intervalos regulares, no olvide verificar que la operación de escritura precedente había acabado antes de arrancar una nueva operación. No dude en experimentar, pero no olvide verificar su programa para evitar escrituras inútiles y frecuentes en su eeprom. De todas formas, no entre en pánico. Si su programa está correctamente diseñado y escrito, no creo que se acerque a gastar el millón de ciclos de escritura garantizados.

Page 157: Manual Instrucciones PIC16F84A

157

15. El watchdog

El watchdog o “perro de guardia” es un mecanismo de protección de su programa. Sirve para vigilar si la eje-cución de su programa se desarrolla en el tiempo que usted le ha asignado para hacerlo.

15.1 El principio de funcionamiento

La puesta en servicio o no del mecanismo del watchdog se decide en el momento mismo de la programación (quemado) del PIC con la ayuda de la directiva _CONFIG. Si «_WDT_OFF» está especificado, el watchdog es-tará fuera de servicio. Si por el contrario, es «_WDT_ON» lo que está escrito, el watchdog se activará. No es posible ponerlo en servicio o quitarlo durante la ejecución del programa. El funcionamiento del watchdog está ligado a un timer específico, que no está sincronizado con el programa o con un evento exterior, y que no depende de la frecuencia del reloj. La duración específica de desbordamiento de este tiempo es de 18 ms. Este valor hay que tomarlo con cierta precaución puesto que varía en función de diversos parámetros tales como el de la tensión de alimentación o el de la temperatura. También puede variar de un modelo a otro de PIC. Conviene consultar los datasheet. El desbordamiento mínimo es de 7 ms y es el que usted deberá utilizar en la práctica. En efecto, MICROCHIP le garantiza que ningún PIC tendrá un desbordamiento antes de esos 7 ms. Nos indica que el tiempo medio de reset es de 18 ms pero no lo garantiza. Es simplemente un tiempo que ha sido cons-tatado. Si usted resetea el watchdog cada 10 ms, esto podrá funcionar en algunos PIC y en ortos no, o bien podría funcionar o no dependiendo de la temperatura. Cada vez que una instrucción clrwdt es enviada al PIC, el timer del watchdog se pone a 0, así como el conteni-do del pre divisor. Si por cualquier causa, esta instrucción no es recibida en el tiempo previsto, se produce un reset y el PIC re arranca desde la dirección 0x00 y el bit TO del registro STATUS es puesto a 0. Leyendo este bit en el arranque, usted sabrá si el PIC viene de ser alimentado por primera vez o bien el reset se produjo por un bloqueo del programa y acción del watchdog.

15.2 El pre divisor del watchdog

Hemos visto en las lecciones precedentes, que el pre divisor puede afectar al tmr0 o al watchdog a través del bit PSA del registro OPTION. Si decidimos poner el pre divisor para el watchdog, bit PSA = 1, la tabla del da-tasheet nos dirá los valores del pre divisor según el estado de los bits PS0/PS2. En realidad, para el watchdog, se trata de pos divisor, pero esto solo tiene relevancia para la electrónica del PIC. No es relevante para su utilización. Este pos divisor multiplica el tiempo de desbordamiento del timer del watchdog. Por ejemplo, con un divisor de 2 tendrá un tiempo mínimo de 2*7 ms = 14 ms. Sabiendo que el reset se producirá, típicamente después de un tiempo de 2*18 ms = 36 ms. Para este ejemplo y con un cuarzo de 4 Mhz, esto le obligará a enviar un clrwdt al menos una vez cada 14.000 ciclos de instrucción. En la mayor parte de los casos, el reset se efectuará después de 18*3 = 36 ms, es decir, 36.000 ciclos de instrucción.

Page 158: Manual Instrucciones PIC16F84A

158

15.3 Los roles del watchdog El watchdog está destinado a verificar que su programa no se ha atascado en una zona no valida (debido a una perturbación de la alimentación, por ejemplo), o se ha atascado en un bucle sin fin (bug de programa-ción). Sirve también para despertar un PIC del modo sleep, como veremos más tarde. Observe que hablamos del modulo watchdog integrado en su PIC. De todas formas, todo micro controlador (y por lo tanto los PIC) pueden estar dotados de un watchdog externo (circuito que es reseteado a intervalos re-gulares por medio de un pin) que puede presentar algunas ventajas particulares, tal como poder tratar direc-tamente con la electrónica externa (corte de la alimentación general, por ejemplo). Voy a listar a continuación algunas nociones ligadas al watchdog tanto interno como externo:

1. El watchdog es un mecanismo interno o externo destinado a asegurar la seguridad de una aplicación in situ. Se activa si no es solicitado a intervalos regulares. Se asegura que le logicial embarcado parece continuar funcionando según una secuencia prevista.

2. El watchdog puede servir, según los casos, para re arrancar una aplicación después del comienzo, pa-ra pararla, para ponerla en un estado securizado, para señalar un defecto o para cualquier otra acción que quiera el diseñador del programa.

3. El watchdog integrado en el PIC es tributario de su buen funcionamiento electrónico. Su rol es provo-car un reset del PIC y es responsabilidad del diseñador del programa el tomar las eventuales medidas necesarias si el reset es debido al watchdog (parada del programa, señalización, re arranque normal o limitado, etc.). Hay bits específicos previstos para saber a qué se ha debido el re arranque del pro-grama.

4. El watchdog puede ser externo, lo que hará que la carta electrónica sea más compleja, pero tendrá la ventaja de poder incluso tratar con problemas del propio PIC y sus comando, dejando de ser tributa-rio de la electrónica del PIC. Podrá así cortar la alimentación del sistema, posicionar motores, etc. Aunque el micro controlador esté en avería.

5. El watchdog integrado es una seguridad muy eficaz a la vista de su simplicidad y relativamente fiable para salir de situaciones no previstas que pongan el estado del PIC en una posición indeterminada. Debería ser usado de oficio en toda aplicación acabada.

6. El watchdog puede remediar situaciones anormales bien sean provocadas por un bug no detectado aún en la fase de “debbuging” o por un desarrollo anormal del programa provocado por una causa ex-terna (perturbaciones, problemas de alimentación), por un blocaje no previsto de un periférico (blo-caje de transmisión) o cualquier otra situación anormal.

7. El watchdog no es un método infalible y absoluto que nos permite salir de cualquier problema que pueda acontecer. Para las aplicaciones de riesgo crítico letal (ascensores, maquinas herramienta, etc.) deben preverse mecanismos redundantes de seguridad vía hardware (sin “inteligencia embarcada”). El tamaño del cinturón de seguridad aumenta la seguridad pero no le garantiza que saldrá indemne de cualquier accidente y no pone en cuestión la utilidad de las ambulancias.

8. El watchdog no está pensado para enmascarar los errores de diseño de los programas. Un programa correctamente concebido debe ser capaz de correr sin problemas sin activar el watchdog, que no de-berá ser puesto en marcha hasta el final del “debbuging”.

9. No se deben poner instrucciones clrwdt en el interior de las rutinas de interrupción so pena de en-mascarar problemas del programa principal.

10. En los PIC, el watchdog integrado nos permite salir del modo dormido del PIC a expensas de una cier-ta imprecisión.

11. Los PIC 16F, al contrario que otros PIC, están desprovistos de una instrucción reset. Una forma simple de provocar un reset software es entrar en un bucle sin fin desprovisto de instrucción clrwdt, que provocará un reset del PIC por desbordamiento del watchdog.

Page 159: Manual Instrucciones PIC16F84A

159

15.4 Utilización correcta del watchdog

La primera cosa a realizar si quiere aprovecharse de esta protección integrada, es la de parametrizar la confi-guración de la puesta en servicio del watchdog. La primera cosa a tener en cuenta es que: “si indica

_WDT_ON para un programa que no gestiona el watchdog, este re arrancará constantemente sin parar, y

no funcionará, pues no contendrá ninguna instrucción clrwdt”. Es un error bastante frecuente para aquellos que no dominan los bits de configuración del programador. Los bits de configuración indicados en el fichero son modificables por la mayoría de los logiciales de los progra-madores usados, los cuales son capaces de forzar un valor de configuración diferente del previsto por el usua-rio en el programa. También es frecuente que los programadores olviden incluir la directiva _CONFIG en sus programas, lo cual impide la configuración automática de los flags de configuración. Es una muy mala práctica puesto que el usuario final deberá conocer o adivinar cuál es el estado de los flags que deberá utilizar en el momento de quemar el PIC. A continuación, deberá colocar una o varias instrucciones clrwdt en su programa de tal forma que al menos una instrucción clrwdt sea recibida antes de que pase el tiempo de desbordamiento. Recuerde no tener en cuenta el tiempo de 18 ms sino el más desfavorable de 7 ms. Tomando este como tiempo más desfavorable se asegura que el programa trabajará en todas las condiciones.

15.5 ¿Qué es lo que NO es necesario hacer?

Acuérdese que una interrupción interrumpe el programa y lo manda a la dirección 0x04. Una vez acabada la interrupción, el programa es devuelto a la zona donde se encontraba incluso si esta se hallase fuera de la zona de trabajo. Debido a esto, si usted coloca una instrucción clrwdt en una rutina de interrupción, esta instrucción puede ser ejecutada aunque su programa se haya detenido. Esto es lo contrario de lo que andamos persiguiendo. Por lo tanto:

“Jamás use una instrucción clrwdt en una rutina de interrupción”.

Usted puede decirme: de acuerdo, pero ¿si una rutina de interrupción se demora demasiado tiempo? Mi res-puesta será: usted se olvidó de un consejo que le di, las rutinas de interrupción deben durar lo menos posible.

15.6 Medida del tiempo real del watchdog

El valor de 7 ms es el valor mínimo. El valor de 18 ms es el valor generalmente constatado. Pero ¿cuál es el va-lor real del watchdog de nuestro PIC? Vamos a intentar responder a esta pregunta. Atención, esta medida será meramente indicativa, no se fie de ella en la práctica, este valor puede variar con las condiciones de alimentación, temperatura, etc. De todas formas siempre es interesante saber si la teoría casa con la práctica. Haga un copiar/pegar del fichero «led_cli. asm». Renombre el fichero como «wdt.asm» y cree un nuevo pro-yecto. Por el momento, no toque la configuración. Modifique los valores del registro OPTION.

OPTIONVAL EQU B'10001111' ; Valor del registro OPT ION ; Resitencia pull-up OFF

; Preescaler Wdt = 128

Page 160: Manual Instrucciones PIC16F84A

160

Suprima la variable cmpt3 y modifique la rutina tempo para quitar el bucle exterior.

;************************************************** ******************* ; SUBRUTINA DE TEMPORIZACION * ;************************************************** ******************* ;-------------------------------------------------- ------------------- ; Esta subrutina introduce un retardo de 500.000 µs. ; No recibe ni retorna ningún parametro ;-------------------------------------------------- ------------------- tempo

clrf cmpt2 ; borra compteur2 boucle2

clrf cmpt1 ; borra compteur1 boucle1

nop ; pierde 1 cycle *256 *256 *2 decfsz cmpt1 , f ; decrementa compteur1 goto boucle1 ; si no 0, boucler decfsz cmpt2 , f ; si 0, decrementa compteur 2 goto boucle2 ; si cmpt2 no 0, volver a empezar bucle1 return ; retorno de la subrutina

Añadimos la línea Clrwdt ; Borrar el watchdog

Justo después de la etiqueta init para estar bien seguros de poner el watchdog a 0 (realmente inútil). Para acabar, modificamos el programa principal para que se encienda el LED después de 250 ms.

; ************************************************* ********* ; PROGRAMA PRINCIPAL ; ************************************************* ********* start

call tempo ; esperamos 250 ms bsf LED ; encender LED goto start ; bucle

loop goto loop end

Ensamble el programa, cárguelo en el PIC y móntelo en la placa de experimentación. ¿Qué pasa? Después de aproximadamente ¼ de segundo el LED se enciende, eso es todo. No ocurre nada más. Es lo que habíamos previsto. Modifiquemos ahora la línea _CONFIG para poner en servicio el watchdog.

_CONFIG _CP_OFF & WDT_ON & _PWRTE_ON & _HS_OSC

Ensamble de Nuevo el programa y cárguelo en el PIC. Alimente el montaje.

Page 161: Manual Instrucciones PIC16F84A

161

¿Qué pasa ahora? El LED parpadea. Observará que el tiempo que separa dos encendidos (o dos apagados) es de aproximadamente 2 segundos. Explicación Si ha seguido todo lo anterior posiblemente ya lo haya comprendido. Nuestro watchdog no ha sido puesto a 0 después del arranque del PIC. Por lo tanto, una vez el tiempo “duración de base * pre divisor” se provoca un desbordamiento del watchdog que provoca un reset del PIC que apaga el led y comienza el programa de nue-vo en un bucle sin fin. Para conocer la base de tiempo en nuestras condiciones actuales del PIC (temperatura, alimentación, etc.) deberemos dividir el tiempo entre dos encendidos (o apagados) por el pre divisor, es decir 128. Personalmente yo he cronometrado 2,2 segundos. Mi watchdog trabaja con un tiempo de base de 2200 ms / 128 = 17,2 ms. Que está en el entorno de los 18 ms típicos ya anunciados. Si yo hubiese utilizado el valor de 18 ms en mi programación, el PIC se habría plantado. Vuelvo a insistir en la utilización del peor valor, es decir 7 ms.

15.7 Simulacion del bloqueo de un programa Haga un copiar/pegar del fichero «led_cli. asm». Renombre el fichero como «secur.asm» y cree un nuevo proyecto. Cambie la línea de configuración.

OPTIONVAL EQU H’0F’ ; Valor del registro OPTION ; Resitencia pull-up ON ; Preescaler 128

Y el define dado que hemos modificado la placa de experimentación.

#DEFINE BOUTON PORTB,0 ; Pulsador

Modifique el programa principal.

; ************************************************* ********* ; PROGRAMA PRINCIPAL ; ************************************************* ********* start

bsf LED ; encender LED call tempo ; esperamos 0,5 s bcf LED ; apagar LED (LEDOFF) call tempo ; esperamos 0,5 s btfsc BOUTON ; test pulsador goto start ; bucle

plante goto plante ; el programa no está pensado ; para que llegue aquí ; producirá un bloqueo end

De esta manera, una pulsación enviará al programa a una zona que hemos creado y que simula un bloqueo del programa bajo la forma de un bucle sin fin.

Page 162: Manual Instrucciones PIC16F84A

162

Podría decirme que no se trata de un bloqueo, es nuestro pulsador quien envía el programa a la dirección in-correcta. Por supuesto, pero es muy difícil bloquear un PIC bajo demanda. Bastará con imaginar que el pulsa-dor no existe y que se trata de una perturbación eléctrica exterior que envía nuestro programa a una zona donde no debería llegar nunca. Compile el programa, cárguelo en el PIC y alimente el montaje. El LED parpadea. Mantenga pulsado el pulsador un instante, el LED deja de parpadear. El programa se encuentra en un bucle sin fin que simula una plantada de nuestro PIC.

15.7.1 Corrección utilizando el watchdog Modifiquemos nuestro programa. Lo primero activemos el watchdog por medio de la configuración, como ya hemos visto. Vamos a arreglarnos a continuación para poner el watchdog a 0 a intervalos regulares. He aquí nuestro programa principal modificado.

; ************************************************* ********* ; PROGRAMA PRINCIPAL ; ************************************************* ********* start

bsf LED ; encender LED clrwdt ; borrar watchdog call tempo ; esperamos 0,5 s bcf LED ; apagar LED (LEDOFF) clrwdt ; borrar watchdog call tempo ; esperamos 0,5 s btfsc BOUTON ; test pulsador goto start ; bucle

plante goto plante ; el programa no está pensado ; para que llegue aquí ; producirá un bloqueo end

Habíamos programado nuestro watchdog con un pre divisor de 128, lo que nos permite enviar un comando clrwdt cada 7*128 ms, es decir cada 896 ms. Como nuestra llamada a tempo demora 500 ms, deberemos enviar un clrwdt al comienzo o al final de la lla-mada a tempo para no sobrepasar los 896 ms. También podríamos haber enviado un solo clrwdt incluido en la rutina tempo. Compile el programa, cárguelo en el PIC y alimente el montaje. El LED parpadea. Pulse el pulsador un instante. El LED deja de parpadear por un momento, después comienza a operar con normalidad otra vez. El watchdog ha recuperado el plante de su programa

15.8 Elección del pre divisor del watchdog En general, hay que intentar calcular el pre divisor de forma que no tengamos que utilizar muchos clrwdt. Pero también hay que tener en cuenta el tiempo de reacción obtenido al aumentar el pre divisor. Si una recu-peración de bloqueo de 2 segundos es factible o necesitamos recuperar bloqueos de 18 ms va a condicionar el valor del pre divisor. Todo es una cuestión de compromiso.

Page 163: Manual Instrucciones PIC16F84A

163

No siempre tendrá opción de elegir. Si su pre divisor ya está ocupado por el tmr0, por ejemplo, no le quedará más remedio que enviar un clrwdt cada 7 ms. Es una muy mala idea, en ese caso, decidir trabajar sin watch-dog. ¿Conduciría usted una moto sin casco? En el caso citado, debería insertar un clrwdt en la rutina de temporización cada 7 ms, puesto que dura más que esto. Recuerde no utilizar esto en una rutina de interrupción, pues estas son contrarias al espíritu del watchdog (salvo en honrosas excepciones). Sin embargo puede utilizar clrwdt en cualquier otra rutina sin problemas.

15.9 Tiempos típico, mínimo y máximo. Hemos visto aparecer diferentes nociones de tiempo relacionadas con el watchdog. Me parece importante precisar entre los diferentes valores. Recapitulemos:

• Tiempo típico (18 ms) : es el tiempo que toma el watchdog, en general, para provocar el reset del programa en caso de bloqueo. Es el tiempo de reacción normal (o típico) del watchdog.

• Tiempo mínimo (7 ms) : es el tiempo máximo entre dos instrucciones clrwdt para evitar un bloqueo en cualquier circunstancia.

• Tiempo máximo (33 ms) : es el tiempo de reacción del watchdog en el caso más desfavorable en fun-ción del componente y de las condiciones de utilización. MICROCHIP le garantiza que el reset se pro-ducirá en tiempo máximo de 33 ms.

15.10 Conclusión

Ahora está en disposición de crear programas resistentes a bloqueos clásicos por poco que utilice el watchdog de forma juiciosa. En general, es preferible hacer el esfuerzo de utilizarlo, puesto que el trabajo extra es des-preciable en comparación con la seguridad de funcionamiento obtenida. ATENCION: el watchdog utilizado como protección en caso de una programación correcta y de un circuito bien concebido no deberá entrar jamás en funcionamiento. Se trata, en general, de que esta protección so-lo entrará en funcionamiento en casos muy esporádicos (parásitos violentos, tormentas, etc.). No debe ser-vir para enmascarar errores de programación o de concepción electrónica. Todo debería poder funcionar sin la ayuda del watchdog. Le aconsejo realizar los programas de la siguiente manera:

1. Escriba el programa colocando las instrucciones clrwdt como tenga previsto pero sin activar el watch-dog.

2. Queme el PIC y téngalo en funcionamiento el tiempo suficiente como para poder estar seguro de no tener errores.

3. Una vez el programa es fiable, active el watchdog reprogramando el PIC. De esta forma estará seguro que el watchdog no va a servir para enmascarar problemas de programación de su parte (relanzando el PIC después de un bloqueo en un bucle sin fin, por ejemplo).

Page 164: Manual Instrucciones PIC16F84A

164

16. El modo sleep

Vamos a estudiar en esta lección un modo de funcionamiento muy particular de los PIC que les permite po-nerse a dormir con la finalidad de reducir su consumo.

16.1 El principio de funcionamiento

El modo «sleep» o «power-down» es un modo particular de estado del PIC que usted puede activar con la ins-trucción sleep. Una vez en este modo, el PIC se lleva a la posición dormido y deja de ejecutar el programa. Desde la recepción de esta instrucción, la secuencia de hechos es la siguiente:

1. El watchdog es puesto a 0 exactamente como si se tratase de una instrucción clrwdt. 2. El bit TO del registro STATUS es puesto a 1. 3. El bit PD del registro STATUS es puesto a 0. 4. El oscilador se para y el PIC no ejecuta ninguna instrucción.

Una vez en este estado, el PIC está parado. El consumo se ve reducido al mínimo. Si tmr0 esta sincronizado con el reloj interno, se verá incapacitado para contar. Por el contrario, conviene recordar que el timer del watchdog posee su propia circuitería y reloj, por lo que seguirá contando normalmente como si nada hubiese pasado

16.2 La salida del modo sleep. El paso a modo sleep no tendría ningún interés si no pudiésemos salir de él. El 16F84 solo reacciona cuando está en este modo a los siguientes eventos, que llevaran al PIC al estado normal de trabajo:

• Aplicación de un nivel 0 sobre el pin MCLR. Esto provocará un reset del PIC. El PIC provocará un reset clásico a la dirección 0x00. El usuario podrá chequear los bits TO y PD para verificar que ocurrió (reset, watchdog o puesta en alimentación).

• Alcance del tiempo del watchdog. Advierta que para que este evento sea capaz de despertar el PIC, el watchdog deberá estar en servicio con los bits de configuración. En este caso en particular, el watch-dog no provoca un reset del PIC, se contentará simplemente con despertarlo. La instruccio0n siguien-te se ejecutará después de despertar.

• Aparición de una interrupción RB0/INT, RB o EEPROM. En este último caso, para que estas interrupciones puedan despertar el PIC, hace falta que los bits de puesta en servicio de la interrupción asociada haya sido posicionado. Por el contrario, el bit GIE no hace falta que esté puesto en servicio (pero puede estarlo) para generar el despertar del PIC. Puede, por ejemplo, decidir despertar el PIC al finalizar un ciclo de escritura de la eeprom. Para hacerlo, de-berá poner a 1 el bit EEIE de INTCON, lanzar el ciclo de escritura y seguir con la instrucción sleep. Una vez que el ciclo de escritura termine, el PIC despertará y podrá seguir con el programa. Es imposible despertar el PIC con una interrupción timer puesto que en este modo el timer está parado y no puede generar una interrupción. Tampoco puede contar los eventos exteriores puesto que estos se sincroni-zan con el reloj interno y este está parado.

16.3 Despertar con GIE fuera de servicio Si el PIC se despierta por una interrupción en tanto que el bit GIE de INTCON está a 0, el programa seguirá simplemente con la siguiente instrucción a sleep.

Page 165: Manual Instrucciones PIC16F84A

165

16.4 Despertar con GIE en servicio En el caso en que GIE esté a 1, un despertar debido a una interrupción motivará la siguiente secuencia de hechos:

1. La instrucción que sigue a sleep es ejecutada. 2. Se produce un salto a la dirección 0x04 como una interrupción ordinaria.

Si no quiere ejecutar la instrucción que sigue a sleep bastará que ponga en su lugar una instrucción NOP.

16.5 Puesta en dormido imposible Si el bit GIE está puesto a 0, y el bit de puesta en servicio y el flag de una interrupción están ambos a 1 en el momento de aparecer una instrucción sleep (por ejemplo INTE=INTF=1) la instrucción sleep es simplemente ignorada. Esto es lógico puesto que las condiciones para despertar ya están en el momento de mandarlo a dormir. Es su trabajo ponerlos eventualmente a 0. El bit PD le permitirá saber si su instrucción sleep ha sido ejecutada (PD=0). Si el bit GIE está posicionado a 1 el caso anterior no podrá ocurrir, dado que la interrupción ahora se gene-rará, interrupción que provocará el borrado del flag asociado, excepto si usted ha puesto la instrucción sleep en el interior de la rutina de interrupción. Observe igualmente, que en el caso de que la instrucción sleep no haya sido ejecutada, su watchdog no se pondrá a 0. Si debe hacerlo en este momento y no está seguro de la ejecución de la instrucción sleep, añada un clrwdt antes de esta instrucción.

16.6 Utilización de la instrucción sleep Vamos a hacer un pequeño ejercicio de poner a dormir el PIC. Haga un copiar/pegar del fichero «m16F84.asm» y renómbrelo como «sleep.asm». Cree un nuevo proyecto. Ponga el pre divisor del watchdog a 32. Esto nos dará un tiempo de desbordamiento típico de 18 ms * 32 = 576 ms.

OPTIONVAL EQU H’8D’ ; Valor del registro OPTION ; Preescaler Wdt = 32

Defina el LED sobre el pin RA2.

#DEFINE LED PORTA,2 ; LED

Ponga el LED como salida en la rutina de inicialización.

Bcf LED ; LED como salida

Page 166: Manual Instrucciones PIC16F84A

166

Escribamos el programa principal.

; ************************************************* ********* ; PROGRAMA PRINCIPAL ; ************************************************* ********* start

bsf LED ; encender LED sleep ; modo dormido bcf LED ; apagar LED sleep ; modo dormido goto start ; bucle

end

El funcionamiento es muy simple. Después de encender el LED el PIC es puesto a dormir. Una vez alcanzado el tiempo del watchdog el PIC se despierta y sigue trabajando. He aquí un programa de parpadeo de un LED con un consumo mínimo. Es más, esta es otra forma simple de medir el tiempo real de su watchdog. Nota El despertar del PIC no es instantáneo. Si usted utiliza un cuarzo, el PIC esperará 1024 ciclos de reloj antes de seguir con el programa. Hay que tenerlo en cuenta. Este tiempo muerto es necesario para que el oscilador de cuarzo adquiera cierta estabilidad. Se consciente de que el despertar del PIC necesitará de este tiempo.

16.7 Caso típico de aplicación Este modo de funcionamiento es principalmente utilizado en los casos en los que el consumo de energía es crítico (uso de baterías). En este caso pondremos el PIC en modo sleep en todo momento que podamos. Otro caso típico es en el que el PIC no tiene nada que hacer excepto esperar que ocurra un evento exterior. En este caso, mejor que utilizar un bucle sin fin en espera de la interrupción, podremos poner a dormir el PIC a la espera de que el hecho ocurra.

16.7.1 Para un consumo mínimo El modo sleep asegura una puesta a dormir del PIC. Su rol principal es limitar el consumo de energía. Para hacer esto no bastará con pasar el PIC a modo sleep, hará falta poner a dormir también toda la periferia. El paso a modo sleep no modifica el estado de los pines configurados como salida. ATENCION: el paso a modo sleep no modifica la configuración ni estado de los pines de salida. Si tiene consu-midores de energía conectados a los pines (LED por ejemplo) seguirán consumiendo corriente (el LED conti-nuará encendido). Para hacer que el paso a modo sleep sea realmente eficiente, antes de realizarlo deberá asegurarse que todas las salidas sean llevadas a un estado que el consumo sea realmente mínimo. Tampoco hay que olvidarse del consumo de las resistencias pull-up en los pines de entrada del PORTB, si las ha puesto en servicio. Toda entrada referida a masa consumirá corriente. Un eventual reloj externo deberá ser parado. Para otros PICs hay más medidas a tomar, como el parar los convertidores analógico/digitales.

Page 167: Manual Instrucciones PIC16F84A

167

Todas las entradas no utilizadas así como T0CKI serán preferiblemente forzadas a masa, o a Vdd, puesto que si las deja flotantes un eventual cambio de estado provocará un pequeño consumo. Observe que llega al mismo resultado configurándolas como salida. Note que en este punto contrariamente a ciertas informaciones desenfocadas encontradas en la red: Un pin al aire no provoca ningún riesgo de bloquear el PIC: sus entradas están protegidas por diodos y, es más, se trata de un dispositivo programable no de una puerta lógica MOS donde sus salidas pueden variar en función de entradas no utilizadas. En su programa usted no utiliza, en principio, ninguna entrada que no esté conectada a algo, luego su transición no tiene ninguna repercusión excepto en caso de errores de programa-ción. El riesgo de conmutación es débil en un entorno clásico, puesto que las entradas no basculan más que para tensiones del orden de Vdd/2 (2,5 V clásicamente) y esto hace que, dada la impedancia de las entradas, tener una cierta inmunidad. La única consecuencia de una entrada al aire en un PIC son los pocos pA de consumo debido a una transición inesperada. Habrá comprendido que hace falta considerar el problema del consumo en su conjunto y dedicarse a colocar instrucciones sleep “sin ton ni son” a lo largo de todo el programa. Al menos si la limitación de consumo for-ma parte de su cuaderno de cargas.

16.8 Conclusión Ahora ya es capaz de poner el PIC en modo sueño con el fin de economizar energía. No dudo que encontrará aplicaciones para este modo de trabajo simple y practico.

Page 168: Manual Instrucciones PIC16F84A

168

17. El resto del datasheet

Por fin hemos llegado al final del estudio del 16F84. En este capítulo vamos a recorrer el datasheet para ver todo lo que aún no hemos visto hasta ahora. Es una especie de revoltijo el que le propongo. De todas formas no entraré en los detalles técnicos que solo podrían interesarle a los electrónicos. Esta gente está perfectamente dotada para entender algo como la figu-ra 3-1 del datasheet. Para los demás, esto no aportaría absolutamente nada para la utilización de los PIC. Esta lección le enseñará cómo entender el datasheet. Cómo los datasheet se ponen al día constantemente por parte de MICROCHIP, lo cual es una muy buena cosa, les adjunto a la lección el que me ha servido a mí para desarrollar este curso, y que se llama “16F84.pdf”.

17.1 La estructura interna

Figura 3-1. Aquí podemos ver como está construido el 16F84. Como le decía anteriormente, esta información tiene un limitado interés de cara a la utilización práctica del micro controlador. A remarcar la anchura de los buses internos que nos recuerdan las limitaciones de los modos de direcciona-miento. Vea, por ejemplo que el PC (Program Counter) solo tiene una anchura de 13 bits. El corazón del 16F84, como en todo procesador, es la ALU. Es en esta Unidad Aritmético Lógica donde se hacen toso los cálculos. Observe la unión privilegiada entre el registro W y la ALU.

17.2 La secuencia de decodificación Figura 3-2. Vemos claramente la división de un ciclo de instrucción en función de los 4 ciclos del oscilador ne-cesarios. Están detalladas cada uno de los 4 ciclos necesarios para una instrucción. Vemos el ejemplo 3-1 que muestra la ejecución de un paso de programa. Observe que, mientras se ejecuta una instrucción, la siguiente ya está cargada (“fetch”). Esto explica por qué durante un salto, la siguiente instrucción cargada no es la que deberá ser ejecutada. De aquí la necesidad de 2 ciclos de reloj suplementarios para cargar la instrucción correcta.

17.3 Organización de la memoria En la figura 4-1 vemos la organización de la memoria del PIC. Observe que la pila y el PC están situados fuera del espacio de direccionamiento, por lo que no son accesibles por el programa.

17.4 Los registros especiales La tabla 4-1 nos da una visión global de los registros especiales. La primera columna nos da la dirección del registro, la segunda el nombre simbólico del registro, seguidamente el nombre de cada bit. La penúltima columna nos da el valor de cada bit después de una puesta en tensión, mientras que la última nos da lo mismo para otros tipos de reset (watchdog y MCLR). Los bits anotados con 0 y 1 son aquellos en los que el nivel es el indicado. Los bits anotados con “u” son los bits no afectados (unchanged=sin cambio). Los bits anotados con “x” son los bits cuyo estado no es conocido en este momento.

Page 169: Manual Instrucciones PIC16F84A

169

17.5 La electrónica de los puertos Las figuras de 5-1 a 5-4 permiten a los electrónicos comprender las especifidades de los puertos IO a nivel de sus características eléctricas. Verá, como ya le indiqué, que el pin RA4, por ejemplo, es una salida de “colector abierto”, o más precisamente, de “drenador abierto”, configuración que no permite imponer un nivel alto a la salida.

17.6 El registro de configuración El registro de configuración está situado en la dirección 0x2007, fuera del espacio de direccionamiento normal del PIC. Solo es accesible en el momento de la programación del dispositivo. Accederá al registro con ayuda de la directiva “_CONFIG”, o con la ayuda de la directiva “DA” precedido de la directiva “ORG 0x2007”. El dato que indica a continuación de la directiva DA precisará el nivel de 14 bits útiles para el 16F84 (5 para el 16C84). La posición y la explicación de esos 14 bits están indicadas en la figura 8-1. No olvide que se trata de una pa-labra de 14 bits (como la instrucción). A título de ejemplo, la línea siguiente:

_CONFIG _CP_OFF & _WDT_ON & _PWRTE_ON & _HS_OSC Corresponderá para el 16F84 a:

ORG 0x2007 DA B’111111111111110’

Le desaconsejo este tipo de práctica. Más abajo verá por qué.

17.7 Los diferentes tipos de oscilador La figura 8-3 nos muestra la configuración de reloj que hemos utilizado para nuestro PIC. Se trata de cuarzo externo. La resistencia RS es inútil para un cuarzo clásico en un entorno estándar. Si utiliza un resonador cerámico con condensadores integrados en lugar de un cuarzo, podrá suprimir los condensadores C1 y C2. Pa-ra una aplicación robusta y comercial le aconsejo que lea “La Biblia del oscilador de PIC” de Michel Stokowski, cargable desde mi pagina web en el curso parte 2. La tabla 8-1 muestra los diferentes valores en función de la frecuencia así como los modos correspondientes seleccionados en los bits de configuración FOSC1 y FOSC0 si usted utiliza un resonador cerámico. La tabla 8-2 hace lo mismo para osciladores a cuarzo. Observe que los bits que se citan están integrados en _HS_OSC y otras configuraciones del oscilador. Esto explica por qué no los ha encontrado directamente hasta ahora. La figura 8-4 indica cómo utilizar un oscilador externo en lugar de un oscilador interno. Recuerde que en este caso no debe jamás parametrizar su oscilador como RC so pena de destruir su PIC. Las figuras 8-5 y 8-6 le muestra cómo construir un oscilador externo. Le aconsejo, a título personal, utilizar en este caso el integrado 74HCU04 que funciona correctamente para frecuencias iguales o superiores a 4 MHz Evite los modelos de tipo LS (74LS04) so pena de tener problemas. En mi caso, yo utilizo un montaje derivado del montaje paralelo indicado. Mi montaje, para 4 MHz, utiliza una R de 3,3 Mohms en lugar de 47 Kohms, reemplazo la resistencia ajustable por una fija de 2 Kohms y suprimo la R ajustable del extremo izquierdo del esquema. Reemplazo los 2 condensadores por otros de 27 pF.

Page 170: Manual Instrucciones PIC16F84A

170

Dejo el modo oscilador como Resistencia Condensador. En este modo, la oscilación está asegurada por dos componentes pasivos, sin necesidad de cuarzo (por razones económicas). Utilice este modo solo en el caso en el que sus constantes de tiempo no son críticas. Conecte según la figura 8-7 y utilice una resistencia del orden de 47 Kohms y un condensador del orden de 27 pF. No olvide que la frecuencia de ejecución de una instrucción valdrá una cuarta parte de la frecuencia del reloj. Utilice la siguiente fórmula para calcular el tiempo de ciclo de su programa. Sea Tcy la duración de un ciclo de instrucción Fosc la frecuencia del oscilador montado:

Tcy = 4 / Fosc Cy es la abreviatura de “Ciclo (de instrucción)” por lo que Tcy = periodo del ciclo y Fcy = Frecuencia del ciclo. Osc es la abreviatura de “Oscilador” por lo que Tosc = periodo del oscilador y Fosc = frecuencia del oscilador. Por ejemplo, para un cuarzo de 4 MHz:

T = 4 / 4000000 = 1 micro segundo Y recíprocamente: Fosc = 4 / Tcy para calcular la frecuencia que corresponde a un tiempo de ciclo dado. Si quiero un tiempo de ciclo de 1,5 µs nos hará falta un reloj a una cadencia de:

Fosc = 4 / (1,5 * 10-6) = 2,667 * 106 = 2,667 MHz

17.7.1 La precisión del oscilador No olvide en sus cálculos tener en cuenta el error que siempre existe. La tolerancia de su reloj es siempre tri-butaria del método escogido. Un reloj de cuarzo tendrá siempre mejores prestaciones que un reloj RC. A título de ejemplo, suponga que quiere construir un reloj con una tarjeta basada en PIC. Vamos a calcular el orden de magnitud de los errores obtenidos en función del tipo de oscilador utilizado. Supongamos que el logicial está desarrollado correctamente y que el error de medida del tiempo a este nivel es nulo. Comencemos por la red RC. Si elige este modo para el reloj ciertamente que habrá hecho una mala elección. La frecuencia del oscilador varía enormemente en función de la temperatura, de la precisión de los componentes, que varia con el tiem-po, y además es diferente de un componente a otro. No nos asombremos de tener un error de horas por día. Rechazable. Si decidimos usar un resonador cerámico, la tabla 12-1 nos da la precisión obtenida en función de diferentes marcas y modelos chequeados por MICROCHIP. Estos valores nos dan una precisión del orden de 0,5 %. Sabiendo que un día tiene 24 horas y que cada hora tiene 3600 segundos, sabemos que un día completo tendrá 24 x 3600 = 86400 s. Un error del 0,5 % nos dará un error estimado de 86400 x 5 x 10-3 s. Nuestro reloj tendrá una posible deriva de 6 minutos por día.

Page 171: Manual Instrucciones PIC16F84A

171

Pasemos al cuarzo. Según la tabla tendremos un error típico de 50 ppm (partes por millón), es decir 0,005 %. Calculemos la deriva de nuestro reloj:

86400 x 50 x 10-6 = 4,32 s Este es un resultado mucho más aceptable. Nuevamente, lea la documentación precisa si su objetivo es tener una precisión máxima como es el caso. Este apartado ha tenido como objetivo hacerle sentir, a través de un ejemplo concreto, el orden de magnitud de las precisiones que podemos tener con métodos corrientes. Observe que podemos tener aún precisiones más grandes utilizando un reloj externo específicamente conce-bido para estas aplicaciones o un contador de ciclos de la red eléctrica. Este último método es muy preciso dado que las sociedades de producción de energía eléctrica tienen la obli-gación de suministrar un determinado número de ciclos cada 24 horas y para ello hacen una vigilancia per-manente de la frecuencia de la red.

17.8 El reset En la figura 8-8 tiene el esquema eléctrico del reset interno del PIC. No se inquiete, todo el trabajo ya está hecho por usted. Simplemente recuerde que: para hacer funcionar normalmente un PIC, conecte a +5 V el pin MCLR. La puesta a masa de este pin provoca un reset del PIC. Si quiere tener un botón de reset en su montaje, conecte el pin MCLR a +5 V a través de una resistencia de 10 Kohms. Conecte el pulsador entre el pin MCLR y la masa como en el siguiente esquema:

Este esquema carece de los condensadores necesarios para una buena conformidad electromagnética. Para hacerlo, coloque un condensador de 100 nF entre Vdd y Vss lo más cerca posible de Vdd. Haga lo mismo con MCLR y Vss. La tabla 8-3 le muestra todos los eventos relacionados al reset y sus efectos sobre el PC (donde se ramificará el programa) y el registro STATUS.

Page 172: Manual Instrucciones PIC16F84A

172

La tabla 8-4 es muy interesante pues muestra el contenido de cada registro después de un reset y después de un despertar. Recuerde que el valor “x” significa estado desconocido, el valor “u” significa sin cambios y “q” significa que el estado depende de la causa que lo originó.

17.9 La puesta en tensión En el momento en que se conecta la alimentación al PIC un circuito interno analiza la tensión de alimentación. Se genera un reset automático en el momento en que esta tensión alcanza valores en el entorno de 1,2 a 1,7 V. Este es el reset de puesta en tensión (Power On Reset). Este circuito no provoca un reset si la tensión baja. Es su misión gestionar este hecho si le causase problemas. Por el contrario, otros PIC como 16F876 sí lo gestionan. Hablaremos de ello en el curso parte 2. Este reset dispara un timer interno independiente de la velocidad del PIC. Este timer mantiene el PIC parado durante un tiempo típico de 72 ms desde la detección de la condición de reset. Este timer se llama “Power-up Timer” o PWRT. Se le puede poner fuera de servicio por medio del bit PWRTE. Le aconsejo tenerlo siempre en servicio excepto en los casos en los que el tiempo de arranque sea critico para su sistema. A continuación, después de pasado el tiempo precedente, tenemos un retardo suplementario bajo la forma de una espera de 1024 oscilaciones del reloj (Oscillator start-up Timer). Esto permitirá asegurar el funcionamiento estable de este oscilador. Este OST no se utiliza cuando el reloj es del tipo RC, y está en servicio para las puestas en tensión y despertares (no para las otras formas de reset donde se supone que el reloj ya es estable). La figura 8-10 nos muestra el cronograma típico de una puesta en tensión. Lo explico rápidamente. Sepa que el desplazamiento horizontal de izquierda a derecha representa el tiempo. La línea 1 muestra la llegada de la alimentación (Vdd). Una vez que esta alimentación llega a 1,2/1,7 V, el pro-cedimiento de puesta en tensión es enganchado. La segunda línea nos muestra que el arranque del proceso no depende del nivel de la línea MCLR. En este ejemplo, esta línea pasa al estado alto un poco más tarde. Si la hubiese conectado a +5 V, pasaría a 1 al mis-mo tiempo que la línea Vdd sin ninguna influencia. A continuación el reset interno (POR) se valida desde el momento en que la alimentación pasa del punto pre-cisado. Se alcanza después el tiempo de 72 ms del PWRT si está puesto en servicio. Al final de este tiempo, el OST arranca sus 1024 ciclos de reloj para los modos concernidos. El procedimiento está ahora acabado. Si en este momento MCLR está a 1, el PIC arranca directamente (tabla 8-10). Si MCLR está a 0, el PIC arrancará instantáneamente cuando pase a valer 1 (tabla 8-11). La tabla 8-13 nos muestra que pasaría si la alimentación sube muy lentamente mientras MCLR está conectado a la alimentación. En este caso, el reset interno acabaría antes de alcanzar la tensión de alimentación normal, lo que no es acon-sejable. En este caso utilice el esquema de la figura 8-9 para hacer más lenta la subida en tensión de MCLR y alargar el tiempo de reset. Las figuras 8-14 y 8-15 nos indican los métodos de protección a utilizar en caso de una bajada de tensión sin parada completa.

Page 173: Manual Instrucciones PIC16F84A

173

17.10 Caracteristicas eléctricas A partir del capítulo 11 encontrará todas las especificaciones eléctricas del componente. Esto es interesante para los electrónicos que desarrollan montajes específicos. Para una utilización normal, las explicaciones da-das son más que suficientes. Para aplicaciones específicas le hará falta estudiar estas tablas más en detalle así como el datasheet general de los PIC de rango medio. Considero inútil detallar más en profundidad estos te-mas.

17.11 Portabilidad de los programas He aquí un punto delicado. ¿Qué ocurrirá con su programa si cambia de modelo de PIC? Veamos que debe hacer para asegurar la portabilidad de sus programas. De todas formas, la portabilidad a este nivel es ilusoria, esperamos sin embargo tener que cambiar lo menos posible.

• Deberá utilizar imperativamente las directivas previstas _CONFIG en detrimento de los accesos direc-tos del estilo ORG 0x2007. Estos emplazamientos y sus contenidos son susceptibles de cambio de un modelo a otro. Desgraciadamente, incluso a nivel de configuraciones, las opciones son diferentes de un modelo a otro.

• Deberá utilizar los ficheros .inc de MICROCHIP correspondiente al PIC sobre el que quiere correr el programa. Por ejemplo “P16F84.inc”. Esto le asegurará tener siempre las direcciones correctas para los registros correctos. Los registros que cambian de banco de un PIC a otro son muy raros, pero ve-rifíquelo.

• Deberá leer los nuevos datasheet para analizar las diferencias entre los componentes inicial y nuevo. Según necesidad, modifique las fuentes. En ciertos casos, MICROCHIP proporciona explícitamente do-cumentos de migración, marcando los puntos a modificar (por ejemplo migración del 16F876 al 16F876A).

• Ensamble a continuación el programa después de haber cambiado la línea include por aquella conte-niendo el nuevo fichero include, la directiva declarando el nuevo PIC y haber indicado el buen objeti-vo del programa.

Observe que su programa será tanto más fácilmente portable si siguió las consignas aportadas en este curso y si ha utilizado al máximo las declaraciones, defines y macros, y si ha comentado el programa. Igualmente, si ha comprendido lo que precede, un fichero .hex concebido para un componente no puede ser utilizado de ninguna forma para otro componente distinto, salvo en casos extremadamente raros. No espere poder colocar su fichero para un 16F84 en un 16F876 y que funcione. En la segunda parte del curso le explicaré como realizar esta migración. Por el contrario, para un 16F84A esto no conlleva ningún problema.

17.12 La puesta al día de los componentes En el apéndice E encontrara una tabla con las diferencias entre un 16C84 y un 16F84. Vamos a echarle un vis-tazo. La primera línea muestra que PWRTE a cambiado de nivel. En efecto, PWRTE a 1 pone en servicio el timer so-bre el reset de 72 ms para el 16C84. Para el 16F84 es el nivel a 0. Si ha utilizado la directiva _CONFIG será suficiente con re ensamblar su programa con el fichero P16F84.inc que integrará automáticamente la modificación. Por el contrario si optó por utilizar una escritura directa de la dirección 0x2007, a pesar de las advertencias de la lecciones, tendrá que modificar el programa.

Page 174: Manual Instrucciones PIC16F84A

174

La capacidad de la RAM para el usuario ha pasado de 36 octetos a 68. Puede observar que MICROCHIP ha añadido un filtro al pin MCLR con el fin de evitar parásitos que puedan provocar un reset inesperado. Esto alarga la duración de la señal necesaria para provocar un reset. A continuación nos encontramos con una serie de advertencias sobre las características eléctricas del PIC. Es-tas advertencias le re envían a diferentes tablas de características. Inmediatamente después una corrección a nivel de funcionamiento del PORTA, cuando el PIC se utiliza con una frecuencia inferior a 500 KHz. MICROCHIP ha añadido un “trigger Schmitt” a la entrada RB0 cuando se utiliza como interrupción. Para los no electrónicos, simplemente esto limita la posibilidad de falsas interrupciones. La puesta a 0 de los bits 6 y 7 de EEADR no provoca más una disminución de la corriente consumida por el PIC. Es una corrección de un error. En el 16C84 el efecto de dejar estos pines a nivel alto provocaba un consumo superior de corriente por parte del PIC. El famoso CP (Code Protect) que en el 16C84 constaba solo de 1 bit pasa ahora a ser de 9 bits. Esto permite crear diferentes tipos de protección de código sobre otros PIC de la familia (protección de la eeprom, protec-ción de datos, etc.). Una vez más, si utilizó la directiva _CONFIG será suficiente con recompilar el programa cambiando el include por el que sea pertinente. Para los que no, deberán leer el datasheet para cada nueva revisión del componen-te y actuar en consecuencia. Finalmente la corrección del error interno cuando se ponía a 0 el bit GIE. De esta forma no se generará una in-terrupción en el bucle siguiente. El bucle loop que era necesario para verificar la puesta a 0 de GIE ya no es necesario en el 16F84.

17.13 Conclusión Ahora se encuentra en posesión de toda la información necesaria para desarrollar aplicaciones basadas en un micro controlador 16F84. De todas formas, deberá practicar mucho antes de llegar a ser un crack de la pro-gramación. Muchas veces se dirá a sí mismo: “esto no es posible. Debería funcionar. Mi 16F84 (o 16F84A) es defectuoso”. En el 99,99% de los casos se tratará de un error de programación (o de un error de concepción del hardware) que usted resolverá después de algunas horas de arduo trabajo. Hace ya mucho tiempo que se desarrolló el 16F84A. Si hubiese algún error en él, hace ya tiempo que estaría resuelto. No se rinda y reléase la documen-tación, incluso si cree que ya la conoce de carrerilla. Le he hecho cometer errores a propósito y los hemos corregido inmediatamente. He seguido ese camino con la finalidad de facilitarle la vida, mostrándole cosas que eran correctas en apariencia y explicándole el buen camino para obtener un resultado fiable. No le voy a dejar caerse todavía y le voy a explicar algunos trucos que resultan muy prácticos para los pro-gramas más corrientes. Aún así, ahora ya está en disposición de trabajar en solitario. Le he enseñado el diccionario de rimas, a su cargo escribir poesía.

Page 175: Manual Instrucciones PIC16F84A

175

18. Trucos de programación En este capítulo vamos a examinar métodos simples para salir airosos de situaciones clásicas.

18.1 Las comparaciones ¿Qué más simple que comparar dos números? Solo hace falta hacer una sustracción. Sea, por ejemplo, la comparación de mem1 y mem2:

movf mem1 , w ; cargar mem1 subwf mem2 , w ; sustraer mem2 – mem1

Ahora solo hay que mirar los bits C y Z del registro STATUS para saber el resultado: Si Z=1 las dos posiciones contienen el mismo valor. Si Z=0 y C=1, el resultado es positivo, luego mem2 es mayor que mem1. Si Z=0 y C=0, el resultado negativo, mem2 es menor que mem1 Si solo desea comparar la identidad de los dos valores, sin modificar C, puede así mismo utilizar la instrucción XOR.

movf mem1 , w ; cargar mem1 xorwf mem2 , w ; Si igual, todos los bits son 0 y Z se pone a 1

Las comparaciones múltiples sobre la igualdad a unas constantes son un caso interesante puesto que también hay sitio para utilizar la astucia. Admitamos un código que pregunta esto:

Si valor = 1 -> ejecución código 1 Si valor = 2 -> ejecución codigo 2 Etc.

Podemos economizar instrucciones usando “xolw” y haciendo calcular las diferencias a MPASM. Me explico:

movf variable,w ; carga variable xorlw 1 ; compara con 1 btfs STATUS,Z ; igual ? goto code1 ; Si, ejecutar codigo 1 xorlw 1^2 ; compara variable con valor 2 btfs STATUS,Z ; igual ? goto code2 ; Si, ejecutar codigo 2 xorlw 2^3 ; comparar con valor 3 …

El truco está en las dos líneas en negrita. Mejor que recargar la variable para efectuar un “xorlw con el valor 2, comparamos directamente el registro W con el valor 1^2 (1 xor 2). MPASM se va a encargar de calcular este

valor. Lo que va a ocurrir es que, mirando lo que ha ocurrido con el primer xorlw 1, habremos al final realizado la si-guiente operación: Variable xor 1 ( 1 xor 2) siendo “1 xor 2” una constante que será calculada por MPASM. Pero la operación xor es asociativa (podemos cambiar el emplazamiento del paréntesis) luego: ( Variable xor 1 xor 1 ) xor 2

Page 176: Manual Instrucciones PIC16F84A

176

Pero el resultado del primer paréntesis es la variable dado que dos inversiones seguidas equivalen a dejar las cosas como estaban. Toda esta operación se resume en “variable xor 2”. Moraleja, hemos comparado la variable con 1, después con 2, después con 3, etc. Sin haber tenido que recargar la variable a cada paso. Lo que hace falta recordar es que cuando trabajamos con constantes (comparaciones, operaciones matemáti-cas, etc.) existen a menudo formas de usar la astucia para simplificarse la vida.

18.2 Sustraer un valor de w

Supongamos que tenemos un valor en W y que deseamos sustraer 5 de ese valor. El primer reflejo es el si-guiente:

Sublw 5

Es un error clásico, puesto que en realidad ejecutamos (5-w) en lugar de (w-5). Nos hará falta efectuar el complemento a 2 de este valor para obtener el resultado correcto. Pero, tratándose nuevamente de una constante, dejemos que sea MPASM el que haga el trabajo por nosotros. Efectuemos:

Addlw -5

Evidentemente, añadir -5 equivaldrá a sustraer 5. Hágalo por escrito si no queda realmente convencido. Por el contrario, la gestión del bit C necesitará un poco más de gimnasia. Se lo dejo a usted hacer y reflexionar.

18.3 Las multiplicaciones

¿Cómo hacer una multiplicación? De la misma manera que las hacemos a mano. Vamos a crear una rutina que multiplique juntos dos números de 8 bits. El resultado necesitará 18 bits, luego 2 octetos. Para obtener el número máximo de dígitos del resultado de una multiplicación, basta con sumar el número de dígitos de los dos multiplicandos. Realicemos una multiplicación manual. Vamos a multiplicar 12 por 13. Vamos a trabajar con 4 bits multiplica-dos por 4 bits, con resultado sobre 8 bits. Esto es así para reducir nuestra explicación. Hagámoslo manual-mente:

¿Qué hemos hecho? Hemos multiplicado 12 por cada cifra de 13 comenzando por la derecha. Hemos decala-do un rango hacia la izquierda cada resultado intermedio antes de su suma final. La particularidad en binario es que solo hay dos cifras, 0 y 1. Por lo tanto, o multiplicamos por 0 (que nos da nada) o multiplicamos por 1 (que nos da la copia del numero).

Page 177: Manual Instrucciones PIC16F84A

177

Veamos, pues, que obtenemos en binario:

• Multiplicamos 1100 por 1. Obtenemos 1100 (D’12’)

• Multiplicamos 1100 por 0 y decalamos a la izquierda. Obtenemos 0000

• Multiplicamos 1100 por 1 y decalamos hacia la izquierda. Obtenemos 1100 completado por 00, es de-cir 110000 (D’48’).

• Multiplicamos 1100 por 1 y decalamos a izquierda. Obtenemos 1100 y completamos con 000, es decir 1100000 (D’96’).

• Sumamos todo obteniendo 10011100, es decir D’156’. Si realizamos este programa, necesitaremos 4 variables suplementarias para almacenar los resultados inter-medios. 8 en el caso de una multiplicación de 8 bits por 8 bits. Podemos imaginarnos pasar de los resultados intermedios procediendo a la suma a medida que estos se van produciendo. Obtendremos el algoritmo siguiente:

• Multiplicamos 1100 por 1. Almacenamos en el resultado final.

• Multiplicamos 1100 por 0. Decalamos una vez. Sumamos al resultado final.

• Multiplicamos 1100 por 1. Decalamos dos veces. Sumamos al resultado final.

• Multiplicamos 1100 por 1. Decalamos tres veces. Sumamos al resultado final. Intente escribir este programa. Esto resulta práctico para las multiplicaciones de 4 bits. Para las de 8 bits, de-berá realizar las sumas con números de 16 bits. Cada vez tendrá que decalar más el multiplicador lo que im-plicará, o bien modificarlo o bien guardarlo (lo que consumirá 1 octeto). Reflexionando un poco, se podría decir que mas que decalar el multiplicador a la izquierda, podríamos decalar el resultado a la derecha. Esto nos dará lo mismo, pero solo tendremos sumas de 8 bits, siempre en el peso fuerte del resultado. Veamos cómo funciona con nuestro ejemplo, para no hacerlo más largo:

• Multiplicamos 1100 por 1. Colocamos el resultado al fondo izquierdo del resultado. Obtenemos 11000000.

• Decalamos a la derecha. Obtenemos 01100000.

• Multiplicamos 1100 por 0. Sumamos al resultado. Obtenemos 01100000.

• Decalamos a la derecha. Obtenemos 00110000.

• Multiplicamos 1100 por 1. Sumamos el resultado: 11000000+00110000 = 11110000.

• Decalamos a la derecha. Obtenemos 01111000.

• Multiplicamos 1100 por 1. Sumamos el resultado: 11000000+01111000 = 100111000 (9 bits). El no-veno bit está en el carry.

• Decalamos a la derecha y obtenemos 10011100 = D’156’. Observe que sumamos siempre en el cuarteto de peso fuerte (centraje a la izquierda). En el caso de una mul-tiplicación de 8 bits por 8 bits. Se sumará en el octeto de peso fuerte. Algunas veces tendremos desborda-miento de la suma en el 9º bit. Recuerde que se encuentra en el carry. En el decalaje, el carry se llevará al re-sultado por lo que recuperaremos automáticamente el 9º bit, que pasará a ser el 8º después del decalaje a la derecha. Este procedimiento es muy simple de programar. Como prueba veamos el pseudocódigo de multiplicación de 8 bits por 8 bits.

Page 178: Manual Instrucciones PIC16F84A

178

• Borrar el resultado.

• Para cada uno de los 8 bits del multiplicador: o Si bit = 1 � añadir multiplicando al peso fuerte del resultado o Decalar 16 bits del resultado a la derecha. o Decalar multiplicador a la derecha.

• Bit siguiente. Si ponemos además el decalaje del multiplicador en la cabeza, antes de comprobar el bit, recuperaremos el bit a comprobar en el carry. El programa se convierte en:

• Borrar el resultado.

• Para cada uno de los 8 bits del multiplicador: o Decalar multiplicador a la derecha. o Si carry = 1 � añadir multiplicando al peso fuerte del resultado o Decalar 16 bits del resultado a la derecha.

• Bit siguiente. Observe que esto es muy simple. Vamos a poner este pseudocódigo en forma de programa. Vamos a utilizar la variable multi como multiplicador, multan como multiplicando, resultH como resultado peso fuerte, resultL como resultado peso débil, multemp es el multiplicador temporal que será modificado y cmpt el contador de bucles. El programa quedará como:

clrf resultH ; borrar resultado peso fuerte clrf resultL ; idem peso débil movlw 0x08 ; por 8 bits movwf cmpt ; inicializa contador de bucles movf multi , w ; carga el multiplicador movwf multemp ; salva en multemp movf multan , w ;multiplicando en w

loop rrf multemp , f ; decala multiplicador a la derech a btfsc STATUS , C ; test si bit que sale = 1 addwf result , f ; SI , añadir a peso fuerte rrf resultH , f ; decalar resultado peso fuerte rrf resultL , f ; decalar resultado peso débil decfsz cmpt , f ; decrementa el contador de bucles goto loop ; no ha acabado , bit siguiente

Puede crear un pequeño proyecto para probar este código en el simulador. Verá que funciona perfectamente. Puede hacer una subrutina o una macro a añadir su fichero include propio. Las dos últimas líneas “rrf” realizan un decalaje del resultado en 16 bits. El bit perdido al decalar resultH se re-cupera en el carry del decalaje de resultL y pasa a ser el bit7 (8º bit). Truco de programación: si la suma pre-cedente ha tenido lugar, el 9º bit resultante de la suma está en el carry y se convierte en el b7 de resultH. Por el contrario, si no ha habido suma, el carry será 0 a efectos del test. Puede ver el interés de haber chequeado el bit débil del multiplicador sirviéndose del carry. Esté seguro que, con la experiencia, usted llegará a este tipo de resultados. Este programa está disponible ba-jo la denominación “multi.asm”. Un truco a recomendar es ir a cargar las librerías matemáticas ya escritas en el sitio de Microchip, y particu-larmente, las librerías AN256 y AN617. Todo lo que allí hay está particularmente optimizado, incluso si ciertas optimizaciones están lejos de ser entendibles sin un amplio bagaje matemático.

Page 179: Manual Instrucciones PIC16F84A

179

18.4 Multiplicación por una constante En el caso precedente, hemos efectuado la multiplicación de 2 variables que pueden tomar cualquier valor. En el caso de utilizar una constante, es decir que conocemos el multiplicador a la hora de concebir el programa, deberemos, por razones de optimización, probar de razonar en multiplicaciones por 2. Para realizar una multiplicación por 2 solo necesitamos decalar hacia la izquierda. Es muy fácil de poner en práctica. No olvide poner C a 0 antes del decalaje para no meter un bit no deseado en la palabra decalada. Supongamos que debe efectuar multiplicaciones por 10 (decimal). Vamos a intentar resolverlo con multiplica-ciones por 2. Es muy simple. Para multiplicar por 10 hará falta: Multiplicar por 2 (decalar a la izquierda). Multiplicar por 2 (= multiplicar por 4, decalar otra vez). Añadir el operando original (operando + operando x 2 = operando x 5). Multiplicar por 2 (decalar a la izquierda). Una multiplicación por 10 muy rápida. Hacemos “ x2 , x2 , +1 , x2”. 3 decalajes y una suma. En general, nos podemos arreglar para obtener los resultados de las multiplicaciones por constantes usando este método. Esto le permitirá ganar un tiempo precioso en la programación. Podemos ver un pequeño programa que multiplica un número de 4 bits contenido en mem1 por 10. El resul-tado estará en resul. Puede adaptarlo para 8 bits fácilmente.

movf mem1 , w ; carga operando en 4 bits movwf resul ; salva en resultado bcf STATUS ,C ; borra el carry rlf resul , f ; multiplica por 2 rlf resul , f ; multiplica por 4 addwf resul , f ; añade mem1 , multiplica por 5 rlf resul , f ; multiplica por 10

18.5 Direccionamiento indirecto apuntando a 2 zonas diferentes

Imagine que tiene que copiar 15 variables de un emplazamiento de memoria a otro (o comparar 2 zonas de memoria diferentes, etc.). Rápidamente se va a encontrar con un problema. Solo dispone de un puntero FSR para apuntar sobre sus variables. Realicemos este programa: mem1 es la dirección de partida de la primera zona y mem2 es la dirección de par-tida de la 2 segunda zona.

movlw mem1 ; apuntamos a la primera zona movwf FSR ; inicializamos el puntero sobre la zon a fuente movlw 15 ; 15 variables a transferir movwf cmpt ;salvamos en contador de bucles

loop movf INDF , w ; cargamos fuente movwf tampon ; salvamos en un emplazamiento tampón movlw mem2-mem1 ; diferencia entre los dos emplaza mientos addwf FSR ; apuntamos al destino movf tampon , w ; cargamos el valor fuente movwf INDF ; salvamos en destino movlw (mem1-mem2)+1 ; diferencia entre destino y f uente siguiente addwf FSR , f ; añadimos al puntero decfsz cmpt , f ; decrementamos el contador de buc les goto loop

Page 180: Manual Instrucciones PIC16F84A

180

Este programa es válido aunque no haya escogido las zonas de partida y destino. Nadie nos impide poner las variables en las zonas que nos convengan. Pongamos mem1 y mem2 en la zona de variables.

CBLOCK 0x10 ; elegimos una zona de memoria en RAM mem1 : 15 ; 15 emplazamientos para la fuente ENDC CBLOCK 0x30 ; elegimos una zona de memoria en RAM mem2 : 15 ; 15 emplazamientos para el destino ENDC

O, mejor aún, aunque no vemos bien la organización de las zonas utilizadas. mem1 EQU 0x10 mem2 EQU 0x30

Ahora, la fuente va de B’00010000’ a B’00011111’, y el destino de B’00110000’ a B’00111111’. Solo cambia el bit 5 de FSR entre la fuente y el destino. Para modificar los bits, no necesitamos el acumulador por lo que no tenemos que recargar el valor a transferir. Podemos modificar nuestro programa de la siguiente manera:

movlw mem1 ; apuntamos a la primera zona movwf FSR ; inicializamos el puntero sobre la zon a fuente movlw 15 ; 15 variables a transferir movwf cmpt ;salvamos en contador de bucles

loop movf INDF , w ; cargamos fuente bsf FSR , 5 ; apuntamos al destino movwf INDF ; salvamos en destino bcf FSR , 5 ; apuntamos a la fuente incf fsr , f ; puntero en siguiente decfsz cmpt , f ; decrementamos el contador de buc les goto loop

Con los PIC que utilizan varios bancos como el 16F876, puede utilizar la misma dirección en 2 bancos diferen-tes, y pasar de uno a otro modificando el bit IRP del registro STATUS.

18.6 Las tablas en memoria de programa Supongamos que necesitamos una tabla de un tamaño importante. Por ejemplo una tabla de 200 elementos. ¿Dónde la colocamos? En la RAM no tenemos sitio. En la EEPROM tampoco. Solo nos queda la memoria de programa. El problema es que el 16F84, al contrario que el 16F876, por ejemplo, no dispone de ningún método para leer los datos de la memoria de la zona de programa. El único método de acceso a esta parte es utilizar instruccio-nes. Supongamos un caso simple: queremos una tabla que contenga el cuadrado de los números del 0 al 15. Podr-íamos utilizar un subprograma de la siguiente forma:

• Chequeamos si el numero pasado como argumento = 0. Si es sí, devolvemos 0.

• Chequeamos si el numero pasado como argumento = 1. Si es sí, devolvemos 1.

• Chequeamos si el numero pasado como argumento = 2. Si es sí, devolvemos 4.

• Etc.

Page 181: Manual Instrucciones PIC16F84A

181

Inmediatamente verá que necesitamos varias instrucciones por cada valor de la tabla. En el caso de una tabla de 200 elementos, no tendremos suficiente memoria de programa para escribirlo. Vamos a utilizar un truco. El corazón del truco consiste en utilizar la instrucción “retlw” que permite devolver un valor pasado como ar-gumento. Escribamos nuestra tabla bajo la forma de retlw. Quedará:

retlw 0 ; cuadrado de 0 = 0 retlw 1 ; cuadrado de 1 = 1 retlw 4 ; cuadrado de 2 = 4 retlw 9 ; cuadrado de 3 = 9 retlw 16 ; cuadrado de 4 = 16 retlw 25 ; cuadrado de 5 = 25 retlw 36 ; cuadrado de 6 = 36 retlw 49 ; cuadrado de 7 = 49 retlw 64 ; cuadrado de 8 = 64 retlw 81 ; cuadrado de 9 = 81 retlw 100 ; cuadrado de 10 = 100 retlw 121 ; cuadrado de 11 = 121 retlw 144 ; cuadrado de 12 = 144 retlw 169 ; cuadrado de 13 = 169 retlw 196 ; cuadrado de 14 = 196 retlw 225 ; cuadrado de 15 = 225

Solo nos queda encontrar un truco para referirnos a la línea correcta de esta tabla/programa. Si nos acorda-mos de que podemos efectuar operaciones sobre el PCL, podremos utilizar esta propiedad. Supongamos que el numero a elevar al cuadrado está contenido en el registro w. Si, una vez sobre la primera línea de la tabla, añadimos w al PCL, saltaremos directamente sobre la línea correcta. Completemos la subru-tina. Solo necesitaremos añadir delante de nuestra tabla la línea:

carre addwf PCL, f ; añadir w a PCL

Si cargamos 4 en w y efectuamos una llamada “call carre”, el programa se desviará sobre esta línea, el PCL se incrementará en 4 y el programa ejecuta la línea “retlw 16”. Recuerde, en efecto, que el PC apunta siempre sobre la instrucción siguiente, luego, en el momento de la eje-cución de “addwf PCL , f” apuntaba a la línea “retlw 0”. En este momento, debe recordar que la dirección para una operación sobre el registro PCL está completada por el contenido de PCLATH. Deberá, pues, inicializar este correctamente con el contenido de la “pagina de 8 bits” de la dirección de su tabla. Por ejemplo, si la tabla está en la dirección 0x200, deberá poner 0x02 en PCLATH. Además, la operación sobre PCL no modificará automáticamente PCLATH por lo que la tabla no deberá pasar de 256 elementos y no deberá desbordar de la pagina de 8 bits siguiente. Una página es el espacio direccio-nado por los 256 valores posibles de PCL, sin tocar PCLATH.

Page 182: Manual Instrucciones PIC16F84A

182

¿Cómo evitar esto? Imponiendo a la tabla una dirección de comienzo tal que la tabla está contenida en la misma “pagina” de 256 elementos. Esto nos permite utilizar una tabla de 256 elementos. Veamos los emplazamientos posibles para una tabla de ese tamaño: Direccion B’00000 00000000’ es decir 0x00. No utilizable por ser dirección del reset. Direccion B’00001 00000000’ es decir 0x100. Primer emplazamiento utilizable. Direccion B’00010 00000000’ es decir 0x200. Segundo emplazamiento utilizable. Direccion B’00011 00000000’ es decir 0x300. Ultimo emplazamiento utilizable. Si su necesidad es de una tabla más pequeña, podrá comenzar en otra parte, teniendo la precaución de no causar desbordamiento. Podrá añadir la línea: org 0x300 antes de la etiqueta de comienzo de la subrutina. Observe que explico aquí que esto es válido para las tablas en las que el final está en la misma página que el comienzo. Como la línea “addwf” está incluida en esa página, habrá que sustraer esta línea de los 256 empla-zamientos disponibles. Obtendremos, pues:

;************************************************** *** ; TABLA DE CUADRADOS ;************************************************** *** ;-------------------------------------------------- --- ; No olvide que la directiva END se debe encontrar , después de la última línea de su programa. No dej e ; esta directiva en el programa principal. ;-------------------------------------------------- --- Org 0x300 ; dirección de la tabla

carre addwf PCL, f ; añadir w a PCL retlw .0 ; cuadrado de 0 = 0 retlw .1 ; cuadrado de 1 = 1 retlw .4 ; cuadrado de 2 = 4 retlw .9 ; cuadrado de 3 = 9 retlw .16 ; cuadrado de 4 = 16 retlw .25 ; cuadrado de 5 = 25 retlw .36 ; cuadrado de 6 = 36 retlw .49 ; cuadrado de 7 = 49 retlw .64 ; cuadrado de 8 = 64 retlw .81 ; cuadrado de 9 = 81 retlw .100 ; cuadrado de 10 = 100 retlw .121 ; cuadrado de 11 = 121 retlw .144 ; cuadrado de 12 = 144 retlw .169 ; cuadrado de 13 = 169 retlw .196 ; cuadrado de 14 = 196 retlw .225 ; cuadrado de 15 = 225 END

Page 183: Manual Instrucciones PIC16F84A

183

Solo nos queda construir un pequeño programa que haga llamadas a esta subrutina.

;************************************************** ********** ; ARRANQUE SOBRE RESET ;************************************************** ********** org 0x000 ; dirección de arranque después de reset ;************************************************** ********** ; PROGRAMA PRINCIPAL ;************************************************** **********

start clrf numero ; borra el numero

loop movf numero , w ; carga el numero call carre ; toma el cuadrado del numero incf numero , f ; incrementa el numero btfss numero , 4 ; chequea si numero > 15 goto loop ; NO , numero siguiente goto start ; SI , comenzamos de nuevo

Compile el programa y páselo al simulador. Vera que no funciona. ¿Por qué? Toda operación en la que el PC es el destino hace intervenir al PCLATH. Hemos dejado el PCLATH a 0. La direc-ción calculada no es la correcta. Antes del salto deberíamos añadir:

movlw 03 movwf PCLATH

Con el fin de permitir a PCLATH apuntar sobre la página 0x300 después del cálculo de incremento de PCL. Podría reemplazar la línea org 0x300 por:

repere ORG (repere+.31) & 0x3E0 ; dirección de la tabla

El objetivo de esta manipulación es obtener automáticamente la primera dirección disponible para la que no habrá un desborde de nuestro PCL (para una tabla de 32 elementos salto incluido). Para una tabla de 256 elementos (salto incluido) utilice “ORG (repere+255) & 0x300”. Haga el cálculo a mano en un papel para con-vencerse. Hay otros métodos de hacerse, como utilizar una directiva de test (IF) para verificar que el final de una tabla está en la misma página que el comienzo. El fichero obtenido está bajo la denominación “tableau.asm”. Es importante entender que es el conjunto de instrucciones “retlw” el que se debe encontrar en la misma página. En el caso precedente (tabla pequeña), puede observar que hay una instrucción que se encuentra ya en esta página, lo que hace disponer de un emplazamiento menos. Podríamos haber modificado la tabla en consecuencia, de manera que le primer elemento de la tabla apunte al offset 0 de la pagina de 8 bits.

Org 0x2FF0 ; dirección de la tabla carre

addwf PCL, f ; añadir w a PCL retlw 8 ; primer elemento de la tabla : dirección 0x300 retlw 4 ; segundo elemento de la tabla : dirección 0x301

Page 184: Manual Instrucciones PIC16F84A

184

Siempre para el caso de una tabla de 256 elementos, vemos que debemos empezar imperativamente la tabla con un offset de 0 (PCL=0). Inútil hacer una suma de 0, será suficiente con poner directamente w en el PCL, sea cual sea la dirección de esta imputación, siempre y cuando que PCLATH esté correctamente configurado. Obtendremos, pues:

carre movwf PCL ; colocar w en PCL (dirección=continuaci ón ; programa principal org 0x300 ; dirección efectiva de la tabla

; (256 lugares disponibles) retlw 8 ; primer elemento de la tabla : dirección 0x300 retlw 4 ; segundo elemento de la tabla : dirección 0x301

18.7 Las variables locales Uno de los reproches más usuales al PIC16F84 es el número restringido de emplazamientos de memoria RAM. En cualquier caso, no debemos creer que, bajo el pretexto de que estamos programando en lenguaje ensam-blador, no dispongamos de variables locales. De hecho la utilización de este tipo de variables es muy simple y reduce considerablemente el número de va-riables necesarias en un programa. He aquí mi método personal de utilización de este tipo de variables. Como recordatorio, este tipo de variables son variables que se utilizan en el interior de una subrutina y que no sirven más una vez la rutina finalizada. Partamos de un programa imaginario que utiliza dos subrutinas.

1. La primera subrutina se llama “tempo”, utiliza 2 contadores para realizar un contaje de tiempo. 2. La segunda es la subrutina “fonction”. Recibe un parámetro en W, debe salvar el resultado en una va-

riable y utiliza otras 2 variables para sus cálculos intermedios. 3. La condición para el uso de variables locales idénticas es que una subrutina no llame a la otra.

18.7.1 Determinación de las variables locales

Si examinamos las subrutinas veremos que las 2 variables de la subrutina “tempo” solo son útiles en su inter-ior. Podemos, pues, utilizar variables locales. La variable resultante de la subrutina “fonction” deberá ser conservada después de la ejecución de la subruti-na por lo que no puede ser una variable local. Diremos que es una variable global. Por el contrario, las 2 va-riable utilizadas para los cálculos intermedios no tendrán ninguna utilizad posterior. Serán variables locales.

18.7.2 Construcción sin variables locales ¿Cómo sería nuestra zona de variables sin la utilización de variables locales? Creemos nuestra zona de varia-bles:

CBLOCK 0x0C Cmpt1 : 1 ; contador 1 para tempo Cmpt2 : 1 ; contador 2 para tempo Resultat : 1 ; resultado para fonction Interm1 : 1 ; resultado intermedio 1 para fonction Interm2 : 1 ; resultado intermedio 2 para fonction ENDC

Vemos que necesitaremos 5 variables. Ahora utilicemos variables locales.

Page 185: Manual Instrucciones PIC16F84A

185

18.7.3 Construcción con variables locales Primero vamos a reservar los emplazamientos de memoria necesarios para las variables locales. El número máximo de variables locales utilizadas por una función es de 2. Tendremos 2 variables locales. Declarémoslas. Después nos queda una variable global a añadir. Creemos nuestra zona de datos:

CBLOCK 0x0C Local1 : 1 ; variable local 1 Local2 : 1 ; variable local 2 Resultat : 1 ; resultado para fonction ENDC

Nos hará falta 3 variables en lugar de las 5 iniciales. Para estar más cómodos podemos atribuir los mismos nombres que anteriormente para nuestras variables locales añadiendo simplemente unos DEFINE o EQU.

#DEFINE Cmpt1 Local1 ; contador 1 = variable local 1 #DEFINE Cmpt2 Local2 ; contador 2 = variable local 2 #DEFINE Interm1 Local1 ; resultado intermedio 1 = v ariable local 1 #DEFINE Interm2 Local2 ; resultado intermedio 2 = v ariable local 2

Y ya está. Se acabó la necesidad de programar en C para tener variables locales.

18.8 División por una constante He aquí un truco que me fue enviado por un internauta, Richard L.: Para dividir un número por una constante es suficiente multiplicar ese número por una constante que valga “256 dividido por la constante”. Obteniendo un resultado sobre 16 bits, el resultado de la división se encuentra en el octeto de peso fuerte. Ejemplo: Para dividir un número por 10 bastará multiplicar por (256/10). En hexadecimal 156/10 da 0x19 o 0x1A (hace falta redondear). Imaginemos que nuestra variable contiene el valor decimal 120, es decir 0x78. Si multiplicamos 0x78 por 0x1A obtenemos 0x0C30. El peso fuerte es 0x0C, que es 12 en decimal. Si hubiésemos escogido 0x19 habríamos obtenido 11 como respuesta. Sirviéndose de más octetos (65536 dividido por la constante, por ejemplo) podemos mejorar la precisión.

18.9 Conclusión Ahora tiene a su disposición algunos trucos de programación. El objetivo era el mostrarle los métodos de ra-zonamiento con la finalidad de permitirle desarrollar sus propios trucos y astucias.

Page 186: Manual Instrucciones PIC16F84A

186

19. Utilización de rutinas en un fichero separado

19.1 Preguntas y punto de partida ¿Cómo podemos poner nuestras rutinas en ficheros separados? ¿Por qué obtengo mensajes de error del tipo “overwriting” cuando intento hacerlo? ¿Por qué MASM me da errores de enlace (link) al añadir ficheros su-plementarios? Todas estas preguntas me las encuentro a intervalos regulares en mi correo por lo que ya es hora de clarificar las diferentes maneras de proceder para colocar código en ficheros secundarios a añadir al fichero principal. La primera cosa a comprender es que no hace falta añadir la referencia al fichero concernido en la jerarquía de un proyecto, dado que esto llevará a MPASM a creer que debe leer multitud de ficheros en el momento del ensamblado, cosa que no corresponde a los ficheros fuente (esto se concibió para los ficheros objeto en los que no estamos muy interesados). Vamos a colocar nuestros subprogramas en un fichero separado. Por convención nombraremos el fichero con el sufijo “.inc”. Por ejemplo, imaginémonos “mesroutines.inc”. Existen dos formas de incluir contenido bajo la forma fuente en nuestro programa principal. Comencemos por la más evidente.

19.2 Utilización directa de rutinas en el fichero Creamos un fichero “mesroutines.inc” en el que ponemos “a lo bestia” nuestros sub programas bajo formato de código fuente. Por ejemplo, imaginemos que deseamos utilizar dos subrutinas: “ajoute16bits” y “sous-trait16bits”. Escribiremos en el código fuente simplemente el contenido de nuestras dos subrutinas:

ajoute16bits Instrucción 1 Instrucción 2 Instrucción 3 …… Instrucción n return

soustrait16bits instrucción 1 instrucción 2 instrucción 3 …… instrucción n return

Y así para cada subrutina que queramos incluir. Observe que este fichero no contendrá ni directiva ORG ni de-claración de variables tipo CBLOCK. Un fichero a incluir no es un fichero autónomo, e inversamente, no incluimos un fichero autónomo del tipo asm en otro fichero asm, pues habría una imposibilidad de ensamblaje debido a la presencia de multitud de datos contradictorios en las mismas direcciones. Nos hace falta ahora incluir este código en nuestro fichero fuente principal (nuestro fichero “asm”). Esto se hace insertando en la fuente la línea:

#include <mesroutines.inc>

Page 187: Manual Instrucciones PIC16F84A

187

MPASM reemplazará nuestra línea #include por todo el contenido del fichero “.inc”. Es una simple operación de copia/pega hecha automáticamente por MPASM, no hay ninguna inteligencia dentro. Atención a la trampa: deberá insertar la línea “#include” no en el comienzo del fichero sino en la posición exacta donde deberá ser copiada/pegada la información. Esto por ejemplo, nos daría error:

#include <mesroutines.inc> ; el código se inserta en 0x00 ORG 0x00 ; lo que sigue ocupará la posición 0x0 0 Instrucción 1 ; instrucción en 0x00 posición ya ocupada Instrucción 2 ; instrucción en 0x01 posición ya ocupada ……

¿Por qué? Simplemente porque el contenido de “mesroutines.inc” va a ser copiado delante de la directiva ORG, y por defecto a partir de la dirección 0x00 (si no precisamos nada, este es el caso), por lo que en la di-rección 0x00 tendremos la primera instrucción de “ajoute16bits” y la instrucción 1 del programa principal. MPASM no puede, evidentemente, colocar dos instrucciones en el mismo sitio por lo que dará un error de sobre escritura (overwriting) para las direcciones 0x00 y siguientes. Me podría decir: si, pero yo he escrito #include <p16F84.inc> delante de la directiva ORG 0x00 y no he tenido ningún error. Es del todo lógico puesto que su fichero “p16F84.inc” solo contiene definiciones, ninguna ins-trucción. Este fichero no añade ningún código, por lo que no escribe nada en la dirección 0x00. Hará falta colocar la directiva include en el lugar correcto como por ejemplo:

ORG 0x00 ; el código se inserta en 0x00 Goto main ; instrucción en 0x00 , correcto

ORG 0x04 ; vector de interrupción

Interrupciones ; tratamiento de las interrupciones Main ; arranque real del programa después del sa lto

Instrucción 1 Call ajoute16bits ; llamada a la subrutina , ejemp lo

…… #include <mesroutines.inc> ; el código se inserta a quí Continuación del programa ; continuación eventual d el programa

Page 188: Manual Instrucciones PIC16F84A

188

De esta forma, sus rutinas se encontrarán después del programa principal y no hay ninguna imposibilidad pa-ra MPASM de escribir el código. MPASM va simplemente a proceder a un copia/pega implícito y ensamblará el siguiente código:

ORG 0x00 ; el código se inserta en 0x00 Goto main ; instrucción en 0x00 , correcto

ORG 0x04 ; vector de interrupción

Interrupciones ; tratamiento de las interrupciones main ; arranque real del programa después del sa lto

Instrucción 1 Call ajoute16bits ; llamada a la subrutina , ejemp lo

……

ajoute16bits instrucción 1 instrucción 2 instrucción 3 …… instrucción n return

soustrait16bits instrucción 1 instrucción 2 instrucción 3 …… instrucción n return

Continuación del programa ; continuación eventual d el programa

Queda el problema de las variables. Si declaramos un bloque “CBLOCK” en nuestro fichero de rutinas, vamos de nuevo a encontrarnos con una imposibilidad: dos bloques relativos a la misma dirección con contenidos di-ferentes. Tenemos dos formas de proceder:

• Declaramos dos bloques diferentes. Uno en el programa principal (CBLOCK 0x00) y otro en el fichero de rutinas (CBLOCK x). Esto presenta graves inconvenientes, puesto que podemos perder espacio en el programa principal, impedir la utilización de variables locales incluidas en las rutinas o impedir in-cluir dos ficheros diferentes de rutinas. Yo le desaconsejo este método.

• Utilizar variables no declaradas en las rutinas. Después del ensamblaje, MPASM le indicará cuales son las variables que faltan y usted las podrá declarar en el CBLOCK del programa principal. Es el método que yo le aconsejo si adopta la forma de proceder que estamos describiendo (verá que las hay mejo-res).

Brevemente, sus variables serán utilizadas en sus rutinas pero declaradas en la zona CBLOCK del programa principal. El fichero de rutinas contendrá líneas del tipo:

movf variable1 , w

Y variable1 deberá ser declarada en el programa principal (fichero .asm) so pena de obtener un error indican-do que “variable1 no existe”.

Page 189: Manual Instrucciones PIC16F84A

189

La ventaja de este método consiste en incluir las rutinas tal como son siendo fácil de realizar, sin ninguna complicación particular. Es un método que utilizo en casos particulares, por ejemplo, cuando una serie de fi-cheros “.asm” de un mismo proyecto (ver proyecto Domocan) comparten las mismas rutinas a incluir (boo-tloader común, estructura de programa común, etc.). En un caso más general, este método presenta una serie de inconvenientes mayores:

• Después de crear el fichero de subrutinas, todo el contenido de este fichero es copiado en el fichero asm y ensamblado. Por consiguiente, se colocará en la memoria de programa todo el contenido del fichero “.inc” incluso si no tiene necesidad de alguna de las subrutinas. Caso típico: tiene realizada una librería de rutinas matemáticas conteniendo suma, resta, multiplicación, división, etc. Y en su programa solo necesita suma. Perderá parte del espacio de memoria de programa.

• Está obligado a declarar en el fichero “asm” todas las variables citadas en el fichero “inc” aunque al-gunas de ellas no se utilicen por ser utilizadas por rutinas que no se van a usar. Pérdida de memoria RAM.

• Está obligado a conocer todos los nombres de las variables del fichero “inc” y de utilizar los mismos nombres en el programa principal.

• Si incluye dos ficheros “inc” con nombres de etiquetas idénticos, obtendrá un error de ensamblaje (imposible de resolver por MPASM).

• Si incluye dos ficheros “inc” con nombres de variables idénticas tiene el riesgo de producir bugs en el programa principal, las mismas variables utilizadas por rutinas diferentes sin que usted se dé cuenta forzosamente.

19.3 Encapsulación de macros simples

Existe un método más sutil para crear el fichero “.inc”, es encapsular todas las subrutinas en macros, como aquí:

m_ajoute16bits macro ; encapsular en una macro instrucción 1 instrucción 2 instrucción 3 …… instrucción n return endm ; fin de macro

m_soustrait16bits macro ; encapsular en una macr o instrucción 1 instrucción 2 instrucción 3 …… instrucción n return endm ; fin de macro

Partiendo de esto, lo ponemos en el comienzo del fichero “.asm” con la finalidad de incluir en el fichero los macros conteniendo los sub programas al comienzo del programa principal (con las otras directivas #include) por ejemplo:

#include <mesroutines.inc> ; añadimos las declaraci ones de las macros ORG 0x00 ; el código se inserta en 0x00

instrucción 1 ; instrucción en 0x00 , aún no utili zada ; posición correcta

instrucción 2

Page 190: Manual Instrucciones PIC16F84A

190

Esta vez ningún error dado que una macro no se transforma en código hasta que no se utiliza aunque sea de-clarada. Por el momento nuestro código “.asm” no contiene ningún código contenido en el fichero “.inc”, esta es la argucia, simplemente le hemos dicho que es lo que tiene que hacer si se encontrase el nombre de uno de esos macros en el fichero fuente. Ahora simplemente tendremos que poner una etiqueta de llamada seguida de la llamada a la macro, para co-locar el código de la macro en el sitio que queramos que esté. Imaginemos que solo necesitamos la suma, y no la resta, nuestro fichero fuente quedará de la siguiente manera:

#include <mesroutines.inc> ; añadimos las declaraci ones de las macros ORG 0x00 ; el código se inserta en 0x00

Goto main ; instrucción en 0x00 , correcto ORG 0x04 ; vector de interrupción

Interrupciones ; tratamiento de las interrupciones main ; arranque real del programa después del sa lto

Instrucción 1 Call ajoute16bits ; llamada a la subrutina , ejemp lo

…… ajoute16bits ; etiqueta = nombre del subprograma m_ajoute16bits ; que contiene el código de la mac ro

Ventajas

• Incluimos el fichero de las macros al comienzo del programa, no importa si vamos a utilizar los sub-programas o no.

• No declaramos más que las variables que vamos a utilizar realmente.

• Solo el código necesario estará presente cuando el fichero quede finalmente ensamblado.

• Todas las etiquetas pueden ser declaradas locales en las macros por lo que no hay ningún riesgo de duplicidad de etiquetas.

Inconvenientes

• Deberá conocer siempre el nombre de las variables utilizadas en las macro a partir del fichero “asm”.

• Si incluye dos macros que utilizan los mismos nombres de variables se arriesgará a tener errores co-mo en el primer método

Pero existe un método de evitar estos últimos obstáculos:

19.4 Método final Los únicos inconvenientes que nos quedan están relacionados con las variables. Existe por lo tanto un método para resolverlo: utilizar macros con parámetros. Nos bastará con modificar las macros incluyendo el nombre de las variables como parámetros, como a conti-nuación:

m_ajoute16bits macro var1,var2,result ; macro con parámetros movf var1 , w ; parámetros = nombre variables addwf var2 , w …… movwf result return endm ; fin de macro

Page 191: Manual Instrucciones PIC16F84A

191

En nuestro fichero “.asm” utilizaremos nuestra macro precisando el nombre que hemos decidido dar a nues-tras variables en el CBLOCK del programa principal. Imaginemos que decidimos declarar “operando1” y “operando2” a nuestras dos variables y añadir “miresul-tado” como resultado, nos bastará escribir en nuestro fichero fuente:

#include <mesroutines.inc> ; añadimos las declaraci ones de las macros ORG 0x00 ; el código se inserta en 0x00

Goto main ; instrucción en 0x00 , correcto ORG 0x04 ; vector de interrupción

Interrupciones ; tratamiento de las interrupciones main ; arranque real del programa después del sa lto

Instrucción 1 Call ajoute16bits ; llamada a la subrutina , ejemp lo

…… ajoute16bits ; etiqueta = nombre del subprograma m_ajoute16bits operando1,operando2,miresultado ; m acro con parámetros

Ventajas Todas las ventajas del método precedente. No necesitamos conocer el nombre de las variables, utilizamos nuestras propias variables, estas variables pueden ser locales (reutilizadas para otras macros). Inconvenientes Ninguno.

19.5 Conclusión Con estos métodos dispone de la manera de crear sus propias librerías de rutinas en código fuente, fácilmen-te recuperables y sin generar código inútil en el fichero ensamblado. Seria idiota no aprovecharse de ello, so-bre todo si no le gusta mucho lo de copiar/pegar, como a mí.

Page 192: Manual Instrucciones PIC16F84A

192

20. La norma ISO 7816 El objetivo de este capítulo es mostrarle como realizar una aplicación práctica sobrepasando los límites apa-rentes del PIC. He elegido crear un esquema de programa de gestión de una tarjeta ISO7816, pues es un tema de actualidad en el momento de la creación de la primera versión del curso, sobre todo en lo que concierne a la decodificación de emisiones de TV por satélite. Tenga en cuenta que la decodificación “para uso privado en un entorno no comercial” (es lo que dice la ley) es perfectamente legal en Bélgica. En Francia la legislación ha evolucionado desfavorablemente para las liberta-des individuales, desgraciadamente es tendencia. A día de hoy, pienso que es ilegal, en este país, decodificar en el hogar aún para uso propio las ondas que nos bombardean a los ciudadanos sin nuestro consentimiento. En cualquier caso, es un excelente pretexto para mostrar que es posible utilizar un 16F84, desprovisto de la gestión serie, para comunicar de esta manera con el exterior. Esta parte se limitara a la norma en general, sin entrar en una aplicación específica de la norma. El objetivo no es construir una aplicación (por ejemplo, una cerradura codificada), sino más bien como concebirla por usted mismo. Discurso “político”: le recuerdo que esta norma es utilizada para las tarjetas de decodificación satélite o TNT, para las tarjetas de identidad, las tarjetas bancarias y muchas otras aplicaciones. SI el proveedor de la tarjeta lo desea, es muy fácil crear tarjetas inviolables. El hecho de saber cómo funciona una tarjeta o tener el código fuente, no es una condición suficiente para tener acceso a la información codificada en su interior. Esto le ex-plica que si las tarjetas “piratas” para satélites han podido existir, es únicamente porque los proveedores de esas cadenas encriptadas lo han querido así, y son los mismos que lloraban por la pérdida de ganancias. Ob-serve que esas tarjetas piratas ya no existen, nos quedaremos por lo tanto en la norma ISO 7816. Las tarjetas bancarias belgas no han podido ser pirateadas jamás, y si, bajo ciertas circunstancias, las francesas lo han si-do, es debido a la crasa negligencia de los banqueros franceses, perfectamente enterados desde el principio de esta posibilidad, inducida por la utilización de una longitud de llave insuficiente. Pero el robo hace marchar la economía, lo que reporta dinero al estado. Fin de la parte “política”.

20.1 Especificaciones útiles de la norma ISO 7816

Brevemente, veamos al principio algunas especificaciones útiles de la famosa norma 7816, base de nuestra aplicación ficticia. Encontrará estas tarjetas por cualquier lado en los comercios de componentes electrónicos, bajo la denominación “tarjeta para cerradura codificada”. Permiten realizar montones de aplicaciones, y los lectores están disponibles por doquier. Necesitará para comunicar con estas tarjetas un interface cuyo es-quema puede encontrar en internet bajo la denominación “Phoenix”, y un logicial de comunicación suminis-trado con el interface. Las especificaciones son las siguientes:

• La tarjeta nos dispone de su propio reloj. Este es suministrado por el interface o maestro, y general-mente sobre el entono de los 4 MHz. Ciertas tarjetas modernas sobrepasan este límite.

• El tiempo de separación de cada bit está definido por la norma como 1 bit emitido cada 372 impulsos del reloj maestro. Esto nos da una velocidad del orden de 9600 baudios con una frecuencia de reloj de 3,57 MHz (3570000/372), o 10752 baudios con un reloj de 4 MHz. Esto le explica por qué en muchas tarjetas encontrara un reloj de 3,57 MHz que permite trabajar a una velocidad “estándar” de 9600 baudios.

• La transmisión es del tipo asíncrono, con 1 bit de arranque, 8 bits de datos, 1 bit de paridad par y 2 bits de parada.

• La comunicación es del tipo “half-duplex”, es decir, la emisión y recepción se efectúa por alternancia sobre la misma línea física de transmisión.

Page 193: Manual Instrucciones PIC16F84A

193

20.1.1 Los comandos ISO 7816

En este ejercicio vamos a utilizar los comandos “bidón” de la norma ISO 7816. Hace falta saber que nuestra tarjeta deberá responder a los comandos organizados según el protocolo estándar de esta norma. Regla: la tarjeta no toma jamás la iniciativa del intercambio de información. Solo responde a los comandos de la forma:

CLASE INSTRUCCIÓN PARAMETRO1 PARAMETRO2 LONGITUD O en forma abreviada:

CLASS INS P1 P2 LEN Veamos el significado de cada uno de los octetos del comando recibido: CLASS: determina el grupo de instrucciones concernidas. Si el número de instrucciones utilizadas no es eleva-do, nadie nos impide utilizar solo una clase. Es lo que haremos en este ejercicio simplificado. INS: es la instrucción propiamente dicha, o mejor, el comando. Es este octeto el que determina la operación a efectuar por la tarjeta en la clase precisada. P1 y P2: son dos parámetros que la tarjeta recibe con el comando. Su significado depende del comando en-viado. Pueden ser no utilizables pero en cualquier caso han de estar presentes. En general, si no se utilizan su valor se pone como 0 (no es obligatorio). LEN: puede representar 2 cosas. El número de octetos que componen el comando, en este caso la tarjeta se esperará a recibir LEN octetos suplementarios antes de tratar el comando en su totalidad. O bien, la longitud de la cadena de respuesta que el maestro espera recibir proveniente de la tarjeta. Es la instrucción quien es-tablece el rol de LEN. Advierta que existen modos extendidos por la norma ISO7816 que permiten a la vez enviar y recibir muchos octetos a la vez. Este modo está definido como modo T=1. En nuestro caso, consideraremos que la informa-ción circula solo en un sentido a la vez dada una instrucción (Modo T=0), como hacían las tarjetas de satélite de la época.

20.1.2 El protocolo de intercambio de información

Veamos cómo se desarrolla un intercambio estándar de información entre el maestro y la tarjeta ISO7816.

• A la puesta en servicio, el maestro genera un RESET sobre el pin MCLR (de la tarjeta). La tarjeta res-ponde con un cierto número de octetos. Esta respuesta se llama ATR, por Answer To Reset (respuesta al reset).

• El maestro envía el comando Class INS P1 P2 LEN

• La tarjeta reenvía la instrucción como acuse de recepción INS

• El maestro envía eventualmente los datos complementarios

• La tarjeta envía eventualmente la respuesta

• La tarjeta envía el status y pasa a modo de espera de comandos

Page 194: Manual Instrucciones PIC16F84A

194

Por lo tanto, si consideráramos la instrucción imaginaria 0x14 de la clase 0xD5 que necesitase una respuesta por parte de la tarjeta (instrucción tarjeta-maestro). El maestro precisa P1 = 0x01 y P2 = 0x02. Tendríamos (imaginándonos los octetos de dato):

• El maestro envía D5 14 01 02 10

• Deducimos que el maestro espera 16 octetos en recepción (0x10 = D’16’)

• La tarjeta responde 14 (acuse de recepción = instrucción recibida)

• La tarjeta envía 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (por ejemplo), es decir 16 octetos de respuesta, excluidos el acuse de recepción y el estatus.

• La tarjeta envía 90 00 (estatus empleado en general para indicar “la operación se ha realizado con éxi-to”). El estatus puede informar de un cierto número de códigos de error, propios de la tarjeta, del tipo “instrucción desconocida”, o “tarjeta con configuración incorrecta”, etc.

A remarcar de pasada que ciertas tarjetas recurren a menudo a comandos designados como “firmados”. Si la información no está encriptada con una llave registrada en la tarjeta (oculta al usuario) se obtiene un error en el retorno. Por el contrario, si en las mismas condiciones, nos imaginamos una instrucción 0x15 acompañada de octetos con destino a la tarjeta (instrucción maestro-tarjeta), tendríamos:

• El maestro envía D5 15 01 02 10

• La tarjeta responde 15 (acuse de recepción)

• El maestro envía 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F (por ejemplo).

• La tarjeta responde 90 00 Como ve solo el sentido de circulación de los 16 octetos de datos cambia y es la instrucción la que determi-nará cuál es ese sentido. Es siempre el maestro quien envía la trama de partida y la tarjeta (esclavo) quien en-vía el acuse de recibo y el estatus.

20.2 Las conexiones serie asíncronas El intercambio de información entre el maestro y el esclavo se hacen a través de una conexión serie asíncro-na. Para entender lo que viene a continuación en el capitulo, hace falta entender que es lo que significa esto. El modo serie está caracterizado por que todos los bits de un octeto van a ser enviados en una serie, es decir, uno detrás de otro. Esto es por oposición al modo paralelo en el que los bits son enviados todos al mismo tiempo, por lo que se necesitaría un conductor eléctrico por bit (al menos). Asíncrono es lo opuesto de síncrono, es decir, que se trata de una conexión que no provee un reloj destinado a indicar el principio y el final de cada bit enviado. Tendremos necesidad de tener un mecanismo destinado a indicar la posición de cada bit. Observe que se tra-ta en nuestro caso, de un modo asíncrono un tanto particular, puesto que la tarjeta utiliza el mismo reloj que el maestro. La velocidad no será, excepcionalmente, dada en baudios sino en número de impulsos de reloj. Un bit cada 372 impulsos de reloj. Dicho de otra forma, aunque la comunicación es asíncrona, los intercam-bios en realidad están sincronizados por la utilización de un reloj común. Es un tanto particular. Para recibir correctamente los bits enviados hace falta convenir un protocolo de comunicación. Este último debe comprender las informaciones siguientes:

• La velocidad de transmisión en baudios.

• El formato, es decir, el número de bits de arranque, el número de bits de parada, el número de bits de datos y el tipo de paridad.

Page 195: Manual Instrucciones PIC16F84A

195

Vamos ahora a explicar estos conceptos e indicar cuáles son sus valores en la norma ISO7816.

20.2.1 El bit de arranque En reposo, la línea se encuentra en estado alto. El emisor ahora, pasa la línea a estado bajo: es el bit de arran-que o start-bit. Es este cambio el que va a permitir detectar el comienzo de la recepción de bits. Los valores posibles son 1 ó 2 start-bit(s). La norma ISO7816 necesita solo 1 start-bit.

20.2.2 Los bits de datos Después de haber recibido el start-bit, se encuentran los bits de datos, comenzando por el bit 0. Las normas usuales utilizan 7 u 8 bits de datos. Para la norma ISO7816 tendremos 8 bits de datos, lo que nos da valores admisibles desde 0x00 a 0xFF para cada octeto recibido.

20.2.3 El bit de paridad El bit de paridad es una verificación de que todo se ha desarrollado correctamente en la transferencia. Des-pués de una emisión, contabilizamos cada bit del envío que valía 1. Al final de la cuenta añadimos un bit a 1 o a 0 de manera que tengamos un número par o impar de bits. Diremos que utilizamos una paridad par si el número de bits a 1, incluido el bit de paridad, es un número par. De la misma forma, paridad impar daría un número impar de bits a 1. Observe que el bit de paridad no es indispensable en una transmisión asíncrona. Tenemos entonces 3 posibi-lidades, par, impar o ninguna. En la norma ISO7816 deberemos utilizar paridad par.

20.2.4 El stop-bit Después de la recepción de los bits precedentes, es imperativo poner la línea en estado alto para poder de-tectar el start-bit del octeto siguiente. Este es el rol del stop-bit. Los valores admisibles son 1 ó 2 stop-bits. En la norma ISO7816 utilizaremos 2 stop-bits, simplemente un stop-bit con una duración de 2 bits (la línea no sufre cambios entre los dos stop-bits).

20.2.5 Velocidad y tasa de envío La duración de cada bit es una constante y depende de la velocidad de transmisión. Por ejemplo, para una ve-locidad de 9600 baudios, es decir 9600 bits por segundo, cada bit durará 1 s / 9600 = 104,17 µs. El tiempo necesario para recibir un octeto completo es la suma del tiempo necesario para recibir cada uno de los bits del octeto. En el caso de la norma ISO7816 utilizaremos 1 start-bit + 8 bits de datos + 1 bit de paridad + 2 stop-bits = 12 bits. El tiempo total para recibir un octeto será de 1250 µs. La tasa máxima en octetos por segundo será pues igual a: Tasa máxima = 1 / (numero de bits * duración de un bit), que para la norma ISO7816 será:

1 / 1250*10-6

= 800 octetos por segundo.

Page 196: Manual Instrucciones PIC16F84A

196

Esto para una velocidad de 9600 baudios. Este valor no está impuesto por la norma, por lo que podemos au-mentar la tasa aumentando la velocidad. Hablamos aquí de tasa máxima puesto que estamos considerando que un octeto comienza al final del prece-dente, lo que no es obligatorio para una conexión asíncrona.

20.3 Adquisición de bits Vamos a representar o que acabamos de decir en una forma grafica. En el eje vertical tendremos los niveles y en el horizontal el tiempo.

Examinemos esta grafica. La línea está alta durante un tiempo indeterminado. Llega el start-bit que comienza después de un tiempo indefinido. Vemos que el mejor momento para leer el bit 0 es a la mitad de su duración con el fin de tener el menor riesgo de error de timing posible. Recuerde que para la norma ISO7816 la anchura de un bit es de 372 impulsos de reloj. Como nuestro PIC divi-de la frecuencia del reloj por 4 para obtener su frecuencia de ciclos de instrucción, esto nos dará una anchura de bit equivalente a 372 / 4 = 93 ciclos de instrucción. El bit 0 será leído a 93 + 46 ciclos de instrucción después del comienzo del start-bit. A continuación podremos leer todos los bits a intervalos de tiempo de 93 ciclos de instrucción. Una vez leído el bit de paridad tenemos 2 posibilidades:

• Si deseamos continuar esta lectura con la lectura de otro octeto, nos debemos posicionar en alguna parte del interior de los 2 bits de parada para estar listos a leer el siguiente start-bit. Deberemos es-perar entre 0,5 y 2,5 bits.

• Si deseamos enviar un octeto después de esta lectura, no podremos hacerlo antes del final del segun-do stop-bit, es decir, un mínimo de 2,5 bits.

Por curiosidad ¿Cuál es entonces el octeto que hemos recibido en este ejemplo? Simplemente: b0=1 b1=0 b2=0 b3=1 b4=0 b5=1 b6=0 b1=1 Paridad = 0

Page 197: Manual Instrucciones PIC16F84A

197

El octeto recibido es B’10101001’, es decir, 0xA9. Aprovechemos para verificar la paridad. Numero de bits a 1 recibidos = 4, por lo tanto paridad par, es conforme a la norma ISO7816, se presume que el octeto se ha reci-bido correctamente.

20.4 Características de las tarjetas estándar En las tarjetas comerciales, de tipo “tarjeta para cerradura codificada” con 16F84, las conexiones utilizadas son, generalmente, las siguientes:

20.5 Creación e inicialización del proyecto Siguiendo nuestro método habitual, copie/pegue el fichero “m16f84.asm” y renómbrelo como “iso7816.asm”. Cree el proyecto en MPLAB. A continuación rellene el encabezado del programa: ;************************************************** ************************* ; Este fichero es la base de partida para la gestió n de una tarjeta * ; respondiendo a la norma ISO 7816 * ; * ;************************************************** ************************* ; * ; NOMBRE: ISO7816 * ; Fecha: 10/03/2001 * ; Autor: Bigonoff * ; * ;************************************************** ************************* ; * ; Fichero requerido: P16F84.inc * ; * ;************************************************** ************************* ; * ; - MCLR Comando de reset de la tarjeta Entrada * ; - RB4 SDA – dato para la eeprom externa Bidirec cional * ; - RB5 SCL – reloj para la eeprom externa Salida * ; - RB7 DATA – datos modo serie Bidireccional * ; * ;************************************************** *************************

Page 198: Manual Instrucciones PIC16F84A

198

Definimos la configuración: no vamos a utilizar el watch-dog para esta aplicación y utilizaremos un reloj ex-terno. Tendremos:

LIST p=16F84 ; definición del procesador #include <p16F84.inc> ; definición de las constant es radix dec ; trabajar en decimal por defecto __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC

; Protección de código OFF ; Timer reset en power on en servicio ; Watch-dog fuera de servicio ; Oscilador de cuarzo de alta velocidad

Calculemos ahora el valor a codificar en el registro OPTION: Debemos utilizar, por razones de compatibilidad electrónica, la resistencia de referencia a+5 V del pin RB7. Tendremos b7 del registro OPTION = 0. Vamos a trabajar con el timer0. Como la distancia entre 2 bits es de 93 instrucciones, vamos a trabajar sin pre divisor, lo que nos lleva a redi-rigir el pre divisor al watch-dog. Por lo tanto el valor para el registro OPTION será:

OPTIONVAL EQU 0x08 ; Valor del registro OPTION ; Resistencia Pull-up ON ; Pre divisor timer a 1

20.6 La base de tiempos

Vemos que vamos a necesitar 2 temporizaciones: una de 93 instrucciones y otra de 93+46 instrucciones, co-rrespondientes a 1 y 1,5 bits. Vamos a construir la subrutina. ;************************************************** ************************* ; TEMPORIZACION * ; * ;************************************************** ************************* ;-------------------------------------------------- ------------------------- ; temp_1bd : inicializa tmr0 para que la temporizac ión se igual al ; equivalente de un bit y medio,es decir, 46 + 93 incrementos ; de tmr0 – el tiempo necesario para llegar a la rutina ; temp_1b : espera que la diferencia entre la tempo precedente y la tempo ; actual sea de 1 bit, es decir 93 instruccion es ;-------------------------------------------------- ------------------------- temp_1bd movlw -38 ; teniendo en cuenta 2 ciclos de para da de tmr0 movwf TMR0 ; inicializar tmr0 call temp_suite ; y esperar ½ bit temp_1b movlw -91 ; diferencia entre 2 bits + 2 ciclos de parada addwf TMR0 , f ; añadir al valor actual temp_suite bcf INTCON , T0IF ; borrar flag temp_wait btfss INTCON , T0IF ; espera desbordamiento

goto temp_wait ; no finalizado , esperar return ; y salir

Page 199: Manual Instrucciones PIC16F84A

199

¿Cómo funciona esta rutina? Observe que tenemos 2 puntos de entrada, temp_1bd y temp_1b. Comencemos por examinar el último punto. Comenzamos por cargar la duración correspondiente a 91 instrucciones antes del desbordamiento del timer, es decir 0-91, o más simplemente, -91. No conviene olvidarse que un cierto número de instrucciones han sido ya ejecutadas después del último paso por la rutina, después a la emisión o recepción del bit precedente (y que perdemos 2 incrementos del timer por el hecho de su modificación). ¿Cuál es el número de instrucciones? Simplemente el número contenido en TMR0, puesto que el último paso había esperado la puesta a 0. No hay que colocar -91 en tmr0, sino más bien, añadir -91 al contenido de TMR0. De esta manera estaremos seguros que el desborde de TMR0 se hará exactamente 93 ciclos después del des-borde precedente, sea cual sea el número de instrucciones ejecutadas entre las dos llamadas. Bien entendido, a condición que el timer no se desborde antes, pero esto implicaría que no hemos tenido el tiempo necesario para ejecutar todo lo necesario entre 2 bits, por lo que, o el PIC no es suficientemente rápido o el código no está suficientemente optimizado en tiempo de ejecución. A continuación ponemos el flag T0IF a 0 y esperamos a que el desbordamiento del timer lo ponga a 1. Examinemos el punto de entrada temp_1bd. Comenzamos por inicializar TMR0 con -38 para arrancar la tem-porización de 40 instrucciones a partir de este punto. Como es al comienzo de la adquisición cuando necesitamos un medio bit, no ha habido un bit precedente por lo que debemos colocar este valor y no realizar una suma. Es más, entre la detección del start-bit y la iniciali-zación de TMR0, han pasado algunas instrucciones. Podemos considerar groso modo que han pasado 6 ins-trucciones, lo que nos da un valor a cargar de –(46-6)=-40, y teniendo en cuenta los 2 ciclos perdidos: -38. ¿Cuál es en realidad el error permitido? Simplemente la mitad de la duración de un bit, es decir 46 ciclos, con el fin de no caer en el bit vecino. No estamos a 1 ciclo de distancia, pero hace falta ser lo más preciso posible para el caso en el que una deriva de un aparato conectado no nos aumente nuestra imprecisión. Hay que pensar que el programador del logicial del maestro se ha podido servir de esta tolerancia también. A continuación llamamos a la subrutina misma para esperar la duración prevista, después la subrutina contin-úa ejecutando la espera de 93 ciclos. Hemos esperado pues 93+40 ciclos mas el tiempo de llamada de la ruti-na mas el tiempo de retorno mas el tiempo eventual perdido en el bucle de espera del start-bit, es decir, una duración aproximada de 1,5 bits. Además esta rutina no utiliza ninguna variable.

20.7 Recepción de un octeto Ahora que ha comprendido como recibir un octeto en modo serie asíncrono, es el momento de escribir nues-tro sub programa de recepción de un octeto. En este ejercicio no verificaremos si el bit de paridad recibido es correcto. A usted le dejo el integrar este test si le interesa. En ese caso deberá leer 9 bits y verificar si el número de 1 es par. Hay muchos métodos posibles, el más simple es el de utilizar el bit recibido como apli-cando un XOR sobre el bit de paridad, así a cada bit 1 recibido, el bit de paridad es invertido, solo queda com-parar al final con el bit recibido. Nuestro sub programa deberá efectuar las siguientes operaciones:

1. Esperar el comienzo del start-bit 2. Espera 93+46 instrucciones 3. Para cada uno de los bits recibidos, leer el bit y colocarlo en la posición correcta 4. Posicionarse en cualquier lugar de los stop-bits

Page 200: Manual Instrucciones PIC16F84A

200

He aquí nuestro sub programa: ;************************************************** ************************* ; Recepción de un octeto procedente del maestro * ;************************************************** ************************* ;-------------------------------------------------- ------------------------- ; Carácter leído en W. La paridad no se verifica ;-------------------------------------------------- ------------------------- Receive ; espera el comienzo del star-bit , ------------------------------- btfsc SERIAL ; test si start bit llegó goto Receive

; se posiciona a mitad del primer bit util , ---------------------------------------- call temp_1bd ; espera 1 bit y medio

; recepción del caracter , ---------------------- movlw 0x08 ; para 8 bits movwf cmptbts ; en contador de bits Recloop bcf STATUS , C ; Carry = 0 btfsc SERIAL ; test si bit = 0 bsf STATUS , C ; carry = bit recibido rrf caract , f ; entrar el bit por la izquierda call temp_1b ; esperar hasta mitad bit siguiente decfsz cmptbts , f ; decrementar contador de bits goto Recloop ; NO es el ultimo , siguiente ; ahora apuntamos en el centro del bit de paridad ; queda pues espera +- 1,5 bits para estar en el se gundo stop-bit ;-------------------------------------------------- ------------------------- call temp_1bd ; esperar 1,5 bits movf caract , w ; carga el carácter leído return ; volver Este sub programa no entraña ninguna dificultad particular. Observe que, más que posicionar el bit recibido en la posición correcta, lo hacemos entrar en el b7 sirviéndose de la instrucción “rrf”. El bit precedente es empujado a la posición b6 y así sucesivamente. Al final, el primer bit leído estará en la posición b0 y el último en la posición b7, que es lo que buscábamos. Con respecto a la última temporización, en ese momento apuntamos al centro del 9º bit, es decir, el de pari-dad. Como no lo tratamos, no lo leemos. Debemos entonces posicionarnos en un entorno en el que la línea este a nivel “1”, es decir, en uno de los stop-bits, con el fin de comenzar a esperar un eventual nuevo carac-ter. Debemos esperar entre 0,5 y 2,5 bits. La subrutina de 1,5 bits está en esa zona de lleno y la podemos utilizar directamente. El sub programa utiliza variables que declararemos más adelante.

Page 201: Manual Instrucciones PIC16F84A

201

20.8 La emisión de un carácter No olvidemos que nuestra tarjeta no emite más que a requerimiento de un “maestro”. Nuestro programa de emisión será llamado después del subprograma de recepción de un octeto. Es importante que nos acordemos que trabajamos en modo half-duplex, es decir, que la misma línea sirve pa-ra entradas y para salidas. Como cada uno de los interlocutores habla por turno, hace falta dejarle a cada uno el tiempo necesario para pasar de la lectura después de un envío. Esto se llama “tiempo de retorno”. También nos tenemos que acordar que nuestra rutina de recepción se encontrará en alguna parte a la mitad de los stop-bits, por lo que hay que dejar al emisor que acabe de enviar los stop-bits. Un buen compromiso y la facilidad de escritura nos permiten elegir una espera de 1,5 bits antes de comenzar a emitir. He elegido este valor pues esta temporización permite inicializar el timer. Este valor no es, de todas formas, critico. Hace falta, simplemente, responder después de que el maestro se ponga en modo recepción y antes de que él considere que la tarjeta no ha respondido. Nuestro programa va a efectuar las siguientes operaciones:

1. Esperar el tiempo escogido antes de la emisión 2. Pasar a emisión y enviar el start-bit 3. Enviar los 8 bits comenzando por b0 4. Para cada “1” enviado invertir la paridad para obtener una paridad par 5. Enviar los 2 stop-bits 6. Pasar a recepción

Page 202: Manual Instrucciones PIC16F84A

202

;************************************************** ************************* ; Envio de un octeto hacia el lector de tarjeta * ;************************************************** ************************* ;-------------------------------------------------- ------------------------- ; Envio del carácter contenido en W ;-------------------------------------------------- ------------------------- Send movwf caract ; salva el carácter a enviar call temp_1bd ; espera 1 bit y medio BANK1 ; pasa al banco 1 bcf SERIAL ; puerto serie en salida BANK0 ; pasa al banco 0

; enviar start-bit , ---------------- bcf SERIAL ; enviar 0 , start-bit clrf parite ; borrar bit de paridad call temp_1b ; espera entre 2 bits

; enviar 8 bits de dato , --------------------- Send_loop

rrf caract , f ; decalar , b0 en Carry rrf caract , w ; carry en b7 de W xorwf parite , f ; posicionar paridad xorwf PORTB , w ;guardar 1 si cambio en SERIAL andlw 0x80 ; solo modificamos RB7 xorwf PORTB , f ; si Si invertir RB7 call temp_1b ; espera entre dos bits decfsz cmptbts , f ; decrementa contador de bits goto Sen_loop ; no es el ultimo , siguiente

; enviar paridad , -------------- movf parite , w ; cargar paridad par calculada xorwf PORTB , w ; Si serial diferente de bit a e nviar

andlw 0x80 ; modificamos solo RB7 xorwf PORTB , f ; entonces invertir RB7 call temp_1b ; espera entre dos bits

; enviar 2 stop-bit , ----------------- BANK1 ; pasa a banco 1 bsf SERIAL ; pasa a entrada (y nivel alto) BANK0 ; pasa a banco 0

call temp_1b ; espera entre dos bits call temp_1b ; espera entre dos bits return ; retorno

Si ha estado atento habrá visto una ligera inversión en lo que concierne al final del protocolo. En vez de enviar un 1 (stop-bit) y esperar 2 bits, pasamos a entrada y después esperamos 2 bits. Esto es estrictamente idéntico debido a la resistencia pull-up que habíamos activado y que impone un valor alto (+ 5 V) en RB7 cuando este pin está puesto en entrada, nivel que corresponde al stop-bit. La rutina que sirve para enviar los 8 bits utiliza la instrucción “xorwf” en lugar de chequear si el bit a emitir va-le 1 ó 0. La rutina comienza por poner el bit a emitir en la posición b7.

Page 203: Manual Instrucciones PIC16F84A

203

¿Por qué b7? Simplemente porque es también en b7 de PORTB donde lo debemos modificar. En efecto utili-zamos RB7. El procedimiento utilizado nos permite posicionar al mismo tiempo el bit de paridad. Efectúe la operación a mano sobre papel para convencerse. Intente escribir una rutina utilizando “btfss” y “btfsc” y compare los resultados. Unas palabras acerca del envío del bit propiamente dicho, es decir las 3 instrucciones:

xorwf PORTB , w ; Si serial diferente de bit a en viar andlw 0x80 ; modificamos solo RB7 xorwf PORTB , f ; entonces invertir RB7

Comenzamos por leer el PORTB y efectuamos un “xorlw” con el bit a enviar contenido en el registro W. Como W solo contiene este bit, de RB0 a RB6 no serán modificados por las siguientes operaciones. Si el bit contenido en W es diferente del que presenta RB7, obtendremos b7=1 en W. En el caso que b7 de W sea igual a RB7 obtendremos un 0. No olvidemos que el “ ,w” nos permite guardar el resultado en W. Si aplicamos W a PORTB efectuando un “xorwf” invertiremos RB7 únicamente si b7 de W vale 1, con otras pa-labras: invertiremos RB7 únicamente si su nivel actual es diferente del nivel que necesitamos enviar. Esto parece un poco retorcido, pero si intenta escribir esta rutina de otra forma, verá que, tras muchos ensa-yos, llegará a un resultado al menos tan largo, a causa de la paridad a gestionar. Excepto si admite modificar las otras líneas de PORTB (puede hacerlo si no se utilizan para nada). A destacar que puede ignorar el bit de paridad en recepción, es su problema. Por el contrario, está obligado a posicionarlo correctamente en modo emisión puesto que hay muchas probabilidades de que el maestro este teniéndolo en cuenta.

20.9 Inicialización Vamos ahora a estudiar el cuerpo de nuestro programa principal. Como todo programa que se respeta, co-menzaremos por la inicialización. Va a ser muy simple, se va a limitar a inicializar el registro OPTION. ;************************************************** ************************* ; INICIALIZACION * ;************************************************** ************************* org 0x00 ; dirección de inicio después de reset init BANK1 ; pasar a banco 1 movlw OPTIONVAL ; carga mascara movwf OPTION_REG ; inicializa registro OPTION BANK0 ; pasar a banco 0

20.10 Envio del ATR ATR significa Answer To Reset, es decir, respuesta a un reset. Es un comando enviado por la tarjeta después de una puesta en tensión o después de un reset generado por el maestro vía conexión del “MCLR”. De entrada vamos a esperar un poco a que el maestro esté listo para recibir nuestro envío de ATR. Puede ser necesario ajustar este tiempo en función de las características del maestro, que tendrá otras cosas para hacer al arranque antes de ocuparse de vuestra tarjeta.

Page 204: Manual Instrucciones PIC16F84A

204

;************************************************** ************************* ; PROGRAMA PRINCIPAL * ; * ;************************************************** ************************* start

; comenzamos esperando un poco , ---------------------------- call temp_1bd ; espera de 1,5 bits Después ya podemos enviar el ATR. Por razones de facilidad, hemos escrito el ATR en la zona de la eeprom in-terna. He escogido un ATR de 5 caracteres que me he inventado. Consulte las características del maestro para conocer los ATR validos en su aplicación. ;================================================== ========================= ; ENVIO DEL ATR = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; Envio de un ATR ficticio: el ATR está en los 5 oc tetos de 0x04 a 0x00 ; de la eeprom interna. El ATR está escrito en sent ido inverso ;-------------------------------------------------- ------------------------- movlw 0x05 ; para 5 octetos movwf cmpt1 ; en el contador de bucles = direcc ión ATR_loop decf cmpt1 , w ; dirección a leer = contador de bucles-1 call Rd_eeprom ; leer octeto eeprom interna call Send ; enviar al maestro decfsz cmpt1 , f ; decrementar contador goto ATR_loop ; si no acabado , siguiente

Observaciones La utilización de la instrucción “decfsz” facilita la escritura de los bucles, pero, como el contador de bucles es al mismo tiempo el offset de la posición en la eeprom, el ATR se escribirá a la inversa, es decir, del último al primer octeto. En el entorno de la llamada a la subrutina “Rd_eeprom”, el contador de bucles varía de 5 a 1. Nuestra direc-ción variará de 4 a 0 por lo que la operación “decf” permite cargar en W el valor del contador de bucles-1. La subrutina “Rd_eeprom” no es otra cosa que nuestra macro de lectura de memoria eeprom transformada en sub programa, a saber: ;************************************************** ************************* ; Lectura de un octeto en eeprom interna * ;************************************************** ************************* ;-------------------------------------------------- ------------------------- ; Lectura un octeto en eeprom. La dirección se pasa en W. Octeto leído en ;-------------------------------------------------- ------------------------- Rd_eeprom movwf EEADR ; dirección a leer en EEADR bsf STATUS , RP0 ; pasa a banco 1

bsf EECON1 , RD ; lanza la lectura eeprom bcf STATUS , RP0 ; pasa a banco 0

movf Rd_eeprom ; leer octeto eeprom interna call EEDATA , w ; carga el valor en W return ; retorno

Page 205: Manual Instrucciones PIC16F84A

205

Pensemos también en escribir nuestro ATR en la zona eeprom: ;************************************************** ************************* ; DECLARACIONES DE LA ZONA EEPROM * ;************************************************** ************************* Org 0x2100 ; direccion de comienzo de la zona e eprom ATR DE 0x07 ; respuesta al ATR DE 0xAB ; B7 01 BB AB 07

DE 0xBB DE 0x01 DE 0xB7

20.11 El envío de status La norma ISO 7816 pide que cada emisión de una respuesta de la tarjeta sea seguida de 2 octetos de status que indican la manera como ha sido interpretado el comando. Me he inventado el status para este ejercicio. El status “80 00” indicará que el comando ha sido ejecutado co-rrectamente (90 00 es un valor usual). Utilizaré “60 40” para indicar que el comando no existe. Vamos a crear 2 sub programas. Uno que envía el status estándar y el otro que envía no importa que status. ;================================================== ========================= ; ENVIO DE STATUS ESTANDAR = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; Envio del status estándar, en este caso 0x80 0x00 ;-------------------------------------------------- ------------------------- Statstd

movlw 0x80 ; coge el primer octeto status call Send ; lo envía clrw ; borra W call Send ; lo envía goto classe ; y trata clase

;================================================== ========================= ; ENVIO DE STATUS ESPECIFICO = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; Envio primero octeto contenido en W , después con tenido status2 ;-------------------------------------------------- ------------------------- Statxx call Send ; envía el valor en W

movf status2 , w ; carga byte a enviar call Send ; envía el valor

Page 206: Manual Instrucciones PIC16F84A

206

20.12 Recepción de la clase Ahora nuestra tarjeta pasa a modo recepción y atiende su primer comando. Nuestro programa, por razones de facilidad, solo gestiona una clase. Nos contentamos con leer el octeto, sin verificarlo ni tratarlo. Se trata de un ejercicio didáctico. ;================================================== ========================= ; LECTURA DE LA CLASSE = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; Consideramos en este ejemplo que solo hay una cla se valida ; esperamos la llegada de la clase y no la tratamos ;-------------------------------------------------- ------------------------- Classe

call Receive ; lee el byte proveniente del maestr o

20.13 Recepción de INS, P1, P2 y LEN Examinemos ahora la rutina de recepción de la instrucción, de los parámetros P1 y P2, así como de la longitud de la cadena. ;================================================== ========================= ; LECTURA DE INS, P1, P2, LEN = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; INS se colocara en la variable Ser_Ins, P1 en Ser _P1 y P2 en Ser_P2 ; la longitud de la cadena de datos en Ser_len ;-------------------------------------------------- ------------------------- movlw Ser_Ins ; apuntar sobre instrucción

movwf FSR ; inicializar puntero read_loop

call Receive ; lee un octeto movwf INDF ; salva en el sitio previsto incf FSR , f ; apunta al siguiente btfss FSR , 0x04 ; chequea si llego a dirección 0 x10 goto read_loop

Vera que utilizamos el direccionamiento indirecto para guardar los octetos recibidos en 4 emplazamientos consecutivos. Elegiremos las direcciones 0x0C a 0x0F, que nos permite detectar fácilmente el fin del coman-do. En efecto, una vez que el 4º octeto está guardado, FSR apuntará a 0x10 (B’0001 0000’) será suficiente con chequear el bit 4 para detectar el final del bucle, sin necesidad de tener un contador de bucles suplementario.

20.14 Control de la instrucción recibida Una vez recibido el comando, debemos tratar los diferentes comandos recibidos. En nuestro ejemplo didácti-co he implementado solamente una instrucción. La única instrucción valida es la 0x25. Esta instrucción calcula la suma de P1 y P2 y reenvía el resultado. Como LEN contiene la longitud de la cadena de respuesta, si LEN es superior a 1, la respuesta se completará con 0xFF. Cualquier otra instrucción será con-siderada como incorrecta.

Page 207: Manual Instrucciones PIC16F84A

207

Veamos nuestro test: ;================================================== ========================= ; SWITCH SIGUIENDO LA INSTRUCCIÓN RECIBIDA = ;================================================== ========================= ;-------------------------------------------------- ------------------------- ; Imaginamos que reaccionamos a una instrucción 0x2 5 ; Cualquier otra instrucción será considerada incor recta ;-------------------------------------------------- -------------------------

; chequear instrucción recibida , ----------------------------- movf Ser_Ins , w ; carga instrucción recibida sublw 0x25 ; compara con 0x25 btfsc STATUS , Z ; test si igual goto Ins25

; tratar instrucción incorrecta , ----------------------------- movlw 0x40 ; carga octet2 status a enviar movwf status2 ; coloca en variable movlw 0x60 ; carga octet1 status goto Statxx ; envía status Vemos aquí que si la instrucción recibida es 0x25 saltamos al tratamiento de la instrucción. En caso contrario, enviamos el status “60 40” que en nuestro caso significa “instrucción incorrecta”.

20.15 Tratamiento de una instrucción Llegamos al tratamiento de la instrucción propiamente dicho. Vamos a tratar la instrucción de la siguiente manera:

1. Como en toda instrucción, reenviamos la instrucción recibida 2. La tarjeta reenvía la suma P1+P2 3. La trama de envío se completa con tantos 0xFF como sean necesarios para que la longitud total sea

igual a Ser_len 4. Se envía nuestro status estándar “80 00”

Page 208: Manual Instrucciones PIC16F84A

208

;================================================== ========================= ; TRATAR INSTRUCCIÓN 25 = ;================================================== ========================= Ins25

; envio eco del comando , --------------------- movf Ser_Ins , w ; carga instrucción recibida call Send ; envía eco

; envio P1 + P2

, ------------- movf Ser_P1 , w ; carga P1

addwf Ser_P2 , w ; + P2 call Send ; envia resultado

; Test longitud respuesta , ----------------------- decf Ser_len , f ; puesto que resultado enviado btfsc STATUS , Z ; test si complete goto Statstd ; Si , enviar status estándar

; Completar con 0xFF , ------------------ Insloop

movlw 0xFF ; valor a enviar call Send ; envia decfsz Ser_len , f ; decrementar contador de bucle s goto Insloop ; No acabado , siguiente

; envio status estandar , --------------------- goto Statstd ; Si , enviar status estándar

Page 209: Manual Instrucciones PIC16F84A

209

20.16 Las variables Nos queda declarar las variables utilizadas. He decidido utilizar variables locales puesto que es posible: ;************************************************** ************************* ; DECLARACION DE VARIABLES * ;************************************************** ************************* CBLOCK 0x00C ; direccion de comienzo de la zona d e variables

Ser_Ins : 1 ; Instrucción ISO7816 Ser_P1 : 1 ; Parametro 1 ISO7816 Ser_P2 : 1 ; Parametro 2 ISO7816 Ser_len : 1 ; longitud dato ISO7816 local1 : 1 ; variable local 1 local2 : 1 ; variable local 2 local3 : 1 ; variable local 3 local4 : 1 ; variable local 4 temp_sauvw :1 ; salvaguardia de w para temp ENDC ; fin zona de variables

; Rutina ATR , ---------- #DEFINE cmpt1 local1 ; contador de octetos para AT R

; Subrutina de envio y recepcion , ------------------------------ #DEFINE caract local2 ; carácter a enviar #DEFINE parite local3 ; bit de paridad #DEFINE cmptbts local4 ; contador de bits

; Para STATUS , ------------ #DEFINE status2 local1 ; octeto 2 de status

; Para instrucción 25 , ------------------- #DEFINE cmpt2 local1 ; ocontador de octetos

20.17 Conclusión Ahora ya sabe comunicar un enlace serie asíncrono. Además ya tiene las nociones básicas necesarias para rea-lizar una tarjeta que responda a la norma ISO7816. Ninguna duda de que encontrará numerosas aplicaciones con la teoría aquí expuesta. Le recuerdo que se trata de un ejemplo imaginario pero los casos que nos encontramos en la práctica son muy parecidos al ejemplo aquí desarrollado y las técnicas utilizadas en el ejemplo han sido usadas con éxito en no pocas aplicaciones trabajando con tarjetas ISO7816 reales: no se trata pues de pura teoría. Yo he pro-bado el software presentado con un lector de tarjetas ISO7816. Yo he recibido (en la gloriosa época de la decodificación de satélites) no pocos correos de personas que co-menzaron a leer el curso empezando por este capítulo, sobre todo en la época revuelta de la realización per-sonal de tarjetas decodificadoras de emisiones de TV vía satélite. Conviene no poner el carro antes de los bueyes, y si este capítulo está al final del curso es por algo. Como anécdota, si este curso existe es debido a las tarjetas ISO7816 y a la decodificación de emisiones vía satélite. En aquel entonces yo era administrador de un gran foro centrado en la decodificación vía satélite y propuse la primera versión de este curso bajo la forma de capítulos independientes presentados como leccio-nes directamente en line en el foro. Contaba con aprovecharme del tirón de la decodificación para enganchar a los jóvenes (y menos jóvenes) a interesarse en la manera de funcionamiento real de un micro controlador. Antes de esa época trabajaba con los 8051, son estas tarjetas las que me hicieron adoptar los PIC.

Page 210: Manual Instrucciones PIC16F84A

210

Anexo 1 : Preguntas frecuentes (F.A.Q.) Voy a intentar responder aquí al máximo de preguntas posibles que se hacen los usuarios en general.

A1.1 Encuentro que 8 sub programas es poco No debe confundirse el número de sub programas con el número de anidamientos de los sub programas. El numero de sub programas es ilimitado (dentro de los limites de memoria de programa disponible). Un anidamiento es cuando un sub programa llama a otro sub programa. Para contar el nivel de anidamientos, siga el camino de su programa en el orden de ejecución. Cuente +1 para cada instrucción “call” y -1 para cada “return” o “retlw”. Si su programa es correcto se deben cumplir las siguientes 3 condiciones:

1. La cuenta no debe dar jamás un numero negativo 2. La cuenta no debe pasar jamás de 8 3. Al final de la ejecución del programa la cuenta será 0

Atención, no olvide tener en cuenta las interrupciones (ver anexo A1.2).

A1.2 No utilizo más que 8 anidamientos pero mi programa se bloquea No debe olvidar que las interrupciones también utilizan la pila. Si utiliza interrupciones, solo tiene derecho a 7 niveles de anidamiento en lugar de 8 (menos las subrutinas utilizadas eventualmente por la rutina de inte-rrupción).

A1.3 Mi programa parece no salir jamás de las interrupciones Se ha olvidado de borrar el flag que provoca la interrupción. No olvide no salir de las interrupciones con re-turn sino con retfie, sin lo cual las interrupciones no serian repuestas en servicio y no podría volver a entrar a ellas. Otro error clásico es poner a 0 el flag RBIF sin haber leído previamente el PORTB.

A1.4 No consigo utilizar el simulador, las opciones no aparecen Ha olvidado decirle a MPLAB que va a utilizar el simulador integrado. Vaya al menú “debugger -> select tool” y seleccione “MPLAB®SIM”. Configúrelo como se explico en el capítulo correspondiente.

A1.5 Recibo el mensaje de error EOF antes de la instrucción END Abra el fichero “.asm” en el bloc de notas de Windows y verifique que se muestra correctamente, con la posi-ción de líneas correctas. Si no es este el caso, es que el fichero se ha escrito con un editor que no genera bien los retornos de carro. En ese caso, trate de copiar/pegar este fichero en un fichero creado con otro editor.

Page 211: Manual Instrucciones PIC16F84A

211

A1.6 ¿Cómo desensamblar un fichero .hex? Le voy a describir 2 métodos para desensamblar un fichero en formato hexadecimal. El primer método utiliza MPLAB®:

1. Vaya al menú “File -> import” 2. Selecciones el fichero “.hex” a desensamblar 3. Seleccione “View -> program memory” 4. Seleccione la pestaña que corresponda a su deseo

El segundo método utiliza IC-Prog, la célebre utilidad de programación de PIC (hoy día pasada de moda) dis-ponible por todas partes en su momento:

1. Cargue el fichero “.hex” con el menú “File -> open file” 2. Seleccione “view -> assembler”. Es todo

A1.7 Utilización de minúsculas y mayúsculas

Por defecto MPLAB® efectúa la distinción entre mayúsculas y minúsculas. Si esto le supone algún problema, puede modificar las propiedades de su proyecto. Para efectuar esta operación: Seleccione “Project -> build options -> project” Seleccione la pestaña “MPASM assembler” Escoja la opción “disable case sensitivity” Le desaconsejo hacerlo puesto que su código no será ya portable a todos los ensambladores.

A1.8 La elección del programador (tostadora) He decidido modificar radicalmente mi consejo inicial de las primeras versiones de este curso después de la estrategia comercial de Microchip que deja, en mi opinión, como opción poco juiciosa la construcción de un programador “personal”. Tanto más cuanto que los programas más famosos de pilotaje de un programador no siguen verdaderamente la salida al mercado de nuevos modelos. Si decide construirlo usted mismo, al menos no construya uno del tipo JDM (fuente de problemas evidente), que se reconocen por el hecho de estar conectados al puerto serie (com) sin disponer de una alimentación eléctrica propia (tira directamente de la alimentación del puerto serie). Evite sobre todo construir un programador serie que tendrá que conectar, en caso de ausencia de puerto se-rie en su PC, a través de un convertidor USB/RS232, y esto en general no funcionará dado que la totalidad de las líneas necesarias para el programador no están tenidas en cuenta en estos convertidores. Mi consejo es claro: compre un programador oficial PicKit® o ICD3®. No compre las versiones “2”, no serán utilizables en los futuros PIC®. Las ventajas son numerosas:

• Posibilidad de programar el PIC® directamente desde MPLAB® (sin necesidad de otro logicial).

• El programador reconocerá todos los PIC® previstos en su versión de MPLAB®, bastará con poner al día a la salida al mercado de un nuevo PIC®.

• Estos programadores funcionan también como debugger sobre el circuito (ver curso parte 4) por lo que podrá depurar “al vuelo” todos los programas sobre los PIC® que posean la funcionalidad “ICD®”, es decir la mayoría de los PIC® recientes (no el 16F84).

Page 212: Manual Instrucciones PIC16F84A

212

• Estará seguro que su programador funciona y se podrá concentrar en cualquier otra cosa útil si su aplicación no funciona.

• La garantía es muy extensa, el ICD2® que poseo, por ejemplo, está considerado por Microchip® aún en garantía a pesar de muchos años de utilización (¿garantía de por vida?).

Mi consejo es claro: no construya su propio programador excepto si solo va a programar un par de PIC® o que le es imposible comprar uno oficial. Evite los clones, fuente potencial de problemas y no tan baratos. Co-mo preferencia compre un PicKit® o un ICD3® oficial.

A1.9 Tengo un error de “stack” Recibo bastantes correos de personas que me dicen: cuando ejecuto mi programa en paso a paso en MPLAB recibo en un momento determinado un error del tipo “Stack overflow” o “Stack underflow”. ¿Qué significa este mensaje? De hecho, un mensaje “stack overflow” puede significar que ha sobrepasado los 8 niveles de anidamiento. Le recuerdo que solo hay 8 emplazamientos en la pila (stack) y que una nueva tentativa de apilamiento, debido a una subrutina o una interrupción, provocará el derrumbe de la pila. En la mayor parte de los casos se trata de un error en la estructura de su programa. El mensaje “stack over-flow” ocurre si apila más de 8 emplazamientos, el mensaje “stack underflow” ocurre si usted desapila mas de lo que había apilado (por ejemplo, return sin call). Un “stack underflow” es siempre un error de programa-ción. Para resolver estos problemas, si no ha sobrepasado los 8 niveles (ver A1.2), verifique los siguientes puntos:

• Cada llamada vía call debe volver al programa por un return o un retlw.

• Cada return o rtlw encontrado debe haber estado precedido de un call correspondiente.

• La salida de una subrutina con un goto debe llevar al programa a un punto donde encuentre un re-turn.

• Una rutina no debe llamarse jamás a sí misma. La llamada de una función por sí misma es llamada “recursividad” y es una práctica corriente en lenguajes evolucionados para sistemas grandes (PC), pe-ro no utilice jamás este método en un PIC® en los que la pila está limitada, salvo si usted es un pro-gramador particularmente “fino” para llegar a gestionar la recursividad simultáneamente con la capa-cidad de la pila.

A1.10 ¿Cuáles son las diferencias entre un 16F84 y un 16F84A?

Otra pregunta que me llega regularmente al correo. Le comento las principales diferencias:

• La frecuencia máxima de reloj pasa de 10 MHz a 20 MHz

• La tensión máxima admisible pasa de 6V a 5,5V

• El reset necesita de un impulso de 2 µs sobre MCLR en lugar de 1 µs

• El tiempo de subida de la tensión de alimentación tomado en cuenta para el arranque cambia (ver da-tasheet)

• La corriente típica para la programación de la memoria flash pasa de 7,3 mA a 3 mA (concierne a los realizadores de programadores)

• La corriente de consumo típica se dobla entre un 16F84 a 10 MHz y un 16F84A a 20 MHz, pero es úni-camente la consecuencia del aumento de la velocidad (a 10 MHz el consumo no es mucho más gran-de que en un 16F84)

• Diferentes modificaciones de características eléctricas (tensiones de nivel, corrientes, etc.)

• División por 2,5 de los tiempos de escritura en memoria eeprom

• División por 2,5 de los tiempos de escritura en memoria flash

Page 213: Manual Instrucciones PIC16F84A

213

Resumiendo, nada que concierna a la programación, son diferencias a nivel eléctrico. Salvo si está alimentan-do su 16F84 a 6V, podrá reemplazarlo por un 16F84 sin cambiar nada. Este curso se aplica indistintamente al 16F84 y al 16F84A.

A1.11 Tengo un error 173 después del ensamblado

En el momento de lanzar el ensamblaje, obtiene un error 173, del tipo:

Error [173] Source file path exceeds 62 characters Esto significa que el camino para acceder a su fuente excede los 62 caracteres, lo que puede ser debido a la utilización de nombres de fichero muy largo o a numerosos directorios y subdirectorios anidados. Por ejem-plo:

C:\Mi_directorio\fuentes\fuentes_MPLAB\Curso\Curso_ parte1\Ejercicios\test1.asm Será suficiente con desplazar su directorio a una carpeta más próxima a la raíz o reducir los nombres de fiche-ro utilizados. Un ejemplo correcto:

C:\Curso_Pic_1\Ejercicios\test1.asm O, mejor aún, colocar sus datos en una partición diferente:

D:\Curso_Pic_1\Ejercicios\test1.asm Este problema era frecuente con las versiones 5.x de MPLAB pero debería desaparecer con versiones más re-cientes dado que la longitud autorizada ha aumentado.

A1.12 El PIC16F84 está obsoleto ¿por qué no utilizar un 16F628? Alguna vez me han hecho esta pregunta. Ciertos “anti-pic primarios” incluso me han agredido en algunos fo-ros acerca de esto. La respuesta es muy simple: no se trata aquí de realizar un montaje específico y preciso, al contrario, el objetivo es explicar las bases de funcionamiento de la familia de PIC16F. El 16F84(A) es el PIC® más simple de toda la familia: todos los módulos explicados en este PIC® se encuen-tran sobre la totalidad de los otros PIC16F. Dicho de otra forma, todo lo que estudiará en este curso es direc-tamente utilizable en toda la gama, nada se estudio inútilmente. El 16F628 es un PIC® con funcionalidades suplementarias, funcionalidades que son explicadas en el curso parte 2 destinado a los PIC16F87x que era uno de los PIC16F más completos en el momento de escribir el cur-so parte 2. El 16F84 está particularmente bien adaptado para el aprendizaje de base de los PIC® de medio rango a pesar de que otros, como el 16F628, lo destronen en relación a capacidades/precio en el caso de una aplicación re-al. El cuaderno de cargas para un curso de aprendizaje no es el mismo que para una aplicación concreta, lo que explica que el 16F84 continúe siendo mi elección maestra para el estudio de los PIC® de rango medio. El 16F87x, utilizado en el curso parte 2, es el pretexto para el aprendizaje de un amplio número de funcionali-dades que podemos encontrar en la mayor parte de los otros modelos de la familia de rango medio e incluso en los PIC High-End 18F abordados en el curso parte 5. De nuevo, el 16F87x no es más que un pretexto de aprendizaje, no representa la mejor elección para una aplicación en un caso real práctico.

Page 214: Manual Instrucciones PIC16F84A

214

Compre mejor un 16F84(A) para el estudio de este curso mejor que un 16F628 para que no tenga que resol-ver problemas no explicados en este curso que le distraigan del aprendizaje en sí mismo. Le aseguro que tiene a ganar haciéndolo así. El curso es gratuito ¿puede hacer el sacrificio de comprar un 16F84(A), no? Una vez acabado el aprendizaje busque un PIC que le convenga mejor a su aplicación. Si ese PIC dispone de funcionalidades avanzadas de las que usted tenga necesidad, tome en cuenta estudiar el curso parte 2. Si el hecho de que yo utilice el 16F84 y el 16F876 le molesta, tenga en cuenta los títulos de mis cursos: Parte 1: Las funciones básicas Parte 2: Las funciones avanzadas facultativas

A1.13 Utilizo una versión de MPLAB® mas reciente Otra pregunta que me sigue llegando a menudo. Los usuarios piensan juicioso utilizar este curso con una ver-sión más reciente de MPLAB® (por ejemplo, la 8.x) y se encuentran agarrotados con las operaciones mas básicas. Ya he dejado escrito que el curso inicialmente está diseñado para MPLAB® 5.x y pasarlo a MPLAB® 6.6. Des-graciadamente las versiones evolucionan demasiado rápidamente y me es imposible reescribir sin parar en función de las evoluciones. Mi consejo es el siguiente con el fin de evitar problemas: para estudiar el curso, instale MPLAB® 6.6 única-mente, esto le permitirá seguir el curso sin problemas. Una vez realizado el aprendizaje, pásese a una versión más reciente, sabiendo que la migración la hará sin grandes problemas una vez asimiladas las nociones bási-cas. Una vez más, evite acumular obstáculos, hágalo simple. Para cargar la versión antigua de MPLAB®:

1. Vaya al sitio de Microchip®: www.microchip.com 2. En la zona “search” escriba “MPLAB IDE Archives” 3. Deberá llegar rápidamente a una página donde se encuentran las versiones archivadas de MPLAB, co-

ja la que corresponda al curso elegido.

A1.14 Mi PIC virgen no oscila He añadido este anexo porque un internauta me comunicó haber intentado probar con un PIC nuevo sin pro-gramar con el fin de saber si había entendido todo bien a nivel electrónico. Ese internauta se rompió la cabeza intentando comprender por qué no tenía ninguna traza de oscilación en el cuarzo de su PIC. Veamos que pasaba:

• Un PIC contiene sus informaciones de configuración (oscilador, watch-dog, etc.) en su memoria flash. Debido a la tecnología, borrar este tipo de memoria conlleva poner todos los bits de cada casilla a 1.

• Un vistazo a la figura 8.1 del datasheet nos muestra que la selección del modo del oscilador se efect-úa vía 2 bits: FOSC1 y FOSC2. En un PIC nuevo estos dos bits estarán a 1.

• Otro vistazo sobre la explicación de estos bits nos muestra que eso corresponde al modo RC seleccio-nado.

Conclusión: un PIC virgen está configurado por defecto en funcionamiento del oscilador en modo RC, por lo que el cuarzo no puede oscilar. Recuerde, por precaución, jamás ponga un PIC virgen en la tarjeta: comience siempre por programarlo y solo aliméntelo a continuación.

Page 215: Manual Instrucciones PIC16F84A

215

Contribución voluntaria La realización de este curso me ha llevado bastante tiempo e inversiones (documentación, material, abonos, etc.). Así, para permitirme continuar, les pido, si eso está dentro de sus posibilidades, y si usted aprecia lo que he hecho, contribuir un poco, cada uno según sus posibilidades y sus deseos. Tengo necesidad de su ayuda para continuar con la aventura. De hecho, no dispongo realmente de la capaci-dad para consagrar la totalidad de mi tiempo libre a escribir cursos y programas sin recibir una pequeña ayu-da. A pesar de todo, no quiero caer en la necesidad de impedir el acceso a los ficheros e imponiendo un pago pa-ra obtenerlos. Quiero que queden disponibles para todos según su necesidad. He decidido instaurar un sistema de contribución sobre una base voluntaria que permita a quien lo desee, en función de sus propios criterios, ayudarme financieramente. L objetivo no es hacerme rico, es más bien ayu-darme a alcanzar el objetivo. No se trata de un pago ni de una obligación. Se trata simplemente de una asistencia sin promesas ni contra-tos. Continuaré respondiendo a los correos de todo el mundo, sin distinción y sin preguntas sobre el asunto. Un buen método consiste, para aquel que lo desee, descargar el documento escogido, leerlo o utilizarlo, y después decidir si merece o no la pena ayudarme sobre la base de lo que usted haya hecho. Si es que sí, vaya a mi sitio: www.abcelectronique.com/bigonoff o a www.bigonoff.org y siga la pagina “cours-part1”. Allí encontrará en la pagina “contributions” el procedimiento a seguir. Piense que esas contribuciones me son muy útiles y me permiten continuar trabajando para usted. No olvide poner su email en caracteres de imprenta para que pueda responderle. Respondo siempre a los correos recibidos. Así que si no obtiene respuesta, no dude en contactarme para veri-ficar si ha habido algún problema. Si no respondo a uno de sus emails, no es que sea grosero, es simplemente que no he podido responderle (su cuenta de correo está llena, su dirección no es válida, no he recibido su mensaje, su mensaje ha sido clasificado automáticamente como “spam” por mi mensajería, etc.) Muchas gracias por adelantado a todos aquellos que me han ayudado o están por ayudarme a continuar con este trabajo de largo recorrido.

Page 216: Manual Instrucciones PIC16F84A

216

Utilización del documento La presente obra está destinada a facilitar la comprensión de la programación de los PIC® en general, y del 16F84 en particular. Las continuaciones están disponibles en mi sitio para descarga gratuita. Comuníqueme (con educación) todo error constatado con el fin de que la puesta al día sea efectuada por el interés de todo el mundo. Por razones de facilidad de mantenimiento y de puesta al día, he decidido (es mi única exigencia) que este curso solo pueda ser descargado desde mi sitio: www.bigonoff.org Por favor, si encuentra mi curso en otros sitios, envíe un email al webmaster del sitio en cuestión para pedirle que respete la única regla del juego. Entiéndase bien, yo autorizo (recomiendo) a los webmaster a colocar un enlace a mi sitio. Yo hare lo mismo a la inversa si la demanda me es hecha. Así espero el máximo de usuarios. La presente obra puede ser utilizada por todos, copiada y/o impresa en su totalidad, a condición de no modi-ficar nada. Puede ser utilizada como soporte de cursos en todo o en parte, a condición de precisar la referen-cia original y el enlace a mi sitio. Autorizo explícitamente a las escuelas y otras organizaciones educativas a imprimir mi curso en número elevado de ejemplares con el objetivo de prestar un servicio a los estudiantes. En ese caso, pido que la compra del curso no sea obligatoria y que se les informe a los estudiantes que tiene la posibilidad de descargarlo de mi sitio. Puede notificarme cualquier abuso en ese sentido. Todos los derechos sobre el contenido de este curso y sobre los programas que lo acompañan quedan pro-piedad intelectual del autor, incluso si una notificación contraria intenta demostrar lo contrario (documento de la web, por ejemplo). El autor no podrá ser tenido como responsable de cualquier consecuencia directa o indirecta resultante de la lectura y/o aplicación del curso o los programas. Está prohibida toda utilización comercial sin el consentimiento escrito del autor. Todo resumen o cita a título de ejemplo debe estar acompañada de la referencia al origen. Espero no haber atentado contra ningún derecho de autor realizando esta obra y no he utilizado más que los programas puestos graciosamente a disposición publica por parte de la sociedad Microchip®. Los datasheet están así mismo disponibles para descarga en el sitio de esa sociedad: http://www.Microchip.com Si le ha gustado esta obra, utilícela, si tiene críticas, gracias si envía un pequeño email. Esto me permitirá sa-ber si debo continuar o no esta aventura con las siguientes partes.

Page 217: Manual Instrucciones PIC16F84A

217

Sepa que respondo siempre a los correos, pero advierta que:

• No realizo programas de fin de carrera para estudiantes (incluso pagando), es una petición que me llega todas las semanas al correo. No tengo tiempo y creo que es un mal servicio el hacerlo. Haciendo un poco de humor, si yo les diera mis tarifas, estos estudiantes se arriesgarían a sufrir un infarto.

• Desgraciadamente, no tengo el tiempo suficiente para corregir programas completos. Inútil enviarme programas con un mensaje del estilo de “esto no funciona ¿me podría decir por qué? Paso mucho tiempo respondiendo correos, si además tuviese que depurar programas me pasaría el día entero. Comprenda que esto es imposible, piense que usted no es el único preguntándome cosas. Hágame preguntas precisas sobre lo que concretamente le parezca inexacto.

• Si tiene aplicaciones personales, no dude en compartirlas con todos. Para hacer esto, envíemelas por email.

• Con esta versión, intento responder a demandas legítimas de personas que trabajan sobre diferentes plataformas (Mac, Linux, Windows, et.). Si, a pesar de todo, la versión suministrada es inexplotable sobre su máquina, gracias si me lo hace saber. Advierta de todas formas, que este curso utiliza MPLAB® para los ejercicios, hará falta eventualmente adaptar los ejercicios en función del logicial que le sea posible utilizar. Advierta que Microchip suministra ahora una versión de MPLAB® bautiza-da como MPLAB-X®, que corre en las plataformas más comunes. No dude en descargarla.

Gracias al webmaster de www.abcelectronique.com por albergarme gratuitamente. Gracias a Byte por su propuesta de albergarme gratuitamente. Gracias a Grosvince por su propuesta de albergarme gratuitamente. Gracias a Bruno por haber asegurado algún tiempo el pago de mi dominio. Gracias a Thierry por haberme ofertado mi nombre de dominio y asegurar su pago. Gracias a todos aquellos que me han reportado correcciones a efectuar. Gracias a todos aquellos que me han enviado contribuciones para compartir. Gracias a mi esposa y mis hijos por su paciencia. Gracias a todos aquellos que me han enviado emails motivantes. Última precisión: es imposible que encuentre trazas de plagio aquí, así como en los cursos precedentes, te-niendo en cuenta que no he leído ninguna obra sobre el sujeto excepto los datasheet de Microchip®. Todo ha salido de mis propias experiencias. Si encuentra trazas de un plagio (ya las vi), sepa que son los otros quienes han copiado, y esto también es válido para las obras a la manera clásica (este comentario no es inocente). Edición terminada el 09/02/2001, compilación de lecciones separadas en una obra completa.

− Puesta al día revisión 2 el 15/06/2001: corrección de algunos errores.

− Puesta al día revisión 3 el 24/07/2001: corrección detallada con los Fribottes

− Puesta al día revisión 4 el 26/10/2001: corrección de algunos pequeños errores encontrados.

− Puesta al día revisión 5 el 27/02/2002: corrección de algunos errores tenaces.

− Puesta al día revisión 6 el 20/04/2002: alguna corrección, mejoras de los puntos que suscitaban pre-guntas frecuentes, paso a formato pdf con compresión rar para permitir la utilización en todas las maquinas, masculinización del término PIC.

− Puesta al día revisión 7 el 2/09/2002: corrección de errores sintácticos y ortográficos menores en al-gunas palabras. Añadido de la prohibición de distribución fuera de mi sitio web.

− Puesta al día revisión 8 el 25/10/2002: pequeña corrección del capítulo 2.4. El enunciado citado en el ejemplo no corresponde a lo efectivamente desarrollado. Corrección del enlace hacia el sitio de Mi-crochip. Modificación de las definiciones CISC y RISC.

− Puesta al día revisión 9 el 01/11/2002: corrección de faltas de ortografía diversas.

− Puesta al día revisión 10 el 04/12/2002: supresión de una línea que se prestaba a confusión en la página 183, algunas correcciones menores, modificación de contribución pagina 209.

Page 218: Manual Instrucciones PIC16F84A

218

− Puesta al día revisión 11 el 19/04/2003: modificación de los registros modificados por la instrucción sleep pagina 74, añadido del programador en la lista de materiales necesarios, modificación en el de-fine de la pagina 123, corrección menor en pagina 70.

− Puesta al día revisión 12 el 16/06/2003: correcciones menores.

− Puesta al día revisión 13 el 19/09/2003: grandes modificaciones para pasar de MPLAB® 5.50 a MPLAB® IDE 6.30. Numeración cambiada, numerosas modificaciones en capítulos enteros. Reimpre-sión completa necesaria, ficheros de ejemplo reconstruidos.

− Puesta al día revisión 14 el 11/12/2003: modificación del esquema de la pagina 134, modificación de radix por paso a MPLAB® 6.

− Puesta al día revisión 15 el 13/02/2004: corrección de un error repetitivo en los ejemplos y en el curso a nivel de la inicialización de EEADR (cambio del banco una línea demasiado pronto), ficheros ejemplo y maqueta.

− Puesta al día revisión 16 el 09/03/2004: corrección pagina 58,59.

− Puesta al día revisión 17 el 09/06/2004: correcciones páginas 34 y 198, anexos, añadido de un truco de programación, índice de materias, modificación de mi dirección de correo.

− Puesta al día revisión 18 el 02/08/2004: añadido del tiempo de despertar pagina 171, nota pagina 113.

− Puesta al día revisión 19 el 27/03/2006: correcciones diversas paginas, recolocación de partes de código debido a una modificación accidental del tabulado, puesta al día pagina 20, notas páginas 24 y 58, añadido de un anexo.

− Puesta al día revisión 20 el 03/04/2006: añadido de un pequeño capítulo sobre consumo mínimo en modo sleep.

− Puesta al día revisión 21 el 13/10/2006: correcciones menores, paso de números H’00xx’ a 8 bits, añadido de una nota pagina 44, revision21b, puesta al día del esquema del opto acoplador que había desaparecido.

− Puesta al día revisión 22 el 24/01/2007: modificaciones importantes en todo aquello que trata del ti-mer0 teniendo en cuenta la obligación de considerarlos 2 ciclos perdidos en toda modificación del TMR0, modificación de paginas 29,44,52,95 y ficheros asm concernidos, correcciones menores, modi-ficación concerniente al simulador (era de la versión antigua de MPLAB®), modificación pagina 21 a nivel de la explicación de la mención “-xx” que había olvidado adaptar a las diversas experiencias efectuadas sobre el sujeto.

− Puesta al día revisión 23 el 04/11/2007: añadido de una lista de consideraciones sobre el watch-dog en el capítulo 15.3, correcciones ortográficas o frases retorcidas, modificación de un camino pagina 38, precisión sobre el término “posicionado”, corrección de numero de bit pagina 12, decalaje de cier-tos números de pagina, a la demanda amistosa de Microchip®, añadido del símbolo ® a todo termino referente a Microchip®.

− Puesta al día revisión 24 el 08/02/2008: modificación menor pagina 47, reemplazo de sistema de nu-meración por numeración, capítulos 12.7.2 a 12.7.4 re hechos (añadidos) implicando la remuneración de todas las pagina siguientes.

− Puesta al día revisión 25 el 07/09/2008: corrección importante pagina 140.

− Puesta al día revisión 26 el 08/02/2009: correcciones menores páginas 50 y 63, añadido del reset ex-plicito del flag de una interrupción de la interrupción eeprom.

− Puesta al día revisión 27 el 15/02/2009: precisiones concernientes al tiempo de latencia de las inte-rrupciones en pagina 109.

− Puesta al día revisión 28 el 24/02/2009: añadido de un párrafo sobre las unidades, adopción del término Kibi mas que de una simple nota.

− Puesta al día revisión 29 el 05/10/2010: correcciones paginas 18, 19, 22, 41, 49, 56, añadido explica-ciones pagina 44 sobre el fin de los programas (directiva END), modificación total del anexo A1.8.

− Puesta al día revisión 30 el 13/10/2010: después del abandono de las versiones “2” de los PicKit® y ICD® , modificación del consejo en anexo A1.8.

− Puesta al día revisión 31 el 28/11/2010: remarque modificado en pagina 148, añadido de un capitulo concerniente a la utilización de rutinas en fichero separado.

Page 219: Manual Instrucciones PIC16F84A

219

− Puesta al día revisión 32 el 13/03/2011: corrección enlace pagina 9, correcciones menores, añadido anexos.

− Puesta al día revisión 33 el 26/11/2011: añadido remarque pagina 129, corrección diversas paginas

− Puesta al día revisión 34 el 05/01/2013: Revisión mayor, lifting completo del curso, supresión de so-bre lineados (facilidad de lectura), correcciones, añadido de explicaciones, modificación de párrafos, modificación de ciertos ficheros suministrados, etc.

En mi sitio web encontrará así mismo toneladas de reportes de información que le permitirán volcarse en es-te curso y sobre los otros ficheros propuestos para descarga. Use preferentemente el email para preguntas y envió de correcciones.

Realizacion: Bigonoff Site: http://www.abcelectronique.com/bigonoff (gracias por el alojamiento) Dominio: www.bigonoff.org Email: [email protected] Traducción al español: Fernando Ruano Vigo – España Mayo 2013