SlideShare une entreprise Scribd logo
1  sur  64
Télécharger pour lire hors ligne
Voy a contar en muy poco tiempo el proceso de auto-aprendizaje que yo he seguido
de los microcontroladores PIC, con un enfoque eminentemente práctico que permita
a cualquiera entender las características principales de los microcontroladores PIC de
gama media y mejorada, y eventualmente usar un microcontrolador para cualquier
diseño de complejidad pequeña o media.
Asimismo esta introducción puede considerarse el primer paso para conocer en
profundidad el amplísimo mundo de los controladores de una forma rápida; desde
aquí se podrá profundizar con la información que provee el fabricante sobre el uso
de funcionalidades más avanzadas o con cualquier otra gama de microcontroladores,
según sean las necesidades del interesado.
Dada la cantidad de información y el carácter práctico de esta introducción, 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 también que se trate de
hacer programas similares a los que se van a presentar con pequeñas variaciones, ya
que la práctica hace que se afiancen mejor los conocimientos.


         A mi Padre. Aún con el dolor de perderte siempre recordaré tu alegría Papá.
                                                                              Tu hijo




                                                                                         1
03.- ¿ Qué es un microcontrolador ?    04.- Familias de microcontroladores
05.- Gamas de los PIC de 8 bits        06.- Características de los PIC
07.- Periféricos 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.- Selección del canal A/D           24.- Configurar el A/D
25.- Programa A/D A2D.ASM              26.- Rebotes de tecla
27.- Eliminación 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.- Configuración                     42.- ‘C’ Led_blink.c
43.- ‘C’ básico 1                      44.- ‘C’ básico 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




                                                                                     2
Un controlador es un dispositivo que se emplea para el gobierno y/o la
monitorización de uno o varios procesos. Hace unas décadas, los controladores se
construían exclusivamente con componentes de lógica 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
corazón (chip) de un circuito integrado. Un microcontrolador es un circuito integrado
de alta escala de integración 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.
· Líneas 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 módulos para el control de periféricos (puertos serie y paralelo,
conversores Analógico/Digital y Digital/Analógico, etc.).




                                                                                        3
Hoy en día prácticamente todos los microcontroladores se fabrican con tecnología CMOS
(Complementary Metal Oxide Semiconductor) por su alta capacidad de integración, su bajo consumo
y su alta inmunidad al ruido.

Entre los más 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 más potente.
• PIC de MicroChip que posee una amplísima gama. Fueron los primeros microcontroladores con
arquitectura RISC (Reduced Instruction Set Computer). Trataremos de éstos a lo largo de esta
presentación.
• AVR de ATMEL, nombre genérico de otra amplia gama de microcontroladores muy potentes y
rápidos: AT90Sxx, ATtinyxx, ATmegaxx. Todos ellos pueden trabajar en C con herramientas libres.

La mayoría 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 PIC’s que
poseen una arquitectura de buses Harvard y un conjunto reducido de instrucciones (RISC). La
arquitectura Von Newman (muy común en casi todos los procesadores como los PC’s) tanto las
instrucciones del programa como los datos llegan a la CPU a través 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 número de líneas (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 interacción con el mundo se hace a través del bus de datos.
Programar los PIC en ensamblador es algo más fácil que para otros tipos porque sólo tienen 35
instrucciones (RISC) frente a por ejemplo las 130 de los AVR; en cualquiera de los dos casos suele ser
más fácil y rápido programar en alto nivel (C o BASIC).




                                                                                                            4
Además de la división según la arquitectura de 8, 16 ó 32 bits, la familia de 8 bits de
MicroChip se divide en gamas según la evolución que han tenido los microcontroladores.
Esta clasificación es muy dinámica, tanto que actualmente MicroChip habla de tres rangos,
en vez de los cinco (históricos) 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 sólo por dar información histórica, asimismo dado
la rapidísima evolución del mercado de microcontroladores PIC, los números que se indican
corresponden a un momento en el tiempo y por tanto varían al irse introduciendo nuevos
productos con mayores prestaciones.
Los nombres de los PIC’s que se dan corresponden a los microcontroladores que tienen
memoria FLASH (de ahí la ‘F’ del nombre), aunque también existen otros identificativos para
distintos tipos de memoria PROM que MicroChip llama OTP (One Time Programming) o
también con memoria ROM que se crea en el proceso de la fabricación. 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 físico.
En la columna de ‘Instrucciones’ tenemos el tamaño 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 sólo 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 también el sistema de desarrollo
EASYPIC.
Es interesante destacar que la gama PIC18Fxxxx posee un número mayor de instrucciones y
un mapa de memoria diferente para permitir la programación de estos dispositivos en C; de
hecho MicroChip sólo provee un compilador C para la gama alta; afortunadamente otros
vendedores han desarrollado compiladores para todas las gamas, aunque con las lógicas
limitaciones debidas al hardware. Nosotros usaremos el compilador de Mikroelektronika
(www.mikroe.com) del que tenemos licencias, es fácil de usar, tiene buenas librerías y se
puede descargar una versión limitada gratuitamente.




                                                                                              5
La tecnología CMOS es muy rápida y como no necesita resistencias de polarización, prácticamente sólo consume
energía 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 baterías el PIC puede detener el
reloj (modo sleep) y dado que la RAM es estática 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 instrucción se hace *durante* la ejecución de la instrucción actual lo que permite una mayor velocidad
de ejecución en casi todos los casos. Sólo cuando se ejecuta un salto en el programa se tiene que desechar la
instrucción leída, por lo que las instrucciones de salto requieren el doble de tiempo que las otras. De cualquier
manera cada instrucción necesita 4 ciclos de reloj para ejecutarse, y según hemos visto las de salto requieren 8
ciclos de reloj para su ejecución. Así pues si un PIC tiene un reloj de 8 MHz, como máximo ejecuta a una
velocidad de 2 MIPS (Mega Instrucciones por Segundo) y como máximo los PIC de gama alta alcanzan 10 MIPS a
40 MHz; los procesadores de ATMEL ejecutan una instrucción por ciclo de reloj llegando a 20 MIPS.
Los PIC tienen un solo registro llamado W (Working register) conocido también como acumulador, que
lógicamente tiene 8 bits. El bus de direcciones de acceso a la memoria es de 7 bits, por lo que sólo se puede
acceder a 128 bytes simultáneamente; para los PICs que tienen más 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 propensión a tener fallos en los programas. Esta es posiblemente la característica 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
selección 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 auténticos registros que
son independientes de la memoria, siendo éste el concepto más usual de registro.
El control de periféricos se hace simplemente escribiendo en una determinada celda de memoria genéricamente
llamada SFR (Special Function Register). Por ejemplo, si quiero poner a nivel lógico alto todas las patillas del
puerto A, tendré que escribir FFh en la dirección de memoria 5, ya que esta es la dirección asignada para leer y/o
escribir en el puerto A. Por tanto para controlar los dispositivos del PIC se usa exactamente la misma instrucción
que para almacenar un dato en memoria, solo que para ciertas posiciones de memoria (SFR’s) estaremos
accediendo a un dispositivo físico que nos conecta con el mundo real; esto es lo que significa que los periféricos
está mapeados en la memoria.
/******** Continua en la diapositiva siguiente ********/




                                                                                                                     6
Este PIC tiene un oscilador interno a 8 MHz de tipo RC, poco estable (+-1%) con un divisor hasta 32
kHz; también puede trabajar con un cuarzo o resonador externo hasta 20 Mhz. El modo de
funcionamiento se selecciona en el momento de la programación del dispositivo y no puede
cambiarse durante la ejecución (aunque con el oscilador interno se puede seleccionar el factor de
división).
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 programación o ‘quemado’ (burn) del dispositivo y la
EEPROM el la que se puede almacenar datos durante la ejecución. La FLASH permite más de 100.000
escrituras, mientras que la EEPROM más de 1.000.000, aunque por velocidad y durabilidad nunca
debe usarse como RAM. La retención de datos en ambos casos es mayor de 40 años.
Los comparadores permiten generar una interrupción o cambiar el estado de un SFR (registro en
memoria) según el valor de un voltaje analógico 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 módulo ECCP+
(Enhanced Capture Compare) trabaja con los temporizadores para medir tiempos con mucha
precisión y también controla un generador de PWM (Pulse Width Modulation) con 10 bits de
resolución 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 interrupción con el bit
de start.
El módulo 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 también permite hacer
debugging en la PCB.
Al entrar en modo sleep se detiene el oscilador, se vuelve a la ejecución después de un reset o una
interrupción; cada vez que se re-inicia el PIC el módulo PWRT (Power-up Timer) espera el arranque
del reloj antes de empezar la ejecución y el OST (Oscillator Start-up Timer) permite incrementar la
espera hasta la estabilización completa del mismo (1024 periodos). El POR (Power-on Reset) genera
una señal limpia de reset independientemente de la pendiente de subida de la alimentación; el BOR
(Brown-out Reset) generará un reset si la tensión de alimentación baja de un cierto nivel pre-
establecido. El módulo watchdog (WDT) es un temporizador totalmente independiente de la
ejecución del programa que generará un reset pasado un cierto tiempo, que es configurable, sin que
la ejecución de programa haya ‘limpiado’ este temporizador; normalmente se usa para evitar que el
procesador se quede ‘colgado’ o ejecutando un bucle sin fin indeseado.




                                                                                                         7
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 veíamos también la
asignación 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 módulos
podrán usarse simultáneamente en una determinada aplicación. Así por ejemplo la
patilla RA3/MCLR/Vpp puede funcionar como línea 3 del puerto A, o como master
clear (reset) o se usa para introducir el voltaje de programación del dispositivo (Vpp)
que es de unos 12 voltios. En la configuración del dispositivo tenemos que decidir si
esta patilla se usará como puerto general RA3 o como reset, y en cualquier caso la
circuitería 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 aplicación (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 propósito general A, B y C; el bus de
programa de 14 bits; la unidad de procesamiento aritmético/lógico ALU con sus
registros asociados; el descodificador de instrucciones; el módulo de control;
oscilador interno; la unidad generadora de tiempos; los temporizadores 0, 1 y 2; el
módulo de comunicaciones serie asíncronas EUSART; el módulo de
captura/comparación mejorado ECCP+; puerto de comunicaciones síncronas SSI; el
módulo conversor A/D de 10 bits con 12 entradas y una referencia; módulo de
comparadores; y la memoria EEPROM de 256 bytes.




                                                                                            8
Vemos aquí la memoria RAM organizada en 4 bancos, de 128 bytes cada uno. Los PIC
sólo pueden acceder a un banco a la vez. El motivo de estas limitaciones es que los
códigos de las instrucciones de ensamblador (conocidos como opcodes) tienen sólo
7 bits para especificar la dirección 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 están 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 también que el número 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 dirección (File
Address) es 3. STATUS es el registro que contiene los bits de estado de la CPU y
también 2 bits llamados RP1 y RP0 que son los que nos permiten establecer el banco
al que estamos accediendo. (Ver diapositiva siguiente para más 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, están 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 deberán
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 interrupción puede llegar
en cualquier momento, necesitamos un área de memoria donde guardar el estado
del procesador, incluyendo la información del banco en el que se esté trabajando
inmediatamente antes de ejecutar la interrupción y luego volver a el mismo estado
antes de retomar la ejecución normal del programa.




                                                                                          9
Este es un ejemplo de cómo 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 después de un reset de la alimentación 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 operación 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 más usados.
Cada puerto tiene como mínimo dos SFR’s 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 nemónico TRISx proviene de TRi-State logic ya que cada patilla de los
puertos tiene un driver tri-state, o sea que además de los estados alto ‘1’ o bajo ‘0’, tiene un
tercer estado conocido como de alta impedancia, lo que significa que la línea 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 línea y por tanto podremos leer el
estado que ponga en ella la circuitería exterior, o sea, esa línea queda configurada como
una línea de entrada. Inversamente si ponemos un 0 en un bit de la palabra TRISA, estamos
activando su línea correspondiente como línea de salida.
Podemos escribir en el SFR TRISx en cualquier momento de la ejecución del programa, pero
lógicamente si por error se activa como salida una línea conectada a una salida de la
circuitería externa se puede producir un corto-circuito de los dos drivers (el del PIC y el de la
circuitería externa) que podría ocasionar averías; es importante por lo tanto que el
programador defina correctamente las líneas de entrada y salida de todo el dispositivo.




                                                                                                    10
Lógicamente antes de trabajar con los PIC’s 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 información,
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 opción esté activada).
Debemos chequear también 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 números de línea en la ventana del texto del programa,
hacer click con el botón derecho sobre la ventana y seleccionar ‘Properties’, luego en la
pestaña de ‘ASM File Types’ marcar ‘Line Numbers’ o cualesquiera otra opción a nuestro
gusto. Después una vez que la compilación 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 botón Write.
En la ventana PICkit2 Programmer podemos pulsar el botón Read y vemos que el dispositivo
tiene las primeras posiciones de la memoria de programa (FLASH) escritas, todos los bits que
no están escritos aparecen como 1, y dado que el PIC tiene 14 bits por palabra, en
hexadecimal vemos 3FFF en todas las demás posiciones de memoria. Si pulsamos ‘On’ en la
zona de VDD PICkit 2, aplicaremos la tensión 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 sólo 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 potenciómetro y el pulsador; se han añadido 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 pequeño motor de corriente continua.
Disponemos también de otras tarjetas de demostración con un PIC16F887 SMD de
44 pines. Ambos tipos de tarjetas son muy convenientes para ser usadas en
aplicaciones pequeñas que no requieran mucha circuitería extra, o como un módulo
que se puede integrar en un sistema mayor a través de los conectores adecuados
para aplicaciones más grandes.

A lo largo de esta introducción veremos programas para medir y/o controlar todos y
cada uno de estos dispositivos. A veces usaremos también alguna placa de
desarrollo de MikroElektronika, llamadas EasyPIC que son mucho más grandes y
tienen por tanto mayor número de dispositivos y posibilidades. Para la mayoría de
los programas en C usaremos los EasyPIC, aunque también algunas aplicaciones para
la placa de desarrollo modificada para ilustrar el uso del programador PICkit2 con
programas desarrollados con mikroC.

La mayoría de los programas en ensamblador que veremos a continuación 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 aplicación sin
necesidad de sacar el PIC del circuito, esto es lo que se conoce como ICSP (In-Circuit
Serial Programming); que ya habíamos mencionado como una de las grandes
ventajas de los PIC.




                                                                                         12
Este pequeño programa está escrito en lenguaje ensamblador de los PICs, cabe destacar:
•Todo lo que esté detrás de un punto y coma se considera comentario.
•Las líneas que empiezan con # son directivas para el preprocesador, también en general las que empiezan por
‘__’ y algunas palabras especiales como org o end que son palabras reservadas. Ninguna directiva produce
código ejecutable, pero pueden modificar la forma en que el compilador lo produce.
•La directiva ‘#include’ carga una serie de definiciones específicas 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 más
legible y fácil de entender. Si no pusiéramos esta directiva el compilador se quejaría, ya que no sabría que
significa STATUS, PORTC, RP0, etc.
•La directiva __CONFIG se usa para especificar los bits de configuración 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
configuración (CONFIG ver datasheet pg. 174) con un SFR, ya que esta palabra sólo se escribe en el momento del
burning del dispositivo y no puede cambiarse durante la ejecución.
•La directiva ‘org x’ establece que el código que sigue debe ensamblarse a partir de la dirección ‘x’. En este caso 0
es la dirección por la que siempre empiezan los PIC’s a ejecutar después de un reset.
•La palabra ‘Start’ sin indentar es una etiqueta, o sea es un identificativo para esa posición del programa a la que
podremos dirigirnos con la orden ‘goto Start’. La sintaxis más usada añade 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 línea 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 través de una resistencia) el LED se encenderá.
•El comando ‘goto $’ salta sobre sí mismo de forma que ya no se ejecuta nada más. El goto es un salto a la
dirección especificada, y ‘$’ significa la posición actual, también 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 actúa la instrucción: BCF 0x71,2?
R: En principio en general el banco depende de las instrucciones de selección de
banco que se hayan ejecutado antes, y por tanto en esa parte de programa que se ha
dado como ejemplo no aparece ninguna operación de selección de banco, luego
parece que no podríamos determinarlo con la información que tenemos. Sin
embargo la dirección 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 instrucción en función del valor del bit que se testea. Notar
que la lógica de estas instrucciones es un poco confusa ya que la siguiente
instrucción no se ejecuta cuando la condición es verdadera, sino falsa; ya que la
instrucción está formulada negativamente (skip = no ejecutar), podríamos 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 operación; ‘d’ puede valer 1 ó 0 según el destino sea la celda de memoria o
el acumulador (W) respectivamente. Realmente esto es un ‘truco’ para mantener bajo el
número 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 posición de memoria. En los ‘include’ de MPLAB están definidas f=1 y w=0, para
hacer el código más fácil de entender; esto nos permite por ejemplo usar:
        incf        Contador,f              ;   Contador = Contador + 1; equivalente a ‘incf
        Contador’
        incf        Contador,w          ;   W = Contador + 1, la variable Contador no cambia


El funcionamiento de cada instrucción está dado en la columna ‘Function’ y no hay mucho
más que añadir; cabe quizás mencionar que la operación NOP se puede usar para introducir
un pequeño retardo, pero hay que considerar que este retardo depende de la velocidad de
reloj lógicamente. Como todas las instrucciones simples NOP se ejecuta en 4 ciclos de reloj.
También son interesantes los comandos DECFSZ y INCFSZ que se usan para hacer bucles; por
ejemplo para ejecutar 100 veces determinado grupo de instrucciones podríamos hacer:
               movlw          .100     ; NOTA: esta es la manera por defecto de
introducir números en decimal
               movwf          Contador
Loop100:
               < grupo de instrucciones a ejecutar ….>
           decfsz     Contador
           goto       Loop100                       ; si el Contador es != 0
vuelve a ejecutar el Loop

Los PIC son atípicos 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 contraposición a
los datos que podemos almacenar en la memoria, que pueden cambiar durante la
ejecución 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 porción de código a la que se puede llamar
desde varias posiciones en el programa, y una vez acabada la ejecución de la
subrutina, la ejecución vuelve a la instrucción 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 máximo 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 más los de la rutina de interrupciones más uno, que es el
nivel que usa la propia interrupción. 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 interrupción 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 dirección
de la instrucción de programa que se está ejecutando.

El comando CLRWDT es el que resetea el contador del watchdog (WDT), evitando la
re-inicialización 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 PIC’s antiguos,
no son ya necesarios y las próximas versiones no los incorporarán.




                                                                                            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 ejecución 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 aritméticas (ADD, SUB) afectan a C, Z y DC; las operaciones
lógicas sólo a Z, las de rotación sólo al C y cabe destacar que las operaciones INCFSZ, DECFSZ,
MOVxx o SWAPF no afectan a ningún bit de STATUS, excepto MOVF que afecta a Z.
Observe en la columna del ‘opcode’ o código de la instrucción que la dirección de memoria
(o file) tiene 7 bits, esto es lo que determina que el tamaño de los bancos de memoria sea
de 128 bytes ya que esto es lo máximo que podemos direccionar con los 7 bits de la
instrucción. Análogamente 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 página), para saltos a
más distancia hay que hacer uso del SFR PCLATH, que nos permite acceder a todas las
páginas 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
Además de las 35 instrucciones mencionadas anteriormente, MicroChip ha introducido esta
serie de instrucciones especiales (en realidad macros) para facilitar la programación. 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 definición de dicha macro y están soportadas por MPLAB; así por ejemplo podríamos
definir en un programa:

#define         SALTA           GOTO

esta sentencia define la macro ‘SALTA’ como ‘GOTO’; las macros son sustituidas antes de la
compilación 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 acompañan al
MPLAB y que además contienen los nombres de los SFRs de cada tipo de microcontrolador
como ya habíamos mencionado. Todas estas instrucciones se obtienen en realidad como
una combinación 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 más que nombres más adecuados a los ‘poco
afortunados’ BTFSC o BTFSS.

En los PIC de gama alta la mayoría de estas instrucciones son instrucciones reales, y además
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 diseñado
para permitir la programación de estos dispositivos en lenguajes de alto nivel,
particularmente el C. Algunas de las instrucciones extendidas hay que activarlas en la
configuración del dispositivo.




                                                                                               18
Entre las líneas 31 y 34 tenemos la declaración 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 posición de memoria 0x20. En realidad en este caso lo único
que estamos haciendo es asignar a los símbolos Delay1 y Delay2 los valores 0x20 y
0x21 respectivamente, pero si tenemos un bloque de memoria con más elementos
es más cómodo 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 sólo afecta al primer bucle, por lo que es casi irrelevante.
•Hay dos partes en el código que son idénticas y por tanto este programa se puede
estructurar mejor creando una subrutina. Se propone como ejercicio.
•También se puede re-diseñar la rutina del ejercicio anterior para que acepte una
variable que determine el retardo total. De esta forma se hace mucho más fácil
hacer más rápido o lento la velocidad de parpadeo; se puede también cambiar la
relación encendido/apagado o incluso modificarlo durante la ejecución 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 rotación, tal y
como puede verse en las líneas 60 a 64.

¿ Se podría usar PORTC como variable en vez de Display ? La respuesta es sí, y en la
mayoría de los casos funcionaría bien; pero podría darse un efecto colateral
indeseado, ya que si por ejemplo la línea 3 del puerto está cortocircuitada la lectura
encubierta que se hace durante la rotación (rrf PORTC,f) nos devolvería 0 al intentar
activar la línea 3 y por tanto la rotación se pararía 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 líneas más o menos típicas de los puertos que
podemos encontrar en los PIC. La complejidad que podemos observar se debe a las
múltiples 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 flip-
flops para el control de la línea. El flip-flop de PORTC es el que proporciona el dato de
salida, mientras el flip-flop TRISC controla la habilitación del driver de salida. Vemos también
que la lectura digital de este pin sólo puede hacerse si no está habilitada su línea de
selección de modo analógico, ya que si el modo analógico está seleccionado para esta línea
siempre leeremos 0 lógico. También observamos que para esta línea se puede seleccionar la
función SDO, en cuyo caso el nivel lógico 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
además 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;
análogamente existen WPUB e IOCB con las mismas funcionalidades para el puerto B.
También vemos que tiene funciones relativas al TMR0 y tiene la capacidad de generar
interrupciones desde dispositivos externos (INT).
Una característica que afecta a todas las líneas de los puertos de los PIC es que *siempre*
usan una secuencia de lectura-modificación-escritura (Read-Modify-Write o RMW), lo que
significa que cualquier instrucción que especifique un registro ejecuta un ciclo RMW, esto
es, el registro es leído, los datos modificados, y el resultado es almacenado de acuerdo con
la instrucción. En otras palabras: una operación de lectura se lleva a cabo en el registro
incluso si la instrucción aparentemente “sólo” escribe en dicho registro. Esto puede tener
resultados indeseados en la interacción 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 centésima 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 tensión de referencia. Esta tensión de referencia se puede seleccionar con
el bit VCFG, bien la tensión de alimentación o una tensión 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 selección 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 están en modo analógico
después de un reset.

IMPORTANTE: hay grandes diferencias en la forma de seleccionar los canales analógicos de
un modelo de PIC a otro y es *imprescindible* consultar la documentación para el correcto
control de las entradas.

Un aspecto importante del control del convertidor A/D es la velocidad de conversión, que se
establece con el SFR ADCON1; este SFR tiene sólo 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 tensión de alimentación (referencia Vdd); el reloj de digitalización es de
Fosc/16.
Nótese el uso de NOP’s para introducir un pequeño retardo desde la activación del A/D
hasta el inicio de la conversión. Este retardo se necesita para que se estabilice el voltaje a la
entrada del módulo A/D.
Aquí introducimos una manera más cómoda de selección de bancos de memoria: en vez de
poner explícitamente 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 selección de banco al valor
necesario, esto puede ser ineficiente ya que la mayoría de las veces no es necesario
cambiarlos todos. Por otra parte el compilador siempre genera un warning cuando
accedemos a cualquier posición de memoria fuera del banco 0, tal y como puede verse en la
figura referido a la línea 63 del programa donde se accede a TRISA que está en el banco 1, y
aunque tengamos la directiva BANKSEL en la línea 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 línea 63 y podría 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 potenciómetro en la placa y veremos como los 4 LEDs presentan
los bits más significativos de la conversión, siendo equivalente a la presentación en binario
del valor del voltaje/Vdd * 16, ya que se trata de los 4 bits más significativos. Es interesante
mover el potenciómetro cerca de la transición 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 pulsación de teclas producen rebotes, es decir el
interruptor se activa y desactiva más veces de las que deseamos.

A la entrada del puerto A<3> tenemos una resistencia de pull-up y un pulsador a
tierra. Notar cómo 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 también se
requiere deshabilitar la entrada analógica correspondiente en el registro ANSEL.

Preguntas: ¿ Son correctos los comentarios del programa ? ¿ Cuándo se actualiza la
cuenta, al pulsar o al soltar la tecla ? R: Los comentarios de las líneas 31 y 36 están
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 pulsación debería contar 2 veces, uno al pulsar y otro al soltar la tecla.




                                                                                          26
Esta versión del programa llamada Debounce.asm, espera a detectar la tecla estable
5 veces, con un retardo de 1 ms entre cada testeo.

La eliminación de los rebotes también se puede hacer por métodos ‘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 colocación de esta circuitería
externa y simplificamos nuestro diseño.

Observar como se puede comprobar si una variable (Counter en este caso) alcanza
un determinado valor en las líneas 73 y siguientes.




                                                                                     27
Entre los programas fuente que acompañan la presentación encontramos 3 que son
muy similares: Vsrotate.asm, Reversible.asm, y Function.asm. Los dos primeros no
añaden nada nuevo por lo que se deja al lector para su compilación y estudio; en
ambos casos se lee el A/D y se establece una rotación de los LEDs con un retardo
que es función del valor leído del A/D. Así moviendo el potenciómetro los LEDs se
desplazan a mayor o menor velocidad. En el programa Reversible.asm también se
puede cambiar el sentido de desplazamiento del LED encendido al pulsar el
interruptor de la tarjeta. Finalmente Function.asm realiza la misma función pero
introduce una subrutina para manejar el tiempo de retardo, como podemos ver en la
diapositiva desde la línea 116 hasta las 123.

Una llamada a la rutina aparece en la línea 85; vemos que el acumulador (W) se usa
para pasar un ‘parámetro’ a la rutina, en este caso es el número de bucles de
segundo nivel que realizaremos; cuanto mayor sea el valor de W mayor será el
retardo generado.

También es posible que una rutina ‘devuelva’ algún valor; en ese caso puede usarse
W o si son más de un valor pueden usarse variables creadas al efecto.




                                                                                     28
Entre otras funciones el SFR OPTION_REG controla al temporizador Timer0. La señal
de reloj que alimenta al Timer0 puede derivarse por división del reloj principal o
puede introducirse al PIC por una línea 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 estaría 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 función del valor del bit
PSA se usará para uno o el otro pero nunca simultáneamente para los dos.
Finalmente los tres bits bajos PS<2:0> seleccionan el valor de división del pre-scaler,
note que los valores de división 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 interrupción (ver diapositiva siguiente).




                                                                                          29
Una interrupción es una señal que recibe el controlador para atender un suceso detectado por el hardware periférico. En los
PIC existen numerosos periféricos que pueden generar interrupciones: como los timers, el cambio de estado en una línea de
un puerto, el conversor analógico/digital, los comparadores, el módulo de comunicaciones serie, el módulo de comparación y
captura, la EEPROM, etc. Asimismo un sistema externo al microcontrolador puede también generar una interrupción a través
de un determinado flanco en la línea de interrupción externa (RA2/INT en el PIC16F690).
Para que una interrupción afecte al microcontrolador, deberán están habilitadas las interrupciones en general (INTCON<GIE>
ver diapositiva siguiente); para los periféricos del microcontrolador deberá también estar habilitadas las interrupciones de
periféricos (PEIE), así como las interrupciones del periférico 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 habilitación, la interrupción no afecta a la ejecución. Si por el contrario se recibe una interrupción que está
habilitada el controlador termina de ejecutar la instrucción actual (en algunos casos puede haber más retardo – latencia),
pone GIE a 0 para deshabilitar cualquier otra interrupción, y ejecuta un salto a la dirección 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 instrucción
siguiente a la que se terminó de ejecutar cuando se recibió la interrupción, usando para ello la instrucción RETFIE que vuelve a
habilitar GIE. Dentro de la rutina de servicio de interrupciones se debe salvar el contexto de ejecución del programa principal
(WREG y STATUS/banco de trabajo), y este contexto deberá recuperarse justo antes de volver de la interrupción, para que la
ejecución normal pueda continuar tal y como se había quedado. Para esto es necesario reservar 2 celdas de memoria en la
zona común de todos los bancos (Ejercicio: razone por qué debe ser en la zona común y no en el banco 1 por ejemplo.) que
nos permitan almacenar el working register W y la palabra STATUS. Este código 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 doesn’t 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 más 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 interrupción mirando
los flags de INTCON, PIR1 y PIR2; sólo 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
interrupción es una señal generada por algún dispositivo interno o externo al PIC y
que requiere una acción 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 interrupción 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 estén habilitadas.

Además de este registro INTCON, existen los registros PIE1 y PIE2 que son los
‘Peripheral Interrupt Enable registers’ ; en ellos se puede habilitar la interrupción de
los dispositivos internos (periféricos) 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; lógicamente existen dos vectores de interrupción en las posiciones 0x08
y 0x18 respectivamente, y muchas otras posibilidades de configuración.




                                                                                           31
Vemos en este sencillo programa que el uso del temporizador lógicamente puede
simplificar la generación de retardos o en general el control del tiempo. Insertado
en el programa se ha incluido un diagrama simplificado del Timer0.

En la línea 36 seleccionamos un factor de división de 256 de la frecuencia de
entrada; en este caso la señal seleccionada es el ciclo de instrucción del
microcontrolador, que a su vez es Tosc*4 ya que cada instrucción requiere 4 ciclos de
reloj para su ejecución. En total pues el periodo de la señal 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 línea 42 el programa espera sin hacer nada ‘útil’; normalmente el procesador
podría dedicarse a cualquier otra cosa mientras el temporizador lleva la cuenta del
tiempo transcurrido. Nótese 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 pequeño error en el programa, ¿ cual es ? ¿ qué efecto
tiene ? R: En la línea 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 conversión en el registro del TMR0 por lo que acelera o decelera la rotación del encendido
de los LEDs en la placa de desarrollo.

Nótese en la línea 52 que la primera instrucción es ahora un salto al principio del programa normal,
ya que la rutina de servicio de las interrupciones (ISR) empieza forzosamente en la posición 4.
Observe entre las líneas 61 y 66 como se puede averiguar que periférico ha generado la interrupción
mirando los flags en INTCON y PIR1, y como se limpia por software el flag en la línea 70.
‘T0Semaphore’ es una posición de memoria que se usa para transmitir información 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 tipográfico en las sentencias de chequeo de los flags, ¿ puede señalarlo ?
Asimismo hay otro error más grave que hace que no se espere correctamente el final de la conversión
del A/D, ¿ puede identificar el error ? Es posible que este error pasara desapercibido porque a efectos
prácticos no se nota en la ejecución normal del programa, ¿ puede explicar porqué no se nota ?
Estos dos errores están corregidos en versión de ‘interrupt.asm’ que acompaña al curso.

Por otra parte el programa está preparado para que el sentido de rotación 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 línea, que
es el Master Clear o reset, no queda liberada cuando usamos MPLAB. Puede comprobarse con un
voltímetro que RA3 está continuamente a nivel lógico bajo y por ello no responde a la pulsación 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 más que una posición 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 operación 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 razón por la que
sólo se necesita 1 bit de selección 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 más 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 código pone a 0
una serie de posiciones de memoria:
                    MOVLW 0x20          ;initialize pointer
                    MOVWF FSR                        ;to RAM
NEXT                CLRF INDF           ;clear INDF register
                    INCF FSR                         ;inc pointer
                    BTFSS FSR,4                      ;all done?
                    GOTO NEXT                        ;no clear next

Pregunta: ¿ cuantas posiciones de memoria inicializa este pequeño programa ? R:16
Observe las instrucciones de las líneas 36 a 39 como pueden reservarse varias posiciones contiguas en la
memoria añadiendo ‘: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 posición 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 líneas 120 y siguientes. El resultado vuelve a tener 8 bits y se
devuelve en W (y también en la variable Round).
Pregunta: Vemos en las líneas 102 a 104 como se resta un valor de 8 bits de la variable de 16 bits ‘RunningSum’;
los PIC realizan la substracción como una adición del número complementario. ¿ Es correcto entonces el uso del
bit de acarreo (Carry) que se hace en la línea 103 y 104 ? Razone la respuesta y ponga algún ejemplo numérico.
R: Sí es correcto, si hay carry significa que la operación 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);
además 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 (GOTO’s o CALL’s) a otras páginas 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
ningún registro) contiene los 5 bits altos. Podemos modificar PCH usando el SFR PCLATH, que se carga
en PCH de forma automática al hacer cualquier operación en PCL. También si hacemos un salto o una
llamada a subrutina los dos bits más altos de PCLATH se transfieren automáticamente a la parte alta
de PC; de esta forma se pueden hacer saltos entre páginas de código.

En esta rutina lo que hacemos es un GOTO computado, sumándole a PCL el valor de entrada a la
rutina que es W. La instrucción ‘retlw XX’ carga el valor del literal XX en W y retorna; en nuestro caso
supongamos que la tabla empieza en la posición 81 (que coincide con el número de la línea) y que el
valor de entrada a la rutina es W=3; entonces al ejecutar ‘addwf PCL,f’ el programa saltará a la línea
84 donde se retorna al programa principal con W cargado con el valor que se encuentra en la posición
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 código Gray de los números 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 podríamos saltar
hacia atrás en vez de a la zona de la tabla; esto es lo que se muestra entre las líneas 71 y 79. Otra
forma más simple de evitar esta complejidad es asegurarnos de que la tabla no atraviesa ninguna
frontera de 256 bytes, usando ORG 0x100 por ejemplo. Aquí también se ilustra como leer el byte alto
y bajo de una dirección de programa usando las palabras reservadas ‘high’ y ‘low’.

Este es el último de los programas ejemplo que acompañan al PICkit2, a partir de ahora veremos
ejemplos desarrollados por el autor, que son un poco más complejos pero también más cercanos a
aplicaciones prácticas.




                                                                                                            35
El programa ‘1digito.asm’ ilustra como se pueden usar las tablas para representar los números 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 están conectados a través de
resistencias a RC0, RC1, …, RC6 respectivamente y el punto decimal está conectado al bit 7 (RC7). El
cátodo común de los displays está conectado al colector de sendos transistores cuyas bases están
controladas por el puerto B<6:7>; siendo la línea 6 (RB6) el dígito menos significativo. La figura de la
diapositiva siguiente representa un esquema similar (aunque no igual). Esta conexión permite la
multiplexación de la representación 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 código para su
representación en formato de 7 segmentos y de esta manera no necesitamos usar un descodificador
del tipo 7447 o 4511 y además nos permite extender la funcionalidad de éstos que sólo pueden
representar del 0 al 9. La tabla es fácil de crear, por ejemplo para el número ‘1’ tendremos que
encender los segmentos b y c, por tanto tendremos que activar las líneas 1 y 2 del puerto C cuyo valor
seria: 0000 0110b ó en hexadecimal 0x06 que es la entrada que vemos en la segunda línea de la tabla
que corresponde al número ‘1’, ó en la figura para el ‘5’: 0110 1101 = 0x6D.
En la línea 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 dígito 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 líneas 35 y 42 donde se llama a la rutina SevenSeg ? ¿
Veremos algo reconocible en el display ? R: No; veremos que se encienden unos segmentos difíciles
de interpretar.
Observe como en las líneas 70 a 73 comprobamos que la tabla no atraviesa ninguna frontera de
página y por tanto podemos hacer el GOTO computado sin mayor complicación (Comparar con la
rutina de la diapositiva anterior). Estas líneas no generan ningún código ya que son sólo directivas
para el preprocesador; si al compilar obtenemos el error ‘Tabla de SevenSeg cruza una frontera de
página’ tendremos que mover la rutina a otro sitio del programa, normalmente esto no supone
ningún 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 dígitos del display de 7 segmentos. Dado que
los dígitos están conectados en paralelo al puerto C tenemos que multiplexar, activando
cada dígito durante un cierto tiempo y luego hacemos la misma operación con el otro dígito.
Si estas operaciones se realizan con la suficiente rapidez, no se aprecia ningún parpadeo tan
solo veremos que la luminosidad es menor que cuando se activaba un dígito
permanentemente.

Para conseguir esto hemos desarrollado la rutina ‘Representa’, que recibe como parámetro
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. Además se llama
a la rutina ‘TiempoEncendido’ para mantener el dígito durante unos 30 us encendido. Para
que esta rutina funcione correctamente tenemos que hacer llamadas a ella de forma
continua, ya que si no observaríamos un molesto parpadeo o incluso los dígitos podrían
desaparecer si hubiese un periodo del orden de una décima de segundo sin que se
activaran. Vemos pues que mientras el programa espera la pulsación de la tecla siempre
tiene la llamada a ‘Representa’ dentro del bucle de comprobación, tal y como puede verse
entre las líneas 41 a 45 del programa.

Introducimos en este programa también la directiva ‘DT x’ que es equivalente a ‘retlw x’,
pero que nos permite poner las tablas más o menos grandes de una forma mucho más
compacta tal y como puede verse entre las líneas 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 serían mayores y hay que hacer
otras operaciones con PCL; además para la gama alta existen otras instrucciones que
trabajan directamente con las tablas: TBLRD* y TBLWT*, que lee o escribe en la posición
apuntada por TBLPTR que es un SFR triple con 21 bits útiles.




                                                                                                  37
El programa ‘7segment.asm’ usa los dos dígitos del display de 7 segmentos de forma
completa, para representar los 10 bits del resultado de la conversión A/D. Además ilustra la
forma de hacer que la conversión 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 conversión; simplemente
comentar la línea 68 (SLEEP) y quitar el comentario de las líneas 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 también en el programa como se configura el PIC para habilitando las
interrupciones de periféricos (PEIE), pero no habilitamos las interrupciones en general
(GIE=0). Esto puede parecer inútil pero nos permite ‘despertar’ al micro cuando el A/D
acaba la conversión, sin necesidad de tener una rutina de servicio de interrupciones. Este
código realiza esta función:

                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 líneas 32 a 34 salvan el contexto del programa
principal; las líneas 35 a 40 determinan la fuente de la interrupción y saltan a la
parte específica para cada periférico, en nuestro caso del TMR0 en las líneas 42 a 59,
o del A/D en las líneas 68 a 83; después retoma el contexto del programa principal
entre las líneas 60 a 64, para finalmente volver al programa principal re-activando las
interrupciones con la orden RETFIE en la línea 65.

Puede observarse en el programa que activamos el modo SLEEP para hacer la
medida del voltaje analógico sin interferencias; es muy importante en este caso
notar que durante la conversión también TMR0 se para, ya que está ligado al
oscilador principal que también deja de oscilar. Por tanto si estuviéramos haciendo
una cuenta de tiempo basado en TMR0 se acumularían retrasos, y si cometiéramos
el error de activar el modo SLEEP sin arrancar la conversión, el microcontrolador se
pararía indefinidamente ya que ningún periférico generaría interrupciones en ese
caso. Si se necesitara mantener un reloj y usar el modo SLEEP, se podría 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 módulo ECCP+ (Enhanced
Capture/Compare/PWM+) del PIC 16F690 para controlar el brillo de los LEDs o cualquier
otra función pseudo-analógica conectada a los pines 2, 3, 4 ó 5 del puerto C. La resolución
del PWM es como máximo de 10 bits, y usa Timer2 (8 bits), más dos bits del pre-scaler nos
permite controlar el ancho del pulso y el periodo; la relación pulso/periodo es el ciclo de
trabajo o duty-cycle. En la diapositiva se puede ver una aplicación half-bridge estándar, half-
bridge controlando un circuito full-bridge, y una aplicación 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 número 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 módulo PWM se usen las librerías de mikroC ya que éstas simplifican notablemente
el trabajo; más 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 función de la lectura;
observamos que el brillo de los LEDs 3 y 4 de la tarjeta de desarrollo varía de forma continua
al mover el potenciómetro RP1; si queremos controlar un dispositivo que requiera más
potencia tendremos que usar un transistor como driver.
Otra aplicación común del módulo 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 módulo PWM no podemos usar el modo SLEEP.




                                                                                                  40
En diapositivas anteriores hemos mencionado determinadas opciones que sólo pueden
establecerse cambiando la configuración del microcontrolador. Todos los PIC tienen una o
varias palabras de configuración que sólo pueden ser escritas en el momento de la
grabación (burn) del dispositivo. A veces a los bits de configuración 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 configuración pueden re-escribirse tantas veces como el programa, ya que
pueden considerarse parte de dicha memoria de programa, y estarían localizados en
posiciones más allá de la memoria de usuario donde se almacena el programa (dirección
2007h para el PIC16F690) y que sólo puede ser escrita en el momento de la programación
del chip. Como hemos dicho para dispositivos con memoria FLASH los bits de configuración
se pueden escribir tantas veces como el programa, por lo que el nombre de fusibles es poco
afortunado.

Los bits de configuración 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 protección del código o los datos
escritos en el chip.

En otras posiciones de memoria de esta área tenemos también la identificación del tipo de
dispositivo que normalmente es reconocido por las herramientas de escritura y gracias a ello
se puede evitar escribir el código escrito para un tipo en un chip distinto.

Desde un punto de vista práctico las herramientas de escritura normalmente proporcionan
un menú de opciones de configuración en función del chip que usemos; tanto en MPLAB
(ver la figura) como con mikroC podemos seleccionar la configuración cuando definimos un
fichero de proyecto que incluye el código 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 después 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
ejecución de forma inmediata. Este programa ilustra algunas ventajas de la programación en C, frente a la
programación en ensamblador. Desglosaremos en detalle este primer programa:
•Todo lo está entre /* y */ es un comentario que puede extenderse en varias líneas, igual que lo que sigue a //.
•En todo programa C hay una función ‘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
declaración de las variables y su tipo. A casi todos los efectos cuando creamos un bloque es sintácticamente
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 línea 7 declaramos la variable ‘n’ como un entero corto.
•En la línea 8 tenemos la primera sentencia que genera código, hace todos los puertos de salida. Vemos que en
una línea puede haber varias sentencias; análogamente podríamos tener una sola sentencia en varias líneas, ya
que una sentencia no termina hasta que aparece el ‘;’.
•En la línea 9 inicializamos los puertos. Note que no tenemos que hacer ningún cambio de banco, ya que el
compilador se encarga de esa complicación por nosotros.
•La línea 10 es necesaria para poder usar las líneas conectadas al A/D como digitales (ver datasheet).
•Bucle “while (condición) ,sentencias;-”, mientras la condición 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 lógicos: &&, || y ! que significan AND, OR y NOT respectivamente. En C
cualquier expresión o asignación es falsa si su resultado es 0, y verdadera en caso contrario. Un error común
entre principiantes es escribir “while (n=3) ….”, esa condición 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 común 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 después. Así por ejemplo si a=3 y ejecutamos “b=a++;”, b valdrá 3 y a 4; pero si ejecutamos “b=++a;”, b y a
valdrán 4. Análogamente existe el operador decremento ‘--’.
•El bucle “for (sentencia1; condición; sentencia3) , bloque -”, es equivalente a: “sentencia1; while (condición) {
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 función de librería que nos proporciona un retardo de ‘x’ milisegundos.
•La construcción “if (condición) {bloque-verdadero} else {bloque-falso-” se ejecuta sólo uno de los bloques en
función de la condición. 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.   El tipo de las variables determina su tamaño 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 sólo
     ‘short’ tiene 8 bits y rango de -128 a +127; ‘long int’ o sólo ‘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 número en coma flotante de 32 bits y rango +-6.8 E+38 y para números
     pequeños +-1.17 E-38. ‘double’ en mikroC es idéntico a float, en otras implementaciones tiene 64 bits.
     ‘void’ sólo se usa para funciones y significa que la función no devuelve ningún valor o no tiene parámetros
     de entrada. ‘const’ es un modificador que indica al compilador que la variable no va a cambiar durante la
     ejecución. ‘volatile’ indica que la variable puede ser cambiada por procesos ajenos al programa (HW).
2.   La directiva del pre-compilador ‘#define xx yy’ crea el parámetro xx con el valor yy. Un parámetro queda
     fijado en el momento de la compilación y no cambia nunca.
3.   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 también un puntero
     constante a ese bloque de memoria; un puntero es una variable que contiene la *dirección* 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 también *p, de estas
     dos maneras estamos accediendo a la misma posición en memoria, ya que ‘*’ como operador unario
     significa ‘contenido de’; análogamente x*1+ == *(p+1), y x*2+ == *(p+2). Por otra parte ‘&’ como operador
     unario sobre una variable significa ‘dirección de’ la variable, luego también es cierto que: &x*0+ == p. Un
     ‘string’ o cadena es un array de caracteres; toda cadena termina con el carácter ‘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
     posición que ocupa la ‘h’. Una matriz es una tabla de datos: int tabla[2][5]; tiene 2 filas y 5 columnas.
4.   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
     genérico por ejemplo: typedef struct {char Nombre[8], Apellidos[20]; int edad; } PERSONA; y después
     podemos usar ‘PERSONA’ como un tipo definido por el usuario y declarar: PERSONA Pepe, *p; donde
     estaríamos declarando la estructura Pepe igual que antes, y también 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; también si hacemos: p=&Pepe; podremos acceder con : p->Nombre o p->edad.
5.   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 línea
     7 del puerto B. También siempre podremos referenciar cualquier bit con nombre: INTCON.GIE=1;




                                                                                                                          43
1.   Los operadores aritméticos (+ - * / %) operan sobre dos datos de tipo simple. El operador % nos da el
     resto de la división entera. En general el resultado es del tipo de mayor tamaño. Por ejemplo si tenemos:
     char c; int i,x; y hacemos: x=i+c; ‘c’ se convierte a entero antes de la operación y el resultado es un entero
     de 16 bits. A veces podemos especificar explícitamente la conversión de tipo usando la técnica de ‘type
     cast’ sobre una expresión de la forma ‘(tipo)(expresión)’, por ejemplo: x= i + (int)(c);
2.   Análogamente los operadores orientados a bits o lógicos también 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.
3.   C nos permite simplificar expresiones del tipo: x = x (operador) y; para cualquier operador aritmético o
     lógico, por su sintaxis equivalente: x (operador)= y; así tenemos que x *= 3; es equivalente a: x = x*3;
4.   Los operadores incremento ‘++’ o decremento ‘--’ se aplican sobre variables de carácter entero o punteros,
     y según vayan como sufijo o prefijo realizan la operación después 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
     tamaño del objeto al que apunta. Por ejemplo si declaramos: int *p; y p apunta a la posición de memoria
     0x70, ++p apuntará a la posición 0x72 ya que el tamaño de un entero es de 2 bytes.
5.   Los operadores condicionales permiten comparar dos expresiones aritmético-lógicas mientras que los
     operadores AND, OR y NOT (&& , || y !) operan con expresiones condicionales. La construcción: (condición)
     ? Expresión_1 : Expresión_2; devuelve la expresión 1 si la condición es verdadera y la 2 si es falsa. Así por
     ejemplo podemos hacer: max = (a > b) ? a : b;. En C toda expresión (incluyendo las aritméticas) puede
     considerarse una condición, en ese caso la condición es verdadera si el resultado de la expresión es distinto
     de 0 y falsa en caso contrario.
6.   Las construcciones: if (condición) { bloque1 } else { bloque2}; while (condición) { bloque3 }; y for
     (sentencia1; condición; sentencia3) { bloque4 }; ya fueron descritas anteriormente. La construcción: do {
     bloque } while (condición); siempre se ejecuta al menos una vez ya que la condición sólo se comprueba al
     final de la ejecución del bloque. En un bloque repetitivo al ejecutar una sentencia ‘break;’ se produce la
     salida inmediata del bloque. La sintaxis de switch es: switch (espresión) , 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.
7.   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 función. Antes de usar cualquier función, ésta debe
     haber sido declarada o definida. En C las funciones reciben copias de las variables de entrada, por tanto si
     una función cambia el valor de estas variables cambia sólo la copia, no la variable original; sin embargo si la
     función trabaja con el puntero a una variable puede cambiar el contenido de la variable original. mikroC
     provee una gran cantidad de funciones agrupadas en librerías que nos permiten trabajar con todos los
     módulos de todos los PIC de forma sencilla, estas funciones están documentadas en la ayuda de mikroC. A
     veces la declaración de las funciones de librería requiere un fichero ‘header’ que se debe incluir en nuestro
     programa con la sentencia ‘#include <librería.h>’, como ‘math.h’, ‘string.h’, etc.




                                                                                                                       44
Este programa ‘Display7seg_int.c’ muestra el resultado de la conversión del A/D
multiplexado en los dos dígitos del display de 7 segmentos, similar al programa en
ensamblador ‘7seg_interr.asm’ que hemos visto anteriormente. Nótese 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 parámetro de inicialización en un fichero de proyecto, en
este caso ‘Display7seg_int.mcppi’. Observamos aquí el programa completo y vemos que al
usar la función de librería ‘ADC_Read(int chan)’, la de ‘Delay_ms()’ y las capacidades del
lenguaje C, ocupa menos de 30 líneas.
En las líneas 11 y 12 tenemos la tabla de conversión de los dígitos de 0 a 15 para su
representación 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 declaración, son
pues variables globales; frente a las variables que sólo existen en un bloque llamadas
locales.
La interrupción es una función que no devuelve ningún valor, ni lógicamente acepta
parámetros. Se activa con cada fin de cuenta del timer 0; entre las líneas 18 a 20 escribe el
código de los 7 segmentos para cada dígito y lo mantiene hasta la siguiente interrupción.
En el programa principal se lee el conversor A/D mediante la función de la librería ‘unsigned
ADC_Read(unsigned short canal)’ y usamos los 8 bits bajos para generar las cifras
hexadecimales de los dígitos y usamos los dos bits superiores para activar los puntos
decimales de los dos dígitos. De esta forma podemos representar los 10 bits del resultado de
la conversión.
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: ¿ Cómo se podría aumentar el brillo de los segmentos de los displays ?
Existe la posibilidad de que la interrupción del TMR0 llegue entre las líneas 37 y 38, ¿ qué
efecto tendría ? ¿ se podría evitar ? (Ver diapositiva siguiente)




                                                                                                45
Para aumentar el brillo en el programa anterior podemos cambiar la sentencia de la línea 22 ‘if
(++digito > 8u) …’ por ‘if (++digito > 1u) …’; por cierto el sufijo ‘u’ significa unsigned y se pone por
compatibilidad ya que digito es también unsigned, aunque en este caso no sea necesario; pero en
caso de que el número sea mayor de 32767 (por ejemplo 35000) el compilador lo interpreta como un
número negativo (!) ya que su primer bit es 1.
¿ Podríamos hacer este cambio sin necesidad de recompilar el programa ? Sí, y es fácil gracias a las
capacidades de mikroC, en particular podremos ver el código 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 línea 22 del fichero C, en el código generado la posición 0x001F contiene la
instrucción ‘SUBLW 8’ que es la que se realiza para hacer la comparación del alto nivel. Vemos
también que el opcode de la instrucción es ‘0x3C08’, si en PICkit2 cambiamos el 8 por un 1 y re-
escribimos 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
multiplexación de los dígitos.

Otra aplicación muy útil del fichero de listado y del código 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
parámetros de las funciones y las etiquetas para poder usarlas en bloques ensamblador que nosotros
podemos insertar en el programa C. Para insertar código 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 interrupción cae en medio de la
actualización de los dígitos, tendremos la cifra baja de una conversión y la alta de la otra. Si
necesitamos evitar esto tendríamos que deshabilitar las interrupciones durante la escritura de los
‘codigo7’ y volver a habilitarlas una vez terminada. Para la (des-)habilitación hacemos INTCON.GIE
igual a 0 ó 1 respectivamente.




                                                                                                           46
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits
Introducción a los microcontroladores PIC de 8 bits

Contenu connexe

Tendances

Codificadores y decodificadores
Codificadores y decodificadoresCodificadores y decodificadores
Codificadores y decodificadoresRopoga
 
TIMERS&TEMPORIZADORES EN "C"
TIMERS&TEMPORIZADORES EN "C"TIMERS&TEMPORIZADORES EN "C"
TIMERS&TEMPORIZADORES EN "C"Diego Rojas Ruiz
 
El transistor bjt
El transistor bjtEl transistor bjt
El transistor bjtFenix Alome
 
Presentacion-Lineas-Transmision-Terminadas.pdf
Presentacion-Lineas-Transmision-Terminadas.pdfPresentacion-Lineas-Transmision-Terminadas.pdf
Presentacion-Lineas-Transmision-Terminadas.pdfMELVINGUTIERREZ16
 
Rectificador de media onda
Rectificador de media ondaRectificador de media onda
Rectificador de media ondaTensor
 
Práctica 5 integrado lm555
Práctica 5   integrado lm555Práctica 5   integrado lm555
Práctica 5 integrado lm555CUN
 
Lab de tele 1 - Fourier using matlab - UNTECS
Lab de tele 1 - Fourier using matlab - UNTECSLab de tele 1 - Fourier using matlab - UNTECS
Lab de tele 1 - Fourier using matlab - UNTECSIng. Electrónica xD
 
Electronica de-potencia-ii1
Electronica de-potencia-ii1Electronica de-potencia-ii1
Electronica de-potencia-ii1erick maldonado
 

Tendances (20)

Campos Electromagneticos - Tema 7
Campos Electromagneticos - Tema 7Campos Electromagneticos - Tema 7
Campos Electromagneticos - Tema 7
 
Optoacopladores
OptoacopladoresOptoacopladores
Optoacopladores
 
Algebra de boole_1
Algebra de boole_1Algebra de boole_1
Algebra de boole_1
 
Codificadores y decodificadores
Codificadores y decodificadoresCodificadores y decodificadores
Codificadores y decodificadores
 
Electrónica digital: Comparadores
Electrónica digital: ComparadoresElectrónica digital: Comparadores
Electrónica digital: Comparadores
 
Modelo híbrido del bjt
Modelo híbrido del bjtModelo híbrido del bjt
Modelo híbrido del bjt
 
TIMERS&TEMPORIZADORES EN "C"
TIMERS&TEMPORIZADORES EN "C"TIMERS&TEMPORIZADORES EN "C"
TIMERS&TEMPORIZADORES EN "C"
 
Amplificadores Multietapa
Amplificadores MultietapaAmplificadores Multietapa
Amplificadores Multietapa
 
Practica de laboratorio
Practica de laboratorioPractica de laboratorio
Practica de laboratorio
 
Teoría Básica de Transistores JFET
Teoría Básica de Transistores JFETTeoría Básica de Transistores JFET
Teoría Básica de Transistores JFET
 
Familias lógicas digitales
Familias lógicas digitalesFamilias lógicas digitales
Familias lógicas digitales
 
2.4. Compuertas AND - OR con Diodos
2.4. Compuertas AND - OR con Diodos2.4. Compuertas AND - OR con Diodos
2.4. Compuertas AND - OR con Diodos
 
El transistor bjt
El transistor bjtEl transistor bjt
El transistor bjt
 
1.7. Hoja de Especificaciones de un Diodo
1.7. Hoja de Especificaciones de un Diodo1.7. Hoja de Especificaciones de un Diodo
1.7. Hoja de Especificaciones de un Diodo
 
Presentacion-Lineas-Transmision-Terminadas.pdf
Presentacion-Lineas-Transmision-Terminadas.pdfPresentacion-Lineas-Transmision-Terminadas.pdf
Presentacion-Lineas-Transmision-Terminadas.pdf
 
Registro status PIC16F84A
Registro status PIC16F84ARegistro status PIC16F84A
Registro status PIC16F84A
 
Rectificador de media onda
Rectificador de media ondaRectificador de media onda
Rectificador de media onda
 
Práctica 5 integrado lm555
Práctica 5   integrado lm555Práctica 5   integrado lm555
Práctica 5 integrado lm555
 
Lab de tele 1 - Fourier using matlab - UNTECS
Lab de tele 1 - Fourier using matlab - UNTECSLab de tele 1 - Fourier using matlab - UNTECS
Lab de tele 1 - Fourier using matlab - UNTECS
 
Electronica de-potencia-ii1
Electronica de-potencia-ii1Electronica de-potencia-ii1
Electronica de-potencia-ii1
 

Similaire à Introducción a los microcontroladores PIC de 8 bits

Similaire à Introducción a los microcontroladores PIC de 8 bits (20)

Libro basico pic
Libro basico picLibro basico pic
Libro basico pic
 
Pic16 f877
Pic16 f877Pic16 f877
Pic16 f877
 
Introducción
IntroducciónIntroducción
Introducción
 
Introducción
IntroducciónIntroducción
Introducción
 
Electronica aplicada primera presentacion
Electronica aplicada primera presentacionElectronica aplicada primera presentacion
Electronica aplicada primera presentacion
 
Electronica aplicada primera presentacion
Electronica aplicada primera presentacionElectronica aplicada primera presentacion
Electronica aplicada primera presentacion
 
Microcomputadoras
MicrocomputadorasMicrocomputadoras
Microcomputadoras
 
Electronica Aplicada Microcomputadores- UNACH
Electronica Aplicada Microcomputadores- UNACHElectronica Aplicada Microcomputadores- UNACH
Electronica Aplicada Microcomputadores- UNACH
 
135127015 microcontroladores-1-1-ppt
135127015 microcontroladores-1-1-ppt135127015 microcontroladores-1-1-ppt
135127015 microcontroladores-1-1-ppt
 
MICROCONTROLADORES.ppt
MICROCONTROLADORES.pptMICROCONTROLADORES.ppt
MICROCONTROLADORES.ppt
 
Micro1.ppt
Micro1.pptMicro1.ppt
Micro1.ppt
 
El microcontrolador PIC16F877
El microcontrolador PIC16F877El microcontrolador PIC16F877
El microcontrolador PIC16F877
 
Programando pi cs_ccs_08
Programando pi cs_ccs_08Programando pi cs_ccs_08
Programando pi cs_ccs_08
 
Micro1
Micro1Micro1
Micro1
 
M I C R O S C A P I T U L O 1
M I C R O S  C A P I T U L O 1M I C R O S  C A P I T U L O 1
M I C R O S C A P I T U L O 1
 
Micros Capitulo 1
Micros Capitulo 1Micros Capitulo 1
Micros Capitulo 1
 
Micros Capitulo 1
Micros Capitulo 1Micros Capitulo 1
Micros Capitulo 1
 
Micros Capitulo 1
Micros Capitulo 1Micros Capitulo 1
Micros Capitulo 1
 
Micros Capitulo 1
Micros Capitulo 1Micros Capitulo 1
Micros Capitulo 1
 
Pic
PicPic
Pic
 

Plus de Sohar Carr

Análisis Mr Holland's Opus
Análisis Mr Holland's OpusAnálisis Mr Holland's Opus
Análisis Mr Holland's OpusSohar Carr
 
Sentencias condicionales y ciclos
Sentencias condicionales y ciclosSentencias condicionales y ciclos
Sentencias condicionales y ciclosSohar Carr
 
Vectores y matrices
Vectores y matricesVectores y matrices
Vectores y matricesSohar Carr
 
Dualidad de ondas
Dualidad de ondasDualidad de ondas
Dualidad de ondasSohar Carr
 
Repaso teoría electromagnética
Repaso teoría electromagnéticaRepaso teoría electromagnética
Repaso teoría electromagnéticaSohar Carr
 
Capitulo 12, 7ma edición
Capitulo 12, 7ma ediciónCapitulo 12, 7ma edición
Capitulo 12, 7ma ediciónSohar Carr
 
Capitulo 10, 7ma edición
Capitulo 10, 7ma ediciónCapitulo 10, 7ma edición
Capitulo 10, 7ma ediciónSohar Carr
 
Capitulo 9, 7ma edición
Capitulo 9, 7ma ediciónCapitulo 9, 7ma edición
Capitulo 9, 7ma ediciónSohar Carr
 
Capitulo 6, 7ma edición
Capitulo 6, 7ma ediciónCapitulo 6, 7ma edición
Capitulo 6, 7ma ediciónSohar Carr
 
Capitulo 5, 7ma edición
Capitulo 5, 7ma ediciónCapitulo 5, 7ma edición
Capitulo 5, 7ma ediciónSohar Carr
 
Capitulo 4, 7ma edición
Capitulo 4, 7ma ediciónCapitulo 4, 7ma edición
Capitulo 4, 7ma ediciónSohar Carr
 
Capitulo 3, 7ma edición
Capitulo 3, 7ma ediciónCapitulo 3, 7ma edición
Capitulo 3, 7ma ediciónSohar Carr
 
Capitulo 2, 7ma edición
Capitulo 2, 7ma ediciónCapitulo 2, 7ma edición
Capitulo 2, 7ma ediciónSohar Carr
 
Capitulo 1, 7ma edición
Capitulo 1, 7ma ediciónCapitulo 1, 7ma edición
Capitulo 1, 7ma ediciónSohar Carr
 
Capitulo 13, 7ma edición
Capitulo 13, 7ma ediciónCapitulo 13, 7ma edición
Capitulo 13, 7ma ediciónSohar Carr
 
Capitulo 12 la onda plana uniforme
Capitulo 12   la onda plana uniformeCapitulo 12   la onda plana uniforme
Capitulo 12 la onda plana uniformeSohar Carr
 
Capitulo 08 el campo magnético estable
Capitulo 08   el campo magnético estableCapitulo 08   el campo magnético estable
Capitulo 08 el campo magnético estableSohar Carr
 
Capitulo 07 ecuaciones de poisson y de laplace
Capitulo 07   ecuaciones de poisson y de laplaceCapitulo 07   ecuaciones de poisson y de laplace
Capitulo 07 ecuaciones de poisson y de laplaceSohar Carr
 
Capitulo 06 dieléctricos y capacitancia
Capitulo 06   dieléctricos y capacitanciaCapitulo 06   dieléctricos y capacitancia
Capitulo 06 dieléctricos y capacitanciaSohar Carr
 
Capitulo 05 corriente y conductores
Capitulo 05   corriente y conductoresCapitulo 05   corriente y conductores
Capitulo 05 corriente y conductoresSohar Carr
 

Plus de Sohar Carr (20)

Análisis Mr Holland's Opus
Análisis Mr Holland's OpusAnálisis Mr Holland's Opus
Análisis Mr Holland's Opus
 
Sentencias condicionales y ciclos
Sentencias condicionales y ciclosSentencias condicionales y ciclos
Sentencias condicionales y ciclos
 
Vectores y matrices
Vectores y matricesVectores y matrices
Vectores y matrices
 
Dualidad de ondas
Dualidad de ondasDualidad de ondas
Dualidad de ondas
 
Repaso teoría electromagnética
Repaso teoría electromagnéticaRepaso teoría electromagnética
Repaso teoría electromagnética
 
Capitulo 12, 7ma edición
Capitulo 12, 7ma ediciónCapitulo 12, 7ma edición
Capitulo 12, 7ma edición
 
Capitulo 10, 7ma edición
Capitulo 10, 7ma ediciónCapitulo 10, 7ma edición
Capitulo 10, 7ma edición
 
Capitulo 9, 7ma edición
Capitulo 9, 7ma ediciónCapitulo 9, 7ma edición
Capitulo 9, 7ma edición
 
Capitulo 6, 7ma edición
Capitulo 6, 7ma ediciónCapitulo 6, 7ma edición
Capitulo 6, 7ma edición
 
Capitulo 5, 7ma edición
Capitulo 5, 7ma ediciónCapitulo 5, 7ma edición
Capitulo 5, 7ma edición
 
Capitulo 4, 7ma edición
Capitulo 4, 7ma ediciónCapitulo 4, 7ma edición
Capitulo 4, 7ma edición
 
Capitulo 3, 7ma edición
Capitulo 3, 7ma ediciónCapitulo 3, 7ma edición
Capitulo 3, 7ma edición
 
Capitulo 2, 7ma edición
Capitulo 2, 7ma ediciónCapitulo 2, 7ma edición
Capitulo 2, 7ma edición
 
Capitulo 1, 7ma edición
Capitulo 1, 7ma ediciónCapitulo 1, 7ma edición
Capitulo 1, 7ma edición
 
Capitulo 13, 7ma edición
Capitulo 13, 7ma ediciónCapitulo 13, 7ma edición
Capitulo 13, 7ma edición
 
Capitulo 12 la onda plana uniforme
Capitulo 12   la onda plana uniformeCapitulo 12   la onda plana uniforme
Capitulo 12 la onda plana uniforme
 
Capitulo 08 el campo magnético estable
Capitulo 08   el campo magnético estableCapitulo 08   el campo magnético estable
Capitulo 08 el campo magnético estable
 
Capitulo 07 ecuaciones de poisson y de laplace
Capitulo 07   ecuaciones de poisson y de laplaceCapitulo 07   ecuaciones de poisson y de laplace
Capitulo 07 ecuaciones de poisson y de laplace
 
Capitulo 06 dieléctricos y capacitancia
Capitulo 06   dieléctricos y capacitanciaCapitulo 06   dieléctricos y capacitancia
Capitulo 06 dieléctricos y capacitancia
 
Capitulo 05 corriente y conductores
Capitulo 05   corriente y conductoresCapitulo 05   corriente y conductores
Capitulo 05 corriente y conductores
 

Dernier

actividades comprensión lectora para 3° grado
actividades comprensión lectora para 3° gradoactividades comprensión lectora para 3° grado
actividades comprensión lectora para 3° gradoJosDanielEstradaHern
 
plande accion dl aula de innovación pedagogica 2024.pdf
plande accion dl aula de innovación pedagogica 2024.pdfplande accion dl aula de innovación pedagogica 2024.pdf
plande accion dl aula de innovación pedagogica 2024.pdfenelcielosiempre
 
Estrategia de prompts, primeras ideas para su construcción
Estrategia de prompts, primeras ideas para su construcciónEstrategia de prompts, primeras ideas para su construcción
Estrategia de prompts, primeras ideas para su construcciónLourdes Feria
 
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptx
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptxSEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptx
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptxYadi Campos
 
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...JAVIER SOLIS NOYOLA
 
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptx
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptxTIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptx
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptxlclcarmen
 
MAYO 1 PROYECTO día de la madre el amor más grande
MAYO 1 PROYECTO día de la madre el amor más grandeMAYO 1 PROYECTO día de la madre el amor más grande
MAYO 1 PROYECTO día de la madre el amor más grandeMarjorie Burga
 
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdf
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdfSELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdf
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdfAngélica Soledad Vega Ramírez
 
La triple Naturaleza del Hombre estudio.
La triple Naturaleza del Hombre estudio.La triple Naturaleza del Hombre estudio.
La triple Naturaleza del Hombre estudio.amayarogel
 
Caja de herramientas de inteligencia artificial para la academia y la investi...
Caja de herramientas de inteligencia artificial para la academia y la investi...Caja de herramientas de inteligencia artificial para la academia y la investi...
Caja de herramientas de inteligencia artificial para la academia y la investi...Lourdes Feria
 
INSTRUCCION PREPARATORIA DE TIRO .pptx
INSTRUCCION PREPARATORIA DE TIRO   .pptxINSTRUCCION PREPARATORIA DE TIRO   .pptx
INSTRUCCION PREPARATORIA DE TIRO .pptxdeimerhdz21
 
La empresa sostenible: Principales Características, Barreras para su Avance y...
La empresa sostenible: Principales Características, Barreras para su Avance y...La empresa sostenible: Principales Características, Barreras para su Avance y...
La empresa sostenible: Principales Características, Barreras para su Avance y...JonathanCovena1
 
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...JAVIER SOLIS NOYOLA
 
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.Alejandrino Halire Ccahuana
 
Imperialismo informal en Europa y el imperio
Imperialismo informal en Europa y el imperioImperialismo informal en Europa y el imperio
Imperialismo informal en Europa y el imperiomiralbaipiales2016
 
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA II
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA IIAFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA II
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA IIIsauraImbrondone
 

Dernier (20)

actividades comprensión lectora para 3° grado
actividades comprensión lectora para 3° gradoactividades comprensión lectora para 3° grado
actividades comprensión lectora para 3° grado
 
plande accion dl aula de innovación pedagogica 2024.pdf
plande accion dl aula de innovación pedagogica 2024.pdfplande accion dl aula de innovación pedagogica 2024.pdf
plande accion dl aula de innovación pedagogica 2024.pdf
 
Estrategia de prompts, primeras ideas para su construcción
Estrategia de prompts, primeras ideas para su construcciónEstrategia de prompts, primeras ideas para su construcción
Estrategia de prompts, primeras ideas para su construcción
 
Presentacion Metodología de Enseñanza Multigrado
Presentacion Metodología de Enseñanza MultigradoPresentacion Metodología de Enseñanza Multigrado
Presentacion Metodología de Enseñanza Multigrado
 
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptx
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptxSEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptx
SEXTO SEGUNDO PERIODO EMPRENDIMIENTO.pptx
 
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...
ACERTIJO DE LA BANDERA OLÍMPICA CON ECUACIONES DE LA CIRCUNFERENCIA. Por JAVI...
 
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptx
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptxTIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptx
TIPOLOGÍA TEXTUAL- EXPOSICIÓN Y ARGUMENTACIÓN.pptx
 
MAYO 1 PROYECTO día de la madre el amor más grande
MAYO 1 PROYECTO día de la madre el amor más grandeMAYO 1 PROYECTO día de la madre el amor más grande
MAYO 1 PROYECTO día de la madre el amor más grande
 
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdf
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdfSELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdf
SELECCIÓN DE LA MUESTRA Y MUESTREO EN INVESTIGACIÓN CUALITATIVA.pdf
 
La triple Naturaleza del Hombre estudio.
La triple Naturaleza del Hombre estudio.La triple Naturaleza del Hombre estudio.
La triple Naturaleza del Hombre estudio.
 
Caja de herramientas de inteligencia artificial para la academia y la investi...
Caja de herramientas de inteligencia artificial para la academia y la investi...Caja de herramientas de inteligencia artificial para la academia y la investi...
Caja de herramientas de inteligencia artificial para la academia y la investi...
 
INSTRUCCION PREPARATORIA DE TIRO .pptx
INSTRUCCION PREPARATORIA DE TIRO   .pptxINSTRUCCION PREPARATORIA DE TIRO   .pptx
INSTRUCCION PREPARATORIA DE TIRO .pptx
 
La empresa sostenible: Principales Características, Barreras para su Avance y...
La empresa sostenible: Principales Características, Barreras para su Avance y...La empresa sostenible: Principales Características, Barreras para su Avance y...
La empresa sostenible: Principales Características, Barreras para su Avance y...
 
Sesión de clase: Fe contra todo pronóstico
Sesión de clase: Fe contra todo pronósticoSesión de clase: Fe contra todo pronóstico
Sesión de clase: Fe contra todo pronóstico
 
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...
LABERINTOS DE DISCIPLINAS DEL PENTATLÓN OLÍMPICO MODERNO. Por JAVIER SOLIS NO...
 
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.
Lecciones 05 Esc. Sabática. Fe contra todo pronóstico.
 
Medición del Movimiento Online 2024.pptx
Medición del Movimiento Online 2024.pptxMedición del Movimiento Online 2024.pptx
Medición del Movimiento Online 2024.pptx
 
Imperialismo informal en Europa y el imperio
Imperialismo informal en Europa y el imperioImperialismo informal en Europa y el imperio
Imperialismo informal en Europa y el imperio
 
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA II
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA IIAFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA II
AFICHE EL MANIERISMO HISTORIA DE LA ARQUITECTURA II
 
Tema 8.- PROTECCION DE LOS SISTEMAS DE INFORMACIÓN.pdf
Tema 8.- PROTECCION DE LOS SISTEMAS DE INFORMACIÓN.pdfTema 8.- PROTECCION DE LOS SISTEMAS DE INFORMACIÓN.pdf
Tema 8.- PROTECCION DE LOS SISTEMAS DE INFORMACIÓN.pdf
 

Introducción a los microcontroladores PIC de 8 bits

  • 1. Voy a contar en muy poco tiempo el proceso de auto-aprendizaje que yo he seguido de los microcontroladores PIC, con un enfoque eminentemente práctico que permita a cualquiera entender las características principales de los microcontroladores PIC de gama media y mejorada, y eventualmente usar un microcontrolador para cualquier diseño de complejidad pequeña o media. Asimismo esta introducción puede considerarse el primer paso para conocer en profundidad el amplísimo mundo de los controladores de una forma rápida; desde aquí se podrá profundizar con la información que provee el fabricante sobre el uso de funcionalidades más avanzadas o con cualquier otra gama de microcontroladores, según sean las necesidades del interesado. Dada la cantidad de información y el carácter práctico de esta introducción, 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 también que se trate de hacer programas similares a los que se van a presentar con pequeñas variaciones, ya que la práctica hace que se afiancen mejor los conocimientos. A mi Padre. Aún con el dolor de perderte siempre recordaré tu alegría Papá. Tu hijo 1
  • 2. 03.- ¿ Qué es un microcontrolador ? 04.- Familias de microcontroladores 05.- Gamas de los PIC de 8 bits 06.- Características de los PIC 07.- Periféricos 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.- Selección del canal A/D 24.- Configurar el A/D 25.- Programa A/D A2D.ASM 26.- Rebotes de tecla 27.- Eliminación 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.- Configuración 42.- ‘C’ Led_blink.c 43.- ‘C’ básico 1 44.- ‘C’ básico 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 2
  • 3. Un controlador es un dispositivo que se emplea para el gobierno y/o la monitorización de uno o varios procesos. Hace unas décadas, los controladores se construían exclusivamente con componentes de lógica 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 corazón (chip) de un circuito integrado. Un microcontrolador es un circuito integrado de alta escala de integración 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. · Líneas 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 módulos para el control de periféricos (puertos serie y paralelo, conversores Analógico/Digital y Digital/Analógico, etc.). 3
  • 4. Hoy en día prácticamente todos los microcontroladores se fabrican con tecnología CMOS (Complementary Metal Oxide Semiconductor) por su alta capacidad de integración, su bajo consumo y su alta inmunidad al ruido. Entre los más 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 más potente. • PIC de MicroChip que posee una amplísima gama. Fueron los primeros microcontroladores con arquitectura RISC (Reduced Instruction Set Computer). Trataremos de éstos a lo largo de esta presentación. • AVR de ATMEL, nombre genérico de otra amplia gama de microcontroladores muy potentes y rápidos: AT90Sxx, ATtinyxx, ATmegaxx. Todos ellos pueden trabajar en C con herramientas libres. La mayoría 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 PIC’s que poseen una arquitectura de buses Harvard y un conjunto reducido de instrucciones (RISC). La arquitectura Von Newman (muy común en casi todos los procesadores como los PC’s) tanto las instrucciones del programa como los datos llegan a la CPU a través 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 número de líneas (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 interacción con el mundo se hace a través del bus de datos. Programar los PIC en ensamblador es algo más fácil que para otros tipos porque sólo tienen 35 instrucciones (RISC) frente a por ejemplo las 130 de los AVR; en cualquiera de los dos casos suele ser más fácil y rápido programar en alto nivel (C o BASIC). 4
  • 5. Además de la división según la arquitectura de 8, 16 ó 32 bits, la familia de 8 bits de MicroChip se divide en gamas según la evolución que han tenido los microcontroladores. Esta clasificación es muy dinámica, tanto que actualmente MicroChip habla de tres rangos, en vez de los cinco (históricos) 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 sólo por dar información histórica, asimismo dado la rapidísima evolución del mercado de microcontroladores PIC, los números que se indican corresponden a un momento en el tiempo y por tanto varían al irse introduciendo nuevos productos con mayores prestaciones. Los nombres de los PIC’s que se dan corresponden a los microcontroladores que tienen memoria FLASH (de ahí la ‘F’ del nombre), aunque también existen otros identificativos para distintos tipos de memoria PROM que MicroChip llama OTP (One Time Programming) o también con memoria ROM que se crea en el proceso de la fabricación. 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 físico. En la columna de ‘Instrucciones’ tenemos el tamaño 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 sólo 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 también el sistema de desarrollo EASYPIC. Es interesante destacar que la gama PIC18Fxxxx posee un número mayor de instrucciones y un mapa de memoria diferente para permitir la programación de estos dispositivos en C; de hecho MicroChip sólo provee un compilador C para la gama alta; afortunadamente otros vendedores han desarrollado compiladores para todas las gamas, aunque con las lógicas limitaciones debidas al hardware. Nosotros usaremos el compilador de Mikroelektronika (www.mikroe.com) del que tenemos licencias, es fácil de usar, tiene buenas librerías y se puede descargar una versión limitada gratuitamente. 5
  • 6. La tecnología CMOS es muy rápida y como no necesita resistencias de polarización, prácticamente sólo consume energía 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 baterías el PIC puede detener el reloj (modo sleep) y dado que la RAM es estática 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 instrucción se hace *durante* la ejecución de la instrucción actual lo que permite una mayor velocidad de ejecución en casi todos los casos. Sólo cuando se ejecuta un salto en el programa se tiene que desechar la instrucción leída, por lo que las instrucciones de salto requieren el doble de tiempo que las otras. De cualquier manera cada instrucción necesita 4 ciclos de reloj para ejecutarse, y según hemos visto las de salto requieren 8 ciclos de reloj para su ejecución. Así pues si un PIC tiene un reloj de 8 MHz, como máximo ejecuta a una velocidad de 2 MIPS (Mega Instrucciones por Segundo) y como máximo los PIC de gama alta alcanzan 10 MIPS a 40 MHz; los procesadores de ATMEL ejecutan una instrucción por ciclo de reloj llegando a 20 MIPS. Los PIC tienen un solo registro llamado W (Working register) conocido también como acumulador, que lógicamente tiene 8 bits. El bus de direcciones de acceso a la memoria es de 7 bits, por lo que sólo se puede acceder a 128 bytes simultáneamente; para los PICs que tienen más 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 propensión a tener fallos en los programas. Esta es posiblemente la característica 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 selección 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 auténticos registros que son independientes de la memoria, siendo éste el concepto más usual de registro. El control de periféricos se hace simplemente escribiendo en una determinada celda de memoria genéricamente llamada SFR (Special Function Register). Por ejemplo, si quiero poner a nivel lógico alto todas las patillas del puerto A, tendré que escribir FFh en la dirección de memoria 5, ya que esta es la dirección asignada para leer y/o escribir en el puerto A. Por tanto para controlar los dispositivos del PIC se usa exactamente la misma instrucción que para almacenar un dato en memoria, solo que para ciertas posiciones de memoria (SFR’s) estaremos accediendo a un dispositivo físico que nos conecta con el mundo real; esto es lo que significa que los periféricos está mapeados en la memoria. /******** Continua en la diapositiva siguiente ********/ 6
  • 7. Este PIC tiene un oscilador interno a 8 MHz de tipo RC, poco estable (+-1%) con un divisor hasta 32 kHz; también puede trabajar con un cuarzo o resonador externo hasta 20 Mhz. El modo de funcionamiento se selecciona en el momento de la programación del dispositivo y no puede cambiarse durante la ejecución (aunque con el oscilador interno se puede seleccionar el factor de división). 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 programación o ‘quemado’ (burn) del dispositivo y la EEPROM el la que se puede almacenar datos durante la ejecución. La FLASH permite más de 100.000 escrituras, mientras que la EEPROM más de 1.000.000, aunque por velocidad y durabilidad nunca debe usarse como RAM. La retención de datos en ambos casos es mayor de 40 años. Los comparadores permiten generar una interrupción o cambiar el estado de un SFR (registro en memoria) según el valor de un voltaje analógico 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 módulo ECCP+ (Enhanced Capture Compare) trabaja con los temporizadores para medir tiempos con mucha precisión y también controla un generador de PWM (Pulse Width Modulation) con 10 bits de resolución 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 interrupción con el bit de start. El módulo 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 también permite hacer debugging en la PCB. Al entrar en modo sleep se detiene el oscilador, se vuelve a la ejecución después de un reset o una interrupción; cada vez que se re-inicia el PIC el módulo PWRT (Power-up Timer) espera el arranque del reloj antes de empezar la ejecución y el OST (Oscillator Start-up Timer) permite incrementar la espera hasta la estabilización completa del mismo (1024 periodos). El POR (Power-on Reset) genera una señal limpia de reset independientemente de la pendiente de subida de la alimentación; el BOR (Brown-out Reset) generará un reset si la tensión de alimentación baja de un cierto nivel pre- establecido. El módulo watchdog (WDT) es un temporizador totalmente independiente de la ejecución del programa que generará un reset pasado un cierto tiempo, que es configurable, sin que la ejecución de programa haya ‘limpiado’ este temporizador; normalmente se usa para evitar que el procesador se quede ‘colgado’ o ejecutando un bucle sin fin indeseado. 7
  • 8. 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 veíamos también la asignación 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 módulos podrán usarse simultáneamente en una determinada aplicación. Así por ejemplo la patilla RA3/MCLR/Vpp puede funcionar como línea 3 del puerto A, o como master clear (reset) o se usa para introducir el voltaje de programación del dispositivo (Vpp) que es de unos 12 voltios. En la configuración del dispositivo tenemos que decidir si esta patilla se usará como puerto general RA3 o como reset, y en cualquier caso la circuitería 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 aplicación (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 propósito general A, B y C; el bus de programa de 14 bits; la unidad de procesamiento aritmético/lógico ALU con sus registros asociados; el descodificador de instrucciones; el módulo de control; oscilador interno; la unidad generadora de tiempos; los temporizadores 0, 1 y 2; el módulo de comunicaciones serie asíncronas EUSART; el módulo de captura/comparación mejorado ECCP+; puerto de comunicaciones síncronas SSI; el módulo conversor A/D de 10 bits con 12 entradas y una referencia; módulo de comparadores; y la memoria EEPROM de 256 bytes. 8
  • 9. Vemos aquí la memoria RAM organizada en 4 bancos, de 128 bytes cada uno. Los PIC sólo pueden acceder a un banco a la vez. El motivo de estas limitaciones es que los códigos de las instrucciones de ensamblador (conocidos como opcodes) tienen sólo 7 bits para especificar la dirección 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 están 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 también que el número 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 dirección (File Address) es 3. STATUS es el registro que contiene los bits de estado de la CPU y también 2 bits llamados RP1 y RP0 que son los que nos permiten establecer el banco al que estamos accediendo. (Ver diapositiva siguiente para más 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, están 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 deberán 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 interrupción puede llegar en cualquier momento, necesitamos un área de memoria donde guardar el estado del procesador, incluyendo la información del banco en el que se esté trabajando inmediatamente antes de ejecutar la interrupción y luego volver a el mismo estado antes de retomar la ejecución normal del programa. 9
  • 10. Este es un ejemplo de cómo 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 después de un reset de la alimentación 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 operación 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 más usados. Cada puerto tiene como mínimo dos SFR’s 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 nemónico TRISx proviene de TRi-State logic ya que cada patilla de los puertos tiene un driver tri-state, o sea que además de los estados alto ‘1’ o bajo ‘0’, tiene un tercer estado conocido como de alta impedancia, lo que significa que la línea 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 línea y por tanto podremos leer el estado que ponga en ella la circuitería exterior, o sea, esa línea queda configurada como una línea de entrada. Inversamente si ponemos un 0 en un bit de la palabra TRISA, estamos activando su línea correspondiente como línea de salida. Podemos escribir en el SFR TRISx en cualquier momento de la ejecución del programa, pero lógicamente si por error se activa como salida una línea conectada a una salida de la circuitería externa se puede producir un corto-circuito de los dos drivers (el del PIC y el de la circuitería externa) que podría ocasionar averías; es importante por lo tanto que el programador defina correctamente las líneas de entrada y salida de todo el dispositivo. 10
  • 11. Lógicamente antes de trabajar con los PIC’s 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 información, 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 opción esté activada). Debemos chequear también 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 números de línea en la ventana del texto del programa, hacer click con el botón derecho sobre la ventana y seleccionar ‘Properties’, luego en la pestaña de ‘ASM File Types’ marcar ‘Line Numbers’ o cualesquiera otra opción a nuestro gusto. Después una vez que la compilación 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 botón Write. En la ventana PICkit2 Programmer podemos pulsar el botón Read y vemos que el dispositivo tiene las primeras posiciones de la memoria de programa (FLASH) escritas, todos los bits que no están escritos aparecen como 1, y dado que el PIC tiene 14 bits por palabra, en hexadecimal vemos 3FFF en todas las demás posiciones de memoria. Si pulsamos ‘On’ en la zona de VDD PICkit 2, aplicaremos la tensión 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 sólo 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
  • 12. 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 potenciómetro y el pulsador; se han añadido 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 pequeño motor de corriente continua. Disponemos también de otras tarjetas de demostración con un PIC16F887 SMD de 44 pines. Ambos tipos de tarjetas son muy convenientes para ser usadas en aplicaciones pequeñas que no requieran mucha circuitería extra, o como un módulo que se puede integrar en un sistema mayor a través de los conectores adecuados para aplicaciones más grandes. A lo largo de esta introducción veremos programas para medir y/o controlar todos y cada uno de estos dispositivos. A veces usaremos también alguna placa de desarrollo de MikroElektronika, llamadas EasyPIC que son mucho más grandes y tienen por tanto mayor número de dispositivos y posibilidades. Para la mayoría de los programas en C usaremos los EasyPIC, aunque también algunas aplicaciones para la placa de desarrollo modificada para ilustrar el uso del programador PICkit2 con programas desarrollados con mikroC. La mayoría de los programas en ensamblador que veremos a continuación 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 aplicación sin necesidad de sacar el PIC del circuito, esto es lo que se conoce como ICSP (In-Circuit Serial Programming); que ya habíamos mencionado como una de las grandes ventajas de los PIC. 12
  • 13. Este pequeño programa está escrito en lenguaje ensamblador de los PICs, cabe destacar: •Todo lo que esté detrás de un punto y coma se considera comentario. •Las líneas que empiezan con # son directivas para el preprocesador, también en general las que empiezan por ‘__’ y algunas palabras especiales como org o end que son palabras reservadas. Ninguna directiva produce código ejecutable, pero pueden modificar la forma en que el compilador lo produce. •La directiva ‘#include’ carga una serie de definiciones específicas 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 más legible y fácil de entender. Si no pusiéramos esta directiva el compilador se quejaría, ya que no sabría que significa STATUS, PORTC, RP0, etc. •La directiva __CONFIG se usa para especificar los bits de configuración 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 configuración (CONFIG ver datasheet pg. 174) con un SFR, ya que esta palabra sólo se escribe en el momento del burning del dispositivo y no puede cambiarse durante la ejecución. •La directiva ‘org x’ establece que el código que sigue debe ensamblarse a partir de la dirección ‘x’. En este caso 0 es la dirección por la que siempre empiezan los PIC’s a ejecutar después de un reset. •La palabra ‘Start’ sin indentar es una etiqueta, o sea es un identificativo para esa posición del programa a la que podremos dirigirnos con la orden ‘goto Start’. La sintaxis más usada añade 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 línea 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 través de una resistencia) el LED se encenderá. •El comando ‘goto $’ salta sobre sí mismo de forma que ya no se ejecuta nada más. El goto es un salto a la dirección especificada, y ‘$’ significa la posición actual, también 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
  • 14. Ya hemos visto las instrucciones BSF y BCF en el ejemplo anterior. Pregunta: ¿ sobre qué banco actúa la instrucción: BCF 0x71,2? R: En principio en general el banco depende de las instrucciones de selección de banco que se hayan ejecutado antes, y por tanto en esa parte de programa que se ha dado como ejemplo no aparece ninguna operación de selección de banco, luego parece que no podríamos determinarlo con la información que tenemos. Sin embargo la dirección 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 instrucción en función del valor del bit que se testea. Notar que la lógica de estas instrucciones es un poco confusa ya que la siguiente instrucción no se ejecuta cuando la condición es verdadera, sino falsa; ya que la instrucción está formulada negativamente (skip = no ejecutar), podríamos decir: ‘test bit, no ejecutar si clear’. 14
  • 15. Casi todas estas instrucciones tienen un sufijo ‘,d’ que es opcional y determina el destino del resultado de la operación; ‘d’ puede valer 1 ó 0 según el destino sea la celda de memoria o el acumulador (W) respectivamente. Realmente esto es un ‘truco’ para mantener bajo el número 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 posición de memoria. En los ‘include’ de MPLAB están definidas f=1 y w=0, para hacer el código más fácil de entender; esto nos permite por ejemplo usar: incf Contador,f ; Contador = Contador + 1; equivalente a ‘incf Contador’ incf Contador,w ; W = Contador + 1, la variable Contador no cambia El funcionamiento de cada instrucción está dado en la columna ‘Function’ y no hay mucho más que añadir; cabe quizás mencionar que la operación NOP se puede usar para introducir un pequeño retardo, pero hay que considerar que este retardo depende de la velocidad de reloj lógicamente. Como todas las instrucciones simples NOP se ejecuta en 4 ciclos de reloj. También son interesantes los comandos DECFSZ y INCFSZ que se usan para hacer bucles; por ejemplo para ejecutar 100 veces determinado grupo de instrucciones podríamos hacer: movlw .100 ; NOTA: esta es la manera por defecto de introducir números en decimal movwf Contador Loop100: < grupo de instrucciones a ejecutar ….> decfsz Contador goto Loop100 ; si el Contador es != 0 vuelve a ejecutar el Loop Los PIC son atípicos 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
  • 16. Estas instrucciones nos permiten introducir datos fijos (literal) en contraposición a los datos que podemos almacenar en la memoria, que pueden cambiar durante la ejecución 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 porción de código a la que se puede llamar desde varias posiciones en el programa, y una vez acabada la ejecución de la subrutina, la ejecución vuelve a la instrucción 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 máximo 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 más los de la rutina de interrupciones más uno, que es el nivel que usa la propia interrupción. 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 interrupción 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 dirección de la instrucción de programa que se está ejecutando. El comando CLRWDT es el que resetea el contador del watchdog (WDT), evitando la re-inicialización 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 PIC’s antiguos, no son ya necesarios y las próximas versiones no los incorporarán. 16
  • 17. 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 ejecución 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 aritméticas (ADD, SUB) afectan a C, Z y DC; las operaciones lógicas sólo a Z, las de rotación sólo al C y cabe destacar que las operaciones INCFSZ, DECFSZ, MOVxx o SWAPF no afectan a ningún bit de STATUS, excepto MOVF que afecta a Z. Observe en la columna del ‘opcode’ o código de la instrucción que la dirección de memoria (o file) tiene 7 bits, esto es lo que determina que el tamaño de los bancos de memoria sea de 128 bytes ya que esto es lo máximo que podemos direccionar con los 7 bits de la instrucción. Análogamente 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 página), para saltos a más distancia hay que hacer uso del SFR PCLATH, que nos permite acceder a todas las páginas 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
  • 18. Además de las 35 instrucciones mencionadas anteriormente, MicroChip ha introducido esta serie de instrucciones especiales (en realidad macros) para facilitar la programación. 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 definición de dicha macro y están soportadas por MPLAB; así por ejemplo podríamos definir en un programa: #define SALTA GOTO esta sentencia define la macro ‘SALTA’ como ‘GOTO’; las macros son sustituidas antes de la compilación 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 acompañan al MPLAB y que además contienen los nombres de los SFRs de cada tipo de microcontrolador como ya habíamos mencionado. Todas estas instrucciones se obtienen en realidad como una combinación 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 más que nombres más adecuados a los ‘poco afortunados’ BTFSC o BTFSS. En los PIC de gama alta la mayoría de estas instrucciones son instrucciones reales, y además 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 diseñado para permitir la programación de estos dispositivos en lenguajes de alto nivel, particularmente el C. Algunas de las instrucciones extendidas hay que activarlas en la configuración del dispositivo. 18
  • 19. Entre las líneas 31 y 34 tenemos la declaración 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 posición de memoria 0x20. En realidad en este caso lo único que estamos haciendo es asignar a los símbolos Delay1 y Delay2 los valores 0x20 y 0x21 respectivamente, pero si tenemos un bloque de memoria con más elementos es más cómodo 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 sólo afecta al primer bucle, por lo que es casi irrelevante. •Hay dos partes en el código que son idénticas y por tanto este programa se puede estructurar mejor creando una subrutina. Se propone como ejercicio. •También se puede re-diseñar la rutina del ejercicio anterior para que acepte una variable que determine el retardo total. De esta forma se hace mucho más fácil hacer más rápido o lento la velocidad de parpadeo; se puede también cambiar la relación encendido/apagado o incluso modificarlo durante la ejecución del programa para que poco a poco vaya acelerando y vuelta a empezar. 19
  • 20. 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 rotación, tal y como puede verse en las líneas 60 a 64. ¿ Se podría usar PORTC como variable en vez de Display ? La respuesta es sí, y en la mayoría de los casos funcionaría bien; pero podría darse un efecto colateral indeseado, ya que si por ejemplo la línea 3 del puerto está cortocircuitada la lectura encubierta que se hace durante la rotación (rrf PORTC,f) nos devolvería 0 al intentar activar la línea 3 y por tanto la rotación se pararía en dicho punto. Ejercicio: Hay un error intencionado en el programa, relacionado con un comentario. ¿ Cual es ? Respuesta en la siguiente diapositiva. 20
  • 21. Vemos aquí el diagrama hardware de dos líneas más o menos típicas de los puertos que podemos encontrar en los PIC. La complejidad que podemos observar se debe a las múltiples 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 flip- flops para el control de la línea. El flip-flop de PORTC es el que proporciona el dato de salida, mientras el flip-flop TRISC controla la habilitación del driver de salida. Vemos también que la lectura digital de este pin sólo puede hacerse si no está habilitada su línea de selección de modo analógico, ya que si el modo analógico está seleccionado para esta línea siempre leeremos 0 lógico. También observamos que para esta línea se puede seleccionar la función SDO, en cuyo caso el nivel lógico 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 además 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; análogamente existen WPUB e IOCB con las mismas funcionalidades para el puerto B. También vemos que tiene funciones relativas al TMR0 y tiene la capacidad de generar interrupciones desde dispositivos externos (INT). Una característica que afecta a todas las líneas de los puertos de los PIC es que *siempre* usan una secuencia de lectura-modificación-escritura (Read-Modify-Write o RMW), lo que significa que cualquier instrucción que especifique un registro ejecuta un ciclo RMW, esto es, el registro es leído, los datos modificados, y el resultado es almacenado de acuerdo con la instrucción. En otras palabras: una operación de lectura se lleva a cabo en el registro incluso si la instrucción aparentemente “sólo” escribe en dicho registro. Esto puede tener resultados indeseados en la interacción 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 centésima de segundo, ya que 13h es 19d y por tanto el tiempo es un 50% mayor. 21
  • 22. 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 tensión de referencia. Esta tensión de referencia se puede seleccionar con el bit VCFG, bien la tensión de alimentación o una tensión 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
  • 23. La selección 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 están en modo analógico después de un reset. IMPORTANTE: hay grandes diferencias en la forma de seleccionar los canales analógicos de un modelo de PIC a otro y es *imprescindible* consultar la documentación para el correcto control de las entradas. Un aspecto importante del control del convertidor A/D es la velocidad de conversión, que se establece con el SFR ADCON1; este SFR tiene sólo 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
  • 24. 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
  • 25. Este programa configura el conversor A/D para leer la entrada PORTA(0) = AN0 = RA0 comparada con la tensión de alimentación (referencia Vdd); el reloj de digitalización es de Fosc/16. Nótese el uso de NOP’s para introducir un pequeño retardo desde la activación del A/D hasta el inicio de la conversión. Este retardo se necesita para que se estabilice el voltaje a la entrada del módulo A/D. Aquí introducimos una manera más cómoda de selección de bancos de memoria: en vez de poner explícitamente 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 selección de banco al valor necesario, esto puede ser ineficiente ya que la mayoría de las veces no es necesario cambiarlos todos. Por otra parte el compilador siempre genera un warning cuando accedemos a cualquier posición de memoria fuera del banco 0, tal y como puede verse en la figura referido a la línea 63 del programa donde se accede a TRISA que está en el banco 1, y aunque tengamos la directiva BANKSEL en la línea 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 línea 63 y podría 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 potenciómetro en la placa y veremos como los 4 LEDs presentan los bits más significativos de la conversión, siendo equivalente a la presentación en binario del valor del voltaje/Vdd * 16, ya que se trata de los 4 bits más significativos. Es interesante mover el potenciómetro cerca de la transición 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
  • 26. Este programa (rebotes.asm) demuestra que si no se toman precauciones, las operaciones manuales de pulsación de teclas producen rebotes, es decir el interruptor se activa y desactiva más veces de las que deseamos. A la entrada del puerto A<3> tenemos una resistencia de pull-up y un pulsador a tierra. Notar cómo 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 también se requiere deshabilitar la entrada analógica correspondiente en el registro ANSEL. Preguntas: ¿ Son correctos los comentarios del programa ? ¿ Cuándo se actualiza la cuenta, al pulsar o al soltar la tecla ? R: Los comentarios de las líneas 31 y 36 están 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 pulsación debería contar 2 veces, uno al pulsar y otro al soltar la tecla. 26
  • 27. Esta versión del programa llamada Debounce.asm, espera a detectar la tecla estable 5 veces, con un retardo de 1 ms entre cada testeo. La eliminación de los rebotes también se puede hacer por métodos ‘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 colocación de esta circuitería externa y simplificamos nuestro diseño. Observar como se puede comprobar si una variable (Counter en este caso) alcanza un determinado valor en las líneas 73 y siguientes. 27
  • 28. Entre los programas fuente que acompañan la presentación encontramos 3 que son muy similares: Vsrotate.asm, Reversible.asm, y Function.asm. Los dos primeros no añaden nada nuevo por lo que se deja al lector para su compilación y estudio; en ambos casos se lee el A/D y se establece una rotación de los LEDs con un retardo que es función del valor leído del A/D. Así moviendo el potenciómetro los LEDs se desplazan a mayor o menor velocidad. En el programa Reversible.asm también se puede cambiar el sentido de desplazamiento del LED encendido al pulsar el interruptor de la tarjeta. Finalmente Function.asm realiza la misma función pero introduce una subrutina para manejar el tiempo de retardo, como podemos ver en la diapositiva desde la línea 116 hasta las 123. Una llamada a la rutina aparece en la línea 85; vemos que el acumulador (W) se usa para pasar un ‘parámetro’ a la rutina, en este caso es el número de bucles de segundo nivel que realizaremos; cuanto mayor sea el valor de W mayor será el retardo generado. También es posible que una rutina ‘devuelva’ algún valor; en ese caso puede usarse W o si son más de un valor pueden usarse variables creadas al efecto. 28
  • 29. Entre otras funciones el SFR OPTION_REG controla al temporizador Timer0. La señal de reloj que alimenta al Timer0 puede derivarse por división del reloj principal o puede introducirse al PIC por una línea 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 estaría 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 función del valor del bit PSA se usará para uno o el otro pero nunca simultáneamente para los dos. Finalmente los tres bits bajos PS<2:0> seleccionan el valor de división del pre-scaler, note que los valores de división 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 interrupción (ver diapositiva siguiente). 29
  • 30. Una interrupción es una señal que recibe el controlador para atender un suceso detectado por el hardware periférico. En los PIC existen numerosos periféricos que pueden generar interrupciones: como los timers, el cambio de estado en una línea de un puerto, el conversor analógico/digital, los comparadores, el módulo de comunicaciones serie, el módulo de comparación y captura, la EEPROM, etc. Asimismo un sistema externo al microcontrolador puede también generar una interrupción a través de un determinado flanco en la línea de interrupción externa (RA2/INT en el PIC16F690). Para que una interrupción afecte al microcontrolador, deberán están habilitadas las interrupciones en general (INTCON<GIE> ver diapositiva siguiente); para los periféricos del microcontrolador deberá también estar habilitadas las interrupciones de periféricos (PEIE), así como las interrupciones del periférico 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 habilitación, la interrupción no afecta a la ejecución. Si por el contrario se recibe una interrupción que está habilitada el controlador termina de ejecutar la instrucción actual (en algunos casos puede haber más retardo – latencia), pone GIE a 0 para deshabilitar cualquier otra interrupción, y ejecuta un salto a la dirección 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 instrucción siguiente a la que se terminó de ejecutar cuando se recibió la interrupción, usando para ello la instrucción RETFIE que vuelve a habilitar GIE. Dentro de la rutina de servicio de interrupciones se debe salvar el contexto de ejecución del programa principal (WREG y STATUS/banco de trabajo), y este contexto deberá recuperarse justo antes de volver de la interrupción, para que la ejecución normal pueda continuar tal y como se había quedado. Para esto es necesario reservar 2 celdas de memoria en la zona común de todos los bancos (Ejercicio: razone por qué debe ser en la zona común y no en el banco 1 por ejemplo.) que nos permitan almacenar el working register W y la palabra STATUS. Este código 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 doesn’t 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 más 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 interrupción mirando los flags de INTCON, PIR1 y PIR2; sólo un flag puede estar a 1, y este flag debe ser puesto a 0 por el software de la ISR. 30
  • 31. INTCON es el registro (SFR) de control de las interrupciones. Como hemos visto una interrupción es una señal generada por algún dispositivo interno o externo al PIC y que requiere una acción 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 interrupción 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 estén habilitadas. Además de este registro INTCON, existen los registros PIE1 y PIE2 que son los ‘Peripheral Interrupt Enable registers’ ; en ellos se puede habilitar la interrupción de los dispositivos internos (periféricos) 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; lógicamente existen dos vectores de interrupción en las posiciones 0x08 y 0x18 respectivamente, y muchas otras posibilidades de configuración. 31
  • 32. Vemos en este sencillo programa que el uso del temporizador lógicamente puede simplificar la generación de retardos o en general el control del tiempo. Insertado en el programa se ha incluido un diagrama simplificado del Timer0. En la línea 36 seleccionamos un factor de división de 256 de la frecuencia de entrada; en este caso la señal seleccionada es el ciclo de instrucción del microcontrolador, que a su vez es Tosc*4 ya que cada instrucción requiere 4 ciclos de reloj para su ejecución. En total pues el periodo de la señal 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 línea 42 el programa espera sin hacer nada ‘útil’; normalmente el procesador podría dedicarse a cualquier otra cosa mientras el temporizador lleva la cuenta del tiempo transcurrido. Nótese 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 pequeño error en el programa, ¿ cual es ? ¿ qué efecto tiene ? R: En la línea 38 no accedemos al banco correcto y por tanto en el primer bucle Display no está inicializado correctamente. 32
  • 33. 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 conversión en el registro del TMR0 por lo que acelera o decelera la rotación del encendido de los LEDs en la placa de desarrollo. Nótese en la línea 52 que la primera instrucción es ahora un salto al principio del programa normal, ya que la rutina de servicio de las interrupciones (ISR) empieza forzosamente en la posición 4. Observe entre las líneas 61 y 66 como se puede averiguar que periférico ha generado la interrupción mirando los flags en INTCON y PIR1, y como se limpia por software el flag en la línea 70. ‘T0Semaphore’ es una posición de memoria que se usa para transmitir información 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 tipográfico en las sentencias de chequeo de los flags, ¿ puede señalarlo ? Asimismo hay otro error más grave que hace que no se espere correctamente el final de la conversión del A/D, ¿ puede identificar el error ? Es posible que este error pasara desapercibido porque a efectos prácticos no se nota en la ejecución normal del programa, ¿ puede explicar porqué no se nota ? Estos dos errores están corregidos en versión de ‘interrupt.asm’ que acompaña al curso. Por otra parte el programa está preparado para que el sentido de rotación 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 línea, que es el Master Clear o reset, no queda liberada cuando usamos MPLAB. Puede comprobarse con un voltímetro que RA3 está continuamente a nivel lógico bajo y por ello no responde a la pulsación de la tecla. 33
  • 34. 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 más que una posición 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 operación 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 razón por la que sólo se necesita 1 bit de selección 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 más 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 código pone a 0 una serie de posiciones de memoria: MOVLW 0x20 ;initialize pointer MOVWF FSR ;to RAM NEXT CLRF INDF ;clear INDF register INCF FSR ;inc pointer BTFSS FSR,4 ;all done? GOTO NEXT ;no clear next Pregunta: ¿ cuantas posiciones de memoria inicializa este pequeño programa ? R:16 Observe las instrucciones de las líneas 36 a 39 como pueden reservarse varias posiciones contiguas en la memoria añadiendo ‘: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 posición 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 líneas 120 y siguientes. El resultado vuelve a tener 8 bits y se devuelve en W (y también en la variable Round). Pregunta: Vemos en las líneas 102 a 104 como se resta un valor de 8 bits de la variable de 16 bits ‘RunningSum’; los PIC realizan la substracción como una adición del número complementario. ¿ Es correcto entonces el uso del bit de acarreo (Carry) que se hace en la línea 103 y 104 ? Razone la respuesta y ponga algún ejemplo numérico. R: Sí es correcto, si hay carry significa que la operación 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
  • 35. 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); además 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 (GOTO’s o CALL’s) a otras páginas 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 ningún registro) contiene los 5 bits altos. Podemos modificar PCH usando el SFR PCLATH, que se carga en PCH de forma automática al hacer cualquier operación en PCL. También si hacemos un salto o una llamada a subrutina los dos bits más altos de PCLATH se transfieren automáticamente a la parte alta de PC; de esta forma se pueden hacer saltos entre páginas de código. En esta rutina lo que hacemos es un GOTO computado, sumándole a PCL el valor de entrada a la rutina que es W. La instrucción ‘retlw XX’ carga el valor del literal XX en W y retorna; en nuestro caso supongamos que la tabla empieza en la posición 81 (que coincide con el número de la línea) y que el valor de entrada a la rutina es W=3; entonces al ejecutar ‘addwf PCL,f’ el programa saltará a la línea 84 donde se retorna al programa principal con W cargado con el valor que se encuentra en la posición 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 código Gray de los números 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 podríamos saltar hacia atrás en vez de a la zona de la tabla; esto es lo que se muestra entre las líneas 71 y 79. Otra forma más simple de evitar esta complejidad es asegurarnos de que la tabla no atraviesa ninguna frontera de 256 bytes, usando ORG 0x100 por ejemplo. Aquí también se ilustra como leer el byte alto y bajo de una dirección de programa usando las palabras reservadas ‘high’ y ‘low’. Este es el último de los programas ejemplo que acompañan al PICkit2, a partir de ahora veremos ejemplos desarrollados por el autor, que son un poco más complejos pero también más cercanos a aplicaciones prácticas. 35
  • 36. El programa ‘1digito.asm’ ilustra como se pueden usar las tablas para representar los números 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 están conectados a través de resistencias a RC0, RC1, …, RC6 respectivamente y el punto decimal está conectado al bit 7 (RC7). El cátodo común de los displays está conectado al colector de sendos transistores cuyas bases están controladas por el puerto B<6:7>; siendo la línea 6 (RB6) el dígito menos significativo. La figura de la diapositiva siguiente representa un esquema similar (aunque no igual). Esta conexión permite la multiplexación de la representación 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 código para su representación en formato de 7 segmentos y de esta manera no necesitamos usar un descodificador del tipo 7447 o 4511 y además nos permite extender la funcionalidad de éstos que sólo pueden representar del 0 al 9. La tabla es fácil de crear, por ejemplo para el número ‘1’ tendremos que encender los segmentos b y c, por tanto tendremos que activar las líneas 1 y 2 del puerto C cuyo valor seria: 0000 0110b ó en hexadecimal 0x06 que es la entrada que vemos en la segunda línea de la tabla que corresponde al número ‘1’, ó en la figura para el ‘5’: 0110 1101 = 0x6D. En la línea 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 dígito 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 líneas 35 y 42 donde se llama a la rutina SevenSeg ? ¿ Veremos algo reconocible en el display ? R: No; veremos que se encienden unos segmentos difíciles de interpretar. Observe como en las líneas 70 a 73 comprobamos que la tabla no atraviesa ninguna frontera de página y por tanto podemos hacer el GOTO computado sin mayor complicación (Comparar con la rutina de la diapositiva anterior). Estas líneas no generan ningún código ya que son sólo directivas para el preprocesador; si al compilar obtenemos el error ‘Tabla de SevenSeg cruza una frontera de página’ tendremos que mover la rutina a otro sitio del programa, normalmente esto no supone ningún problema. 36
  • 37. El programa ‘2digitos.asm’ es similar al anterior: va contando pulsaciones de la tecla en la variable ‘Counter’, pero ahora usamos los dos dígitos del display de 7 segmentos. Dado que los dígitos están conectados en paralelo al puerto C tenemos que multiplexar, activando cada dígito durante un cierto tiempo y luego hacemos la misma operación con el otro dígito. Si estas operaciones se realizan con la suficiente rapidez, no se aprecia ningún parpadeo tan solo veremos que la luminosidad es menor que cuando se activaba un dígito permanentemente. Para conseguir esto hemos desarrollado la rutina ‘Representa’, que recibe como parámetro 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. Además se llama a la rutina ‘TiempoEncendido’ para mantener el dígito durante unos 30 us encendido. Para que esta rutina funcione correctamente tenemos que hacer llamadas a ella de forma continua, ya que si no observaríamos un molesto parpadeo o incluso los dígitos podrían desaparecer si hubiese un periodo del orden de una décima de segundo sin que se activaran. Vemos pues que mientras el programa espera la pulsación de la tecla siempre tiene la llamada a ‘Representa’ dentro del bucle de comprobación, tal y como puede verse entre las líneas 41 a 45 del programa. Introducimos en este programa también la directiva ‘DT x’ que es equivalente a ‘retlw x’, pero que nos permite poner las tablas más o menos grandes de una forma mucho más compacta tal y como puede verse entre las líneas 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 serían mayores y hay que hacer otras operaciones con PCL; además para la gama alta existen otras instrucciones que trabajan directamente con las tablas: TBLRD* y TBLWT*, que lee o escribe en la posición apuntada por TBLPTR que es un SFR triple con 21 bits útiles. 37
  • 38. El programa ‘7segment.asm’ usa los dos dígitos del display de 7 segmentos de forma completa, para representar los 10 bits del resultado de la conversión A/D. Además ilustra la forma de hacer que la conversión 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 conversión; simplemente comentar la línea 68 (SLEEP) y quitar el comentario de las líneas 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 también en el programa como se configura el PIC para habilitando las interrupciones de periféricos (PEIE), pero no habilitamos las interrupciones en general (GIE=0). Esto puede parecer inútil pero nos permite ‘despertar’ al micro cuando el A/D acaba la conversión, sin necesidad de tener una rutina de servicio de interrupciones. Este código realiza esta función: 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
  • 39. 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 líneas 32 a 34 salvan el contexto del programa principal; las líneas 35 a 40 determinan la fuente de la interrupción y saltan a la parte específica para cada periférico, en nuestro caso del TMR0 en las líneas 42 a 59, o del A/D en las líneas 68 a 83; después retoma el contexto del programa principal entre las líneas 60 a 64, para finalmente volver al programa principal re-activando las interrupciones con la orden RETFIE en la línea 65. Puede observarse en el programa que activamos el modo SLEEP para hacer la medida del voltaje analógico sin interferencias; es muy importante en este caso notar que durante la conversión también TMR0 se para, ya que está ligado al oscilador principal que también deja de oscilar. Por tanto si estuviéramos haciendo una cuenta de tiempo basado en TMR0 se acumularían retrasos, y si cometiéramos el error de activar el modo SLEEP sin arrancar la conversión, el microcontrolador se pararía indefinidamente ya que ningún periférico generaría interrupciones en ese caso. Si se necesitara mantener un reloj y usar el modo SLEEP, se podría 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
  • 40. El programa ‘PWM.asm’ ilustra como se puede usar el módulo ECCP+ (Enhanced Capture/Compare/PWM+) del PIC 16F690 para controlar el brillo de los LEDs o cualquier otra función pseudo-analógica conectada a los pines 2, 3, 4 ó 5 del puerto C. La resolución del PWM es como máximo de 10 bits, y usa Timer2 (8 bits), más dos bits del pre-scaler nos permite controlar el ancho del pulso y el periodo; la relación pulso/periodo es el ciclo de trabajo o duty-cycle. En la diapositiva se puede ver una aplicación half-bridge estándar, half- bridge controlando un circuito full-bridge, y una aplicación 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 número 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 módulo PWM se usen las librerías de mikroC ya que éstas simplifican notablemente el trabajo; más 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 función de la lectura; observamos que el brillo de los LEDs 3 y 4 de la tarjeta de desarrollo varía de forma continua al mover el potenciómetro RP1; si queremos controlar un dispositivo que requiera más potencia tendremos que usar un transistor como driver. Otra aplicación común del módulo 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 módulo PWM no podemos usar el modo SLEEP. 40
  • 41. En diapositivas anteriores hemos mencionado determinadas opciones que sólo pueden establecerse cambiando la configuración del microcontrolador. Todos los PIC tienen una o varias palabras de configuración que sólo pueden ser escritas en el momento de la grabación (burn) del dispositivo. A veces a los bits de configuración 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 configuración pueden re-escribirse tantas veces como el programa, ya que pueden considerarse parte de dicha memoria de programa, y estarían localizados en posiciones más allá de la memoria de usuario donde se almacena el programa (dirección 2007h para el PIC16F690) y que sólo puede ser escrita en el momento de la programación del chip. Como hemos dicho para dispositivos con memoria FLASH los bits de configuración se pueden escribir tantas veces como el programa, por lo que el nombre de fusibles es poco afortunado. Los bits de configuración 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 protección del código o los datos escritos en el chip. En otras posiciones de memoria de esta área tenemos también la identificación del tipo de dispositivo que normalmente es reconocido por las herramientas de escritura y gracias a ello se puede evitar escribir el código escrito para un tipo en un chip distinto. Desde un punto de vista práctico las herramientas de escritura normalmente proporcionan un menú de opciones de configuración en función del chip que usemos; tanto en MPLAB (ver la figura) como con mikroC podemos seleccionar la configuración cuando definimos un fichero de proyecto que incluye el código fuente de nuestro programa. 41
  • 42. Vemos aquí un programa en C (Led_blink.c) tal y como se ve en la ventana de mikroC PRO justo después 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 ejecución de forma inmediata. Este programa ilustra algunas ventajas de la programación en C, frente a la programación en ensamblador. Desglosaremos en detalle este primer programa: •Todo lo está entre /* y */ es un comentario que puede extenderse en varias líneas, igual que lo que sigue a //. •En todo programa C hay una función ‘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 declaración de las variables y su tipo. A casi todos los efectos cuando creamos un bloque es sintácticamente 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 línea 7 declaramos la variable ‘n’ como un entero corto. •En la línea 8 tenemos la primera sentencia que genera código, hace todos los puertos de salida. Vemos que en una línea puede haber varias sentencias; análogamente podríamos tener una sola sentencia en varias líneas, ya que una sentencia no termina hasta que aparece el ‘;’. •En la línea 9 inicializamos los puertos. Note que no tenemos que hacer ningún cambio de banco, ya que el compilador se encarga de esa complicación por nosotros. •La línea 10 es necesaria para poder usar las líneas conectadas al A/D como digitales (ver datasheet). •Bucle “while (condición) ,sentencias;-”, mientras la condición 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 lógicos: &&, || y ! que significan AND, OR y NOT respectivamente. En C cualquier expresión o asignación es falsa si su resultado es 0, y verdadera en caso contrario. Un error común entre principiantes es escribir “while (n=3) ….”, esa condición 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 común 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 después. Así por ejemplo si a=3 y ejecutamos “b=a++;”, b valdrá 3 y a 4; pero si ejecutamos “b=++a;”, b y a valdrán 4. Análogamente existe el operador decremento ‘--’. •El bucle “for (sentencia1; condición; sentencia3) , bloque -”, es equivalente a: “sentencia1; while (condición) { 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 función de librería que nos proporciona un retardo de ‘x’ milisegundos. •La construcción “if (condición) {bloque-verdadero} else {bloque-falso-” se ejecuta sólo uno de los bloques en función de la condición. 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
  • 43. 1. El tipo de las variables determina su tamaño 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 sólo ‘short’ tiene 8 bits y rango de -128 a +127; ‘long int’ o sólo ‘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 número en coma flotante de 32 bits y rango +-6.8 E+38 y para números pequeños +-1.17 E-38. ‘double’ en mikroC es idéntico a float, en otras implementaciones tiene 64 bits. ‘void’ sólo se usa para funciones y significa que la función no devuelve ningún valor o no tiene parámetros de entrada. ‘const’ es un modificador que indica al compilador que la variable no va a cambiar durante la ejecución. ‘volatile’ indica que la variable puede ser cambiada por procesos ajenos al programa (HW). 2. La directiva del pre-compilador ‘#define xx yy’ crea el parámetro xx con el valor yy. Un parámetro queda fijado en el momento de la compilación y no cambia nunca. 3. 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 también un puntero constante a ese bloque de memoria; un puntero es una variable que contiene la *dirección* 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 también *p, de estas dos maneras estamos accediendo a la misma posición en memoria, ya que ‘*’ como operador unario significa ‘contenido de’; análogamente x*1+ == *(p+1), y x*2+ == *(p+2). Por otra parte ‘&’ como operador unario sobre una variable significa ‘dirección de’ la variable, luego también es cierto que: &x*0+ == p. Un ‘string’ o cadena es un array de caracteres; toda cadena termina con el carácter ‘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 posición que ocupa la ‘h’. Una matriz es una tabla de datos: int tabla[2][5]; tiene 2 filas y 5 columnas. 4. 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 genérico por ejemplo: typedef struct {char Nombre[8], Apellidos[20]; int edad; } PERSONA; y después podemos usar ‘PERSONA’ como un tipo definido por el usuario y declarar: PERSONA Pepe, *p; donde estaríamos declarando la estructura Pepe igual que antes, y también 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; también si hacemos: p=&Pepe; podremos acceder con : p->Nombre o p->edad. 5. 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 línea 7 del puerto B. También siempre podremos referenciar cualquier bit con nombre: INTCON.GIE=1; 43
  • 44. 1. Los operadores aritméticos (+ - * / %) operan sobre dos datos de tipo simple. El operador % nos da el resto de la división entera. En general el resultado es del tipo de mayor tamaño. Por ejemplo si tenemos: char c; int i,x; y hacemos: x=i+c; ‘c’ se convierte a entero antes de la operación y el resultado es un entero de 16 bits. A veces podemos especificar explícitamente la conversión de tipo usando la técnica de ‘type cast’ sobre una expresión de la forma ‘(tipo)(expresión)’, por ejemplo: x= i + (int)(c); 2. Análogamente los operadores orientados a bits o lógicos también 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. 3. C nos permite simplificar expresiones del tipo: x = x (operador) y; para cualquier operador aritmético o lógico, por su sintaxis equivalente: x (operador)= y; así tenemos que x *= 3; es equivalente a: x = x*3; 4. Los operadores incremento ‘++’ o decremento ‘--’ se aplican sobre variables de carácter entero o punteros, y según vayan como sufijo o prefijo realizan la operación después 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 tamaño del objeto al que apunta. Por ejemplo si declaramos: int *p; y p apunta a la posición de memoria 0x70, ++p apuntará a la posición 0x72 ya que el tamaño de un entero es de 2 bytes. 5. Los operadores condicionales permiten comparar dos expresiones aritmético-lógicas mientras que los operadores AND, OR y NOT (&& , || y !) operan con expresiones condicionales. La construcción: (condición) ? Expresión_1 : Expresión_2; devuelve la expresión 1 si la condición es verdadera y la 2 si es falsa. Así por ejemplo podemos hacer: max = (a > b) ? a : b;. En C toda expresión (incluyendo las aritméticas) puede considerarse una condición, en ese caso la condición es verdadera si el resultado de la expresión es distinto de 0 y falsa en caso contrario. 6. Las construcciones: if (condición) { bloque1 } else { bloque2}; while (condición) { bloque3 }; y for (sentencia1; condición; sentencia3) { bloque4 }; ya fueron descritas anteriormente. La construcción: do { bloque } while (condición); siempre se ejecuta al menos una vez ya que la condición sólo se comprueba al final de la ejecución del bloque. En un bloque repetitivo al ejecutar una sentencia ‘break;’ se produce la salida inmediata del bloque. La sintaxis de switch es: switch (espresión) , 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. 7. 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 función. Antes de usar cualquier función, ésta debe haber sido declarada o definida. En C las funciones reciben copias de las variables de entrada, por tanto si una función cambia el valor de estas variables cambia sólo la copia, no la variable original; sin embargo si la función trabaja con el puntero a una variable puede cambiar el contenido de la variable original. mikroC provee una gran cantidad de funciones agrupadas en librerías que nos permiten trabajar con todos los módulos de todos los PIC de forma sencilla, estas funciones están documentadas en la ayuda de mikroC. A veces la declaración de las funciones de librería requiere un fichero ‘header’ que se debe incluir en nuestro programa con la sentencia ‘#include <librería.h>’, como ‘math.h’, ‘string.h’, etc. 44
  • 45. Este programa ‘Display7seg_int.c’ muestra el resultado de la conversión del A/D multiplexado en los dos dígitos del display de 7 segmentos, similar al programa en ensamblador ‘7seg_interr.asm’ que hemos visto anteriormente. Nótese 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 parámetro de inicialización en un fichero de proyecto, en este caso ‘Display7seg_int.mcppi’. Observamos aquí el programa completo y vemos que al usar la función de librería ‘ADC_Read(int chan)’, la de ‘Delay_ms()’ y las capacidades del lenguaje C, ocupa menos de 30 líneas. En las líneas 11 y 12 tenemos la tabla de conversión de los dígitos de 0 a 15 para su representación 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 declaración, son pues variables globales; frente a las variables que sólo existen en un bloque llamadas locales. La interrupción es una función que no devuelve ningún valor, ni lógicamente acepta parámetros. Se activa con cada fin de cuenta del timer 0; entre las líneas 18 a 20 escribe el código de los 7 segmentos para cada dígito y lo mantiene hasta la siguiente interrupción. En el programa principal se lee el conversor A/D mediante la función de la librería ‘unsigned ADC_Read(unsigned short canal)’ y usamos los 8 bits bajos para generar las cifras hexadecimales de los dígitos y usamos los dos bits superiores para activar los puntos decimales de los dos dígitos. De esta forma podemos representar los 10 bits del resultado de la conversión. 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: ¿ Cómo se podría aumentar el brillo de los segmentos de los displays ? Existe la posibilidad de que la interrupción del TMR0 llegue entre las líneas 37 y 38, ¿ qué efecto tendría ? ¿ se podría evitar ? (Ver diapositiva siguiente) 45
  • 46. Para aumentar el brillo en el programa anterior podemos cambiar la sentencia de la línea 22 ‘if (++digito > 8u) …’ por ‘if (++digito > 1u) …’; por cierto el sufijo ‘u’ significa unsigned y se pone por compatibilidad ya que digito es también unsigned, aunque en este caso no sea necesario; pero en caso de que el número sea mayor de 32767 (por ejemplo 35000) el compilador lo interpreta como un número negativo (!) ya que su primer bit es 1. ¿ Podríamos hacer este cambio sin necesidad de recompilar el programa ? Sí, y es fácil gracias a las capacidades de mikroC, en particular podremos ver el código 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 línea 22 del fichero C, en el código generado la posición 0x001F contiene la instrucción ‘SUBLW 8’ que es la que se realiza para hacer la comparación del alto nivel. Vemos también que el opcode de la instrucción es ‘0x3C08’, si en PICkit2 cambiamos el 8 por un 1 y re- escribimos 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 multiplexación de los dígitos. Otra aplicación muy útil del fichero de listado y del código 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 parámetros de las funciones y las etiquetas para poder usarlas en bloques ensamblador que nosotros podemos insertar en el programa C. Para insertar código 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 interrupción cae en medio de la actualización de los dígitos, tendremos la cifra baja de una conversión y la alta de la otra. Si necesitamos evitar esto tendríamos que deshabilitar las interrupciones durante la escritura de los ‘codigo7’ y volver a habilitarlas una vez terminada. Para la (des-)habilitación hacemos INTCON.GIE igual a 0 ó 1 respectivamente. 46