Está en la página 1de 64

Voy a contar en muy poco tiempo el proceso de auto-aprendizaje que yo he seguido de los microcontroladores PIC, con un enfoque eminentemente

prctico que permita a cualquiera entender las caractersticas principales de los microcontroladores PIC de gama media y mejorada, y eventualmente usar un microcontrolador para cualquier diseo de complejidad pequea o media. Asimismo esta introduccin puede considerarse el primer paso para conocer en profundidad el amplsimo mundo de los controladores de una forma rpida; desde aqu se podr profundizar con la informacin que provee el fabricante sobre el uso de funcionalidades ms avanzadas o con cualquier otra gama de microcontroladores, segn sean las necesidades del interesado. Dada la cantidad de informacin y el carcter prctico de esta introduccin, se recomienda una vez concluida sta, que la persona interesada instale los paquetes software que se usan para programar los PIC por s mismo, y tambin que se trate de hacer programas similares a los que se van a presentar con pequeas variaciones, ya que la prctica hace que se afiancen mejor los conocimientos.

A mi Padre. An con el dolor de perderte siempre recordar tu alegra Pap. Tu hijo

03.- Qu es un microcontrolador ? 04.- Familias de microcontroladores 05.- Gamas de los PIC de 8 bits 06.- Caractersticas de los PIC 07.- Perifricos y auto-control 08.- PIC16F690 en BLOQUES 09.- MAPA DE MEMORIA 10.- STATUS 11.- MPLAB IDE y PICkit2 12.- Placa de desarrollo ++ 13.- Nuestro primer programa (ASM) 14.- INSTRUCCIONES PARA BITS 15.- INSTRUCCIONES PARA BYTES 16.- INSTRUCCIONES CON LITERAL Y DE CONTROL 17.- TABLA DE INSTRUCCIONES 18.- Macros 19.- PROGRAMA BLINK.ASM 20.- PROGRAMA ROTATE.ASM 21.- Puertos polivalentes 22.- Diagrama del A/D 23.- Seleccin del canal A/D 24.- Configurar el A/D 25.- Programa A/D A2D.ASM 26.- Rebotes de tecla 27.- Eliminacin de rebotes 28.- Subrutina Delay 29.- OPTION_REG 30.- Interrupciones 31.- INTCON - INTERRUPT CONTROL REGISTER 32.- Timer0 33.- INTERRUPT.ASM 34.- FILTER.ASM 35.- GREYCODE.ASM 36.- 1DIGITO.ASM 37.- 2DIGITOS.ASM 38.- 7SEGMENT.ASM 39.- 7SEG_INTERR.ASM 40.- PWM.ASM 41.- Configuracin 42.- C Led_blink.c 43.- C bsico 1 44.- C bsico 2 45.- Display7seg_int.c 46.- Fichero de listado.lst 47.- Humid.c 48.- LCDs 49.- LCD_rutinas_16F690_PORT-C.c - 1 50.- LCD_rutinas_16F690_PORT-C.c - 2 51.- Humid_LCD.c 52.- PWM_LCD.c 53.- LCD_EasyPIC.c 54.- Timer1_Cal_quartz.c PIC18F1320 55.- Debugging: Teclado.c 56.- Watchdog: Test_WDT.c 57.- Encoder_check.c 58.- Photodiode_PCB_controller.c 59.- P18F2420_ADconverter.c 1 60.- P18F2420_ADconverter.c 2 61.- P18F2420_ADconverter.c 3 62.- P18F2420_ADconverter.c 4 63.- P18F2420_ADconverter.c 5 64.- P18F2420_ADconverter_callertable.txt

Un controlador es un dispositivo que se emplea para el gobierno y/o la monitorizacin de uno o varios procesos. Hace unas dcadas, los controladores se construan exclusivamente con componentes de lgica discreta, posteriormente se emplearon los microprocesadores, que se rodeaban con chips de memoria y E/S sobre una tarjeta de circuito impreso. En la actualidad, todos los elementos del controlador se han podido incluir en un chip, el cual recibe el nombre de microcontrolador. El microcontrolador es un sencillo pero completo computador contenido en el corazn (chip) de un circuito integrado. Un microcontrolador es un circuito integrado de alta escala de integracin que incorpora la mayor parte de los elementos que configuran un controlador. Un microcontrolador dispone normalmente de los siguientes componentes: Procesador o CPU (Unidad Central de Proceso). Memoria para el programa tipo ROM/PROM/EPROM o FLASH. Memoria RAM y EEPROM para contener los datos. Lneas de E/S digitales para comunicarse con el exterior (puertos). Generador de impulsos de reloj que sincronizan el funcionamiento de todo el sistema. Osciladores, contadores y temporizadores. Diversos mdulos para el control de perifricos (puertos serie y paralelo, conversores Analgico/Digital y Digital/Analgico, etc.).

Hoy en da prcticamente todos los microcontroladores se fabrican con tecnologa CMOS (Complementary Metal Oxide Semiconductor) por su alta capacidad de integracin, su bajo consumo y su alta inmunidad al ruido. Entre los ms conocidos podemos citar: 8048 de Intel. Es el padre de los microcontroladores actuales. 8051 de Intel. Es antiguo (~1980) muy popular ya que es producido por varios fabricantes, y su arquitectura se sigue usando para productos nuevos, por ejemplo por ATMEL. 68HC11 de Motorola y Toshiba, precursor de la familia 68xxx de Motorola mucho ms potente. PIC de MicroChip que posee una amplsima gama. Fueron los primeros microcontroladores con arquitectura RISC (Reduced Instruction Set Computer). Trataremos de stos a lo largo de esta presentacin. AVR de ATMEL, nombre genrico de otra amplia gama de microcontroladores muy potentes y rpidos: AT90Sxx, ATtinyxx, ATmegaxx. Todos ellos pueden trabajar en C con herramientas libres. La mayora de stos microcontroladores tienen una arquitectura de buses Von Newman (o Princeton) y conjunto de instrucciones grande (CISC Complex Instruction Set Computer); frente a los PICs que poseen una arquitectura de buses Harvard y un conjunto reducido de instrucciones (RISC). La arquitectura Von Newman (muy comn en casi todos los procesadores como los PCs) tanto las instrucciones del programa como los datos llegan a la CPU a travs de un solo bus, mientras que en la arquitectura Harvard hay dos buses, uno para las instrucciones de programa y otro para los datos; esto permite tener distinto nmero de lneas (ancho del bus) para las instrucciones del que tiene el bus de datos; en el caso de los PICs el bus de instrucciones tiene 12, 14 16 bits y el de datos es de 8 bits; dado que la memoria de programa (FLASH-ROM) est integrada en el chip no existe acceso al bus de instrucciones desde el exterior, la interaccin con el mundo se hace a travs del bus de datos. Programar los PIC en ensamblador es algo ms fcil que para otros tipos porque slo tienen 35 instrucciones (RISC) frente a por ejemplo las 130 de los AVR; en cualquiera de los dos casos suele ser ms fcil y rpido programar en alto nivel (C o BASIC).

Adems de la divisin segn la arquitectura de 8, 16 32 bits, la familia de 8 bits de MicroChip se divide en gamas segn la evolucin que han tenido los microcontroladores. Esta clasificacin es muy dinmica, tanto que actualmente MicroChip habla de tres rangos, en vez de los cinco (histricos) dados en la primera tabla; las gamas enana y baja se han fusionado (baseline), as como las gamas alta y mejorada se integran en la alta (high performance) aunque realmente la antigua gama alta (PIC17Fxxx) ha desaparecido. Esta tabla es pues inexacta y se menciona slo por dar informacin histrica, asimismo dado la rapidsima evolucin del mercado de microcontroladores PIC, los nmeros que se indican corresponden a un momento en el tiempo y por tanto varan al irse introduciendo nuevos productos con mayores prestaciones. Los nombres de los PICs que se dan corresponden a los microcontroladores que tienen memoria FLASH (de ah la F del nombre), aunque tambin existen otros identificativos para distintos tipos de memoria PROM que MicroChip llama OTP (One Time Programming) o tambin con memoria ROM que se crea en el proceso de la fabricacin. La gran ventaja de la memoria FLASH es que puede borrarse y regrabarse muchas veces y es por tanto muy adecuada para el desarrollo o para las actualizaciones en el mismo chip fsico. En la columna de Instrucciones tenemos el tamao de la palabra de la memoria de programa donde se almacenan las instrucciones que ejecuta el microcontrolador, vemos que para las gamas media y alta las instrucciones tienen respectivamente 14 y 16 bits. Nosotros trabajaremos slo con dispositivos con memoria FLASH de las gamas media y alta. Usaremos un sistema de desarrollo llamado PicKit2 Low Pin Count Demo Board, con un PIC16F690; mientras no se especifique lo contrario nos referiremos a ese sistema de desarrollo y tipo de PIC. Para los programas en C usaremos tambin el sistema de desarrollo EASYPIC. Es interesante destacar que la gama PIC18Fxxxx posee un nmero mayor de instrucciones y un mapa de memoria diferente para permitir la programacin de estos dispositivos en C; de hecho MicroChip slo provee un compilador C para la gama alta; afortunadamente otros vendedores han desarrollado compiladores para todas las gamas, aunque con las lgicas limitaciones debidas al hardware. Nosotros usaremos el compilador de Mikroelektronika (www.mikroe.com) del que tenemos licencias, es fcil de usar, tiene buenas libreras y se puede descargar una versin limitada gratuitamente.

La tecnologa CMOS es muy rpida y como no necesita resistencias de polarizacin, prcticamente slo consume energa en las transiciones o si tiene que proporcionar corriente a las cargas que pueda tener en el circuito. Puede funcionar con un rango de voltajes de 2 a 5.5 V. Para aplicaciones con bateras el PIC puede detener el reloj (modo sleep) y dado que la RAM es esttica el consumo es virtualmente cero en dicho modo. El procesador RISC con arquitectura Harvard facilita implementar un esquema pipeline, o sea que la lectura de la siguiente instruccin se hace *durante* la ejecucin de la instruccin actual lo que permite una mayor velocidad de ejecucin en casi todos los casos. Slo cuando se ejecuta un salto en el programa se tiene que desechar la instruccin leda, por lo que las instrucciones de salto requieren el doble de tiempo que las otras. De cualquier manera cada instruccin necesita 4 ciclos de reloj para ejecutarse, y segn hemos visto las de salto requieren 8 ciclos de reloj para su ejecucin. As pues si un PIC tiene un reloj de 8 MHz, como mximo ejecuta a una velocidad de 2 MIPS (Mega Instrucciones por Segundo) y como mximo los PIC de gama alta alcanzan 10 MIPS a 40 MHz; los procesadores de ATMEL ejecutan una instruccin por ciclo de reloj llegando a 20 MIPS. Los PIC tienen un solo registro llamado W (Working register) conocido tambin como acumulador, que lgicamente tiene 8 bits. El bus de direcciones de acceso a la memoria es de 7 bits, por lo que slo se puede acceder a 128 bytes simultneamente; para los PICs que tienen ms memoria esta se organiza en bancos de 128 bytes o menores; para cambiar de banco de memoria hay que ejecutar ciertas instrucciones, lo que introduce cierta complejidad y propensin a tener fallos en los programas. Esta es posiblemente la caracterstica menos agradable de los PIC y siempre debe tenerse en cuenta, al menos trabajando en ensamblador; en C el compilador se encarga de ello aunque por un precio en la eficiencia, ya que se hacen muchas operaciones de seleccin de banco de memoria que no son necesarias. La nomenclatura que usa McroChip para referirse a las celdas de memoria es un tanto confusa ya que a veces se refiere a ellas como registros y otras como file (nombre ciertamente desafortunado ); los microcontroladores de ATMEL tienen 16 autnticos registros que son independientes de la memoria, siendo ste el concepto ms usual de registro. El control de perifricos se hace simplemente escribiendo en una determinada celda de memoria genricamente llamada SFR (Special Function Register). Por ejemplo, si quiero poner a nivel lgico alto todas las patillas del puerto A, tendr que escribir FFh en la direccin de memoria 5, ya que esta es la direccin asignada para leer y/o escribir en el puerto A. Por tanto para controlar los dispositivos del PIC se usa exactamente la misma instruccin que para almacenar un dato en memoria, solo que para ciertas posiciones de memoria (SFRs) estaremos accediendo a un dispositivo fsico que nos conecta con el mundo real; esto es lo que significa que los perifricos est mapeados en la memoria. /******** Continua en la diapositiva siguiente ********/

Este PIC tiene un oscilador interno a 8 MHz de tipo RC, poco estable (+-1%) con un divisor hasta 32 kHz; tambin puede trabajar con un cuarzo o resonador externo hasta 20 Mhz. El modo de funcionamiento se selecciona en el momento de la programacin del dispositivo y no puede cambiarse durante la ejecucin (aunque con el oscilador interno se puede seleccionar el factor de divisin). El PIC posee dos memorias permanentes, la FLASH con palabras de 14 bits contiene el programa grabado en el chip en el momento de la programacin o quemado (burn) del dispositivo y la EEPROM el la que se puede almacenar datos durante la ejecucin. La FLASH permite ms de 100.000 escrituras, mientras que la EEPROM ms de 1.000.000, aunque por velocidad y durabilidad nunca debe usarse como RAM. La retencin de datos en ambos casos es mayor de 40 aos. Los comparadores permiten generar una interrupcin o cambiar el estado de un SFR (registro en memoria) segn el valor de un voltaje analgico sea mayor o menor de una referencia que puede ser interna o externa. Temporizadores: Timer0 de 8 bits con pre-scaler, Timer1 de 16 bits puede trabajar como contador, con pre-scaler y oscilador independiente, y el Timer2 de 8 bits con pre y post-scaler. El mdulo ECCP+ (Enhanced Capture Compare) trabaja con los temporizadores para medir tiempos con mucha precisin y tambin controla un generador de PWM (Pulse Width Modulation) con 10 bits de resolucin y 1, 2 4 salidas. EUSART es el Enhanced Universal Synchronous Asynchonous Receiver Transmitter, soporta RS485, RS232, LIN e I2C; es capaz de auto-detectar la velocidad y puede generar una interrupcin con el bit de start. El mdulo ICSP (In-Circuit Serial Programming) es el que nos permite programar el dispositivo sin necesidad de retirarlo de su PCB (con algunas condiciones). Para algunos PICs tambin permite hacer debugging en la PCB. Al entrar en modo sleep se detiene el oscilador, se vuelve a la ejecucin despus de un reset o una interrupcin; cada vez que se re-inicia el PIC el mdulo PWRT (Power-up Timer) espera el arranque del reloj antes de empezar la ejecucin y el OST (Oscillator Start-up Timer) permite incrementar la espera hasta la estabilizacin completa del mismo (1024 periodos). El POR (Power-on Reset) genera una seal limpia de reset independientemente de la pendiente de subida de la alimentacin; el BOR (Brown-out Reset) generar un reset si la tensin de alimentacin baja de un cierto nivel preestablecido. El mdulo watchdog (WDT) es un temporizador totalmente independiente de la ejecucin del programa que generar un reset pasado un cierto tiempo, que es configurable, sin que la ejecucin de programa haya limpiado este temporizador; normalmente se usa para evitar que el procesador se quede colgado o ejecutando un bucle sin fin indeseado.

Vemos aqu el diagrama de bloques del PIC16F690 cada salida o entrada est marcada con un cuadrado con una X, y en la diapositiva anterior veamos tambin la asignacin de los 20 pines del dispositivo. Observamos que tenemos del orden de 50 salidas/entradas, lo que obviamente nos lleva a concluir que no todos los mdulos podrn usarse simultneamente en una determinada aplicacin. As por ejemplo la patilla RA3/MCLR/Vpp puede funcionar como lnea 3 del puerto A, o como master clear (reset) o se usa para introducir el voltaje de programacin del dispositivo (Vpp) que es de unos 12 voltios. En la configuracin del dispositivo tenemos que decidir si esta patilla se usar como puerto general RA3 o como reset, y en cualquier caso la circuitera conectada a esta patilla deber tener en cuenta que se le puede aplicar un voltaje alto (hasta 13.5 V) si se quiere tener la posibilidad de programar el dispositivo en su placa de aplicacin (in-circuit). De izquierda a derecha y arriba hacia abajo tenemos: la memoria de programa 4k x 14 bits; el contador de programa PC; la pila subrutinas de 8 niveles; el bus de datos de 8 bits; la RAM 256 bytes; los puertos de propsito general A, B y C; el bus de programa de 14 bits; la unidad de procesamiento aritmtico/lgico ALU con sus registros asociados; el descodificador de instrucciones; el mdulo de control; oscilador interno; la unidad generadora de tiempos; los temporizadores 0, 1 y 2; el mdulo de comunicaciones serie asncronas EUSART; el mdulo de captura/comparacin mejorado ECCP+; puerto de comunicaciones sncronas SSI; el mdulo conversor A/D de 10 bits con 12 entradas y una referencia; mdulo de comparadores; y la memoria EEPROM de 256 bytes.

Vemos aqu la memoria RAM organizada en 4 bancos, de 128 bytes cada uno. Los PIC slo pueden acceder a un banco a la vez. El motivo de estas limitaciones es que los cdigos de las instrucciones de ensamblador (conocidos como opcodes) tienen slo 7 bits para especificar la direccin de la celda de memoria; en los PIC de gama alta cuyas instrucciones son de 16 bits los bancos son de 256 bytes, y pueden tener hasta 16 bancos o sea 3.9 kB de RAM. Todas las celdas con nombre son los llamados SFR (Special Function Registers) que estn relacionadas directamente con el hardware; las celdas marcadas en gris son inexistentes para el PCI16F690; si por error se accede a ellas se lee siempre 0 y si se escribe en ellas el hardware lo ignora. Vemos tambin que el nmero total de bytes de la RAM es de 96 en banco 0, y 80 en los bancos 1 y 2; lo que nos da un total de 256 bytes para almacenamiento general. El SFR STATUS est presente en todos los bancos y vemos que su direccin (File Address) es 3. STATUS es el registro que contiene los bits de estado de la CPU y tambin 2 bits llamados RP1 y RP0 que son los que nos permiten establecer el banco al que estamos accediendo. (Ver diapositiva siguiente para ms detalles). Las posiciones F0 a FF en el banco 1, las 170 a 17F en el banco 2 y las 1F0 a 1FF en el banco 3, estn mapeadas a las 16 posiciones 70 a 7F en el banco 0; esto es que sea cual sea el banco que tengamos seleccionado siempre leeremos lo mismo en stas 16 posiciones del final del banco. Esto es muy til para almacenar datos que debern ser accesibles en varias partes del programa y no tendremos que hacer cambios de banco para acceder a ellos. Asimismo esta zona de memoria es fundamental a la hora de trabajar con las interrupciones, ya que dado que la interrupcin puede llegar en cualquier momento, necesitamos un rea de memoria donde guardar el estado del procesador, incluyendo la informacin del banco en el que se est trabajando inmediatamente antes de ejecutar la interrupcin y luego volver a el mismo estado antes de retomar la ejecucin normal del programa.

Este es un ejemplo de cmo se especifica el significado de los bits de un SFR (STATUS en este caso) en el datasheet del PIC. El indicativo R/W-0 que aparece encima de algunos bits, significa que el bit es legible (R) y escribible (W) y que su valor despus de un reset de la alimentacin es 0. Observe otros casos como R/W-x o R-1 que se explican en la leyenda. De este registro son especialmente importante los bits de acarreo (C carry) y cero (Z) que nos indican si la ltima operacin ejecutada produjo rebosamiento (overflow) o dio como resultado 0 respectivamente. Los bits RP0 y RP1 son los que seleccionan los bancos de memoria RAM.

Entre los dispositivos contenidos en los PIC posiblemente los puertos son los ms usados. Cada puerto tiene como mnimo dos SFRs asociados: uno para las lecturas del propio puerto PORTx y otro denominado TRISx que determina para cada bit si se usar como entrada o salida digital. El nemnico TRISx proviene de TRi-State logic ya que cada patilla de los puertos tiene un driver tri-state, o sea que adems de los estados alto 1 o bajo 0, tiene un tercer estado conocido como de alta impedancia, lo que significa que la lnea del puerto queda libre o flotante. Por tanto si activamos un bit de la palabra TRISA, estaremos activando el estado de alta impedancia en el driver de esa lnea y por tanto podremos leer el estado que ponga en ella la circuitera exterior, o sea, esa lnea queda configurada como una lnea de entrada. Inversamente si ponemos un 0 en un bit de la palabra TRISA, estamos activando su lnea correspondiente como lnea de salida. Podemos escribir en el SFR TRISx en cualquier momento de la ejecucin del programa, pero lgicamente si por error se activa como salida una lnea conectada a una salida de la circuitera externa se puede producir un corto-circuito de los dos drivers (el del PIC y el de la circuitera externa) que podra ocasionar averas; es importante por lo tanto que el programador defina correctamente las lneas de entrada y salida de todo el dispositivo.

10

Lgicamente antes de trabajar con los PICs hay que instalar el software (gratuito) MPLAB y PICkit2 que son el programa ensamblador y el programador/debugger de MicroChip que usaremos. En la web de www.MicroChip.com existe una cantidad ingente de informacin, sobre estos programas, datasheets, application notes, etc. Una vez instalados conectamos el PICkit2 con un cable USB, si todo est bien el dispositivo ser reconocido y queda listo para trabajar. MPLAB es muy potente y entre sus muchas cualidades est la de tener un simulador, que en caso de no disponer de un PIC real, se puede usar para aprender o hacer desarrollos y pruebas. En MPLAB pulsamos File->Open Hello World.asm vemos que se abre una ventana con el fichero de texto que usa distintos colores para resaltar la sintaxis; luego Programmer->Select Programmer->PICkit2 y ejecutamos Project->Quick Build Hello World.asm (deber estar seleccionada la ventana con este programa fuente para que esta opcin est activada). Debemos chequear tambin que el sistema ha reconocido el PIC que tenemos conectado, clickar en Configure->Select Device y comprobar que aparece el PIC16F690 en nuestro caso. Si queremos que aparezcan los nmeros de lnea en la ventana del texto del programa, hacer click con el botn derecho sobre la ventana y seleccionar Properties, luego en la pestaa de ASM File Types marcar Line Numbers o cualesquiera otra opcin a nuestro gusto. Despus una vez que la compilacin ha terminado correctamente, podremos escribir el programa en el PIC, para esto ejecutar Programmer->Program en MPLAB, o en PICkit2 Programmer seleccionar File->Import HEX, y pulsar el botn Write. En la ventana PICkit2 Programmer podemos pulsar el botn Read y vemos que el dispositivo tiene las primeras posiciones de la memoria de programa (FLASH) escritas, todos los bits que no estn escritos aparecen como 1, y dado que el PIC tiene 14 bits por palabra, en hexadecimal vemos 3FFF en todas las dems posiciones de memoria. Si pulsamos On en la zona de VDD PICkit 2, aplicaremos la tensin que aparece a la derecha (5.0 V en nuestro caso) a la tarjeta de desarrollo, e inmediatamente veremos que el programa grabado se ejecuta. En este caso muy simple el programa tiene slo 5 instrucciones tal y como puede verse en la ventana del PICkit2 programmer, y al ejecutarse enciende el LED conectado a la patilla 0 del puerto C.

11

Vemos en la diapositiva el dispositivo PICkit2 de MicroChip conectado a placa de desarrollo Low Pin Count que ha sido modificada por el autor para probar las distintas funcionalidades del microcontrolador. La tarjeta de desarrollo original contiene el PIC, los 4 LEDs rojos, el potencimetro y el pulsador; se han aadido los transistores T1 y T2 junto con los displays de 7 segmentos; el sensor de humedad; el display LCD y el MOSFET T3 para controlar el pequeo motor de corriente continua. Disponemos tambin de otras tarjetas de demostracin con un PIC16F887 SMD de 44 pines. Ambos tipos de tarjetas son muy convenientes para ser usadas en aplicaciones pequeas que no requieran mucha circuitera extra, o como un mdulo que se puede integrar en un sistema mayor a travs de los conectores adecuados para aplicaciones ms grandes. A lo largo de esta introduccin veremos programas para medir y/o controlar todos y cada uno de estos dispositivos. A veces usaremos tambin alguna placa de desarrollo de MikroElektronika, llamadas EasyPIC que son mucho ms grandes y tienen por tanto mayor nmero de dispositivos y posibilidades. Para la mayora de los programas en C usaremos los EasyPIC, aunque tambin algunas aplicaciones para la placa de desarrollo modificada para ilustrar el uso del programador PICkit2 con programas desarrollados con mikroC. La mayora de los programas en ensamblador que veremos a continuacin son los desarrollados por MicroChip para ilustrar la forma de programar los PICs con MPLAB, y como se pueden cargar los programas en la propia placa de aplicacin sin necesidad de sacar el PIC del circuito, esto es lo que se conoce como ICSP (In-Circuit Serial Programming); que ya habamos mencionado como una de las grandes ventajas de los PIC.

12

Este pequeo programa est escrito en lenguaje ensamblador de los PICs, cabe destacar: Todo lo que est detrs de un punto y coma se considera comentario. Las lneas que empiezan con # son directivas para el preprocesador, tambin en general las que empiezan por __ y algunas palabras especiales como org o end que son palabras reservadas. Ninguna directiva produce cdigo ejecutable, pero pueden modificar la forma en que el compilador lo produce. La directiva #include carga una serie de definiciones especficas para el PIC con el que estamos trabajando, contenidas en el fichero p16F690.inc en un cierto directorio de MPLAB. Esto nos permite usar en el programa nombres para las posiciones de memoria y los SFR (Special Function Registers) que hacen el programa ms legible y fcil de entender. Si no pusiramos esta directiva el compilador se quejara, ya que no sabra que significa STATUS, PORTC, RP0, etc. La directiva __CONFIG se usa para especificar los bits de configuracin del PIC, stos bits se escriben en el momento de la escritura de la memoria de programa y configuran dispositivos como el oscilador, el watchdog, los retardos de arranque, el detector de voltaje bajo (brown-out), etc. Nota: no confundir la palabra de configuracin (CONFIG ver datasheet pg. 174) con un SFR, ya que esta palabra slo se escribe en el momento del burning del dispositivo y no puede cambiarse durante la ejecucin. La directiva org x establece que el cdigo que sigue debe ensamblarse a partir de la direccin x. En este caso 0 es la direccin por la que siempre empiezan los PICs a ejecutar despus de un reset. La palabra Start sin indentar es una etiqueta, o sea es un identificativo para esa posicin del programa a la que podremos dirigirnos con la orden goto Start. La sintaxis ms usada aade dos puntos : al final de las etiquetas pero MPLAB puede trabajar de las dos formas. El comando bsf f,b significa Bit Set File la_celda_f, el_bit_b; al ejecutarse pone a 1 (set) el bit b de la celda f, en nuestro caso es el bit 5 (RP0) de la celda 3 (STATUS), y por tanto a partir de ese momento accederemos al banco 1 de la memoria RAM. (Pregunta: por qu sabemos que el bit RP1 es 0 ?). El comando bcf f,b significa Bit Clear File la_celda_f, el_bit_b; al ejecutarse pone a 0 (clear) el bit b de la celda f, en nuestro caso es el bit 0 de la celda 87h (TRISC), y por tanto la lnea 0 del puerto C se activa como salida. Luego seleccionamos el banco 0: bcf STATUS,RP0. Finalmente activamos el bit 0 del puerto C: bsf PORTC,0. Pone un 1 en el bit bajo de la celda 07 (PORTC). Dado que este puerto tiene un LED enchufado (a travs de una resistencia) el LED se encender. El comando goto $ salta sobre s mismo de forma que ya no se ejecuta nada ms. El goto es un salto a la direccin especificada, y $ significa la posicin actual, tambin puede usarse con incrementos, por ejemplo goto $-1 salta al comando anterior. End es una directiva que le indica al compilador que no siga compilando, o sea fin del programa.

13

Ya hemos visto las instrucciones BSF y BCF en el ejemplo anterior. Pregunta: sobre qu banco acta la instruccin: BCF 0x71,2? R: En principio en general el banco depende de las instrucciones de seleccin de banco que se hayan ejecutado antes, y por tanto en esa parte de programa que se ha dado como ejemplo no aparece ninguna operacin de seleccin de banco, luego parece que no podramos determinarlo con la informacin que tenemos. Sin embargo la direccin 0x71 est en la zona de memoria mapeada en todos los bancos, o lo que es lo mismo independiente del banco; luego la respuesta correcta es en todos los bancos. Las instrucciones BTFSC y BTFSS (Bit Test File Skip if Clear o Set) nos permiten ejecutar o no la siguiente instruccin en funcin del valor del bit que se testea. Notar que la lgica de estas instrucciones es un poco confusa ya que la siguiente instruccin no se ejecuta cuando la condicin es verdadera, sino falsa; ya que la instruccin est formulada negativamente (skip = no ejecutar), podramos decir: test bit, no ejecutar si clear.

14

Casi todas estas instrucciones tienen un sufijo ,d que es opcional y determina el destino del resultado de la operacin; d puede valer 1 0 segn el destino sea la celda de memoria o el acumulador (W) respectivamente. Realmente esto es un truco para mantener bajo el nmero de instrucciones (arquitectura RISC) pero a la vez cada una de estas instrucciones puede ejecutarse de una forma o de otra. Por defecto d=1, o sea que si no se especifica d el destino es la posicin de memoria. En los include de MPLAB estn definidas f=1 y w=0, para hacer el cdigo ms fcil de entender; esto nos permite por ejemplo usar: incf
Contador

Contador,f Contador,w ;

Contador = Contador + 1; equivalente a incf

incf

W = Contador + 1, la variable Contador no cambia

El funcionamiento de cada instruccin est dado en la columna Function y no hay mucho ms que aadir; cabe quizs mencionar que la operacin NOP se puede usar para introducir un pequeo retardo, pero hay que considerar que este retardo depende de la velocidad de reloj lgicamente. Como todas las instrucciones simples NOP se ejecuta en 4 ciclos de reloj. Tambin son interesantes los comandos DECFSZ y INCFSZ que se usan para hacer bucles; por ejemplo para ejecutar 100 veces determinado grupo de instrucciones podramos hacer: movlw .100 ; NOTA: esta es la manera por defecto de introducir nmeros en decimal movwf Contador Loop100: < grupo de instrucciones a ejecutar .> decfsz Contador goto Loop100 vuelve a ejecutar el Loop ; si el Contador es != 0

Los PIC son atpicos en el sentido de que muchas de stas operaciones hay que aplicarlas forzosamente sobre la memoria, y no existe la posibilidad de aplicarlas directamente al acumulador (W), como por ejemplo: COMF, DECF, INCF, RLF, RRF o SWAPF.

15

Estas instrucciones nos permiten introducir datos fijos (literal) en contraposicin a los datos que podemos almacenar en la memoria, que pueden cambiar durante la ejecucin de un programa. Los PIC de gama media tiene una pila de subrutinas de 8 niveles, y de 31 niveles para los de gama alta. Una subrutina es una porcin de cdigo a la que se puede llamar desde varias posiciones en el programa, y una vez acabada la ejecucin de la subrutina, la ejecucin vuelve a la instruccin siguiente a la de la llamada a la rutina. A su vez dentro de una rutina, se puede llamar a otra subrutina y as sucesivamente hasta un mximo de 8 niveles ( 31 para la gama alta). Normalmente hay que tener en cuenta que si se usan interrupciones tendremos que reservar tantos niveles como se usen en el programa ms los de la rutina de interrupciones ms uno, que es el nivel que usa la propia interrupcin. La llamada a una rutina se hace con CALL <etiqueta>, y el retorno desde las rutinas se hace al ejecutar RETURN o RETLW xx; desde la rutina de servicio de la interrupcin hay que usar RETFIE. Nota: TOS significa Top Of Stack, es un puntero a la pila donde se almacenan las posiciones de retorno de las rutinas. PC significa Program Counter y es la direccin de la instruccin de programa que se est ejecutando. El comando CLRWDT es el que resetea el contador del watchdog (WDT), evitando la re-inicializacin del procesador si el watchdog est activado y se ha consumido el tiempo de gracia. Los comandos OPTION y TRIS se mantienen por compatibilidad con PICs antiguos, no son ya necesarios y las prximas versiones no los incorporarn.

16

Notes 1: When an I/O register is modified as a function of itself (e.g., MOVF PORTA, 1), the value used will be that value present on the pins themselves. For example, if the data latch is 1 for a pin configured as input and is driven low by an external device, the data will be written back with a 0. 2: If this instruction is executed on the TMR0 register (and where applicable, d = 1), the prescaler will be cleared if assigned to the Timer0 module. 3: If the Program Counter (PC) is modified, or a conditional test is true, the instruction requires two cycles. The second cycle is executed as a NOP.

Esta es la tabla completa de las 35 instrucciones de los PIC de gama media. Los tiempos de ejecucin son casi siempre 1 ciclo, equivalente a 4 periodos del reloj; para las operaciones que requieren un salto son dos ciclos. Es importante observar los bits del registro de estado (STATUS) que se ven afectados por las operaciones; las operaciones aritmticas (ADD, SUB) afectan a C, Z y DC; las operaciones lgicas slo a Z, las de rotacin slo al C y cabe destacar que las operaciones INCFSZ, DECFSZ, MOVxx o SWAPF no afectan a ningn bit de STATUS, excepto MOVF que afecta a Z. Observe en la columna del opcode o cdigo de la instruccin que la direccin de memoria (o file) tiene 7 bits, esto es lo que determina que el tamao de los bancos de memoria sea de 128 bytes ya que esto es lo mximo que podemos direccionar con los 7 bits de la instruccin. Anlogamente las direcciones de salto de GOTO y CALL tienen 11 bits, lo que nos permite un rango de salto de 2048 (lo que se conoce como una pgina), para saltos a ms distancia hay que hacer uso del SFR PCLATH, que nos permite acceder a todas las pginas que tenga la memoria de programa del dispositivo.

Al principio es til tener esta tabla a mano para entender los programas y al escribir nuestros primeros programas en ensamblador.

17

Adems de las 35 instrucciones mencionadas anteriormente, MicroChip ha introducido esta serie de instrucciones especiales (en realidad macros) para facilitar la programacin. Las macros son instrucciones del pre-procesador (no confundir con las instrucciones ejecutables simples) que especifican una equivalencia del nombre de la macro con el cuerpo de la definicin de dicha macro y estn soportadas por MPLAB; as por ejemplo podramos definir en un programa: #define SALTA GOTO

esta sentencia define la macro SALTA como GOTO; las macros son sustituidas antes de la compilacin por su equivalente, luego en este caso si tenemos una sentencia en el programa SALTA Posicion3, es equivalente a GOTO Posicion3. MicroChip introduce estas macros en los ficheros #include p16F690.inc que acompaan al MPLAB y que adems contienen los nombres de los SFRs de cada tipo de microcontrolador como ya habamos mencionado. Todas estas instrucciones se obtienen en realidad como una combinacin de las instrucciones simples que realmente son las que ejecuta el microcontrolador, por este motivo estas instrucciones rompen la regla de que todas las instrucciones de los PIC se ejecutan en 4 u 8 ciclos de reloj, ya que por ejemplo BNZ puede tardar 12 ciclos; otras sin embargo no son ms que nombres ms adecuados a los poco afortunados BTFSC o BTFSS. En los PIC de gama alta la mayora de estas instrucciones son instrucciones reales, y adems se han introducido otras muchas hasta un total de 83 por lo que ya no pueden considerarse estrictamente RISC, pero este juego de instrucciones extendido est indicado y fue diseado para permitir la programacin de estos dispositivos en lenguajes de alto nivel, particularmente el C. Algunas de las instrucciones extendidas hay que activarlas en la configuracin del dispositivo.

18

Entre las lneas 31 y 34 tenemos la declaracin de dos variables Delay1 y Delay2 usando la directiva cblock, que le indica al compilador que necesitamos un bloque de datos a partir de la posicin de memoria 0x20. En realidad en este caso lo nico que estamos haciendo es asignar a los smbolos Delay1 y Delay2 los valores 0x20 y 0x21 respectivamente, pero si tenemos un bloque de memoria con ms elementos es ms cmodo que el compilador se encargue de asignarle sus posiciones consecutivas. El programa es simple: primero inicializa el puerto C, enciende el LED, espera un cierto tiempo en un doble bucle y apaga el LED, vuelve a esperar la misma cantidad de tiempo y salta a la sentencia que enciende el LED para repetir todo el proceso desde ese punto. Preguntas: Por qu no se inicializan las variables Delay1, Delay2 ? es esto correcto ? R: no pero slo afecta al primer bucle, por lo que es casi irrelevante. Hay dos partes en el cdigo que son idnticas y por tanto este programa se puede estructurar mejor creando una subrutina. Se propone como ejercicio. Tambin se puede re-disear la rutina del ejercicio anterior para que acepte una variable que determine el retardo total. De esta forma se hace mucho ms fcil hacer ms rpido o lento la velocidad de parpadeo; se puede tambin cambiar la relacin encendido/apagado o incluso modificarlo durante la ejecucin del programa para que poco a poco vaya acelerando y vuelta a empezar.

19

Esencialmente el programa (Rotate.asm) es muy similar al anterior. Ahora definimos todo el puerto C como de salida y usamos la variable Display para almacenar el valor de los bits que pasamos a los cuatro diodos LED y para realizar la rotacin, tal y como puede verse en las lneas 60 a 64. Se podra usar PORTC como variable en vez de Display ? La respuesta es s, y en la mayora de los casos funcionara bien; pero podra darse un efecto colateral indeseado, ya que si por ejemplo la lnea 3 del puerto est cortocircuitada la lectura encubierta que se hace durante la rotacin (rrf PORTC,f) nos devolvera 0 al intentar activar la lnea 3 y por tanto la rotacin se parara en dicho punto. Ejercicio: Hay un error intencionado en el programa, relacionado con un comentario. Cual es ? Respuesta en la siguiente diapositiva.

20

Vemos aqu el diagrama hardware de dos lneas ms o menos tpicas de los puertos que podemos encontrar en los PIC. La complejidad que podemos observar se debe a las mltiples conexiones posibles de algunos pines; en el caso de la derecha se trata del pin RC7 (PORTC.7) y como vemos est conectado al conversor A/D y hay varios drivers y dos flipflops para el control de la lnea. El flip-flop de PORTC es el que proporciona el dato de salida, mientras el flip-flop TRISC controla la habilitacin del driver de salida. Vemos tambin que la lectura digital de este pin slo puede hacerse si no est habilitada su lnea de seleccin de modo analgico, ya que si el modo analgico est seleccionado para esta lnea siempre leeremos 0 lgico. Tambin observamos que para esta lnea se puede seleccionar la funcin SDO, en cuyo caso el nivel lgico a la salida es independiente del valor escrito en el flip-flop del PORTC. En el diagrama de la izquierda, que corresponde a la patilla RA2 (PORTA.2), vemos que adems de los flip-flops para PORTA y TRISA, tenemos WPUA e IOCA; stos SFRs controlan la funcionalidad de weak-pull-up y la de interrupt-on-change que posee este pin; anlogamente existen WPUB e IOCB con las mismas funcionalidades para el puerto B. Tambin vemos que tiene funciones relativas al TMR0 y tiene la capacidad de generar interrupciones desde dispositivos externos (INT). Una caracterstica que afecta a todas las lneas de los puertos de los PIC es que *siempre* usan una secuencia de lectura-modificacin-escritura (Read-Modify-Write o RMW), lo que significa que cualquier instruccin que especifique un registro ejecuta un ciclo RMW, esto es, el registro es ledo, los datos modificados, y el resultado es almacenado de acuerdo con la instruccin. En otras palabras: una operacin de lectura se lleva a cabo en el registro incluso si la instruccin aparentemente slo escribe en dicho registro. Esto puede tener resultados indeseados en la interaccin con el hardware, que el programador siempre debe de tener en cuenta. En los PIC de gama alta existe un tercer registro (LATA, LATB, etc) que puede leer el dato del latch no el del puerto de salida.

Respuesta al ejercicio de la diapositiva anterior: movlw 13 no da 1 centsima de segundo, ya que 13h es 19d y por tanto el tiempo es un 50% mayor.

21

Vemos que tiene 14 entradas posibles, que se selecciona con los 4 bits CHS<3:0> de la palabra ADCON0. La entrada seleccionada se digitaliza con 10 bits (0 .. 1024 niveles) comparada con la tensin de referencia. Esta tensin de referencia se puede seleccionar con el bit VCFG, bien la tensin de alimentacin o una tensin menor aplicada a la patilla RA1. El SFR que controla casi todo esto es:
ADCON0 A/D CONTROL REGISTER (ADDRESS: 1Fh) -------------------------------------------------------------------------------------| R/W-0 | R/W-0 | R/W-0 | R/W-0 | R/W-0 | R/W-0 | R/W-0 | R/W-0 | -------------------------------------------------------------------------------------| ADFM | VCFG | CHS3 | CHS2 | CHS1 | CHS0 |GO/DONE|ADON | -------------------------------------------------------------------------------------bit 7 bit 0 bit 7 ADFM: A/D Result Formed Select bit 1 = Right justified (ADRESH contains the two most significant bits) 0 = Left justified (ADRESH contains the eight most significant bits) bit 6 VCFG: Voltage Reference bit 1 = VREF pin 0 = VDD bit 5-2 CHS<3:0>: Analog Channel Select bits 0000 = Channel 00 (AN0); 0001 = Channel 01 (AN1); 1011 = Channel 11 (AN11) 1100 = CVREF 1101 = VP6 Esta es una referencia de voltaje interna de 0.60 V 1110 = Reserved. Do not use. 1111 = Reserved. Do not use. bit 1 GO/DONE: A/D Conversion Status bit 1 = A/D conversion cycle in progress. Setting this bit starts an A/D conversion cycle. This bit is automatically cleared by hardware when the A/D conversion has completed. 0 = A/D conversion completed/not in progress bit 0 ADON: A/D Enable bit 1 = A/D converter module is enabled 0 = A/D converter is shut off and consumes no operating current

22

La seleccin de canal es muy sencilla, hay un bit para cada canal en los SFR ANSEL y ANSELH. Notar que para el PIC16F690 por defecto todos los canales estn en modo analgico despus de un reset. IMPORTANTE: hay grandes diferencias en la forma de seleccionar los canales analgicos de un modelo de PIC a otro y es *imprescindible* consultar la documentacin para el correcto control de las entradas. Un aspecto importante del control del convertidor A/D es la velocidad de conversin, que se establece con el SFR ADCON1; este SFR tiene slo 3 bits del 6 al 4 con el siguiente significado: ADCON1 A/D CONTROL REGISTER 1 (ADDRESS: 9Fh) ADCS2 ADCS1 ADCS0 bit 7 bit 0 bit 7 Unimplemented: Read as 0 bit 6-4 ADCS<2:0>: A/D Conversion Clock Select bits 000 = FOSC/2 001 = FOSC/8 010 = FOSC/32 x11 = FRC (clock derived from a dedicated internal oscillator = 500 kHz max) 100 = FOSC/4 101 = FOSC/16 110 = FOSC/64 bit 3-0 Unimplemented: Read as 0

23

These steps should be followed for an A/D conversion: 1. Configure the A/D module: Configure analog/digital I/O (ANSx) Select A/D conversion clock (ADCON1<6:4>) Configure voltage reference (ADCON0<6>) Select A/D input channel (ADCON0<5:2>) Select result format (ADCON0<7>) Turn on A/D module (ADCON0<0>) 2. Configure A/D interrupt (if desired): Clear ADIF bit (PIR1<6>) Set ADIE bit (PIE1<6>) Set PEIE and GIE bits (INTCON<7:6>) 3. Wait the required acquisition time. 4. Start conversion: Set GO/DONE bit (ADCON0<1>) 5. Wait for A/D conversion to complete, by either: Polling for the GO/DONE bit to be cleared (with interrupts disabled); OR Waiting for the A/D interrupt 6. Read A/D Result register pair (ADRESH:ADRESL), clear bit ADIF if required. 7. For next conversion, go to step 1 or step 2 as required. The A/D conversion time per bit is defined as TAD. A minimum wait of 2 TAD is required before the next acquisition starts.

24

Este programa configura el conversor A/D para leer la entrada PORTA(0) = AN0 = RA0 comparada con la tensin de alimentacin (referencia Vdd); el reloj de digitalizacin es de Fosc/16. Ntese el uso de NOPs para introducir un pequeo retardo desde la activacin del A/D hasta el inicio de la conversin. Este retardo se necesita para que se estabilice el voltaje a la entrada del mdulo A/D. Aqu introducimos una manera ms cmoda de seleccin de bancos de memoria: en vez de poner explcitamente los bits RP0 y RP1 de la palabra STATUS, usamos la directiva banksel <nombre_registro>, el pre-compilador introducir los comandos BSF o BCF necesarios para seleccionar el banco en el que se encuentra el registro nombrado. Advertencia: el uso de BANKSEL siempre pone todos los bits de seleccin de banco al valor necesario, esto puede ser ineficiente ya que la mayora de las veces no es necesario cambiarlos todos. Por otra parte el compilador siempre genera un warning cuando accedemos a cualquier posicin de memoria fuera del banco 0, tal y como puede verse en la figura referido a la lnea 63 del programa donde se accede a TRISA que est en el banco 1, y aunque tengamos la directiva BANKSEL en la lnea anterior Esto es claramente mejorable por parte de los desarrolladores de MPLAB, si bien es cierto que no siempre se puede asegurar que en alguna otra parte del programa haya un salto a la lnea 63 y podra darse el caso de que se accediera a otro banco. Es responsabilidad del programador asegurarse de que este problema no se presenta. Al ejecutar este programa sobre la tarjeta de desarrollo podemos cambiar el valor del voltaje en RA0 usando el potencimetro en la placa y veremos como los 4 LEDs presentan los bits ms significativos de la conversin, siendo equivalente a la presentacin en binario del valor del voltaje/Vdd * 16, ya que se trata de los 4 bits ms significativos. Es interesante mover el potencimetro cerca de la transicin entre 7 y 8 por ejemplo y veremos como el ruido afecta a las conversiones, ya que observaremos cambios en la lectura debido normalmente a interferencias.

25

Este programa (rebotes.asm) demuestra que si no se toman precauciones, las operaciones manuales de pulsacin de teclas producen rebotes, es decir el interruptor se activa y desactiva ms veces de las que deseamos. A la entrada del puerto A<3> tenemos una resistencia de pull-up y un pulsador a tierra. Notar cmo a la hora de configurar la patilla 3 del puerto A como entrada digital, no basta con hacer el TRISA<3> igual a 1, sino que en general tambin se requiere deshabilitar la entrada analgica correspondiente en el registro ANSEL. Preguntas: Son correctos los comentarios del programa ? Cundo se actualiza la cuenta, al pulsar o al soltar la tecla ? R: Los comentarios de las lneas 31 y 36 estn intercambiados, por lo se la cuenta se incrementa al soltar la tecla. Ejercicio: modificar el programa para que cuente cambios de estado de la tecla, o sea en cada pulsacin debera contar 2 veces, uno al pulsar y otro al soltar la tecla.

26

Esta versin del programa llamada Debounce.asm, espera a detectar la tecla estable 5 veces, con un retardo de 1 ms entre cada testeo. La eliminacin de los rebotes tambin se puede hacer por mtodos hardware tal y como puede verse en las dos figuras de la diapositiva; en la primera por filtrado (paso bajo) y en la segunda usando un flip-flop tipo RS. Obviamente usando la potencia de microcontrolador nos ahorramos la colocacin de esta circuitera externa y simplificamos nuestro diseo. Observar como se puede comprobar si una variable (Counter en este caso) alcanza un determinado valor en las lneas 73 y siguientes.

27

Entre los programas fuente que acompaan la presentacin encontramos 3 que son muy similares: Vsrotate.asm, Reversible.asm, y Function.asm. Los dos primeros no aaden nada nuevo por lo que se deja al lector para su compilacin y estudio; en ambos casos se lee el A/D y se establece una rotacin de los LEDs con un retardo que es funcin del valor ledo del A/D. As moviendo el potencimetro los LEDs se desplazan a mayor o menor velocidad. En el programa Reversible.asm tambin se puede cambiar el sentido de desplazamiento del LED encendido al pulsar el interruptor de la tarjeta. Finalmente Function.asm realiza la misma funcin pero introduce una subrutina para manejar el tiempo de retardo, como podemos ver en la diapositiva desde la lnea 116 hasta las 123. Una llamada a la rutina aparece en la lnea 85; vemos que el acumulador (W) se usa para pasar un parmetro a la rutina, en este caso es el nmero de bucles de segundo nivel que realizaremos; cuanto mayor sea el valor de W mayor ser el retardo generado. Tambin es posible que una rutina devuelva algn valor; en ese caso puede usarse W o si son ms de un valor pueden usarse variables creadas al efecto.

28

Entre otras funciones el SFR OPTION_REG controla al temporizador Timer0. La seal de reloj que alimenta al Timer0 puede derivarse por divisin del reloj principal o puede introducirse al PIC por una lnea de entrada. Con el bit T0CS podemos seleccionar como fuente para el Timer0 la patilla 2 del puerto A, y de esta forma el temporizador estara funcionando como un contador; en cuyo caso el bit T0SE permite seleccionar si la cuenta se hace en el flanco de bajada o el de subida. El Timer0 comparte su pre-scaler con el watchdog (WDT), en funcin del valor del bit PSA se usar para uno o el otro pero nunca simultneamente para los dos. Finalmente los tres bits bajos PS<2:0> seleccionan el valor de divisin del pre-scaler, note que los valores de divisin son distintos para el TIMER-0 y el WDT. El Timer0 tiene 8 bits, y por tanto cuenta desde 0 a 255, al siguiente pulso vuelve a 0 y se genera una interrupcin (ver diapositiva siguiente).

29

Una interrupcin es una seal que recibe el controlador para atender un suceso detectado por el hardware perifrico. En los PIC existen numerosos perifricos que pueden generar interrupciones: como los timers, el cambio de estado en una lnea de un puerto, el conversor analgico/digital, los comparadores, el mdulo de comunicaciones serie, el mdulo de comparacin y captura, la EEPROM, etc. Asimismo un sistema externo al microcontrolador puede tambin generar una interrupcin a travs de un determinado flanco en la lnea de interrupcin externa (RA2/INT en el PIC16F690). Para que una interrupcin afecte al microcontrolador, debern estn habilitadas las interrupciones en general (INTCON<GIE> ver diapositiva siguiente); para los perifricos del microcontrolador deber tambin estar habilitadas las interrupciones de perifricos (PEIE), as como las interrupciones del perifrico en particular que la genera (ADIE, RCIE, TXIE, TMR1IE, etc.). Para los sistemas externos, el timer 0 los puertos, deber estar habilitada INTE, T0IE o RABIE respectivamente. Si no se dan estas condiciones de habilitacin, la interrupcin no afecta a la ejecucin. Si por el contrario se recibe una interrupcin que est habilitada el controlador termina de ejecutar la instruccin actual (en algunos casos puede haber ms retardo latencia), pone GIE a 0 para deshabilitar cualquier otra interrupcin, y ejecuta un salto a la direccin 4 donde se debe encontrar la rutina de servicio de las interrupciones. Una vez acabada la rutina de servicio de interrupciones se ejecuta el retorno a la instruccin siguiente a la que se termin de ejecutar cuando se recibi la interrupcin, usando para ello la instruccin RETFIE que vuelve a habilitar GIE. Dentro de la rutina de servicio de interrupciones se debe salvar el contexto de ejecucin del programa principal (WREG y STATUS/banco de trabajo), y este contexto deber recuperarse justo antes de volver de la interrupcin, para que la ejecucin normal pueda continuar tal y como se haba quedado. Para esto es necesario reservar 2 celdas de memoria en la zona comn de todos los bancos (Ejercicio: razone por qu debe ser en la zona comn y no en el banco 1 por ejemplo.) que nos permitan almacenar el working register W y la palabra STATUS. Este cdigo nos permite realizar estas operaciones: cblock 0x70 W_TEMP STATUS_TEMP endc MOVWF W_TEMP ;Copy W to TEMP register SWAPF STATUS,W ;Swap status to be saved into W, SWAPF doesnt change any flag CLRF STATUS ;bank 0, regardless of current bank, Clears IRP,RP1,RP0 MOVWF STATUS_TEMP ;Save status to bank zero STATUS_TEMP register : :(ISR) ;Insert user code here : SWAPF STATUS_TEMP,W ;Swap STATUS_TEMP register into W MOVWF STATUS ;Move W into Status register (sets bank to original state) SWAPF W_TEMP,F ;Swap W_TEMP SWAPF W_TEMP,W ;Swap W_TEMP into W RETFIE ; TOS -> PC ; 1 -> GIE Pregunta: parece ms sencillo ejecutar movf W_TEMP,w al final en vez de dos SWAPF Por qu no se usa movf ? R: Porque movf altera el valor del flag Z en STATUS. Una vez dentro de la rutina de servicio de interrupciones podemos saber que dispositivo ha generado la interrupcin mirando los flags de INTCON, PIR1 y PIR2; slo un flag puede estar a 1, y este flag debe ser puesto a 0 por el software de la ISR.

30

INTCON es el registro (SFR) de control de las interrupciones. Como hemos visto una interrupcin es una seal generada por algn dispositivo interno o externo al PIC y que requiere una accin inmediata; el registro INTCON nos permite habilitar o no las interrupciones que pueda generar cada dispositivo. Relativos al Timer0 tenemos dos bits el T0IE que permite habilitar la interrupcin del Timer0; y T0IF es un flag que nos indica que el Timer0 ha llegado al final de su cuenta. Este flag deber ser limpiado por el software antes de que ocurra el siguiente overflow. Este flag se actualizar independientemente de que las interrupciones estn habilitadas. Adems de este registro INTCON, existen los registros PIE1 y PIE2 que son los Peripheral Interrupt Enable registers ; en ellos se puede habilitar la interrupcin de los dispositivos internos (perifricos) como el A/D, USART, Timer1, Timer2, etc. En los PIC de gama alta se pueden definir la prioridad de las interrupciones como alta o baja; lgicamente existen dos vectores de interrupcin en las posiciones 0x08 y 0x18 respectivamente, y muchas otras posibilidades de configuracin.

31

Vemos en este sencillo programa que el uso del temporizador lgicamente puede simplificar la generacin de retardos o en general el control del tiempo. Insertado en el programa se ha incluido un diagrama simplificado del Timer0. En la lnea 36 seleccionamos un factor de divisin de 256 de la frecuencia de entrada; en este caso la seal seleccionada es el ciclo de instruccin del microcontrolador, que a su vez es Tosc*4 ya que cada instruccin requiere 4 ciclos de reloj para su ejecucin. En total pues el periodo de la seal que alimenta al temporizador es 1024*Tosc. En nuestro caso el oscilador es de 8 MHz por lo que el periodo del Timer0 es: Timer0 = 1024 * 1/8 us = 128 us el flag de overflow se activar cada 256 impulsos recibidos, dando un total de: 32.768 ms. En la lnea 42 el programa espera sin hacer nada til; normalmente el procesador podra dedicarse a cualquier otra cosa mientras el temporizador lleva la cuenta del tiempo transcurrido. Ntese que el flag T0IF se debe poner a cero en el software. En este programa usamos el flag de fin de cuenta del TIMER-0 pero no tenemos habilitadas las interrupciones, y por tanto no hemos definido la rutina de servicio de las mismas; en la diapositiva siguiente vemos un ejemplo con interrupciones. Pregunta: de nuevo hay un pequeo error en el programa, cual es ? qu efecto tiene ? R: En la lnea 38 no accedemos al banco correcto y por tanto en el primer bucle Display no est inicializado correctamente.

32

Programa de ejemplo de uso de interrupciones interrupt.asm; este programa usa las interrupciones que se generan con el overflow del Timer0. En la rutina de servicio de las interrupciones carga en el valor de conversin en el registro del TMR0 por lo que acelera o decelera la rotacin del encendido de los LEDs en la placa de desarrollo. Ntese en la lnea 52 que la primera instruccin es ahora un salto al principio del programa normal, ya que la rutina de servicio de las interrupciones (ISR) empieza forzosamente en la posicin 4. Observe entre las lneas 61 y 66 como se puede averiguar que perifrico ha generado la interrupcin mirando los flags en INTCON y PIR1, y como se limpia por software el flag en la lnea 70. T0Semaphore es una posicin de memoria que se usa para transmitir informacin entre la ISR y el programa principal; es un flag que indica que el temporizador ha terminado la cuenta. En el trozo de programa visualizado en la diapositiva (que es el programa original de MicroChip) hay un error tipogrfico en las sentencias de chequeo de los flags, puede sealarlo ? Asimismo hay otro error ms grave que hace que no se espere correctamente el final de la conversin del A/D, puede identificar el error ? Es posible que este error pasara desapercibido porque a efectos prcticos no se nota en la ejecucin normal del programa, puede explicar porqu no se nota ? Estos dos errores estn corregidos en versin de interrupt.asm que acompaa al curso. Por otra parte el programa est preparado para que el sentido de rotacin de los LEDs cambie cuando se pulsa la tecla SW1 de la tarjeta de desarrollo. Sin embargo esto no funciona cuando el PICkit2 est controlado por MPLAB, y sin embargo funciona correctamente si est controlado por el programa PICkit2 Programmer; esto se debe a que el switch est conectado a RA3/MCLR/Vpp y esta lnea, que es el Master Clear o reset, no queda liberada cuando usamos MPLAB. Puede comprobarse con un voltmetro que RA3 est continuamente a nivel lgico bajo y por ello no responde a la pulsacin de la tecla.

33

Programa Filter.asm. Este programa ilustra el uso del llamado acceso indirecto a la memoria, para ello los PICs usan dos SFR que son accesibles en todos los bancos: FSR (04h) y INDF (00h), File Select Register e INDirect File respectivamente. FSR es lo que se conoce como un puntero y no es ms que una posicin de memoria cuyo contenido nos indica donde accederemos al operar con INDF; es decir que si escribimos el valor 34h en FSR y luego leemos INDF, estaremos leyendo la celda de memoria 34h; en general cualquier operacin que hagamos sobre INDF se realizar sobre la celda de memoria a la que apunte FSR. El banco al que accedemos viene determinado por STATUS.IRP que vale 0 para los bancos 0 y 1, y vale 1 para los bancos 2 y 3; la razn por la que slo se necesita 1 bit de seleccin de banco es que ahora el puntero (FSR) tiene 8 bits, en vez de los 7 que se codifican en las instrucciones de acceso a memoria normales, y por tanto el bit ms alto de FSR es el que selecciona si accedemos al banco 0 o al 1 si IRP=0, o al 2 o al 3 si IRP=1. Por ejemplo el siguiente cdigo pone a 0 una serie de posiciones de memoria: MOVLW 0x20 MOVWF FSR CLRF INDF INCF FSR BTFSS FSR,4 GOTO NEXT ;initialize pointer ;to RAM ;clear INDF register ;inc pointer ;all done? ;no clear next

NEXT

Pregunta: cuantas posiciones de memoria inicializa este pequeo programa ? R:16 Observe las instrucciones de las lneas 36 a 39 como pueden reservarse varias posiciones contiguas en la memoria aadiendo :x para reservar x celdas de memoria dentro del bloque. Por ejemplo para acceder a las dos celdas reservadas con Delay:2, se puede usar Delay y Delay+1 , y el preprocesador las sustituir por las direcciones correctas. Observe en el programa como se limpian las 8 celdas de memoria Queue:8 en la rutina FilterInit. Por otra parte en la rutina Filter se almacena el dato de entrada (que viene en W) en la posicin de memoria donde apunta FSR. El programa realiza el filtrado sumando las ltimas 8 lecturas y calculando su valor medio; note que la suma puede ser mayor de 8 bits, por lo que se almacena en un entero de 16 bits y para dividir por 8 se usan 3 pares de rotaciones, tal y como puede verse en las lneas 120 y siguientes. El resultado vuelve a tener 8 bits y se devuelve en W (y tambin en la variable Round). Pregunta: Vemos en las lneas 102 a 104 como se resta un valor de 8 bits de la variable de 16 bits RunningSum; los PIC realizan la substraccin como una adicin del nmero complementario. Es correcto entonces el uso del bit de acarreo (Carry) que se hace en la lnea 103 y 104 ? Razone la respuesta y ponga algn ejemplo numrico. R: S es correcto, si hay carry significa que la operacin SUBWF no tuvo overflow. Ej. 3 1 se calcula como 3 + 255 = 2 + C; mientras que 1 3 se calcula como 1 + 253 = 254 (=-2) sin acarreo.

34

Programa GreyCode.asm. En este programa hacemos uso de una funcionalidad de los PICs que se conoce como GOTO computado (computed goto), esto es que se puede ejecutar un salto modificando el registro especial PCL que contiene la parte baja del contador de programa (PC); adems el programa ilustra como se puede usar el registro PCLATH que nos permite modificar los bits altos del PC y que es necesario a la hora de hacer saltos (GOTOs o CALLs) a otras pginas de memoria distinta de la que estamos. Tal y como puede verse en la figura, para el PIC16F690 que tiene una memoria de programa de 8 kwords, hace falta el PC tenga 13 bits; PCL contiene los 8 bits bajos y PCH (que *no* est mapeado en ningn registro) contiene los 5 bits altos. Podemos modificar PCH usando el SFR PCLATH, que se carga en PCH de forma automtica al hacer cualquier operacin en PCL. Tambin si hacemos un salto o una llamada a subrutina los dos bits ms altos de PCLATH se transfieren automticamente a la parte alta de PC; de esta forma se pueden hacer saltos entre pginas de cdigo. En esta rutina lo que hacemos es un GOTO computado, sumndole a PCL el valor de entrada a la rutina que es W. La instruccin retlw XX carga el valor del literal XX en W y retorna; en nuestro caso supongamos que la tabla empieza en la posicin 81 (que coincide con el nmero de la lnea) y que el valor de entrada a la rutina es W=3; entonces al ejecutar addwf PCL,f el programa saltar a la lnea 84 donde se retorna al programa principal con W cargado con el valor que se encuentra en la posicin 3 de la tabla. El primer valor de la tabla se corresponde con el ndice 0; en este caso la tabla tiene 16 elementos con el cdigo Gray de los nmeros del 0 al 15. Cuando el salto que se hace usando PCL pueda superar una frontera de 256*i, hay que modificar PCLATH antes de modificar PCL para que el salto se haga dentro de la tabla, si no podramos saltar hacia atrs en vez de a la zona de la tabla; esto es lo que se muestra entre las lneas 71 y 79. Otra forma ms simple de evitar esta complejidad es asegurarnos de que la tabla no atraviesa ninguna frontera de 256 bytes, usando ORG 0x100 por ejemplo. Aqu tambin se ilustra como leer el byte alto y bajo de una direccin de programa usando las palabras reservadas high y low. Este es el ltimo de los programas ejemplo que acompaan al PICkit2, a partir de ahora veremos ejemplos desarrollados por el autor, que son un poco ms complejos pero tambin ms cercanos a aplicaciones prcticas.

35

El programa 1digito.asm ilustra como se pueden usar las tablas para representar los nmeros del 0 al 15 en formato hexadecimal. En la tarjeta de desarrollo modificada por el autor tenemos 2 displays de 7 segmentos conectados al puerto C; los 7 segmentos a,b,c,d,e,f, y g estn conectados a travs de resistencias a RC0, RC1, , RC6 respectivamente y el punto decimal est conectado al bit 7 (RC7). El ctodo comn de los displays est conectado al colector de sendos transistores cuyas bases estn controladas por el puerto B<6:7>; siendo la lnea 6 (RB6) el dgito menos significativo. La figura de la diapositiva siguiente representa un esquema similar (aunque no igual). Esta conexin permite la multiplexacin de la representacin de cada nibble secuencialmente (Ver diapositiva siguiente.) En este programa contamos las pulsaciones de la tecla, en la variable Counter, igual que se hizo en el ejemplo rebotes.asm. En la tabla SevenSeg convertimos el valor de la cuenta en el cdigo para su representacin en formato de 7 segmentos y de esta manera no necesitamos usar un descodificador del tipo 7447 o 4511 y adems nos permite extender la funcionalidad de stos que slo pueden representar del 0 al 9. La tabla es fcil de crear, por ejemplo para el nmero 1 tendremos que encender los segmentos b y c, por tanto tendremos que activar las lneas 1 y 2 del puerto C cuyo valor seria: 0000 0110b en hexadecimal 0x06 que es la entrada que vemos en la segunda lnea de la tabla que corresponde al nmero 1, en la figura para el 5: 0110 1101 = 0x6D. En la lnea 32 activamos el transistor de la cifra baja de las dos que tenemos, y queda activado continuamente durante todo el programa, por lo tanto cuando activamos el puerto C con un valor de la tabla aparecer el dgito correspondiente en la cifra derecha del display; dado que RB7 queda desactivado siempre la cifra alta nunca se activa. Pregunta: Qu sucede si eliminamos las lneas 35 y 42 donde se llama a la rutina SevenSeg ? Veremos algo reconocible en el display ? R: No; veremos que se encienden unos segmentos difciles de interpretar. Observe como en las lneas 70 a 73 comprobamos que la tabla no atraviesa ninguna frontera de pgina y por tanto podemos hacer el GOTO computado sin mayor complicacin (Comparar con la rutina de la diapositiva anterior). Estas lneas no generan ningn cdigo ya que son slo directivas para el preprocesador; si al compilar obtenemos el error Tabla de SevenSeg cruza una frontera de pgina tendremos que mover la rutina a otro sitio del programa, normalmente esto no supone ningn problema.

36

El programa 2digitos.asm es similar al anterior: va contando pulsaciones de la tecla en la variable Counter, pero ahora usamos los dos dgitos del display de 7 segmentos. Dado que los dgitos estn conectados en paralelo al puerto C tenemos que multiplexar, activando cada dgito durante un cierto tiempo y luego hacemos la misma operacin con el otro dgito. Si estas operaciones se realizan con la suficiente rapidez, no se aprecia ningn parpadeo tan solo veremos que la luminosidad es menor que cuando se activaba un dgito permanentemente. Para conseguir esto hemos desarrollado la rutina Representa, que recibe como parmetro de entrada el valor del registro de trabajo (W), y desde esta rutina se llama dos veces a SevenSeg, una para los 4 bits bajos (nibble bajo) y otra para el nibble alto. Adems se llama a la rutina TiempoEncendido para mantener el dgito durante unos 30 us encendido. Para que esta rutina funcione correctamente tenemos que hacer llamadas a ella de forma continua, ya que si no observaramos un molesto parpadeo o incluso los dgitos podran desaparecer si hubiese un periodo del orden de una dcima de segundo sin que se activaran. Vemos pues que mientras el programa espera la pulsacin de la tecla siempre tiene la llamada a Representa dentro del bucle de comprobacin, tal y como puede verse entre las lneas 41 a 45 del programa. Introducimos en este programa tambin la directiva DT x que es equivalente a retlw x, pero que nos permite poner las tablas ms o menos grandes de una forma mucho ms compacta tal y como puede verse entre las lneas 74 a 78. El uso de esta directiva (o de retlw) *no* se recomienda para los PIC de gama alta, ya que para stos las instrucciones son de 16 bits pero el PC cuenta bytes, por lo que las tablas seran mayores y hay que hacer otras operaciones con PCL; adems para la gama alta existen otras instrucciones que trabajan directamente con las tablas: TBLRD* y TBLWT*, que lee o escribe en la posicin apuntada por TBLPTR que es un SFR triple con 21 bits tiles.

37

El programa 7segment.asm usa los dos dgitos del display de 7 segmentos de forma completa, para representar los 10 bits del resultado de la conversin A/D. Adems ilustra la forma de hacer que la conversin se realice mientras el reloj principal del microcontrolador est parado (modo SLEEP), y como consecuencia el resultado es menos ruidoso. Se propone como ejercicio comprobar la mejora del ruido de conversin; simplemente comentar la lnea 68 (SLEEP) y quitar el comentario de las lneas 70 a 76. Se ha modificado la rutina RepresentaAD para que use los puntos decimales de los 2 displays para representar los dos bits altos de los 10 bits del resultado del conversor A/D, y los 8 bits bajos se presentan en hexadecimal en las dos cifras del display. As por ejemplo podemos tener en el display 9.A. lo que significa 0x39A, o E 2. lo que significa 0x1E2. Observe tambin en el programa como se configura el PIC para habilitando las interrupciones de perifricos (PEIE), pero no habilitamos las interrupciones en general (GIE=0). Esto puede parecer intil pero nos permite despertar al micro cuando el A/D acaba la conversin, sin necesidad de tener una rutina de servicio de interrupciones. Este cdigo realiza esta funcin:
movlw b'01110000 ;F(RC) para poder usar SLEEP movwf ADCON1 banksel PIE1 bsf PIE1,ADIE banksel INTCON bsf INTCON,PEIE; activa INT del A/D para despertar del SLEEP bcf INTCON,GIE; desactiva INT general para que no salte a 0004

38

El programa 7seg_interr.asm hace lo mismo que el anterior, pero esta vez usando las interrupciones del TMR0 y del conversor A/D. Vemos en la diapositiva la rutina de servicio de interrupciones (ISR): las lneas 32 a 34 salvan el contexto del programa principal; las lneas 35 a 40 determinan la fuente de la interrupcin y saltan a la parte especfica para cada perifrico, en nuestro caso del TMR0 en las lneas 42 a 59, o del A/D en las lneas 68 a 83; despus retoma el contexto del programa principal entre las lneas 60 a 64, para finalmente volver al programa principal re-activando las interrupciones con la orden RETFIE en la lnea 65. Puede observarse en el programa que activamos el modo SLEEP para hacer la medida del voltaje analgico sin interferencias; es muy importante en este caso notar que durante la conversin tambin TMR0 se para, ya que est ligado al oscilador principal que tambin deja de oscilar. Por tanto si estuviramos haciendo una cuenta de tiempo basado en TMR0 se acumularan retrasos, y si cometiramos el error de activar el modo SLEEP sin arrancar la conversin, el microcontrolador se parara indefinidamente ya que ningn perifrico generara interrupciones en ese caso. Si se necesitara mantener un reloj y usar el modo SLEEP, se podra usar el Timer 1, que posee un oscilador independiente, en el que normalmente se coloca un cuarzo de 32 kHz, tal y como se representa en la parte superior de la diapositiva, que permite mantener un reloj de tiempo real, independiente del oscilador principal y del modo de trabajo usado en todo momento.

39

El programa PWM.asm ilustra como se puede usar el mdulo ECCP+ (Enhanced Capture/Compare/PWM+) del PIC 16F690 para controlar el brillo de los LEDs o cualquier otra funcin pseudo-analgica conectada a los pines 2, 3, 4 5 del puerto C. La resolucin del PWM es como mximo de 10 bits, y usa Timer2 (8 bits), ms dos bits del pre-scaler nos permite controlar el ancho del pulso y el periodo; la relacin pulso/periodo es el ciclo de trabajo o duty-cycle. En la diapositiva se puede ver una aplicacin half-bridge estndar, halfbridge controlando un circuito full-bridge, y una aplicacin full-bridge usando las 4 salidas del PIC; en este ltimo caso el PIC puede introducir un tiempo muerto entre las activaciones de dos transistores en la misma columna. Puede observarse que el nmero de SFRs que es necesario usar es bastante grande incluyendo los relativos al timer2: PIR1, TMR2, T2CON, CCPR1L, CCPR1H, CCP1CON, PWM1CON, PIE1, PR2, etc. La estructura y uso de estos registros se puede consultar en la datasheet del dispositivo; aunque dada la complejidad asociada se recomienda que para usar el mdulo PWM se usen las libreras de mikroC ya que stas simplifican notablemente el trabajo; ms adelante veremos un programa en C con similar funcionalidad pero menos complejo. El programa lee el conversor A/D y programa el duty cycle del PWM en funcin de la lectura; observamos que el brillo de los LEDs 3 y 4 de la tarjeta de desarrollo vara de forma continua al mover el potencimetro RP1; si queremos controlar un dispositivo que requiera ms potencia tendremos que usar un transistor como driver. Otra aplicacin comn del mdulo ECCP+ es la de generar pitidos o alarmas; basta conectar un altavoz y variando la frecuencia y/o el ciclo de trabajo podremos cambiar el tono y el matiz del sonido de alarma. Note que no podemos leer el A/D en modo SLEEP porque si se activa este modo se parar el oscilador principal, que es el origen del reloj del TMR2; por lo que en general cuando estamos usando el mdulo PWM no podemos usar el modo SLEEP.

40

En diapositivas anteriores hemos mencionado determinadas opciones que slo pueden establecerse cambiando la configuracin del microcontrolador. Todos los PIC tienen una o varias palabras de configuracin que slo pueden ser escritas en el momento de la grabacin (burn) del dispositivo. A veces a los bits de configuracin se les llama fusibles (fuses) por su similitud con los chips PAL (Programmable Array Logic) en los que efectivamente se quemaban determinadas conexiones internas del dispositivo. En los PIC los bits de configuracin pueden re-escribirse tantas veces como el programa, ya que pueden considerarse parte de dicha memoria de programa, y estaran localizados en posiciones ms all de la memoria de usuario donde se almacena el programa (direccin 2007h para el PIC16F690) y que slo puede ser escrita en el momento de la programacin del chip. Como hemos dicho para dispositivos con memoria FLASH los bits de configuracin se pueden escribir tantas veces como el programa, por lo que el nombre de fusibles es poco afortunado. Los bits de configuracin se pueden programar (poner a 0) o dejar no-programado (se lee como 1) para seleccionar varias configuraciones del dispositivo tal y como puede verse en la diapositiva. Observe que podemos entre otras opciones, seleccionar el modo del reloj principal, habilitar el watchdog, el master reset MCLR o la proteccin del cdigo o los datos escritos en el chip. En otras posiciones de memoria de esta rea tenemos tambin la identificacin del tipo de dispositivo que normalmente es reconocido por las herramientas de escritura y gracias a ello se puede evitar escribir el cdigo escrito para un tipo en un chip distinto. Desde un punto de vista prctico las herramientas de escritura normalmente proporcionan un men de opciones de configuracin en funcin del chip que usemos; tanto en MPLAB (ver la figura) como con mikroC podemos seleccionar la configuracin cuando definimos un fichero de proyecto que incluye el cdigo fuente de nuestro programa.

41

Vemos aqu un programa en C (Led_blink.c) tal y como se ve en la ventana de mikroC PRO justo despus de compilar correctamente; en la ventana de mensajes nos dice cuanta RAM y ROM usa el programa. Si tenemos conectado el EASYPIC a nuestro ordenador, presionando F11 se escribir el programa en el PIC y empezar la ejecucin de forma inmediata. Este programa ilustra algunas ventajas de la programacin en C, frente a la programacin en ensamblador. Desglosaremos en detalle este primer programa: Todo lo est entre /* y */ es un comentario que puede extenderse en varias lneas, igual que lo que sigue a //. En todo programa C hay una funcin main() que es el comienzo del programa. Lo que est entre llaves , sentencias; - es un bloque de sentencias. Al principio de un bloque puede aparecer la declaracin de las variables y su tipo. A casi todos los efectos cuando creamos un bloque es sintcticamente equivalente a una sola sentencia. Los tipos de variables en mikroC son: char, int, float, void, (y double). Los tipos enteros pueden tener prefijos como: short, long y unsigned. En la lnea 7 declaramos la variable n como un entero corto. En la lnea 8 tenemos la primera sentencia que genera cdigo, hace todos los puertos de salida. Vemos que en una lnea puede haber varias sentencias; anlogamente podramos tener una sola sentencia en varias lneas, ya que una sentencia no termina hasta que aparece el ;. En la lnea 9 inicializamos los puertos. Note que no tenemos que hacer ningn cambio de banco, ya que el compilador se encarga de esa complicacin por nosotros. La lnea 10 es necesaria para poder usar las lneas conectadas al A/D como digitales (ver datasheet). Bucle while (condicin) ,sentencias;-, mientras la condicin sea verdadera se repite el bloque de sentencias. Las condiciones se construyen con los operadores relacionales: >, >=, <, <=, ==, != . Las condiciones simples se pueden unir con los operadores lgicos: &&, || y ! que significan AND, OR y NOT respectivamente. En C cualquier expresin o asignacin es falsa si su resultado es 0, y verdadera en caso contrario. Un error comn entre principiantes es escribir while (n=3) ., esa condicin es *siempre* verdadera porque lo que hace es asignar el valor 3 a n y dado que el resultado es != 0, el bucle ser un bucle sin fin; normalmente se quiere expresar while (n==3) , que ejecutar el bucle mientras n valga 3. En C es muy comn expresar x = x +1, condensado como x++. Cuando el operador incremento ++ precede a la variable, el incremento se hace *antes* de ejecutar la sentencia, y si aparece como sufijo el incremento se hace despus. As por ejemplo si a=3 y ejecutamos b=a++;, b valdr 3 y a 4; pero si ejecutamos b=++a;, b y a valdrn 4. Anlogamente existe el operador decremento --. El bucle for (sentencia1; condicin; sentencia3) , bloque -, es equivalente a: sentencia1; while (condicin) { bloque; + sentencia3; - . Note que tanto la sentencia1 como la sentencia3 pueden ser una serie de sentencias simples separadas por comas; por ejemplo: for (k=7, n=0; n-k != 0; n++, k--) ,- Delay_ms(x) es una funcin de librera que nos proporciona un retardo de x milisegundos. La construccin if (condicin) {bloque-verdadero} else {bloque-falso- se ejecuta slo uno de los bloques en funcin de la condicin. Se pueden encadenar tantos if else if else if como se quieran. Los operadores sobre los bits (bitwise) son: <<, >>, ~, &, | y ^; siendo respectivamente desplazamiento a la izquierda, derecha, complemento, AND, OR, y XOR.

42

1.

2. 3.

4.

5.

El tipo de las variables determina su tamao en memoria y el rango en valores de la variable. char tiene 8 bits y va de 0 a 255. int es el tipo por defecto, tiene 16 bits y rango de -32768 a +32767; short int o slo short tiene 8 bits y rango de -128 a +127; long int o slo long tiene 32 bits y rango de -231 a +231-1. Para unsigned short, unsigned y unsigned long los rangos van de 0 a 255, 65535 y 4294967295 (=232-1) respectivamente. float es un nmero en coma flotante de 32 bits y rango +-6.8 E+38 y para nmeros pequeos +-1.17 E-38. double en mikroC es idntico a float, en otras implementaciones tiene 64 bits. void slo se usa para funciones y significa que la funcin no devuelve ningn valor o no tiene parmetros de entrada. const es un modificador que indica al compilador que la variable no va a cambiar durante la ejecucin. volatile indica que la variable puede ser cambiada por procesos ajenos al programa (HW). La directiva del pre-compilador #define xx yy crea el parmetro xx con el valor yy. Un parmetro queda fijado en el momento de la compilacin y no cambia nunca. Todas las variables simples se almacenan en 1, 2 4 celdas de memoria; un array es una serie de datos del mismo tipo que ocupan lugares contiguos en memoria, por ejemplo: int x[3]; declara x como un array de 3 elementos x*0+, x*1+ y x*2+ que ocupan un bloque de memoria de 6 bytes. En C x es tambin un puntero constante a ese bloque de memoria; un puntero es una variable que contiene la *direccin* de un objeto (variable, array, matriz, cadena o estructura) en la memoria. En general se declara as: int *p; p no es un entero sino un puntero a una variable de tipo entero. Si ahora ejecutamos p=x; x[0] es tambin *p, de estas dos maneras estamos accediendo a la misma posicin en memoria, ya que * como operador unario significa contenido de; anlogamente x*1+ == *(p+1), y x*2+ == *(p+2). Por otra parte & como operador unario sobre una variable significa direccin de la variable, luego tambin es cierto que: &x*0+ == p. Un string o cadena es un array de caracteres; toda cadena termina con el carcter \0, por ejemplo: char s*+=hola; es una cadena de 5 caracteres que son: h o l a \0, y s es un puntero constante a la posicin que ocupa la h. Una matriz es una tabla de datos: int tabla[2][5]; tiene 2 filas y 5 columnas. Una estructura es un conjunto de datos agrupados, normalmente relacionados en el mundo real. Por ejemplo struct PERSONA { char Nombre[8], Apellidos[20]; int edad; } Pepe; define la estructura de datos Pepe de 30 bytes con dos cadenas y un entero. Normalmente es mejor definir un tipo de estructura genrico por ejemplo: typedef struct {char Nombre[8], Apellidos[20]; int edad; } PERSONA; y despus podemos usar PERSONA como un tipo definido por el usuario y declarar: PERSONA Pepe, *p; donde estaramos declarando la estructura Pepe igual que antes, y tambin declaramos p como un puntero a estructura de tipo PERSONA. Para acceder a los componentes de la estructura se usa ., de forma que Pepe.Nombre o Pepe.edad; tambin si hacemos: p=&Pepe; podremos acceder con : p->Nombre o p->edad. En mikroC todos los SFRs se pueden acceder como variables o como estructuras compuestas de 8 bites (B0, B1,,B7) o campos (F0,F1,,F7). As podemos escribir: PORTB|=0x80; o PORTB.F7=1; para poner a 1 la lnea 7 del puerto B. Tambin siempre podremos referenciar cualquier bit con nombre: INTCON.GIE=1;

43

1.

2. 3. 4.

5.

6.

7.

Los operadores aritmticos (+ - * / %) operan sobre dos datos de tipo simple. El operador % nos da el resto de la divisin entera. En general el resultado es del tipo de mayor tamao. Por ejemplo si tenemos: char c; int i,x; y hacemos: x=i+c; c se convierte a entero antes de la operacin y el resultado es un entero de 16 bits. A veces podemos especificar explcitamente la conversin de tipo usando la tcnica de type cast sobre una expresin de la forma (tipo)(expresin), por ejemplo: x= i + (int)(c); Anlogamente los operadores orientados a bits o lgicos tambin operan sobre dos datos y son: rotar a la izquierda <<, rotar a la derecha >>, AND &, OR | y XOR ^; complementar ~ opera sobre un solo dato. Si tenemos short j=0xF1; int i=0x281; y hacemos X= j|i; X valdr 0x2F1, i>>2 vale 0xA0. C nos permite simplificar expresiones del tipo: x = x (operador) y; para cualquier operador aritmtico o lgico, por su sintaxis equivalente: x (operador)= y; as tenemos que x *= 3; es equivalente a: x = x*3; Los operadores incremento ++ o decremento -- se aplican sobre variables de carcter entero o punteros, y segn vayan como sufijo o prefijo realizan la operacin despus o antes de usar la variable. Estos operadores se usan mucho en C y permiten hacer sentencias muy concisas. Cuando se aplican sobre punteros, este pasa a apuntar al elemento siguiente (o anterior) en la memoria *independientemente* del tamao del objeto al que apunta. Por ejemplo si declaramos: int *p; y p apunta a la posicin de memoria 0x70, ++p apuntar a la posicin 0x72 ya que el tamao de un entero es de 2 bytes. Los operadores condicionales permiten comparar dos expresiones aritmtico-lgicas mientras que los operadores AND, OR y NOT (&& , || y !) operan con expresiones condicionales. La construccin: (condicin) ? Expresin_1 : Expresin_2; devuelve la expresin 1 si la condicin es verdadera y la 2 si es falsa. As por ejemplo podemos hacer: max = (a > b) ? a : b;. En C toda expresin (incluyendo las aritmticas) puede considerarse una condicin, en ese caso la condicin es verdadera si el resultado de la expresin es distinto de 0 y falsa en caso contrario. Las construcciones: if (condicin) { bloque1 } else { bloque2}; while (condicin) { bloque3 }; y for (sentencia1; condicin; sentencia3) { bloque4 }; ya fueron descritas anteriormente. La construccin: do { bloque } while (condicin); siempre se ejecuta al menos una vez ya que la condicin slo se comprueba al final de la ejecucin del bloque. En un bloque repetitivo al ejecutar una sentencia break; se produce la salida inmediata del bloque. La sintaxis de switch es: switch (espresin) , case valor1: sentencias; ; break; case valor2: sentencias; ; break; ; default: sentencias; ; break; - y permite ejecutar una serie de sentencias en los distintos casos que se enumeren. Las funciones son subrutinas que pueden recibir una serie de variables de entrada, realizan una serie de operaciones y pueden devolver un valor del tipo de la funcin. Antes de usar cualquier funcin, sta debe haber sido declarada o definida. En C las funciones reciben copias de las variables de entrada, por tanto si una funcin cambia el valor de estas variables cambia slo la copia, no la variable original; sin embargo si la funcin trabaja con el puntero a una variable puede cambiar el contenido de la variable original. mikroC provee una gran cantidad de funciones agrupadas en libreras que nos permiten trabajar con todos los mdulos de todos los PIC de forma sencilla, estas funciones estn documentadas en la ayuda de mikroC. A veces la declaracin de las funciones de librera requiere un fichero header que se debe incluir en nuestro programa con la sentencia #include <librera.h>, como math.h, string.h, etc.

44

Este programa Display7seg_int.c muestra el resultado de la conversin del A/D multiplexado en los dos dgitos del display de 7 segmentos, similar al programa en ensamblador 7seg_interr.asm que hemos visto anteriormente. Ntese que en el programa no hay ninguna referencia al modelo de PIC; para mikroC tenemos que declarar el modelo de PIC, la velocidad y cualquier parmetro de inicializacin en un fichero de proyecto, en este caso Display7seg_int.mcppi. Observamos aqu el programa completo y vemos que al usar la funcin de librera ADC_Read(int chan), la de Delay_ms() y las capacidades del lenguaje C, ocupa menos de 30 lneas. En las lneas 11 y 12 tenemos la tabla de conversin de los dgitos de 0 a 15 para su representacin en formato de 7 segmentos. Todas las variables que se declaran fuera de un bloque de sentencias son visibles en todas las funciones que siguen a la declaracin, son pues variables globales; frente a las variables que slo existen en un bloque llamadas locales. La interrupcin es una funcin que no devuelve ningn valor, ni lgicamente acepta parmetros. Se activa con cada fin de cuenta del timer 0; entre las lneas 18 a 20 escribe el cdigo de los 7 segmentos para cada dgito y lo mantiene hasta la siguiente interrupcin. En el programa principal se lee el conversor A/D mediante la funcin de la librera unsigned ADC_Read(unsigned short canal) y usamos los 8 bits bajos para generar las cifras hexadecimales de los dgitos y usamos los dos bits superiores para activar los puntos decimales de los dos dgitos. De esta forma podemos representar los 10 bits del resultado de la conversin. Una vez que hayamos compilado el programa, cargaremos el fichero .hex con el PICkit2 y lo ejecutamos en el sistema de desarrollo modificado para comprobar su correcto funcionamiento. Pregunta: Cmo se podra aumentar el brillo de los segmentos de los displays ? Existe la posibilidad de que la interrupcin del TMR0 llegue entre las lneas 37 y 38, qu efecto tendra ? se podra evitar ? (Ver diapositiva siguiente)

45

Para aumentar el brillo en el programa anterior podemos cambiar la sentencia de la lnea 22 if (++digito > 8u) por if (++digito > 1u) ; por cierto el sufijo u significa unsigned y se pone por compatibilidad ya que digito es tambin unsigned, aunque en este caso no sea necesario; pero en caso de que el nmero sea mayor de 32767 (por ejemplo 35000) el compilador lo interpreta como un nmero negativo (!) ya que su primer bit es 1. Podramos hacer este cambio sin necesidad de recompilar el programa ? S, y es fcil gracias a las capacidades de mikroC, en particular podremos ver el cdigo ensamblador generado por el compilador, e incluso los datos del fichero .hex en el fichero de listado: Display7seg_int.lst; donde vemos que para la lnea 22 del fichero C, en el cdigo generado la posicin 0x001F contiene la instruccin SUBLW 8 que es la que se realiza para hacer la comparacin del alto nivel. Vemos tambin que el opcode de la instruccin es 0x3C08, si en PICkit2 cambiamos el 8 por un 1 y reescribimos el fichero hex en el PIC, podremos observar el cambio de brillo de forma inmediata. Otro efecto interesante es incrementar mucho dicho valor, por ejemplo cambiar a 0x3A0, entonces observaremos un molesto parpadeo ya que no se alcanza la velocidad suficiente para la multiplexacin de los dgitos. Otra aplicacin muy til del fichero de listado y del cdigo ensamblador generado, es que nos permite conocer como implementa el compilador las sentencias de alto nivel, de donde podemos extraer algunas ideas; as como podemos usar los nombres de las variables tanto globales, locales, los parmetros de las funciones y las etiquetas para poder usarlas en bloques ensamblador que nosotros podemos insertar en el programa C. Para insertar cdigo en ensamblador se usa la directiva asm { sentencias_ensamblador -. Podemos necesitar usar el ensamblador en alguna rutina que queramos optimizar, o para ejecutar comandos que no existen en C, como por ejemplo: CLRWDT, SLEEP o SWAPF. Respecto a la ltima pregunta de la diapositiva anterior, si la interrupcin cae en medio de la actualizacin de los dgitos, tendremos la cifra baja de una conversin y la alta de la otra. Si necesitamos evitar esto tendramos que deshabilitar las interrupciones durante la escritura de los codigo7 y volver a habilitarlas una vez terminada. Para la (des-)habilitacin hacemos INTCON.GIE igual a 0 1 respectivamente.

46

Este programa lee la salida de un sensor de humedad del tipo CHS-MSS de TDK, que produce una seal de voltaje relacionada con la humedad relativa de la forma: RH(%) = 100 * voltaje. En nuestro caso leemos el canal 2 del A/D con una referencia de 5V (que es el voltaje de alimentacin); entonces tenemos: voltaje = x*5V/1024; siendo x el valor ledo por el A/D de 10 bits, por tanto tendremos que: RH = 100 * x * 5/1024 = x *125/256; y luego representamos el valor de RH en el display de 7 segmentos. El trabajo con el display de 7 segmentos usando el TMR0 es idntico al que se haca en la diapositiva anterior por lo que no lo mencionamos aqu. Para procesar las medidas del sensor de humedad hemos creado la funcin short ReadHumidity(short chan), que recibe como parmetro el canal en el que tenemos conectado el sensor de humedad y devuelve el valor de la humedad relativa en %. Vemos en la parte baja de la diapositiva como se usa la funcin y como escribimos en los cdigos de 7 segmentos los valores de las decenas (RH / 10) y de las unidades (RH % 10). Con objeto de mejorar la calidad de la medida, en cada llamada a ReadHumidity repetimos la conversin 40 veces y calculamos el valor medio. Las operaciones con enteros hay que hacerlas con cierto cuidado, revise las sentencias donde se hacen los clculos, estn hechas de distintas formas para ilustrar diferentes maneras de hacer las cosas. Observe como usamos los parntesis, y como hacemos algunas operaciones por partes ya que por ejemplo si calculamos: ad0 *= 5/8; nos dara 0 (!!), puede explicar porqu ? R: 5/8 es cero en divisin entera. Asimismo a veces hacemos las divisiones por potencias de 2 como desplazamientos ya que esto es ms rpido. Si hubiramos usado un float para las operaciones intermedias podramos haber escrito las expresiones directamente sin ningn problema (por ejemplo: k =(short)((float)ad0/40*125.0/256.0);), pero el uso de datos en coma flotante requiere mucho poder de clculo y en general no se recomienda para los PIC de gama media. Los PIC de gama alta tienen un multiplicador hardware por lo que pueden realizar operaciones en coma flotante con mayor rapidez y eficiencia, y por ello si necesitamos realizar clculos en coma flotante es mejor usar un PIC18Fxxx.

47

Los LCD (Liquid Crystal Display) son los mdulos de presentacin que ms comnmente se usan con los microcontroladores por su versatilidad y bajo consumo. Estos mdulos suelen tener caracteres de matriz de puntos (5x7) lo que les permite presentar 192 caracteres alfanumricos distintos, as como algunos smbolos. Su contraste puede variarse con un potencimetro conectado a Vo como puede verse en la figura inferior izquierda, y muchos tienen una luz trasera que permite la legibilidad sin luz exterior. Dado que casi todos los LCDs tienen un controlador HD44780 de Toshiba o compatible de otros mltiples fabricantes, existe un estndar de facto; de manera que casi todos los mdulos LCDs comerciales tienen los mismos pines similar al de la figura superior izquierda. El significado de los pines es el siguiente: Vdd y Vss son la alimentacin (5V y GND respectivamente) Vo la entrada de contraste RS entrada de Register Selection (RS=0 dato, RS=1 instruccin) R/W lectura o escritura E enable, su flanco de bajada es la seal que empieza la lectura o escritura internamente DB0-DB7 bus de datos, cuando se configura en modo 4-bits se usan slo DB4-DB7. El bit DB7 es tambin conocido como BF (Busy Flag) y es una salida del LCD que nos informa de que est ocupado. A-K nodo y ctodo del diodo de iluminacin trasera. En la imagen de la derecha vemos el proceso de inicializacin de un mdulo LCD estndar cuando se configura en el modo 4-bits. En todo el proceso RS=R/W=0; ya que escribimos en registros de control. Para ver el significado de todos los bits en las instrucciones consulte la datasheet que acompaan a estas notas. Nota: para algunos fabricantes de LCD se cambian alguno de los cdigos de inicializacin. Observe que todos los comandos del ltimo bloque estn compuestos en realidad de 8 bits que se introducen en dos tandas de 4 cada una.

48

Este programa es relativamente complejo y explota las capacidades del compilador de mikroC, por lo que el lector no debe preocuparse si le cuesta entenderlo. Hemos extrado aqu dos bloques del programa; uno es la definicin de una serie de macros que describen como se ha conectado las lneas de control (E, R/W y RS) y datos (DB4-DB7) del display LCD al microcontrolador. Adems ilustramos como se pueden construir macros usando otras macros previamente definidas. En realidad estamos mapeando las conexiones del LCD y podremos escribir las rutinas de control del LCD sin hacer referencia a ningn puerto concreto, y por tanto re-usables en cualquier otro programa previa re-definicin slo de las macros que definen las conexiones hardware. En general la re-utilizacin de cdigo probado es una medida inteligente ya que elimina la duplicidad del trabajo y evita problemas antes de su aparicin. Para usar el LCD se han desarrollado una serie de rutinas que siguen las reglas de uso de los LCDs estndar: LCD_Read(), EsperaSiBusy(), LCDWriteNibble(), LCD_WriteByte(), InitLCD(), y printLCD(). En la diapositiva vemos como ejemplo la rutina LCDWriteNibble(i), que introduce el nibble alto del parmetro de entrada i al LCD. En nuestro caso la primera lnea es equivalente a: TRISC = TRISC & 0x0F; ya que DataBusDir=TRISC y ~(NIBBLE)=0x0F; o sea hace las lneas 4 a 7 del puerto C lneas de salida. Luego pone a 0 la lnea de R/W para escribir, espera unos microsegundos y activa la lnea Enable. Despus observamos como se puede usar la directiva #if #endif del pre-procesador; las lneas del bloque slo se compilarn si la condicin es cierta, en caso contrario se ignoran. Vemos en el bloque la directiva asm ,- para introducir sentencias en ensamblador en el cdigo C, donde usamos la referencia a la variable i del C como FARG_LCDWriteNibble en ensamblador. Siempre que usemos la directiva asm con variables del C es necesario comprobar que el programa realiza la funcin correcta chequeando la salida del fichero *.lst que mencionamos anteriormente. Si estamos usando la parte baja de un puerto permutaremos los nibbles del byte de entrada; luego ponemos el dato en el bus sin cambiar ninguno de los otros bites del DataBus, y despus de una pequea espera activamos el flanco de bajada de LCD_E que completa la operacin de escritura. Observe como esta rutina obedece la especificacin del proceso de escritura representado en la figura.

49

Este fragmento del programa ilustra tambin el uso de los parmetros definidos en la diapositiva anterior. En la rutina InitLCD() vemos tambin el uso de las rutinas LCDWriteNibble(short byte) y de LCDWriteByte(short byte, short modoRS). Vemos que la secuencia de inicializacin se ha introducido en un array InitSequence[] que inicializamos en su declaracin, donde hay un bloque de 4 elementos de los que slo usamos el nibble superior y 6 siguientes en los que usamos el byte completo; estos dos bloques estn separados por el nmero 0, que acta como un flag y no se enva al LCD. A los valores de inicializacin se ha aadido el byte 0x80 que posiciona el cursor de datos en la posicin 0 (ver la datasheet del LCD), i.e. posicionamos el cursor en la esquina superior izquierda del LCD. La rutina LCDWriteByte(short i, short modo) enva el dato i en dos llamadas a LCDWriteNibble() en el modo especificado, que puede ser instruccin o dato. Note el uso que hacemos de el comando ensamblador swapf, que aunque no es estrictamente necesario es eficiente; asimismo el nombre de la variable C i lo extraemos por inspeccin de la salida ensamblador que nos proporciona mikroC. Tambin vemos algunas lneas del main() donde se declaran las cadenas texto1 y texto2, y vemos como se puede compilar de manera distinta en funcin del modelo de PIC con el que trabajamos. La rutina printLCD(short linea, char *texto) escribe el texto en la lnea pedida. Observe como se puede usar el puntero local texto para recorrer la cadena carcter a carcter, y esto no cambia nada en la rutina main() desde la que se hace la llamada. Como norma general conviene buscar nombres significativos tanto de las variables como de las rutinas, ya que el propio nombre nos orienta sobre la funcin de la rutina, haciendo ms fcil la comprensin del programa. Cuando se estime conveniente es bueno complementar determinadas sentencias o bloques con comentarios. Es tambin aconsejable mantener el tamao de las rutinas relativamente pequeo, de 20 a 30 lneas; y hacer rutinas que llamen a otras rutinas ms simples, o sea usar un paradigma tipo divide y vencers. La gran mayora de las rutinas que vemos aqu se podrn usar sin modificaciones en cualquier otro programa, siendo sta otra de las grandes ventajas de la programacin estructurada que nos permite desarrollar el lenguaje C. Recuerde sin embargo que en los PIC no se pueden llamar ms de 8 rutinas encadenadas y que a veces dentro de cualquier rutina puede haber una llamada a la librera encubierta (por ejemplo al multiplicar); mikroC nos genera un fichero llamado LCD_rutinas_16F690_PORT-C.mcppi_callertable.txt que nos ayuda a comprobar esto. Pregunta: el LCD que usamos tiene 2 lneas de 16 caracteres cada una, porqu entonces las cadenas texto1[17] reservan 17 bytes ? R: Porque hay que aadir el carcter terminador de cadena \0.

50

Este programa tambin usa las rutinas de control y escritura en el display LCD que hemos visto en las diapositivas anteriores para representar el valor de la humedad relativa en el display LCD. Hemos modificado la rutina ReadHumidity para que devuelva el valor de la humedad relativa en % y multiplicado por 100, para tener una medida ms precisa sin necesidad de usar nmeros en coma flotante. Esto es relativamente comn ya que, como se ha mencionado anteriormente, los microcontroladores en general no disponen de mucha potencia de clculo. Ilustramos aqu otra forma de llevar a cabo estas operaciones aritmticas dividiendo slo por potencias de 2, lo que puede hacerse mediante rotaciones bit a bit que son muy rpidas. Observe tambin como el programa principal hace uso de una serie de funciones de librera: WordToStr( unsigned, char *); strcpy(char *to, char *from); y strcat(char *, char *); mikroC provee una ayuda en lnea muy completa sobre todas las funciones de librera. Las dos ltimas son funciones generales de manejo de cadenas y en ANSI C se encuentran en la librera string.h; existen del orden de una veintena de estas funciones cuyo funcionamiento puede consultar en la ayuda del mikroC. La primera sin embargo es especfica de mikroC y est contenida en la llamada librera de conversiones. En algunos casos no usaremos las funciones de librera y es ms conveniente crear nuestras propias funciones para adaptarnos a los datos y formatos que necesitemos.

51

En este fragmento de programa vemos como las funciones de librera del mikroC facilitan enormemente el trabajo con la mayora de los perifricos, en particular del mdulo PWM. De este programa se ha eliminado toda la parte de inicializacin de los puertos y del LCD ya que son idnticas a las del programa anterior. Tal y como puede verse en la figura tenemos conectado en la salida RC5 (P1A) un transistor MOSFET, entre cuyo drenador y la alimentacin tenemos un motor de corriente continua y un diodo de proteccin. La salida PWM P1A se produce a travs de la lnea RC5 que tambin se usa en el LCD, por ese motivo tenemos que parar la salida del PWM cuando escribimos en el display LCD. Pruebe lo que sucede si no paramos la salida PWM. Inicializamos el mdulo PWM a una frecuencia de 500 Hz usando la rutina de librera PWM_init(long fre) que escribe los valores necesarios en todos los SFRs del mdulo PWM. Despus usamos PWM_Change_Duty(short pwm); note que esta funcin ignora los dos bits bajos del PWM ya que slo usa un entero corto. Si quisiramos usar la mxima resolucin del PWM tendramos que hacernos nuestra propia rutina que escriba en los SFR CCPR1L y en CCP1CON<5:4> los dos bits menos significativos. Nota: dependiendo de la frecuencia de PWM seleccionada y de la del reloj del PIC, no siempre es posible alcanzar los 10 bits de resolucin; ver en la datasheet para encontrar la informacin completa al respecto. Vemos tambin aqu que en la segunda lnea del LCD escribimos el voltaje que est alimentando al PIC cuyo valor medimos de manera indirecta, usando una referencia interna del PIC de 0.6 voltios. Si medimos este voltaje seleccionando el canal 13, referido a la Vdd tendremos una lectura x = 0.6 * 1024/Vdd; as que en funcin de la lectura que obtengamos podemos calcular fcilmente Vdd, tal y como puede verse en el programa. Aqu de nuevo evitamos las operaciones en coma flotante y representamos el valor de Vdd*100 para evitar los decimales.

NOTA: las funciones PWM_init(), PWM_start(), etc. han sido renombradas en la ltima versin de mikroC a
PWM1_init(), PWM1_start(), etc. Adems PWM_Change_Duty es ahora PWM1_Set_Duty(). Consulte la ayuda de mikroC para ms detalles.

52

En este programa vemos como se pueden usar las rutinas de mikroC PRO para trabajar con el display LCD, que usa el tipo especfico de mikroC sbit en conjuncin con la palabra clave at, que no hace ms que declarar un alias a una variable. El tipo sbit se usa para definir la conexin hardware bit a bit del LCD a los puertos que usemos para esa funcin. La rutina LCD_init() necesita encontrar definidos los sbit LCD_RS, LCD_EN, , LCD_D7; as como sus correspondientes LCD_RS_Direction, etc. que apuntan a los bits correspondientes en los registros TRISx. Observe que la conexin hardware del LCD en el EasyPIC tiene la lnea RW=0, luego slo podemos escribir, y por ejemplo no podemos chequear el bit busy del LCD. Una vez inicializado el display LCD podemos usar las funciones para enviar comandos al LCD LCD_Cmd(char comando); para escribir en cualquier posicin LCD_Out(char fila, char columna, char *texto); o para escribir en la posicin del cursor LCD_Out_Cp(char *texto). mikroC define una serie de constantes que permiten realizar operaciones de control sobre el LCD para limpiar la pantalla (_LCD_CLEAR), ocultar el cursor (_LCD_CURSOR_OFF), hacer scroll a la derecha (_LCD_SHIFT_RIGHT) e izquierda (_LCD_SHIFT_LEFT) , etc. Puede consultar la lista completa en la ayuda del mikroC o comprobar los cdigos necesarios en el datasheet de los LCDs que acompaa al curso. Observe tambin que en la declaracin de las cadenas txt4 se declara de forma diferente, aunque todas las formas son equivalentes. Este programa est extrado directamente de la ayuda de mikroC y tiene un pequeo error en los comentarios de los dos ltimos bucles for. Puede decir cual es ? R: se hace scroll 8 veces no 7

53

Este programa permite contar pulsos de overflow del TIMER-1 y presentar hh:mm:ss, as como el nmero de veces que el TIMER-1 ha generado una interrupcin. Si se pulsa la tecla RB0 detiene el display de la cuenta de tics; si se pulsa RB7 presenta el nmero de microsegundos transcurridos. El pre-escalado se pone a 1, por lo que el timer-1 va a Fclk/4 y cada 2^16 pulsos se genera una interrupcin que incrementa el lapso de tiempo transcurrido. Lapso es un unsigned long que nos da un mximo de: Tmax = 2^32 /(8 MHz /4 /2^16) = 140.7 E+06 seg = 1629 das Si transcurrido un tiempo t (segundos) el lapso de tiempo contabilizado es diferente, se deber a que la frecuencia del reloj no es exactamente 8MHz sino: t = lapso /(8 MHz /4 /2^16) --> f = lapso*2^18/t En las primeras sentencias usamos typedef para definir tipos de datos que usaremos frecuentemente. Observe que la variable Lapso es volatile, esto informa al compilador que esta variable puede ser cambiada en cualquier momento por la interrupcin. El diagrama representa el TIMER-1 de un PIC de gama alta 18F1320, que puede acceder los dos bytes TMR1H y TMR1L sin preocuparse por el posible rebosamiento del L al H; para los de gama media habra que chequear los valores ledos o al escribir parar el reloj del temporizador durante la operacin. La funcin QuitaCeros(char *p) de la diapositiva no siempre funciona, podra decir cuando falla ? Puede ver como se puede corregir el error en la versin del programa que acompaa al curso. R: falla cuando la cadena es 0.

54

Una de las capacidades ms potentes de los PIC es su capacidad de correr los programas tanto en ensamblador como en C en el modo debugger; lo que significa que podemos ejecutar el programa en tiempo real y a la vez instalar puntos de paro en el programa (breakpoints) y revisar el valor de cualquier variable, SFR o posicin de memoria. En este programa Teclado.c se ha desarrollado una rutina para manejar un teclado de 4x4 que devuelve el cdigo ASCII de la tecla pulsada. Desde el punto de vista de programacin la rutina teclado() es correcta, pero sin embargo cuando ejecutamos un sencillo programa de prueba no funciona, porqu ? Investiguemos En el esquema de la diapositiva vemos como funciona el teclado, que en nuestro caso controlamos con el puerto C; las lneas C4 a C7 se usan como drivers de cada una de las filas y las lneas C0 a C3 nos permiten determinar si alguna de las 4 teclas de la fila activada est pulsada; se asume que las entradas tienen una resistencia de pull-down a tierra. Para comprobar la rutina, debemos activar la capacidad de ejecucin paso a paso: pinchamos en Project Settings y activamos el ICD Debug, ya que tenemos que re-compilar el programa. Despus hacemos run-> Start Debugger F9 y con esto se ejecuta el programa bajo nuestro control; podremos ejecutar paso a paso, entrar en las rutinas, o pasar sobre ellas, activar puntos de paro, ver el valor de cualquier variable, etc. Ejecutando paso a paso veremos que la rutina funciona correctamente, pero a velocidad normal tenemos errores. En general a la hora de hacer chequeos siempre podemos introducir algunas sentencias que nos indiquen lo que est pasando durante la ejecucin del programa, tal y como puede verse en las lneas marcadas en rojo. Con ellas representamos el valor ledo del puerto y el nmero de teclas detectadas en la lnea inferior del LCD. Llama la atencin que cuando tenemos pulsada la tecla 1 se detecten 2 teclas pulsadas (la 1 y la 4), mientras que la ltima fila funciona bien. Puede encontrar una solucin ? Se deja como ejercicio, en cualquier caso al final del programa puede encontrar la rutina con dos soluciones posibles. Sirva este ejemplo como llamada de atencin sobre el hecho de que en el mbito de los microcontroladores, las conexiones y desconexiones pueden ocurrir en intervalos de tiempo del orden del us y esto est fuera de nuestra percepcin normal de las cosas

55

En este programa activamos el perro guardin o watchdog del PIC; se trata de un temporizador de tipo RC independiente del reloj del microcontrolador. Para activarlo en los PIC de gama media debemos activar la opcin correspondiente en los bits de configuracin, para lo que tendremos que editar el proyecto. Una vez en ejecucin el WDT genera un RESET si llega al final de su cuenta sin haber sido re-iniciado por el comando CLRWDT, de esta forma se puede detectar si el microcontrolador est perdido esperando alguna respuesta o en un bucle sin fin; tpicamente el tiempo de gracia es de 18 ms, pero sus mrgenes mnimo y mximo son 7 a 33 ms. El WDT tiene la posibilidad de usar un preescaler (que est compartido con el TIMER0 !??!) que puede incrementar el tiempo de gracia hasta 128 veces, por lo que puede alcanzar un tiempo de unos 2 segundos tpicamente. Para evitar que el WDT se agote hay que ejecutar frecuentemente CLRWDT en todos los procesos en los que el microcontrolador pueda emplear un tiempo mayor que el tiempo de gracia que tengamos establecido. En el caso de este programa si mantenemos a nivel alto la lnea PORTC0, el controlador entra en un bucle sin fin y se agotar el tiempo de gracia. Tal y como se ilustra en la figura, el PIC puede saber si se ha producido una parada chequeando el bit STATUS.NOT_TO, y dada la naturaleza esttica de la memoria RAM no se pierde el valor de las variables. Esto es tremendamente til, ya que podemos hacer programas muy inteligentes y resistentes a fallos durante la ejecucin. Observe que la variable i que cuenta el nmero de veces que se ha consumido el tiempo del WDT no se borra con el reset, aunque por otra parte por ejemplo s que se modifican los valores de configuracin de los puertos TRISx, por lo tanto deberemos resetear todos los dispositivos hardware cuya configuracin haya sido cambiada por el reset. Para los PIC de gama alta la activacin del WDT se puede hacer durante la ejecucin, as como su desactivacin. El programa test_WDT.c ilustra este diferente comportamiento introduciendo algunas lneas de compilacin condicional.

56

Este programa se desarroll para chequear un encoder RI-17 de ITEK. Este encoder tiene dos salidas en cuadratura llamadas A y B que conectamos al puerto C pines 0 y 1. Como puede verse en la figura las dos seales A y B determinan si la cuenta debe hacerse en un sentido o en otro. La cuenta incremental del encoder se incrementa o disminuye en funcin de cuando se produzca un flanco en alguna de las salidas A o B y del valor lgico que tenga la otra salida en el momento del flanco. As contamos hacia arriba cuando: A B ---------- --------1 flancoflanco- 0 0 flanco+ flanco+ 1 Anlogamente contamos hacia abajo cuando: A B ---------- --------0 flancoflanco- 1 1 flanco+ flanco+ 0 En el programa usamos el puerto D para controlar el display LCD usando las rutinas que ya hemos visto anteriormente. Hemos definido la variable count de tipo long ya que la cuenta puede alcanzar 2^17 por vuelta en este encoder, por lo que un entero normal de 16 bits no es suficiente. Observe como se hace la discriminacin para determinar si tenemos que contar hacia arriba o hacia abajo. Tambin tenemos en cuenta la situacin en que se haya producido cambio en A y B desde la ltima lectura, esto significa que el encoder se ha movido ms de un paso y por tanto la cuenta pierde exactitud. Adems note que mientras escribimos en el LCD no estamos leyendo por lo que este programa es puramente experimental; si necesitamos hacer uno funcional tendramos que haber usado las interrupciones de cambio de estado.

57

Este es un programa de aplicacin real para controlar una tarjeta acondicionadora de seal desarrollada en el exterior, en la que se encontraron varios errores hardware, adems de errores en las seales de control generadas con un PIC16F877. La tarjeta acondicionadora tiene dos conjuntos de integradores, uno a 500 Hz y otro a 2 Hz; la salida del integrador pasa a un peak hold (detector de pico) y de ah a un circuito S&H (Sample & Hold) que proporciona una salida continua. Cada grupo tiene tres seales de control comunes (siguiendo la nomenclatura de los esquemas hardware): INT_500Hz e INT_2Hz -> son la seales que descargan los condensadores del integrador y del detector de pico PD1_500Hz y PD1_2Hz -> seales que mantiene el proceso de integracin activo PD2_500Hz y PD2_2Hz -> seales que conectan la salida del detector de pico al S&H Vemos en la figura estas tres seales de la rama 500Hz tal y como se ven en el osciloscopio, correspondientes al inicio del ciclo de integracin, y que se repite cada 2 ms. En el programa dentro del bucle que se repite cada 2 ms tenemos la sentencia de limpiado del contador de watchdog asm clrwdt, de manera que si el programa se detiene por cualquier causa, el propio PIC se reinicializara y volvera a generar las seales sin que el usuario notase nada ms que un ciclo anmalo; esto hace que el programa sea muy robusto. Observe que dado que este es un programa de una aplicacin real, est profusamente comentado con objeto de facilitar el mantenimiento del mismo. Nunca se acenta suficientemente la necesidad de introducir buenos comentarios que complementan un buen programa.

58

Este es un programa de aplicacin real y complejidad media/alta desarrollado para realizar una serie de medidas de temperatura, humedad, el voltaje de salida de unos fotodiodos y algunos otros voltajes necesarios para la monitorizacin de un equipo. El microcontrolador procesa todas las medidas y las enva a travs de un puerto serie RS232 junto con un timestamp. Tambin acepta comandos simples que son: A -> entra en modo auto, se envan todas las medidas cada segundo t -> enva el time-stamp o tiempo trascurrido desde el arranque Thh:mm:ss -> establece la hora, minutos y segundos (slo para presentacin) I -> re-inicializa el hardare (A/D y TIMER-1) Cualquier otro carcter -> imprime los datos actuales En la ventana derecha vemos como se inicializa el interface RS232, que requiere de un conversor de nivel de tipo MAX232 para convertir las seales TTL del micro a +-12 V de la norma RS232. En diapositivas siguientes veremos las rutinas de inicializacin: InitStructs() e InitHardware(); de realizacin de las medidas: LeeAD(MEDIDA *); y de presentacin: SprintMedida(char *, MEDIDA *). Observe como se ha definido la estructura MEDIDA, como un tipo definido por el usuario, y el array de estructuras X[], ya que tenemos 10 medidas diferentes. Para cada medida la estructura contiene: su valor v, un factor de calibracin cal, unos mrgenes de variacin de la medida (Delta) Dpos y Dneg, un canal analgico asociado, un parmetro para filtrado por el mtodo de Kalkman y una cadena nombre. Esta definicin de la estructura pretende ser homomrfica, lo que significa que se parece a la realidad que describe; en el paradigma de la programacin estructurada orientada a objetos, ese es uno de los criterios de calidad del software, ya que una buena definicin de las estructuras de datos es el primer paso para un programa de calidad. Todos los nombres de las medidas ests definidos en el array Nombre[NSIGNALS], ordenados segn el nmero del canal analgico en el que se han conectado. La ltima se corresponde con una referencia de voltaje cuyo valor es Vref= 1.280 V; que se usa tanto para medir indirectamente la alimentacin del PIC (Vcc), as como referencia para valores de voltaje por debajo de 1.2 V con objeto de aprovechar mejor la resolucin del conversor A/D.

59

En esta diapositiva vemos las rutinas de inicializacin de los datos del array de estructuras, y del hardware (A/D y TIMER-1). En el primer bucle for asignamos los canales correspondientes a cada medida; teniendo en cuenta que el canal 3 es el de la referencia alternativa y que el PIC18F2420 tiene los canales del 0 al 4 y del 9 al 12. Observe como distinguimos las medidas en funcin de los caracteres de su nombre. Despus introducimos los valores de calibracin que nos permiten hacer coincidir por ejemplo las medidas de temperatura de los dos sensores cuando los calibramos a la misma temperatura. El segundo bucle for introduce las constantes del filtro de Kalkman de primer orden para las temperaturas y las humedades relativas; para las dems medidas haremos las mediciones en crudo sin filtrar, ya que dichas medidas podran tener variaciones ms rpidas. En la segunda rutina se configuran el A/D y el TIMER-1. La primera medida que hacemos es la de la alimentacin Vcc, que se hace indirectamente midiendo el voltaje de la referencia. Despus el Vcc medido se usa para tener medidas ms precisas y coherentes con las medidas que hagamos usando la referencia de 1.28V como voltaje de referencia del A/D, valga la redundancia. En esta rutina usamos las funciones RS232Out(char *) y LeeAD(MEDIDA *) que describimos ms adelante. Observe que siempre (re-)usamos la cadena declarada global MSG[24] lo que normalmente no se hace en C, ya que el tamao de la memoria RAM de los microcontroladores es mucho ms limitado que en los ordenadores normales en varios rdenes de magnitud (de GB a kB).

60

Esta es una de las rutinas ms importantes del programa, ya que es la que realiza la lectura del A/D como su propio nombre indica. En general poner nombres adecuados para las rutinas y las variables mejora mucho la legibilidad del programa, reduciendo los comentarios necesarios, y por tanto mejora la calidad del software y facilita su mantenimiento. Note que a esta funcin se le pasa como parmetro un puntero m a la medida que vamos a hacer, y recordamos que en la estructura de datos MEDIDA tenamos todos los parmetros que determinan como realizar dicha medida y tambin su procesamiento. Con el primer switch seleccionamos la referencia que vamos a usar en funcin de la primera letra del nombre, que referenciamos como: *(m->Name); recuerde que m->Name accede a la variable Name dentro de la estructura a la que apunta m; esa variable Name es a su vez un puntero a la cadena con el nombre de la medida, por lo que el operador unario * accede a la posicin de memoria de la primera letra de dicha cadena. Note que para las medidas de temperatura (T) siempre usamos la referencia de 1.28V, para los fotodiodos (P) usamos sta o Vcc en funcin del valor de la ltima medida; y para todas las dems usamos siempre Vcc. En el caso de los fotodiodos tambin usamos el bit 4 del nmero del canal (que es < 15) como un flag que indica si la ltima vez usamos la referencia de 1.28V; el cambio de una referencia a otra se hace en los valores de 90% y 95% de Vref con histresis. El bucle for que sigue hace NMED medidas, y vamos almacenando un sumatorio, la mxima y la mnima de las medidas. Luego como vemos en la segunda ventana, calculamos el valor medio y los incrementos hasta la medida mxima y mnima registradas (que nos dan una estimacin de la estabilidad de la medida). Finalmente en el ltimo switch hacemos la conversin del voltaje medido a la magnitud fsica que mide cada sensor; se le aplica el factor aditivo de calibracin y procedemos al filtrado de Kalkman de primer orden. Un filtro de primer orden simplemente hace una medida, digamos x y calcula: x(i+1) = a * x(i) + (1-a)*x; en nuestro caso a= m->kalk / KALKMAX

siendo a el parmetro de filtrado 0<a<1, cuanto ms cercano a 1 mayor ser la magnitud del filtrado.

61

En la ventana izquierda tenemos una serie de funciones relacionadas con la presentacin. Las dos primeras reciben y devuelven un puntero a una cadena; estn pensadas para concatenar los elementos que forman un mensaje, tal y como podemos ven en la funcin Printahms(char *). QuitaCeros(char *) elimina los ceros de una cadena que representa un nmero y luego mueve las cifras significativas al principio de la cadena y devuelve un puntero al final de la cadena; as por ejemplo si recibimos un puntero a 00123\0, lo convierte en 123\0, y con el puntero que devuelve podremos seguir aadiendo lo que sea despus del 3. sprint(char *p, uint32 k, short dig) escribe dig dgitos del nmero k en la cadena p, y devuelve un puntero al final de la cadena. Note que si en nmero tiene ms de dig cifras pone un * para indicar overflow. El nombre de esta rutina imita al de sprintf de la librera estndar del C que significa imprimir en una cadena con formato; esta funcin tambin se puede usar en mikro C con los PIC de gama alta, pero es una funcin muy potente por lo que consume mucha memoria y recursos. Printahms(char *) usa sprint y construye la cadena hh:mm:ss usando las variables globales: hor, mi, seg. RS232out(char *) enva la cadena dada usando la funcin de librera UART1_Write(char) que escribe carcter a carcter. En la segunda ventana SprintFn(char *p,float x, short ndig) escribe el nmero x con ndig decimales en la cadena p. Empieza por el signo, luego la parte entera y la parte decimal usando sprint. Pregunta: por qu SprintFn no puede ( ni debe ) imprimir nmeros mayores de 1.0E10 ? R: porque la parte entera es un long que tiene como mximo 9 cifras. Finalmente PrintimeStamp(void) enva una cadena tipo ts= 45.123 al puerto serie. En la primera versin del programa se calculaba el nmero entero de segundos aparte; ahora tenemos el tiempo en una variable float, se podra simplificar esta rutina ? R: usando SprintFn(MSG+3,tstamp,3)

62

Con estas tres funciones completamos la descripcin del programa: RS232in(char *p, ushort max) lee carcter a carcter lo que se recibe va puerto serie hasta un mximo de max caracteres o hasta que se pulsa enter, almacenando los caracteres recibidos en la cadena p. Devuelve el ltimo carcter recibido 0 si no se recibi nada. Note como el bucle for tiene un condicin compuesta que permite terminar cuando se han recibido max caracteres, o un enter, o se espera demasiado. Observe que slo deberemos leer un carcter cuando se haya recibido, para esto usamos las funciones de librera UART1_Data_Ready() y UART1_Read(); una vez ledo lo re-enviamos para confirmar su recepcin, o sea hacemos eco. Sethora(char *) actualiza las variables globales h0, m0 y s0, segn la cadena de entrada de la forma hh:mm:ss, e inicializa la cuenta de ticks del reloj. En esta rutina usamos la funcin isdigit(char) que pertenece a la librera de C estndar (ctype.h en ANSI C) que devuelve 1 si el carcter es una cifra ente 0 y 9. Otras funciones similares son: isalpha(), islower(), isupper(), isxdigit(), isalnum(), etc. consulte la ayuda de mikroC para ver la referencia de estas funciones. Vea como convertimos cada bloque de dos caracteres al valor decimal que representan; tambin podramos haber usado otra funcin del C estndar que es atoi() cuya descripcin, y la de algunas variantes, puede consultar en la ayuda de mikroC. SprintMedida(char *p, MEDIDA *m) escribe en la cadena p el resultado de la medida a la que apunta m, incluyendo el nombre de la medida, su valor y el intervalo de los valores registrados en las lecturas mltiples que se realizan como vimos, en la funcin LeeAD(). Segn el nombre de la medida, esta rutina determina el nmero de decimales apropiado; los valores del intervalo se dan siempre en crudo y representan el ruido de la medida sobre el rango mximo del A/D que es 1024 (2^10). As podemos tener T1=23.445+05-03 lo que significa que el valor medio de la temperatura T1 es 23.445C, y en la tanda de medidas en crudo todas resultaron entre 5 unidades por encima y 3 unidades por debajo del valor medio.

63

Para terminar vemos en esta diapositiva el contenido del fichero de texto que genera mikroC con la tabla de llamadas. En un programa de la complejidad del que hemos analizado en las ltimas diapositivas, es muy importante revisar este fichero y asegurarse de que el nmero mximo de llamadas anidadas que permita el PIC con el que estemos trabajando no se supera (8 para los PIC de gama media y 31 para la gama alta), ya que como se ha mencionado anteriormente, si esto sucede el sistema funcionara incorrectamente sin dar ninguna informacin de fallo, y puede ser muy difcil determinar el origen del problema. Vemos en la tabla que tenemos hasta 7 niveles de llamadas, muchas de ellas son funciones que nosotros no hemos definido, ya que pertenecen a la librera de mikroC, pero que igualmente consumen su correspondiente nivel en la pila de direcciones de retorno. Conviene recordar aqu que cuando estamos usando las interrupciones, debemos incrementar el nmero mximo de niveles que vemos en la tabla como mnimo en otro nivel ms y si en la rutina de la interrupcin hacemos ms llamadas tambin deberemos sumar todos los niveles usados, puesto que la interrupcin puede ocurrir en cualquier momento de la ejecucin del programa. Las imgenes que se muestran fueron obtenidas para la calibracin de dos sensores de temperatura y otros dos de humedad. El sistema registr los valores medidos en una oficina *casi* imperturbada. Podemos ver que las medidas son muy coherentes y que la precisin es muy buena. El pico en la humedad se debe al trabajo de la limpiadora y los picos de la temperatura se deben a la presencia del autor cerca de los sensores chequeando el sistema.

64