PIC uControl + Ensamblador desde 0
Short Description
Descripción: Tutorial sobre microcontroladores pics y lenguaje ensamblador para principiantes...
Description
Microcontroladores PIC Estructura, programación y ejemplos prácticos
1
Introducción al PIC16F84A
3
Mi Primer Programa, control de un LED
6
Creación de Demoras, LED titilando
9
Creación de Tablas, Control display 7 Segmentos
12
Direccionamiento Indirecto, Control display 7 Segmentos
16
Control de varios displays 7 Segmentos por multiplexado
18
Control de varios displays 7 Segmentos con decodificadores BCD
22
Interrupciones, Registros y guardado temporario de STATUS y W
23
Interrupción TMR0, modo Temporizador
25
Interrupción TMR0, modo Contador
28
Interrupción RB0, Control de LED
31
Interrupción RB4 a RB7, Control Teclado Matricial
33
Control del LCD, Pantalla de Cristal Líquido
36
Comunicación RS232
41
Introducción
45
Timer1 + Ejemplo
51
Timer2 + Ejemplo
54
Módulo de comunicación serie (Usart) + Ejemplo
56
Comparadores analógicos + Ejemplo
60
Introducción Módulo CCP
65
Modo Captura
69
Modo PWM
71
Registro de desplazamiento 74LS164N
76
Como hacer Cartel de LEDs 7×32
79
Herramientas:
Software para escribir nuestro código y compilar: MPLAB
Software para simular: Proteus
Una plaquita programadora.
Software para grabar nuestro PIC: WinPic800 o el Icprog
ConfigPIC: Software utilitario para crear automáticamente el código de configuración de estos PICs, además de tener otras herramientas que nos permite calcular temporizaciones, Baud Rate, PWM, etc.
Protoboard PIC16F84A PIC16F628A Cristal de 4 MHz Resistencias, capacitores, leds, Display, LCD, teclado matricial, etcétera.
Hojas de datos (datasheets):
Datasheet del PIC16F84A
Datasheet del PIC16F628A
2
Introducción al PIC16F84A En esta entrega veremos en detalle la estructura interna del microcontrolador PIC16F84A. Se trata de un modelo antiguo, pero que se utiliza con frecuencia como herramienta de aprendizaje porque es mucho más sencillo que los modelos más modernos. Resulta ideal para conocer las partes de un microcontrolador, la función que realizan y cómo podemos hacer que funcionen de la forma que nos resulte conveniente para nuestros proyectos. Introducción El PIC16F84A está fabricado en tecnología CMOS, posee memoria FLASH, y consumo bajo de potencia. Está compuesto básicamente de una memoria ROM (1024 palabras de memoria de programa), una memoria RAM (de acceso aleatorio, 68 bytes), líneas de entrada y salida (2 Puertos) y una lógica de control que coordina la interacción de los demás bloques. Estos micros pertenecen a la gama media y dispones de un set de 35 instrucciones, tipo RISC (Computador con Set de Instrucciones Reducido) pocas pero muy poderosas. Algunas funciones especiales que dispone este PIC:
Temporizador programable (Timer). Si se quiere medir periodos de tiempo entre eventos, generar temporizaciones o salidas con frecuencia específica, etc.
Perro Guardián o Watchdog. Consiste en un temporizador que, cuando se desborda ya pasa por 0, provoca un reset automático, utilizado para sistemas que no tienen control de un supervisor, y al bloquearse el micro se resetea. Si se utiliza el código debe resetearlo antes de que se desborde.
Memoria EEPROM de 64 bytes, para guardar datos que no se alteran a pesar de quitar la alimentación.
Interrupciones, cuando una señal externa, o una condición interna activa una línea de interrupción, dejando de lado la tarea que está ejecutando, atiende dicha interrupción y luego continúa con lo que estaba haciendo.
Protección ante fallo de alimentación. Se trata de un circuito que resetea al micro cuando el voltaje Vdd es inferior al mínimo.
Estado de bajo consumo (Sleep). Si el micro debe esperar mucho tiempo sin hacer nada, posee una instrucción especial, Sleep, que lo pasa al estado de reposo. Al activarse una interrupción se “despierta” y reanuda su trabajo. (Reset externo, desbordamiento de Watchdog, interrupción por RB0, interrupción por cambio de nivel en RB4 a RB7, interrupción por escritura completada en EEPROM)
Veamos el diagrama de pines, para ver cómo están distribuidos sus pines. Este microcontrolador cuenta con dos puertos configurables como estradas y salidas, y consta de 18 pines los cuales se encuentran asignados de la siguiente manera:
El puerto A tiene solo cinco pines y el pin 3, o sea, el pin RA4/TOCKI puede ser configurado a su vez como entrada/salida o como temporizador/contador. Cuando es salida se comporta como colecto abierto, por lo tanto debemos poner una resistencia Pull‐up a Vdd de 1 Kohm. Cuando se configura como entrada, funciona como disparador Schmitt Trigger por lo que puede reconocer señales con un poco de distorsión. El puerto B tiene 8 pines que pueden ser configurados como entrada/salida. RB0 puede programarse además como entrada de interrupción externa. Los pines RB4 a RB7 pueden programarse para responder a interrupciones por cambio de estado y los pines RB6 y RB7 se corresponden con líneas de entrada de reloj y entrada de datos cuando esta en modo programación.
3
MCLR/Vpp es la entrada de reset si está a nivel bajo, también es habilitador de tensión de programación. Cuando su tensión es Vdd el PIC funciona normalmente. Vss y Vdd son los pines de masa y alimentación. La tensión de alimentación está comprendida entre los 2 y 5.5 Volt. OSC1/CLKIN y OSC2/CLKOUT, pines de entrada externa de reloj y salida de oscilador a cristal respectivamente. Capacidad de corriente de los puertos: La máxima capacidad de corriente de cada uno de los pines de los puertos en modo sumidero es de 25 mA y modo fuente de 20 mA. La máxima capacidad de corriente total de los puestos es, Puerto A: Modo sumidero 80 mA; Modo fuente 50 mA. Puerto B: Modo sumidero 150 mA; Modo fuente 100 mA. El Oscilador externo: Es un circuito externo que le indica al microcontrolador la velocidad a la que debe trabajar. Puede utilizar cuatro tipos distintos:
RC, Oscilador con resistencia y condensador (Poco preciso)
XT, Cristal de cuarzo.
HS, Cristal de alta velocidad
LP, Cristal de baja frecuencia y bajo consumo de potencia.
Al momento de programar un micro se debe especificar qué tipo de oscilador se usa. Internamente la frecuencia del oscilador es dividida por 4, así que si temeos un oscilador de 4 MHz, la frecuencia de trabajo es de 1 MHz, por lo que cada instrucción se ejecuta cada 1 us. Aquí utilizaremos un cristal XT de 4 MHz que debe ir acompañado de dos condensadores:
Reset: El PIC 16F84A posee un temporizador interno conectado al pin de reset, que funciona cuando se da alimentación al microcontrolador. Esto hace que al encender el sistema el microcontrolador quede en reset por un tiempo mientras se estabilizan todas las señales del circuito. Para tener control sobre el reset se utiliza el siguiente circuito:
Estructura interna del Microcontrolador
4
Arquitectura del PIC, existen dos arquitecturas, la clásica de Von Neumann y la arquitectura Harvard, esta última es la que usan los PIC’s. Dispone de dos memorias independientes, una que contiene solo instrucciones y la otra solo contiene datos. Ambas disponen de sus respectivos buses de acceso y es posible realizar operaciones de acceso simultáneamente en ambas.
Memoria del programa: Aquí almacenamos nuestro programa o código que debe ejecutar, en el PIC16F84A es FLASH, es rápida, de bajo consumo y alta capacidad de almacenamiento. Se divide en 2048 posiciones, pero este PIC solo tiene implementadas 1024 posiciones, de 0×00 hasta 0x3FF.
Cuando ocurre un reset, el contador de programa (PC) apunta a la dirección 0×00, y el micro inicia nuevamente. Aquí se debe escribir todo lo relacionado con la iniciación del mismo, por ejemplo configuración de puertos, etc. Si ocurre una interrupción PC apunta a la dirección 0×04, y aquí debemos escribir el código necesario para atender a dicha interrupción.
5
Memoria RAM estática: Donde se encuentran los 24 registros específicos (SFR) y 68 registros de propósito general (GPR). Se halla dividida en 2 Bancos de 128 bytes cada uno. Algo que se debe tener en cuenta es la pila o Stack, que consta de 8 posiciones, cada posición contiene la dirección y datos de la instrucción que se está ejecutando, así cuando se ejecuta una llamada call o una interrupción, el PC sabe dónde regresar.
Mi Primer Programa, control de un LED En esta entrega escribiremos un programa que es, en el mundo de los PICs, el equivalente al clásico “Hello World” que se utiliza como primer programa durante el aprendizaje de cualquier lenguaje de programación. Dado que todavía no sabemos cómo utilizar una pantalla LCD para escribir un mensaje, nuestro “Hello World” será encender y apagar un LED conectado a un pin del microcontrolador. A pesar de su aparente sencillez, este ejemplo nos permitirá conocer los primeros elementos de la programación de esta plataforma. A continuación vamos a desarrollar nuestro primer programa. Consideramos de mucha utilidad repasar atentamente el curso de lenguaje assembler (MPLAB) que el usuario Leon Pic ha desarrollado en el foro uControl. Este programa activará un led conectado a RB0 siempre que el interruptor conectado a RA0 esté cerrado. Para ello vamos a necesitar el siguiente circuito:
6
En RA0 tenemos conectado un pulsador de forma que cuando lo pulsemos se introduzca un cero lógico en el pin y cuando no lo pulsemos se introduzca un uno lógico. Tenemos un Led con su correspondiente resistencia limitadora de corriente en el pin RB0. Diagrama de Flujo:
Primero que nada debemos especificar con que microcontrolador estamos trabajando, esto lo realizamos es las dos primeras líneas:
En el archivo P16F84A.inc se encuentran las definiciones de las direcciones de los registros específicos, los bits utilizados en cada registro y los fusibles del micro.
7
Configuración de fusibles: Hay ciertos aspectos del PIC que han de ser activados o desactivados mediante hardware a la hora de programarlo. Esto quiere decir que no se pueden volver a cambiar hasta que el chip no se reprograme de nuevo. El PIC16F84A dispone de 4 fuses o fusibles (los modelos superiores tienen más). Cada fuse activa o desactiva una opción de funcionamiento: 1.
OSC: Este fuse controla el modo de oscilación que usará el PIC para funcionar. Como ya sabemos, el oscilador se puede configurar de 4 maneras distintas, dependiendo de la velocidad y del tipo de circuito oscilador empleado.
2.
WDT: El famoso “perro guardián” del PIC se configura aquí. Esta es una capacidad del microcontrolador de autorresetearse.
3.
PWRT: Si activamos este fuse, lo que conseguimos es que se genere un retardo en la inicialización del microcontrolador.
4.
CP: Activando este fuse tendremos la garantía de que el código que escribamos en el PIC no pueda ser leído por otra persona, para que no nos lo copien, modifiquen, etc. (Code Protection). Esto no impide que el PIC funcione como siempre, ni que no se pueda sobrescribir su contenido
Definición de variables que utilizaremos en nuestro proyecto: En este caso solo definiremos bits, por ejemplo Led y Pulsador. Para organizar nuestro programa lo estructuraremos de la siguiente manera: Nivel Directiva Operandos ; Comentarios
Configuración de puertos: Para la configuración necesitamos los siguientes registros:
STATUS > 0×03
PORTA > 0×05
PORTB > 0×06
TRISA > 0×86
TRISB > 0×86
Por defecto los puertos quedan configurados como entradas de datos y si se quiere cambiar hay que configurarlos. Esto se realiza con los registros TRISA y TRISB, teniendo en cuenta que si se asigna un cero (0) a un pin, quedara como salida y si se asigna un uno (1), quedara como entrada. En nuestro caso se necesita colocar TRISA igual a 11111 (o se puede dejar por default) y TRISB 11111110. Ahora bien, cuando el PIC arranca se encuentra en el Banco 0, TRISA y TRISB se encuentran en el Banco 1, entonces debemos cambiar de Banco. Esto se realiza con el bit RP0 del registro STATUS. Si este se pone un cero a RP0, estaremos en el Banco 0. Si se coloca un uno, estaremos en el Banco 1. Registro de trabajo W: es el registro más importante que tiene el microcontrolador y es denominado ACUMULADOR. Este registro almacena temporalmente uno de los datos que intervienen en la operación de la Unidad lógica y Aritmética (ALU). ALU como indica su nombre, realiza las operaciones aritméticas y lógicas previstas en la colección de instrucciones del microcontrolador.
8
Ya configurado nuestro PIC, vamos a realizar la rutina que ejecutará:
Aquí solamente en un bucle infinito testeamos continuamente el estado del pulsador, y según su estado se encenderá o apagará el Led. Programa completo:
Bueno aquí ya tenemos nuestro programita terminado, solo falta compilarlo y simularlo para detectar errores, esto esta mínimamente explicado en el tutorial del Utilitario MPLAB adjuntado al principio.
Creación de Demoras, LED titilando Uno de los temas más importantes a la hora de programar una aplicación en un PIC es la correcta implementación de las rutinas de demoras. A pesar de que a primera vista puede parecer que nuestro código debería ser lo más rápido posible y carecer por completo de demoras, lo cierto es que continuamente necesitamos de estas rutinas, ya sea para esperar que un periférico esté listo o que haya trascurrido el tiempo necesario para que un dato haya sido transmitido o recibido. En esta entrega aprenderemos a programar demoras con una gran precisión. Denominamos Ciclo de maquina a la unidad básica de tiempo que utiliza el microcontrolador, y equivale a 4 ciclos de reloj. Ósea, si tenemos un oscilador de 4 MHz, el ciclo de reloj seria de 250 ns y el ciclo máquina de 1 us. Las instrucciones del microcontrolador necesitan 1 ciclo máquina, excepto algunas excepciones como son los comandos que incluyen saltos (goto, call, btfss, btfsc, return, etc) que necesitan dos ciclos máquina.
9
Demoras mediante lazo simple:
Entre paréntesis se muestra el número de ciclos que demora cada instrucción. De manera que el número de ciclos de instrucción Tsub consumidos por la rutina, incluyendo los 2 ciclos de la llamada (CALL) serán Tsub = [2 + 1 + 1 + (0xXX ‐ 1)*(1 + 2) + 2 + 2] ciclos = (3*0xXX + 5) *Tcy Donde Tcy es la duración en segundos de un ciclo de instrucción. Utilizando un oscilador de 4 MHz la mayor duración posible es de 770 us, con 0xXX = 0xFF. Demoras mediante Lazos anidados: Para lograr demoras de mayor duración deben utilizarse lazos anidados, poniendo un lazo de demora dentro de otro.
La duración de esta rutina en ciclos de reloj será Tsub = 2 + 1 + 1 + (0xXX)*[1 + 1 + (0xYY ‐ 1)*(1 + 2) + 2 + 1 + 2] + [1 + 1 + (0xYY ‐ 1)*(1 + 2) + 2 + 2 + 2] ciclos Lo cual se puede simplificar como sigue Tsub = [0xXX*((0xYY ‐ 1)*3 + 7) + 5] Tcy En este caso el tiempo máximo de demora que se puede conseguir es de aproximadamente 196 milisegundos. Bueno ahora que se entiende como se realizan las demoras, les adjunto un programita que obtiene el código necesario para una pausa, ingresando el valor de la misma y la frecuencia del oscilador utilizado. Ejemplo: Veamos como calcular demoras. Se hará titilar un led conectado a RB0 siempre que el interruptor conectado a RA0 esté cerrado.
10
Diagrama de Flujo:
Programa:
11
Creación de Tablas, Control display 7 Segmentos Uno de los componentes más utilizados para mostrar información en los proyectos controlados por un microcontrolador son los displays de 7 segmentos. Constan de 7 (u ocho) diodos LED conectados en forma de “8″, a veces con un punto decimal, y son capaces de representar dígitos del 0 al 9. En esta entrega veremos cómo utilizarlos en nuestros proyectos, a la vez que evitamos los “rebotes” de un pulsador. Como siempre, una entrega muy importante de este tutorial. Creación de Tablas: Control de un Display de 7 Segmentos. Un display es una colección de LEDs ubicados de forma estratégica. Si se los agrupa uniendo sus cátodos será de CÁTODO COMÚN, o bien agrupando sus ánodos, un display de ÁNODO COMÚN. Por otro lado estos LEDs pueden ser fabricados en forma de puntos o segmentos, tal es así que se encuentran display de 7 segmentos, como los de la imagen:
El programa que realizaremos leerá la cantidad de veces que se activa un pulsador y mostraremos el resultado. Conectaremos el display en forma directa, es decir conectando el puerto B del micro a los pines del display, y luego encender cada uno de los segmentos del para visualizar el valor correspondiente. Para ello crearemos una tabla que contenga los distintos códigos para el numero que necesitemos visualizar.
12
Es obvio que con un solo display solamente podremos contar de 0 a 9. Diagrama de Flujo:
13
Antes de continuar tratare de explicar algunos registros importantes: El PC (Program Counter o Contador de Programa). Direccionamiento del programa: Especifica la dirección de la instrucción que se ejecutará. Consta de 13 bits, con lo que es posible direccionar hasta 8K palabras, pero en el 16F84A solo se implementa 1k.
La parte alta del contador de programa (PCH) no se puede acceder directamente, ella debe cargarse desde los 5 bits más bajos del registro llamado PCLATCH (dirección 0×08). En la creación de tablas, la posición a leer de la misma se realiza con el control del registro PCL. Este registro es de 8 bits, por lo que direcciona solo 256 posiciones, por ello se debe tener en cuenta:
La posición de la tabla en la memoria de programa.
El tamaño de la tabla, si nuestra tabla tiene más de 255 posiciones, si o si debemos manejar los bits más significativos de PC [PCLATCH]).
Para devolver el valor direccionado se utiliza retlw, esta instrucción devuelve un valor en el acumulador al retornar de una subrutina. La creación de la tabla se hará de la siguiente forma:
Donde Valor0, Valor1, Valor2… etc. son los valores que queremos almacenar en la tabla. La estrategia a seguir para consultar algún valor de la tabla es cargar en el acumulador (W) la dirección de la tabla donde se encuentra el valor que quieres leer y después llamar a la subrutina TABLA (con un CALL). Advertencia: la carga de W no puede superar el número de valores de la tabla, sino se estará ejecutando una instrucción errónea provocando un mal funcionamiento del programa.
14
Explicado lo necesario pasamos al código del ejemplo:
15
Una manera más cómoda de escribir la tabla de instrucciones RETLW puede lograrse usando la directiva DT (Define Table) del ensamblador, la cual nos permite definir una tabla de datos que será sustituida por una lista de instrucciones RETLW; así, la tabla anterior puede quedar como sigue:
Control anti rebote: En el momento de presionar un botón pulsador o cualquier conmutador electromecánico es inevitable que se produzca un pequeño arco eléctrico durante el breve instante en que las placas del contacto se aproximan o se alejan de sus puntos de conexión.
La duración de este depende de la calidad de los switches y la velocidad de accionamiento, pero no dura más de 20 milisegundos. Se adjunta
Direccionamiento Indirecto, Control display 7 Segmentos En la entrega anterior aprendimos a utilizar tablas para guardar datos, y aplicamos ese conocimiento para elaborar un programa capaz de mostrar dígitos en un display de 7 segmentos. En la presente entrada veremos otras formas de implementar una tabla, concretamente la que se conoce como direccionamiento indirecto, mediante el uso de los registros FSR y INDF. Esto nos brindará una nueva manera de acceder a tablas o arreglos de datos. Otra forma de crear una tabla: Direccionamiento Indirecto. En la programación de los microcontroladores PIC la mayoría de las instrucciones emplean direccionamiento directo, pero también es posible que operen en un modo de direccionamiento indirecto. Para el direccionamiento indirecto se emplean dos registros especiales: el FSR y el INDF (este último no es un registro físico). El registro FSR se emplea para “señalar o apuntar” a una dirección de la memoria RAM cuyo contenido puede ser leído o escrito de forma indirecta empleando cualquier instrucción que use como operando al registro INDF. Esta forma de direccionamiento es particularmente útil cuando se manejan tablas o arreglos de datos. Directo vs Indirecto:
16
Utilizaremos el direccionamiento Indirecto para crear la tabla de control del display. Aquí no utilizaremos el pulsador, solo se hará el contador automático de 0 a 9. Al iniciar el microcontrolador cargaremos el código de 7 Segmentos para controlar el Display en la memoria de Datos con direccionamiento indirecto. Luego, al realizar el conteo leeremos el código correspondiente almacenado y lo enviaremos al PORTB. Aquí utilizamos el registro STATUS nuevamente, pero para control de las operaciones aritméticas. Nosotros guardaremos el código de 7 Segmentos del 0 al 9, en los registros 0×10 a 0×19. Si nuestro contador nos direcciona el registro ubicado en 0x1A, que sería el “10”, lo reseteamos y direccionamos el “0”, ósea registro 0×10. Esto lo hacemos realizando la resta del registro seleccionado y 0x1A, FSR – 0x1A, y si el resultado es cero, reseteamos. El bit Z (Zero) del registro STATUS, este indica si una operación lógica o aritmética realizada da como resultado cero. También tenemos el bit C (Carry) (0), que en instrucciones aritméticas se activa cuando se presenta un acarreo desde el bit mas significativo del resultado, el bit DC (Digit Carry), que en operaciones aritméticas se activa si ocurre acarreo entre el bit 3 y bit 4. Código completo:
17
Control de varios displays 7 Segmentos por multiplexado Hemos visto cómo acceder a datos guardados en tablas, ya sea de modo directo como de modo indirecto. En esas entregas utilizamos este mecanismo para representar dígitos sobre un display de 7 segmentos. Pero en general estos displays se utilizan en grupos, a veces muy numerosos. Cuando esto ocurre se implementa lo que se denomina “multiplexado“, que es la técnica que aprenderemos a utilizar en la presente entrega de este tutorial. Para el control de varios display la idea es multiplexar la señal enviada por el microcontrolador, con él administraremos el encendido de cada display y sus segmentos (lo cual se hace por programa). Para ejemplificar haremos un contador automático de 0 a 999. El hardware necesario es el siguiente:
18
Diagramas de Flujo:
19
Se observa que el Puerto B se utiliza para enviar los datos a mostrar en cada display, mientras que por el Puerto A seleccionas el display que mostrará ese dato. Supongamos que quiero mostrar “231“, pues muy fácil, pongo el puerto B en 0000110 (código para el 1), y activo ahora el 3º transistor por un periodo de tiempo corto, desactivamos este transistor, cargamos el puerto B con 1001111 y activamos el 2º transistor por un instante, y lo mismo hacemos para mostrar “1”. Repetimos esta misma secuencia mientras se quiera mostrar este valor. La secuencia es tan rápida que el observador no nota el momento en que cambias de display. Control de conteo: Para realizar el conteo incrementamos continuamente Unidad, cuando está llega a 10, las reseteamos a 0, e incrementamos en 1 Decena. La misma operación se realiza con Decena, al llegar a 10 se lleva a 0 y se incrementa Centena.
20
21
En este ejemplo se mantiene la visualización del mismo valor durante aprox. 300 ms, se puede determinar ya que utilizamos 3 demoras de 5 ms despreciando los ciclos utilizados en los comandos, que son aprox. 30 (30 us). Entonces por ciclo tenemos 15 ms, y por 20 repeticiones, 300 ms.
Control de varios displays 7 Segmentos con decodificadores BCD Siempre hay más de una forma de realizar una tarea, y la programación de PICs no es una excepción a esta regla. Hemos visto como controlar varios displays de 7 segmentos mediante el multiplexado de las señales que genera el PIC, pero esa no es la única forma de hacerlo. Hoy veremos cómo utilizar los populares circuitos 74LS47, 74LS249 o el CD4511, que son lo que se denominan “decodificadores BCD“. Decodificador BCD: La otra posibilidad es utilizar un decodificador BCD como el 74LS47 o el 74LS249, o el CD4511. Estos integrados disponen de 4 entradas correspondientes a un código binario BCD, y 7 salidas que se conectan a un directamente a un display.
Lo importante de este integrado, es que posee 4 pines de entrada y 7 de salida, más unos cuantos de configuración. El hecho es que los 4 pines de entrada (A, B, C y D) serán los que reciban el código en binario enviado por el micro. Una vez recibido el dato, el integrado se hará cargo de decodificarlo y enviarlo por los pines de salida (a, b, c, d, e, f y g) para mostrarlo en el display. Lo que necesitamos saber es que dato deberé enviar al decodificador. Y eso lo podemos hacer implementando una tabla como la siguiente: DCBA Valor que muestra el Display 0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 1000 8 1001 9 Una vez que hemos construido la tabla, podemos implementar el programa como hemos hecho antes, utilizando el modo de direccionamiento directo o el direccionamiento indirecto.
22
Interrupciones, Registros y guardado temporario de STATUS y W Una de las características más interesantes de los microcontroladores es el manejo de interrupciones. En pocas palabras, es un mecanismo que interrumpe (de ahí su nombre) la ejecución normal de un programa cuando ocurre un evento determinado. Se pueden provocar interrupciones por tiempo, por la modificación del estado de un pin, etcétera. El conocimiento de las técnicas necesarias para programar interrupciones es fundamental para aprovechar a fondo un microcontrolador. INTERRUPCIONES Una de las características más importante de los microcontroladores y que mencionamos al inicio del tutorial, es que tienen la posibilidad de manejar interrupciones. Se trata de un acontecimiento que hace que el micro deje de lado lo que se encuentra realizando, atienda ese suceso y luego regrese y continúe con lo suyo. Hay dos tipos de interrupciones posibles, una es mediante una acción externa (es decir por la activación de uno de sus pines), la otra es interna (por ejemplo cuando ocurre el desbordamiento de uno de sus registros) En el PIC16F84A hay 4 fuentes de interrupción:
Por el pin RB0/INT, que regresa al PIC del modo SLEEP (interrupción externa).
Por los pines RB4 a RB7, configurados como entrada y en caso de que alguno de ellos cambie de estado (interrupción externa).
Por desbordamiento del registro TMR0, cuando este registro pasa de 255 a 0 en decimal (interrupción interna).
Al completar la escritura de la EEPROM de datos (interrupción interna).
Cada fuente de interrupción está controlada por 2 bits. Un bit local de interrupciones (Terminado en E) de permiso o prohibición de ejecución. Si está en 0 bloqueará la solicitud de interrupción, y si esta en 1 permitirá la ejecución. Un bit que actúa como señalizador (Terminado en F) el cual es activado (puesto a 1) si se ha producido la interrupción. Además existe 1 bit de control global, el bit GIE (INTCON ) el cual si esta desactivado bloquea todas las solicitudes de interrupción. Lo anterior descrito puede entenderse observando el diagrama lógico de la siguiente figura:
El bit GIE se borra automáticamente cuando se reconoce una interrupción para evitar que se produzca otra mientras se está atendiendo a la primera y al retornar de la interrupción con la instrucción RETFIE, el bit GIE se vuelve a activar poniéndose a 1. En cambio los bits señalizadores o banderas de interrupción deben ser puestos a cero por el tratamiento de la interrupción realizada por el usuario (el programador) Cuando una interrupción está habilitada (su bit local de habilitación está activado, el bit GIE está activado) y ocurre el evento que la
23
activa, el valor de PC se guarda en la PILA y en éste se carga el 0×04 (único vector de interrupción). Es a partir de esta dirección que se debe colocar el tratamiento de la interrupción, detectando por medio de los bits banderas cuál de los eventos ha ocurrido y actuar según sea el caso. Nota: El único registro que se salva en la PILA es PC, para preservar algún otro registro debe ser el propio programa de atención a la interrupción el que se encargue de salvar su estado al inicio de la rutina y de devolverlos al final del mismo. Resumiendo, las acciones que se realizan automáticamente el microcontrolador y las que el programador debe tener en cuenta en sus programas son las siguientes:
Cuando se activa una posible causa de interrupción, el flag correspondiente se activa. Si el bit de permiso correspondiente está a 1 y el bit de habilitación de todas las interrupciones (GIE) está a 1, se produce la interrupción.
Para evitar que se produzca otra interrupción mientras se está atendiendo a otra anterior, el bit GIE se pone a 0.
El valor del PC se guarda en la PILA
El PC se carga con el valor 0×04, que es el vector de interrupciones
El programador, debe comenzar la rutina de atención a la interrupción con un salto a la posición de memoria donde se encuentra el programa, seguidamente se guardan todos los registros que puedan ser modificados por esta, seguidamente si están habilitadas varias vías de interrupción, se debe explorar el valor de las banderas para determinar la causa de la interrupción.
Dependiendo de la causa de la interrupción, la rutina de interrupción se bifurca a la subrutina correspondiente.
Se deben devolver los valores que tenían los registros antes de producirse la interrupción y se deben borrar por software las banderas que indican las fuentes de las interrupciones, antes del retorno al programa principal.
Cuando se llega a la última instrucción de la rutina de interrupción, RETURN, se carga el PC con el valor que se guardó inicialmente en la PILA y el bit GIE se pone automáticamente a 1.
Bits utilizados:
INTF para RB0/INT, bit 1 de INTCON, si es 1 ocurrió interrupción externa
RBIF para los pines B4 a RB7, bit 0 de INTCON, si es 1 por lo menos un pin cambio de estado
T0IF para TMR0, bit 2 de INTCON, si es 1 TMR0 desbordado
EEIF para la EEPROM, bit 4 de EECON1, si es 1 se ha completado escritura
GIE, bit 7 de INTCON, si es 1 habilita todas las interrupciones
EEIE, bit 6 de INTCON, si es 1 se activa interrupciones de periféricos
T0IE, bit 5 de INTCON, si es 1 int. TMR0 activada
INTE, bit 4 de INTCON, si es 1 int. Externa activada
RBIE, bit 3, si es 1 int. Por RB4 a RB7 activada
Todos estos bits al resetearse o iniciarse el micro se encuentran en 0. Rutina de Servicio de Interrupciones: Primero debes guardar el contenido del registro W, el problema de mover W a otro registro (haciendo uso de movf) es que esta instrucción corrompe la bandera Z, modificando el registro de STATUS. Según la hoja de datos otorgada por Microchip, en uno de sus apartados recomienda una secuencia de código que permite guardar y restaurar los registros sin modificarlos.
24
Los registros W_Temp y STATUS_Temp son registros alternativos para guardar temporariamente sus valores correspondientes.
Interrupción TMR0, modo Temporizador En esta oportunidad veremos cómo tratar las interrupciones que se “disparan” mediante el contador / temporizador de 8 bits TMR0. Se trata de un registro que se incrementa continuamente, a un ritmo que le impone un preescaler y el reloj interno del microcontrolador. Puede funcionar en modo temporizador, y seguramente encontrarás decenas de aplicaciones para todo lo que aprendas en esta entrega. INTERRUPCION POR TMR0 El Timer 0 es un contador / temporizador de 8 bits. El registro principal de este módulo es TMR0 (0×01). Este registro se incrementa continuamente a una frecuencia seleccionable manejada por un preescalador y el reloj interno Fosc/4 (modo temporizador) o bien, por un preescalador y una señal externa (modo contador). En la siguiente figura se muestra un diagrama de bloques de este módulo, en donde se indican los bits que afectan su operación y la manera en que lo hacen.
25
El modo Temporizador En el modo temporizador la señal de reloj que controla el incremento del registro TMR0 es la frecuencia Fcy = Fosc/4, la cual puede ser dividida opcionalmente por el preescalador. Este modo es seleccionado al limpiar el bit T0CS (OPTION_REG). En este modo si se realiza una escritura al registro TMR0, su incremento es inhibido por los siguientes dos ciclos de instrucción (Tcy) y si el preescalador está asignado se pierde la cuenta pero no su asignación. El modo Contador En el modo contador, la señal que controla los incrementos del registro TMR0 es una señal externa que proviene de la patita T0CKI poniendo el bit T0CS en alto. Se puede seleccionar la transición que provoca los incrementos mediante el bit “Timer0 Source Edge Select” T0SE (OPTION_REG), limpiando este bit se selecciona la transición de subida, mientras que al ponerlo en alto se selecciona la de bajada. Observación: En este modo, la señal conectada a TOCKI es muestreada durante los ciclos Q2 y Q4 del reloj interno, por ello es necesario que permanezca en alto al menos por 2 Tosc más un pequeño retardo de 20nseg y lo mismo en bajo. (Es decir, señales demasiado rápidas no podrán ser detectadas). El preescalador: El preescalador es un divisor de frecuencia de módulo seleccionable. Como se puede ver en la figura anterior, el preescalador está compartido entre el timer0 y el módulo Watchdog, sin embargo sólo puede conectarse a uno de los dos y esto se establece mediante el bit PSA (OPTION_REG), así, con este bit en alto el preescalador es asignado al reloj del Watchdog, mientras que con un nivel bajo en PSA el preescalador dividirá la frecuencia que maneja al Timer 0. La selección del módulo (valor de división de frecuencia) del preescalador se puede realizar mediante los bits PS2, PS1, PS0 (OPTION_REG) de acuerdo a la siguiente tabla:
Ejemplo modo temporizador: Para calcular una temporización se necesita el tiempo de un ciclo de instrucción (es decir 1 microsegundo, si estás trabajando con un XT de 4 Mhz), el valor del Divisor de Frecuencia (el que seleccionabas con los bits PS2, PS1 y PS0), y finalmente el complemento del valor cargado en TMR0 (es decir 255‐TMR0). Entonces tenemos: Temporización = Ciclo de instrucción. (256‐TMR0) .Divisor de Frecuencia De este modo si queremos temporizar 4 ms con un divisor de frecuencia de 32, tendríamos: TMR0 = 256 – ^[4000us / (1us x 32)] TMR0 = 131 Vemos que la máxima temporización posible es con TMR0 = 0, y Divisor de Frecuencia en 256, lográndose unos 65.5 ms aprox. Para ejemplificar el uso de esta interrupción haremos titilar un led conectado al PIN RB0 cada 200 ms, para ello haremos una temporización con TMR0 de 50ms y contaremos 4 desbordes del mismo para lograr los 200 ms necesarios. Lo interesante de usar interrupción es que con el micro se puede estar ejecutando cualquier tarea y no ocupar este tiempo en un bucle de demora. El hardware necesario es equivalente al primer ejemplo realizado.
26
Diagrama de flujo:
27
Una ayuda adicional: El programita presentado en el primer post tiene una utilidad que nos ayudará en el cálculo del preescaler y valor inicial del Timer para obtener una temporización deseada, aparte de generar el código.
Interrupción TMR0, modo Contador En la entrega anterior comenzamos a ver cómo aprovechar las interrupciones, y explicamos que podíamos utilizar las interrupciones en modo temporizador y en modo contador. Cada uno de estos modos tiene sus ventajas, y resulta adecuado en diferentes aplicaciones. En esta oportunidad veremos un ejemplo de las interrupciones en modo contador, contando (valga la redundancia) el número de veces que se produce un cambio de estado en un pin del PIC. Ejemplo modo contador:
28
El siguiente programa realiza el conteo del número de veces que produce una transición de bajo a alto en la patita T0CKI. El valor del contador se incrementará una vez por cada dos transiciones, y al detectarse 10 cambiamos el estado del LED conectado a RB0.
Diagrama de Flujo:
29
Código:
30
Interrupción RB0, Control de LED Seguimos aprendiendo como utilizar las interrupciones. Ya hemos visto con anterioridad algunas de sus particularidades, y en esta oportunidad aprenderemos como utilizar interrupciones externas, es decir, las que se “disparan” como respuesta a un evento que tiene lugar fuera del microcontolador. Comenzaremos con las que provienen de modificaciones en el estado del pin RBO/INT. INTERRUPCION EXTERNA, RB0/INT Para el control de la interrupción externa se necesitan dos bits más, ellos son RPBU (OPTION_REG, que activa o desactiva las resistencias Pull‐Up internas del PORTB, en caso de que el dispositivo conectado al puerto sea de colector abierto y el más importante INTEDG (OPTION_REG), si esta en 1, la interrupción se genera por flanco ascendente, y en 0, la interrupción se genera por flanco descendente. Para mostrar su uso haremos un ejemplo sencillo que muestre como se configura, el cual al presionar un pulsador conectado a RB0 cambiará el estado de un led conectado a RB1, para ello configuramos que la interrupción de genere por flanco ascendente. Diagrama de Flujo:
31
Código:
.
32
Interrupción RB4 a RB7, Control Teclado Matricial Continuamos aprendiendo a sacar provecho de las interrupciones externas. Habíamos visto como utilizar los cambios en el estado del pin RB0/INT, y hoy aprenderemos a utilizar las que provienen de los pines RBA a RB7 para detectar que tecla se ha presionado en un teclado matricial. Esta entrega es muy importante, ya que nos permite aprender a utilizar teclados, algo muy común en los proyectos que utilizan microcontroladores. INTERRUPCION EXTERNA, RB4 a RB7 Aprovecharemos esta interrupción para detectar cuando se ha presionado una tecla de un Teclado Matricial. Un teclado matricial es un simple arreglo de botones conectados en filas y columnas, de modo que se pueden leer varios botones con el mínimo número de pines requeridos. Un teclado matricial 4×3 solamente ocupa 4 líneas de un puerto para las filas y otras 3 líneas para las columnas, de este modo se pueden leer 12 teclas utilizando solamente 7 líneas de un microcontrolador. Para detectar la tecla presionada se utilizara el siguiente hardware:
Configuraremos RB0 a RB3 como salidas y las colocaremos a nivel bajo. RB4 a RB7 configuradas como entradas, y en estado normal (sin presión de teclas) estarán a nivel alto. Al presionar una tecla se conecta una fila con una columna, se produce un cambio de nivel en alguna de las columnas (de nivel alto a bajo), y se genera la interrupción. Para detectar que tecla se ha presionado, se colocan RB0 a RB3 a nivel alto, y se pasan a nivel bajo de a una por vez, detectando si se produce algún cambio en las columnas.
33
Se utiliza una variable que se incrementa con la cuenta de las teclas revisadas, de este modo al detectar una pulsación el valor de la cuenta será el valor de la tecla presionada. Si al final no se presionó ninguna tecla la variable se pone a cero y la cuenta vuelve a comenzar. En nuestro ejemplo representaremos la tecla presionada en forma binaria con leds conectados al puerto A. Diagrama de Flujo:
34
Código:
35
También con la variable NTecla (Numero de Tecla presionada) se puede utilizar como entrada a una tabla para codificar en ASCKII la tecla presionada: Código:
Control del LCD, Pantalla de Cristal Líquido Cuando explicamos como mostrar información en uno o más displays de LEDs seguramente pensaste que en algunos casos sería mejor utilizar una pequeña pantalla LCD alfanumérica. En esta entrega, luego de haber visto cómo utilizar un teclado matricial, vamos a analizar las rutinas necesarias para que nuestros proyectos puedan incorporar estas versátiles pantallas. Con estos elementos nuestros proyectos estarán a la altura de los profesionales. Control del LCD La pantalla de cristal líquido o LCD (Liquid Crystal Display) es un dispositivo µControlado de visualización grafico para la presentación de caracteres, símbolos o incluso dibujos (en algunos modelos). En este caso dispone de 2 filas de 16 caracteres cada una y cada carácter dispone de una matriz de 5×7 puntos (pixels), aunque los hay de otro número de filas y caracteres. Este dispositivo está gobernado internamente por un microcontrolador Hitachi 44780 y regula todos los parámetros de presentación, este modelo es el más comúnmente usado y la información que se adjunta se basará en el manejo de este u otro LCD compatible.
36
Conexión del módulo LCD al PIC16F84A mediante bus de 4 bits. En este ejemplo haremos uso de dos directivas más de ensamblador. Estas son #DEFINE y macro. #DEFINE es empleado para crear sustituciones dentro del texto del programa que lo simplifiquen. La forma correcta es #DEFINE NOMBRE TEXTO, con lo que, cada vez que el compilador encuentre la orden NOMBRE, la sustituirá por el texto. El problema que se nos plantea es que, si bien es más flexible que la directiva EQU, puesto que esta sólo nos permitía asignar un valor, sólo se nos permite con #DEFINE una línea de texto, y esta debe ser fija. Este problema se soluciona mediante macro. Esta directiva tiene la siguiente forma:
De este modo NOMBRE será sustituido como comando por la secuencia completa definida tras macro hasta endm, y los sucesivos argumentos serán, a su vez, sustituidos dentro del texto. En nuestro ejemplo lo utilizaremos para enviar un carácter o un comando al LCD, de la siguiente manera: Código:
37
Rutinas de Control:
LCD_Config_puertos: Configura los puertos del PIC para el uso del módulo LCD, solo afecta a los pines utilizados.
LCD_Init: Inicializa el módulo LCD para su correcto funcionamiento. Es necesario ejecutar esta subrutina al principio de los programas que vayan a utilizar la visualización mediante LCD.
LCD_Bandera: Explora el estado de la bandera Busy (ocupado) del módulo LCD y espera que termine cualquier comando previo antes de volver a la rutina que le llamo.
LCD_Enable: Habilita el módulo LCD durante 2us para recepción de datos o envío.‐
LCD_Comando: Configura módulo LCD para recibir un comando mediante rutina LCD_Envio_Data.
LCD_Caracter: Configura módulo LCD para recibir un carácter mediante rutina LCD_Envio_Data.
LCD_Envio_Data: Envía dato al LCD, Cargando el nibble alto y luego el nibble bajo.
Nota: Observar que las líneas de control y bus de datos es fácilmente modificable en Declaración de bits para control LCD y Declaración de Bytes del LCD. Como ejemplo de aplicación se muestra un ejemplo donde se visualiza un mensaje (“Ucontrol”): Código:
38
39
40
Comunicación RS232 En esta entrega llega a su fin la primera parte de este curso. En efecto, con la explicación de cómo utilizar el puerto serie RS232 para enviar y recibir datos con un microcontrolador terminamos la sección de este tutorial dedicada al microcontrolador PIC16F84A. En la entrega siguiente aprovecharemos todo lo aprendido para comenzar a desarrollar temas relativos al microcontrolador PIC16F628A, más potente, moderno y económico el clásico PIC16F84A que hemos utilizado hasta este momento. El RS232 es un estándar de comunicaciones propuesto por la Asociación de Industrias Electrónicas (EIA) la cual define la interfase mecánica, los pines, las señales y los protocolos que debe cumplir la comunicación serial. La velocidad se mide en baudios (bits/segundo) y está normalizada a 2400, 4800, 9600, 19200, 38400, etc. Y sólo son necesarios dos cables, uno de transmisión y otro de recepción. Todas las normas RS‐232 cumplen con los siguientes niveles de voltaje:
Un “1” lógico es un voltaje comprendido entre –5v y –15v
Un “0” lógico es un voltaje comprendido entre +5v y +15 v
Los puertos series son accesibles mediante conectores. La norma RS232 establece dos tipos de conectores llamados DB‐25 y DB‐9, machos y hembras. La norma RS232 se estableció para conectar un ordenador con un modem, por lo que aparecen muchas patillas en los conectores DB‐25 que en otro tipo de aplicaciones no se utilizan y en las que es más común utilizar el conector DB‐9. Cada una de las patillas del conector RS232 tiene una función específica. Patillas del DB‐9:
41
Los pines que portan los datos son RxD y TxD los demás se encargan de otros trabajos, el DTR indica que el ordenador esta encendido, DSR que el dispositivo conectado al puerto esta encendido, RTS que el ordenador al no estar ocupado puede recibir datos, al revés de CTS que lo que informa es que es el dispositivo el que puede recibir datos, DCD detecta que existen presencia de datos, etc. Formato de un byte: El protocolo establecido por la norma RS232 envía la información estructurada en 4 partes:
Bit de inicio o arranque (START). Es un paso de ‐12V a +12V, es decir de un “1” a un “0” lógico en la lógica negativa de la norma RS232.
Bits de datos (Datas) Los bits de datos son enviados al receptor después del bit Start. El bit de menos peso LSB es trasmitido primero. Un carácter de datos suele consistir en 7 u 8 bits.
Bit de Paridad (Parity) Dependiendo de la configuración de la transmisión un bit de paridad puede ser enviado después de los bits de datos. Con este bit se suele descubrir errores en la transmisión, puede dar paridad par o impar.
Bit de Parada (STOP) la línea que a ‐12V después del último bit enviado, es decir queda a “1” en lógica negativa de la norma. El protocolo permite 1, 1.5 o 2 bits de parada.
MAX 232: En el mercado hay muchos circuitos integrados que permiten la conversión entre niveles TTL y niveles RS232. El más destacado es el transceptor MAX232:
Este convierte los niveles RS232 a voltajes TTL y viceversa sin requerir más que una fuente de +5V y un par de capacitores. Para ejemplificar el uso de este protocolo para establecer comunicación con la PC, haremos un programita que reciba la data de la PC y se la reenviaremos inmediatamente.
42
Para la recepción de datos aprovecharemos la interrupción externa por RB0, configurada en flanco descendente (detectará cuando la PC envié un bit de Start). Deja pasar un tiempo una y media veces mayor que el periodo de transmisión para saltarse el bit de Start y lee el primer bit en su mitad. Lee el resto de los bits de datos, esperando un tiempo igual a la duración del periodo entre lectura y lectura para testearlos en mitad del bit. Kbhit indica si ha llegado o no un dato desde PC. Para el envío de datos se envía un “0″ durante un tiempo igual al periodo de la velocidad de transmisión. Este es el bit de “Start”. Luego se envía el bit correspondiente al dato a enviar: Si va a enviar un “0″ permanece en bajo durante el periodo correspondiente y si se va a escribir un “1″ permanece en alto durante el periodo correspondiente. Al enviar los 8 bits de datos se envía un bit de Stop, nivel alto durante un periodo. Los parámetros adoptados para la comunicación son los siguientes:
Velocidad 9600 baudios
Dato de 8 bits
Sin Paridad
1 bit de Stop
Para establecer comunicación con el microcontrolador utilizaremos el software Siow, es un monitor del puerto serie muy sencillo de usar, solo hay que configurar los parámetros de comunicación. (También se puede usar el Hyperterminal de Windows)
43
Código:
44
Con la explicación de cómo utilizar el puerto serie RS232 para enviar y recibir datos con un microcontrolador terminamos la sección de este tutorial dedicada al microcontrolador PIC16F84A. En la entrega siguiente aprovecharemos todo lo aprendido para comenzar a desarrollar temas relativos al microcontrolador PIC16F628A, más potente, moderno y económico el clásico PIC16F84A que hemos utilizado hasta este momento
Introducción al PIC 16F628A Cuando comenzamos a trabajar con microcontroladores en ASM necesitamos de toda la ayuda con la que podamos contar. Ya hemos visto en profundidad el microcontrolador PIC16F84A, y ahora es el turno de su “hermano mayor” PIC16F628A. Al igual que la primera parte del tutorial, esta consta de varios capítulos que estamos seguros de que te resultarán muy útiles. Esta entrega es algo más larga de lo normal, pero te aseguramos que vale la pena. PIC16F628A
45
Haré una introducción a los micros y realizare ejemplos para los módulos que no trae el PIC16F84A. Empezaré por sus características principales
Conjunto reducido de instrucciones (RISC). Solamente 35 instrucciones que aprender a utilizar
Oscilador interno de 4MHz
Opera con una frecuencia de reloj externa de hasta 20 MHz (ciclo de máquina de 200 ns)
Memoria de programa: 2048 locaciones de 14 bits
Memoria de datos: Memoria RAM de 224 bytes (8 bits por registro)
Memoria EEPROM: 128 bytes (8 bits por registro)
Stack de 8 niveles
16 Terminales de I/O que soportan corrientes de hasta 25 mA
3 Temporizadores
Módulo de comunicación serie (Usart
Módulo CCP (captura/comparación/PWM)
2 Comparadores analógicos, una referencia de voltaje programable
Nota: Los módulos descriptos para el PIC16F628A son idénticos a los del PIC16F648A, lo único que varía entre ellos son las siguientes características:
Memoria de programa: PIC16F648A‐4096 locaciones de 14 bits
Memoria de datos: PIC16F648A‐Memoria RAM de 256 bytes (8 bits por registro)
Memoria EEPROM: PIC16F648A‐256 bytes (8 bits por registro)
De aquí en adelante me refiero al PIC16F628A pero los ejemplos son aplicables al PIC16F648A haciendo el cambio de encabezado:
Puertos: Los PIC16F628/648A cuentan con dos puertos (PORTA y PORTB), algunos pines de estos puertos de entrada/salida son multiplexados con una función alternativa de los periféricos del dispositivo. Cuando un periférico es activado el pin no puede ser usado para propósitos generales de e/s. El PUERTO A es un puerto de entrada de 8 bits. Todos los pines, excepto RA5, pueden ser configurados como entrada o salida con la respectiva configuración del registro TRISA. El pin RA4 esta multiplexado con la entrada de reloj T0CKI y como salida se comporta como colecto abierto, por lo tanto debemos poner una resistencia Pull‐up a Vdd. El pin RA5 es un disparador Schmitt solo de entrada y no cuenta con controladores de salida, según la configuración, puede ser usado como Mclr (Reset externo), y además sirve también para entrar en el modo de programación cuando se aplica una tensión igual a Vpp (13,4V mínimo). Los demás pines del puerto trabajan de entrada como disparador de Schmitt Trigger y como salida lógica CMOS. Los pines RA0‐RA3 sirven de entrada para el comparador analógico. Importante: Si se utiliza en puerto A como I/O Digital, se debe deshabilitar los Comparadores Analógicos, cargando un 0×07 en CMCON.
46
El PUERTO B es un puerto bidireccional de 8 bits, del cual por software se pueden habilitar resistencias de pull‐up internas. El PORTB es multiplexado con interrupciones externas, tales como detección de flanco por RB0, cambio de nivel por RB4 a RB7, USART, el módulo CCP y el reloj de entrada/salida TMR1. Otros pines:
VDD: Pin de alimentación positiva. De 2 a 5,5 Vcc
VSS: Pin de alimentación negativa. Se conecta a tierra o a 0 Vcc
MCLR: Master Clear (Reset). Si el nivel lógico de este terminal es bajo (0 Vcc), el microcontrolador permanece inactivo. Este Reset se controla mediante la palabra de configuración del PIC
OSC1/CLKIN: Entrada de oscilador externo
OSC2/CLKOUT: Salida del oscilador. Dependiendo de cómo se configure puede proporcionar una salida de reloj por medio de este pin
Organización de la memoria:
Organización de la memoria
47
El PIC16F628A posee un contador de programa de 13 bits, capaz de direccionar un espacio de memoria de 8Kx14. Sin embargo, únicamente los primeros 2Kx14, desde 0000h hasta 07FFh, están implementados. Los vectores de reset e interrupción están en las direcciones 0000h y 0004h, respectivamente. La pila (stack) es de 8 niveles, lo cual significa que puede soportar hasta 8 direcciones de retorno de subrutina. El PIC16F628A posee un espacio de memoria RAM de datos de 512×8, dividido en 4 bancos de 128 bytes cada uno. Sin embargo, sólo están implementados 330 bytes, correspondiendo 224 al área de los registros de propósito general (GPR) y 36 al área de los registros de función especial (SFR). Los restantes 70 bytes implementados son espejos de algunos SFR de uso frecuente, así como de los últimos 16 GPR del banco 0. Por ejemplo, las posiciones 0Bh, 8Bh, 10Bh y 18Bh corresponden al registro INTCON, de modo que una operación hecha en cualquiera de ellos, se refleja automáticamente en los otros. Se dice, entonces, que las posiciones 8Bh, 10Bh y 18Bh están mapeadas en la posición 0Bh. Esta característica agiliza el acceso a estos registros, puesto que no siempre es necesario especificar el banco donde se encuentran. La selección del banco de ubicación de un SFR o un GPR particular se hace mediante los bits 6 (RP1) y 5 (RP0) del registro STATUS.
Registros
48
Interrupciones: Registros utilizados: INTCON: Registro de lectura y escritura que contiene varios bits de señalización y habilitación para el desbordamiento del TMR0, cambio sobre el puerto RB e interrupción externa en la patilla RB0/INT.
0. RBIF: Indicador de interrupción por cambio de estado RB4‐RB7
1. INTF: Indicador de interrupción externa
2. T0IF: Indicador de interrupción por desbordamiento de Timer 0
3. RBIE: Habilitación de interrupción por cambio de estado RB4‐RB7
4. INTE: Habilitación de interrupción externa
5. T0IE: Habilitación de interrupción por desbordamiento de Timer 0
6. PEIE: Habilitación de interrupción de periféricos
7. GIE: Habilitación general de interrupciones
PIR1: El registro PIR1 contiene los bits de señalización individual de las interrupciones de periféricos 0. TMR1IF: Indicador de interrupción por desbordamiento de Timer 1
1. TMR2IF: Indicador de interrupción por desbordamiento de Timer 2
2. CCP1IF: Indicador de interrupción del módulo de Captura/Comparación.
a) Modo Comparador: Coincidencia entre TMR1 y CCP1
b) Modo Captura: Ha ocurrido una captura de TMR1
3. No Implementado.
4. TXIF: Indicador de interrupción de fin de transmisión USART
5. RCIF: Indicador de interrupción de llegada de datos USART
6. CMIF: Indicador de interrupción por cambio de estado de alguna de las salidas de los comparadores.
7. EEIF: Indicador de interrupción de fin de escritura eeprom interna.
PIE1: Registro que posee los bits de habilitación individual para las interrupciones de periféricos. El bit PEIE del registro INTCON debe ser 1 para permitir la habilitación de cualquier interrupción de periférico.
0. TMR1IE: Habilitación de interrupción por desbordamiento de Timer 1
1. TMR2IE: Habilitación de interrupción por desbordamiento de Timer 2
2. CCP1IE: Habilitación de interrupción del módulo de Captura/Comparación/PWM.
3. No Implementado.
4. TXIE: Habilitación de interrupción de fin de transmisión USART
5. RCIE: Habilitación de interrupción de llegada de datos USART
6. CMIE: Habilitación de interrupción por cambio de estado de alguna de las salidas de los comparadores.
7. EEIE: Habilitación de interrupción de fin de escritura eeprom interna.
Lógica de Interrupciones:
49
Palabra de Configuración: El PIC16F628 ha sido construido con características tales que se puede configurar para funcionar en modos de operación que no necesitan componentes externos tales como el circuito de reloj o de reset. Esto implica que es necesario configurar su modo de operación a través de una palabra de configuración. La palabra de configuración se encuentra mapeada en la dirección 2007h de la memoria de programa y solo puede ser accedida durante la programación de dispositivo.
Palabra de Configuración CP1:CP0: Bits de protección de código Los bits 13‐10 encargados de proteger la memoria de programa. CPD: Bit de protección para código de datos
1 = Protección deshabilitada de la memoria de datos.
0 = Protección habilitada en la memoria de datos.
LVP: Habilitación de la programación por voltaje bajo
1 = LVP habilitado, la terminal RB4/PGM tiene tal función.
0 = LCP: deshabilitado, RB4/PGM es una terminal I/O.
BODEN: Bit de reset por voltaje de alimentación bajo
1 = Reset por BOD habilitado
0 = Reset por BOD deshabilitado
MCLRE: Habilitacion del terminal de reset
1 = Terminal de reset en RA5.
0 = MCLR conectado internamente a Vdd, RA5 es un pin I/O.
PWRTEN: Bit de habilitación de temporizador al energizar
1 = PWRT habilitado.
0 = PWRT deshabilitado.
WDTEN: Bits de habilitación de Watch‐Dog
1 = WDT habilitado
0 = WDT deshabilitado.
FOSC2:FOSC1:FOSC0: Bits de selección del tipo de oscilador
50
Timer1 + Ejemplo Comenzaremos a analizar los módulos TImer0, Timer1 y Timer2 del microcontrolador PIC16F628A. En realidad, el primero de los tres no tiene diferencias con el que se incluye en el microcontrolador PIC16F84A, por lo que simplemente podemos leer lo escrito en la primera parte de esta serie de entradas. En el resto, por supuesto, hay diferencias que ya mismo vamos a analizar. El Módulo del Timer 0, como decíamos, es idéntico al del PIC16F84A. El Módulo del Timer 1: El Timer 1 a diferencia del Timer 0 es un temporizador/contador de 16 bits. El conteo es realizado por dos registros de 8 bits: (TMR1H (0Fh) y TMR1L (0Eh)) que son tanto leíbles como escribibles. Así, el registro TMR1 se incrementa de 0000h a FFFFh y en la siguiente cuenta se reinicia en 0000h y así sucesivamente, al reciclarse se activa (en alto) la bandera TMR1IF (PIR1), la cual puede ser utilizada para generar una interrupción, o bien, para ser consultada por poleo. En la siguiente figura se muestra un diagrama de bloques de este módulo, en donde se indican los bits que afectan su operación y la manera en que lo hacen:
51
Modo temporizador En este modo el Timer se incrementa (si no se considera preescalador) en cada ciclo de instrucción (a la frecuencia Fosc/4). Este modo se selecciona limpiando el bit TMR1CS (T1CON). El preescalador que se puede intercalar entre el reloj Fosc/4 y el registro TMR1 puede tener sólo uno de 4 valores: 1/1, 1/2, 1/4 y 1/8. En este caso la temporización de calcula: Temporización = Ciclo de instrucción. (65536‐TMR1) .Divisor de Frecuencia Vemos que la máxima temporización posible es con TMR1 = 0, y Divisor de Frecuencia en 8, lográndose unos 524.3 ms aprox. Modo contador: El Timer 1 también puede operar como contador asíncrono o síncrono contando los flancos ascendentes que ocurren en pin RB6/T1OSO/T1CKI poniendo a 1 el bit TMR1CS. Después de que el Timer1 se programe en modo contador, el módulo esperará un flanco de bajada antes de comenzar los incrementos con los flancos de subida. Contador sincronizado: Si SYNC T1 se borra, la entrada externa de reloj se sincroniza con la fase interna de reloj. La sincronización se produce después de la etapa del Predivisor. Este bloque es un contador cíclico asíncrono. Contador asincrónico: Si el bit de control SYNC T1 se activa, la entrada de reloj externa no queda sincronizada con el reloj interno. El temporizador continuará funcionando durante el estado de SLEEP, y puede provocar una interrupción en su desbordamiento que saque de su “siesta” a la CPU. Oscilador del Timer1: Se debe conectar un cristal de cuarzo entre las patillas T1OSI (entrada) y T1OSO (salida del amplificador oscilador). El oscilador se habilita con el bit T1OSCEN (T1CON), y funcionará en bajo consumo a partir de 200 KHz. Este oscilador continúa funcionando aunque la CPU entre en modo SLEEP, y está diseñado especialmente para trabajar a 32.768 Hz. Configuraciones: El Timer 1 posee un bit para habilitación / deshabilitación, este es el bit TMR1ON (T1CON) y habilita en alto. Además, el Timer 1 posee una entrada interna de RESET, el cual puede ser activado por uno cualquiera de los módulos CCP . A continuación se describe el principal registro relacionado con el Timer 1 y todos sus bits, excepto los que tienen que ver con el modo contador: Registro T1CON (10h)
Principal registro relacionado con el Timer 1 y todos sus bits. Bits 5:4 T1CKPS1:T1CKPS0.‐ Bits de selección del valor del divisor de frecuencia del preescalador:
1 1 = divisor 1/8
1 0 = divisor 1/4
0 1 = divisor 1/2
0 0 = divisor 1/1
bit 3 T1OSCEN: Bit de control de habilitación de oscilador para TMR1.
1 = Oscilador habilitado.
0 = Oscilador anulado.
Bit 2 T1SYNC: Bit de control de sincronización de la entrada de reloj externo de Timer1. TMR1CS = 1
1 = Entrada de reloj externo no sincronizada.
0 = Entrada de reloj externo sincronizada
52
TMR1CS = 0 En esta circunstancia, Timer1 usa el reloj interno. Bit 1 TMR1CS.‐ Bit de selección de la fuente de reloj
1 = Modo contador (fuente de reloj: patita RB6/T1OSO/T1CKI)
0 = Modo Temporizador (fuente de reloj Fosc/4)
Bit 0 TMR1ON.‐ Bit de habilitación / deshabilitación del Timer 1:
1 = habilita Timer 1
0 = Deshabilita Timer 1
ConfigPIC trae una herramienta adicional que nos ayuda a seleccionar el preescaler y valor inicial del Timer para obtener una temporización deseada:
Para ejemplificar haremos un simple ejemplo donde lo utilizamos como temporizador. Se hará titilar un led conectado al PIN RB0 cada 300 ms. Como se puede ver es idéntico al ejemplo realizado para Timer 0 del PIC16F84A, solo que en este caso no necesitaremos un registro adicional para lograr la demora deseada. Hardware:
53
Código:
Timer2 + Ejemplo Seguimos analizando los módulos TImer0, Timer1 y Timer2 del microcontrolador PIC16F628A. En la entrega anterior nos ocupamos de los dos primeros, e hicimos notar que el primero de ellos no tiene diferencias con el que se incluye en el microcontrolador PIC16F84A, por lo que simplemente podíamos aplicar lo aprendido en la primera parte de esta serie de entradas. Hoy nos ocupamos de los detalles del tercero de ellos, para sacar todo el jugo a las posibilidades del Timer2. El Módulo del Timer 2: El Timer2 es un temporizador (sin opción de trabajar como contador) de 8 bits. Su registro principal denominado TMR2 (11h) es un registro de 8 bits que se incrementa continuamente a la frecuencia seleccionada de Fosc/4 dividida por un preescalador. En la siguiente figura se muestra un diagrama de bloques del módulo del Timer2.
El preescalador: La frecuencia que incrementa al registro TMR2 puede ser dividida por un preescalador por un factor de 1/1, 1/4 o 1/16, seleccionable por los bits T2CKPS1:T2CKPS0 (T2CON) El Registro de comparación o de Periodo: En operación, el contenido del registro TMR2 se compara continuamente con un registro de periodo denominado PR2 (92h) cuyo valor podemos establecer por software. Cada vez que la cuenta de TMR2 es igual a PR2, se reinicia el conteo en TMR2 desde cero, y además se genera una señal de salida, la cual es tratada por un postescalador, para poder generar una señal TMR2IF (PIR1) que puede ser usada para solicitar una interrupción, o para ser leída por poleo.
54
El Postescalador: El postescalador divide la frecuencia con que ocurre una activación de la bandera TMR2IF, es decir, si el valor del postescalador es 1/1, esta bandera se activará cada vez que TMR2 se reinicie, en cambio, si es 1/16 (por ejemplo), TMR2IF se activará cada 16 reinicios de TMR2. En forma similar a los otros dos Timers, esta bandera debe ser limpiada previamente, si se quiere detectar su activación, esto puede ser hecho en la rutina de atención a la interrupción, o bien en la rutina que la detecta por poleo. El valor de división del postescalador puede establecerse por software mediante los bits T2OUPS3:T2OUPS0 (T2CON). En este caso la temporización de calcula así: Temporización = Ciclo de instrucción. (PR2‐TMR2) .Divisor de Frecuencia Pre. Divisor de frecuencia Post Vemos que la máxima temporización posible es con TMR2 = 0, y Divisor de Frecuencia Pre en 16, Divisor de frecuencia Post en 16, lográndose unos 65.5 ms aprox. A continuación se describe el principal registro relacionado con el Timer 2 y todos sus bits. Registro T2CON (12h)
Bits 6:3 T2OUPS3:T2OUPS0 Bits de selección del valor del divisor de frecuencia del postescalador, de acuerdo a la siguiente tabla:
0 0 0 0 = divisor 1/1
0 0 0 1 = divisor 1/2
0 0 1 0 = divisor 1/3
…
1 1 1 1 = divisor 1/16
Bit 2 TMR2ON Bit de encendido del Timer 2
1 = Enciende (energiza) el Timer 2
0 = Apaga (desconecta) el Timer 2
Bits 1:0 T2CKPS1:T2CKPS0 Bits de configuración del valor del preescalador de acuerdo a la siguiente tabla:
0 0 = divisor 1/1
0 1 = divisor 1/4
1 x = divisor 1/16
Ejemplo de configuración para lograr una temporización de 20 ms: Código:
55
Módulo de comunicación serie (Usart) + Ejemplo Luego de analizar los módulos TImer0, Timer1 y Timer2 del microcontrolador PIC16F628A En vamos a ver los detalles de su USART. Al igual que antes, esta entrada tiene su equivalente en la primera parte de este curso, cuando estudiamos el microcontrolador PIC16F84A, por lo sería de mucha ayuda volver a leer lo escrito en la primera parte de esta serie de entradas. En el resto, por supuesto, hay diferencias que ya mismo vamos a analizar. La USART del PIC16F628: La USART del PIC16F628Apuede ser configurada para operar en tres modos: 1.
Modo Asíncrono (full duplex (transmisión y recepción simultáneas)),
2.
Modo Síncrono – Maestro (half duplex)
3.
Modo Síncrono – Esclavo (half duplex)
Aquí solo trataremos el modo asíncrono. Modo Asíncrono: En este modo la USART usa un formato estándar NRZ asíncrono, el cual para la sincronización usa: 1 bit de inicio (I), 8 o 9 bits de datos y 1 bit de paro (P). Mientras no se están transmitiendo datos la USART envía continuamente un bit de marca. El modo asíncrono se selecciona limpiando el bit SYNC del registro TXSTA (98H). El modo asíncrono es deshabilitado durante el modo SLEEP. Cada dato es transmitido y recibido comenzando por el LSB. El hardware no maneja bit de Paridad, pero el noveno bit puede ser usado para este fin y manejado por software. El módulo Asíncrono de la USART consta de los siguientes módulos fundamentales:
El circuito de muestreo
El generador de frecuencia de transmisión (Baud Rate)
El transmisor asíncrono
El receptor asíncrono.
El circuito de muestreo: El dato en la patita de recepción (RB1/RX/DT) es muestreado tres veces para poder decidir mediante un circuito de mayoría, si se trata de un nivel alto o un nivel bajo. El Generador de Baud Rate (BRG): Este generador consiste de un contador/divisor de frecuencia de 8 bits controlado por el registro SPBRG (99H) . De tal manera que la frecuencia de transmisión se calcula de acuerdo a la siguiente tabla:
En esta tabla X=valor de 8 bits en el registro del divisor, SPBRG. El bit BRGH corresponde a TXSTA. En el datasheet del PIC16F628 hay tablas donde se muestran algunos valores baud estándares, el divisor necesario (X=SPBRG) bajo diferentes frecuencias Fosc y el error producido en porcentaje.
56
El transmisor asíncrono: En la siguiente figura se muestra el diagrama de bloques del transmisor de la USART:
El corazón de este módulo es el registro de corrimiento (transmit shift register, TSR). La única manera de acceder al registro TSR es a través del registro TXREG (19H). Para transmitir un dato, el programa deberá ponerlo primero en el registro TXREG. En cuanto el TSR termina de enviar el dato que tenía (en cuanto transmite el bit de paro) lee el dato contenido en TXREG (si hay alguno) esto ocurre en un ciclo TCY. En cuanto el dato de TXREG es transferido al TSR el TXREG queda vacío esta condición es indicada mediante el bit bandera TXIF (que es el bit 4 del registro PIR1 (0Ch) ), el cual se pone en alto. Este bit NO puede ser limpiado por software, sólo dura un instante en bajo cuando se escribe un nuevo dato a TXREG. Si se escribe un dato seguido de otro (back to back) a TXREG el primero se transfiere inmediatamente a TSR y el otro tiene que esperar hasta que el TSR termine de enviar el bit de Stop del primero. Durante esta espera TXIF permanece en bajo. Existe otro bit, llamado TRMT (TXSTA), el cual muestra el estado del TSR. TRMT se pone en alto cuando TSR está vacío, y en bajo cuando TSR está transmitiendo un dato. Mientras que TXIF puede generar una interrupción TRMT no lo puede hacer, TRMT está pensado para ser consultado por “poleo” (sin usar interrupciones). Para habilitar el módulo de transmisión es necesario poner en alto el bit TXEN (TXSTA), mientras no se habilite el módulo, la patita de transmisión (RB2/TX/CK) se mantiene en alta impedancia. Si TXEN es deshabilitada a la mitad de una transmisión, está será abortada y el transmisor será reseteado. Si se está usando un noveno bit TX9 (TXSTA), éste deberá ser escrito antes de escribir los 8 bits restantes a TXREG, ya que en cuanto se escribe un dato a este registro inmediatamente es transferido a TSR (si éste está vacío). De acuerdo a lo anterior, la inicialización del módulo de transmisión consiste en los siguientes pasos: 1.
Inicializar baud rate escribiendo al registro SPBRG el divisor adecuado y opcionalmente al bit BRGH.
2.
Habilitar comunicación asíncrona limpiando el bit SYNC y poniendo el bit SPEN.
3.
Si se van a usar interrupciones, poner el bit TXIE (PIE).
4.
Poner el bit TX9 si se desea transmitir datos de 9 bits
5.
Habilitar transmisión poniendo el bit TXEN, lo cual pondrá el bit TXIF.
6.
Colocar el noveno bit del dato en TX9D si se están usando datos de 9 bits.
7.
Cargar el dato al registro TXREG (inicia la transmisión).
El receptor asíncrono: El módulo de recepción es similar al de transmisión, en la siguiente figura se muestran los bloques que lo constituyen:
57
Una vez que se ha seleccionado el modo asíncrono, la recepción se habilita poniendo en alto el bit CREN (RCSTA) El dato es recibido mediante la línea RB1/RX/DT, la cual maneja un registro de corrimiento de alta velocidad (16 veces el Baud rate). El corazón del receptor es el registro de corrimiento RSR. Este registro no es accesible por software, pero, cuando el dato recibido se ha completado (se ha recibido el bit de Stop) el dato de RSR es transferido automáticamente al registro RCREG (1Ah) si éste está vacío y al mismo tiempo es puesto en alto la bandera de recepción RCIF (PIR1). La única manera de limpiar la bandera RCIF es leyendo el/los datos del registro RCREG. El registro RCREG puede contener hasta dos datos, ya que es un buffer doble que funciona como una cola de dos posiciones. Si las dos posiciones del registro RCREG están llenas (no han sido leídas) y se detecta el bit de Stop de un tercer dato de recepción, lo cual ocasiona un transferencia automática del dato recibido a RCREG, esto destruirá el primer dato recibido y activará el indicador de sobreescritura OERR (RCSTA). Para evitar esto, se deberán leer los dos datos en RSREG haciendo dos lecturas consecutivas. La única manera de limpiar el bit OERR una vez que ha sido activado es reseteando el módulo de recepción (limpiando CREN y volviéndolo a poner), si no se limpia OERR se bloquea la transferencia de datos de RSR a RCREG y no puede haber más recepción de datos. Si se detecta un bit nivel bajo en la posición del bit de stop se pone el indicador de error de encuadre (frame error) FERR RCSTA. Tanto este indicador como el noveno bit RX9D de los datos están en una cola de dos posiciones al igual que los datos recibidos, de manera que al leer RCREG se actualizan FERR y RX9D con nuevos valores, por lo cual estos bits deberán ser leídos antes de leer RCREG para no perder su información. De acuerdo a lo anterior, la inicialización del módulo de recepción es como sigue: 1.
Inicializar el baud rate escribiendo al registro SPBRG el divisor adecuado y opcionalmente al bit BRGH .
2.
Habilitar el puerto serie asíncrono limpiando el bit SYNC y poniendo el bit SPEN.
3.
Si se van a usar interrupciones, poner el bit RCIE (PIE).
4.
Si se desea recepción de datos de 9 bits se deberá poner el bit RX9 (RCSTA).
5.
Habilitar la recepción poniendo el bit CREN (RCSTA)
6.
El bit RCIF se pondrá cuando la recepción de un dato se complete y se generará una interrupción si RCIE está puesto.
7.
Leer el registro RCSTA para obtener el noveno bit (si se están recibiendo datos de 9 bits) o para determinar si ha ocurrido un error de recepción.
8.
Leer los 8 bits del dato recibido leyendo el registro RCREG.
9.
Si ocurrió algún error este se limpia al limpiar el bit CREN, el cual deberá volver a ponerse si se desea continuar la recepción.
ConfigPIC permite seleccionar algunos parámetros para configurar el módulo USART del pic, y generar el código de ayuda Veamos un ejemplo. En este caso usando el módulo Usart del PIC16F628A y la interrupción que genera el mismo al recibir un dato haremos un ejemplo donde esperamos dato desde PC, lo recibimos y lo reenviamos inmediatamente: Hardware necesario:
58
Código:
59
Comparadores analógicos + Ejemplo En esta entrega analizaremos los módulos de comparación analógica y tensión de referencia. Estos módulos están presentes en el PIC16F628A pero no en el microcontrolador PIC16F84A, por lo que se trata de un tema completamente nuevo en esta serie. Como verás a continuación, es un tema muy importante y extenso, por lo que seguramente deberemos dividirlo en 3 o 4 entradas. El Módulo Comparador: Dispone de dos comparadores analógicos C1 y C2 cuyas entradas pueden ser seleccionadas entre los pines RA0 a RA3 y cuyas salidas pueden utilizarse para lectura digital interna o bien conectarse a dos pines de salida: RA4 y RA5. También podría ser posible emplear un nivel de tensión configurable y generado por otro bloque, el denominado Módulo de Referencia de Tensión. El multiplexado para la selección de las entradas a comparar y las salidas a generar depende del registro de control de comparación CMCON (0x1F). Existen 8 configuraciones posibles de entradas y salidas de comparación que se seleccionan con los 3 bits menos significativos (CM2‐ CM1‐CM0). En ese mismo registro se pueden leer los estados en la salida de cada uno de los comparadores C1 y C2 y si se desea invertir o no la lógica del resultado de la comparación que aparece a la salida. Registro CMCOM
Bit 7. C2OUT: Bit de salida del Comparador 2. Cuando C2INV = 0:
Si C2Vin+>C2Vin‐ = 1
Si C2Vin+C2Vin‐ = 0
Si C2Vin+C1Vin‐ = 1
Si C1Vin+C1Vin‐ = 0
Si C1Vin+
Bit5. C2INV: Bit de inversión de la salida del Comparador 2.
1 = C2 Salida invertida.
0 = C2 Salida no invertida.
Bit 4. C1INV: Bit de inversión de la salida del Comparador 1.
1 = C1 Salida invertida.
0 = C1 Salida no invertida.
Bit 3. CIS: Selector de pin de entrada al Comparador. Cuando CM = 001:
1 = C1Vin‐ conectado a RA3.
0 = C1Vin‐ conectado a RA0.
60
Cuando CM = 010:
1 = C1Vin‐ conectado a RA3. C2Vin‐ conectado a RA2.
0 = C1Vin‐ conectado a RA0.C2Vin‐ conectado a RA1.
Bit 2‐0. CM: bits de selección de configuración de los Comparadores.
61
Modo de Trabajo de los Comparadores: Si la entrada analógica VIN+ es mayor que la entrada analógica VIN‐, entonces la salida del comparador será un estado digital alto Si la entrada analógica VIN+ es menor que la entrada analógica VIN‐, entonces la salida del comparador será un estado digital bajo
Las áreas sombreadas de la salida del comparador en la figura representan la incertidumbre debido a tensiones de desviación y tiempo de respuesta. Referencias de Comparación: La referencia de tensión para la comparación puede ser: Externa: se conectará a cualquiera de los pines de entrada externos del comparador y esa tensión deberá estar comprendida entre VDD y VSS Interna: se puede emplear una referencia de tensión generada a nivel interno por parte de un módulo interno específico presente dentro del microcontrolador cuando se configuran los bits CM con la combinación 110 Módulo de Referencia de Tensión para Comparación: Se trata de un divisor resistivo a partir de la tensión de alimentación del microcontrolador. Registro VRCON (9Fh):
VR Selección de la tensión de referencia: Si VRR = 1: Vref = (VR .Vdd) / 24 Si VRR = 0: Vref = (Vdd . 1/4) + (VR . Vdd) / 24 Interrupciones del Módulo de Comparación: El módulo de comparación puede generar una interrupción por activación del flag CMIF (Flag de Interrupción del Comparador) presente en el registro PIR1. El flag CMIF se pondrá a 1 si se produce un cambio en la salida de cualquiera de los dos comparadores (C1OUT o C2OUT) desde la última lectura que se realizó del registro CMCON. El flag debe ponerse a cero por software pero además debe realizarse previamente una operación de lectura (o escritura) del registro CMCON para que deje de darse la discrepancia entre el valor actual y el valor leído. Para que se produzca la interrupción deben encontrarse activadas la máscara particular (CMIE en PIE2), la de periféricos (PEIE) y la global (GIE). Característica especial del Módulo Comparador: Si un comparador se encuentra activo y se sitúa al microcontrolador en modo de bajo consumo (SLEEP), el comparador permanecerá activo y las interrupciones si están activadas mediante sus máscaras serán funcionales y podrán sacar al micro de su modo de bajo consumo si cambia la salida de alguno de los comparadores desde la última lectura.
62
ConfigPIC permite seleccionar la configuración del módulo comparador analógico y tensión de referencia, generando el código de configuración. A modo de ejemplo conectaremos un potenciómetro al comparador 1, fijando una tensión de referencia interna a 3.59 Volt e invirtiendo la salida del comparador. Cuando la tensión sobre el pin RA0 (C1Vin‐) sea mayor a 3.59 Volt haremos titilar un led 10 veces a modo de alarma. Hardware necesario:
Diagrama de Flujo:
63
Código:
64
Introducción Módulo CCP El primer módulo que veremos en detalle es el Módulo CCP. Los microcontroladores PIC16F628A/648Adisponen de un módulo de Captura/Comparación/PWM que en conjunto con los temporizadores, permite realizar en forma sencilla las tareas de medición de tiempo y frecuencia, y generación de señales digitales. Con tres modos de funcionamiento que veremos en profundidad a partir de esta entrega. Módulo CCP Los microcontroladores PIC16F628A/648A disponen de un módulo de Captura/Comparación/PWM que en conjunto con los temporizadores, permite realizar en forma sencilla las tareas de medición de tiempo y frecuencia, y generación de señales digitales. El módulo CCP tiene 3 modos de funcionamiento, que se describen a continuación:
Modo captura: Permite capturar el valor que tiene en registro TMR1 cuando ocurre un evento especial en la terminal RB3/CCP1.
Modo comparación: Permite comparar el valor de 16 bits del TMR1 con un valor previamente definido en los registros CCPRL1H y CCPR1L
Modo PWM: Permite generar señales digitales moduladas en ancho de pulso
A continuación se da un breve resumen de los registros relacionados con cada módulo: El registro principal de este módulo se compone de dos registros de 8 bits, denominados CCPR1H (16h) (parte más significativa) y CCPR1L (15h) (parte menos significativa). La operación del módulo se controla mediante el registro CCP1CON (17h) y el disparo de evento especial, el cual es generado al alcanzarse la igualdad en un registro de comparación reseteará el Timer 1. Selección del modo de operación: La selección del modo en que trabajara el módulo CCP se realiza mediante los cuatro bits menos significativos del registro CCP1CON, es decir, mediante los bits CCP1M3:CCP1M0 (CCP1CON) de acuerdo a lo siguiente:
0000 Captura/Comparación/PWM deshabilitados
0100 Captura cada transición de bajada
0101 Captura cada transición de subida
0110 Captura cada cuarta transición de subida
0111 Captura cada 16 transiciones de subida
1000 Comparación, pone salida cada coincidencia
1001 Comparación, limpia salida cada coincidencia
1010 Comparación, genera interrupción cada coincidencia (Se setea bit CCP1IF, salida inalterada)
1011 Comparación, dispara evento especial (Se setea bit CCP1IF , resetea TMR1(TMR1IF inalterado) )
11xx Modo PWM
El Modo de Captura: En el modo de captura el registro CCPR1(CCPR1H:CCPR1L) captura el valor de 16 bits registro TMR1 cuando ocurre un evento en la patitaRB3/CCP1. El evento en cuestión puede especificarse previamente como alguno de los siguientes:
65
Cada transición de bajada
Cada transición de subida
Cada cuarta transición de subida
Cada dieciseisava transición de subida
Además de que el valor de TMR1 es capturado, la bandera de solicitud de interrupción CCP1IF es activada, la cual deberá ser limpiada por software para poder detectarla si se está consultando por poleo. El tipo de acción que se desea detectar en esta patita se configura mediante los bits de control CCP1M3:CCP1M0 (CCP1CON). Si ocurre otro evento de captura antes de que haya sido leído el registro CCPR1, el valor capturado anterior se perderá, ya que con la nueva captura este registro es reescrito. En la figura siguiente se muestra un diagrama de bloques en donde se ilustra la manera en que trabaja el módulo CCP en modo de captura:
El preescalador del CCP: El valor del preescalador se configura mediante los bits CCP1M3:CCP1M0. Sin embargo, al realizar un cambio en la configuración del preescalador se puede generar una interrupción falsa, para evitar lo anterior se deberá apagar el módulo CCP (limpiando el registroCCP1CON) previamente al cambio de valor del preescalador. Este preescalador es independiente al preescalador del Timer 1 (el cual puede usarse como ya se explicó con sus posibles divisores de 1/1, 1/2, 1/4, 18). Configuraciones: Se debe configurar el CCP1 como entrada a través del registro TRISB. El Timer1 debe estar en modo temporizador o modo contador sincronizado Al cambiar el modo de captura hay que tener inhabilitadas las interrupciones y borrar el flag para evitar interrupciones espúreas. Si el pin RB3/CCP1 es configurado como salida, se deberá tener en cuenta que una escritura al puerto B puede causar una condición de captura. Ejemplo: Para ejemplificar el uso del módulo CCP en modo captura vamos a realizar la medida del periodo de una onda cuadrada para así determinar su frecuencia. La forma de trabajar va a ser la siguiente: Configuramos CCP modo captura con cada transición de subida del pin RB3/CCP1 con Timer1 modo temporizador 1/1. Al llegar un flanco ascendente, reseteamos Timer1 y esperamos al siguiente flanco ascendente, en el cual se guarda el valor capturado y lo enviamos a la PC para poder determinar la frecuencia de la señal. Lo que hemos capturado es la cantidad de ciclos de reloj transcurridos en un ciclo de la señal a medir. Con este valor y el Tosc al cual trabaja el microcontrolador podemos determinar el periodo de la señal. Periodo(s)=(4/20MHz).CCPR1 Frecuencia(Hz)=1/Periodo
66
Con un cristal de 20 MHz podemos medir desde aprox. 77Hz hasta los 500kHz, claro que a más frecuencia más error en las mediciones. Por ejemplo, con algunos ensayos que he realizado a 1kHz se tiene un error de +‐1Hz, 10kHz +‐5Hz, 100kHz +‐ 0.5kHz, 200kHz +‐ 2kHz, etc. Para determinar la frecuencia adjunto un programita hecho en Visual Basic que toma 10 valores enviados desde el microcontrolador, calcula un promedio del periodo y determina la frecuencia. Se hace de este modo para reducir el error cometido a altas frecuencias (descargalo de acá)
Hardware necesario:
67
Código:
68
Modo Captura En la entrega de hoy veremos cómo utilizar el modo comparador. En este modo, el microcontroladorPIC16F628A aprovecha el registro de 16 bits CCPR1 (CCPR1H:CCPR1L) y el registro de 16 bits TMR1. Cuando estos valores coinciden se dispara una interrupción que podemos aprovechar para realizar alguna acción en nuestro proyecto. Se trata de otra entrega muy importante en esta serie de entradas. El Modo Comparador: En el modo de comparación el registro de 16 bits CCPR1 (CCPR1H:CCPR1L) se compara constantemente con el valor del registro de 16 bits TMR1. De manera que cuando sus valores coinciden además de activarse la bandera para solicitar interrupción CCP1IF (PIR1), puede ocurrir en la patita RB3/CCP1 (previa configuración) alguna de las siguientes acciones:
RB3/CCP1 Se pone en alto
RB3/CCP1 Se pone en Bajo
RB3/CCP1 no cambia
La acción que ocurra en esta patita se configura mediante los bits de control CCP1M3:CCP1M0 (CCP1CON). En la figura siguiente se muestra un diagrama de bloques en donde se ilustra la manera en que trabaja el módulo CCP en modo comparador,
Configuraciones: El pin RB3/CCP1 debe configurarse como salida limpiando el bit TRISB El Timer 1 debe estar corriendo en modo temporizador (o en modo contador sincronizado) Al limpiar el registro CCP1CON el latch de salida de la patita RB3/CCP1 se forza a su valor “default” de cero. En el modo “interrupción software” no se realiza ninguna acción en el pin RB3/CCP1. El modo “Special Event Trigger” ocurre inmediatamente al igualarse el par de registros TMR1H, TMR1L al par de registros CCPR1H,CCPR1L. El par de registros TMR1H, TMR1L no se resetean hasta el próximo flanco ascendente del clock de Timer1. Esto permite que el registro CCPR1 sea un registro de periodo programable para el Timer1 Ejemplo: En este programa se hace uso del modo de comparación para realizar la conmutación de una señal cada vez que transcurre un tiempo, el cual se ajusta al oprimir un pulsador de incremento o uno de decremento.
69
Hardware necesario:
Código:
70
Modo PWM Hoy veremos el Modo PWM (Modulación de Ancho de Pulso). Este importante modo es el que nos permite, por ejemplo, modificar la intensidad con la que brilla un diodo LED o generar una onda de salida con frecuencia variable, permitiendo algunas aplicaciones que a priori parecen imposibles, como “hacer hablar” a un PIC. Por lo pronto, antes de llegar a semejante nivel de sofisticación veremos los principios básicos del PWM y algunos ejemplos. Modo PWM (Modulación de Ancho de Pulso) Con este modo de trabajo se consiguen impulsos lógicos cuya anchura del nivel alto es de duración variable, que son de enorme aplicación en el control de dispositivos tan populares como los motores y triacs. El pin RB3/CCP1 esta configurado como salida y cambia entre los niveles 0 y 1 a intervalos de tiempos variables, logrando un pulso cuyo nivel alto tiene un ancho variable dentro del intervalo del periodo de trabajo:
Modo PWM La base de tiempos es el Timer2 más dos bits adicionales para tener los 10. El periodo se determina con el registro PR2, ya que cuando TMR2=PR2:
Se borra el TMR2
Se pone a “1” el pin CCP1
Se pasa el valor del CCPR1L al CCPR1H (para evitar glitch)
71
De esta manera, de acuerdo a la figura anterior, el siguiente valor de comparación para TMR2 en el comparador de 10 bits es el Ciclo de Trabajo, el cual al alcanzarse limpiará la patita CCP1. El ciclo de trabajo se determina con el contenido del CCPR1L y los dos bits de CCP1CON (CCP1X y CCP1Y). Periodo del PWM: PeriodoPWM=(PR2+ 1).4.Tosc.TMR2 Preescaler FrecuenciaPWM=1/PeriodoPWM Ciclo de Trabajo del PWM: El ciclo de Trabajo se especifica escribiendo un valor de 10 bits al registro CCPR1L (los 8 bits más significativos (msb)) y los dos bits menos significativos (lsb) a CCP1CON: CTPWM=(CCPR1L:CCP1CON).Tosc.TMR2 Preescaler Como se puede ver en la figura anterior, el valor que determina la duración de C.T. del PWM no es el cargado en CCPR1L, sino en CCPR1H, el cual sólo se actualiza en el momento en que TMR2 alcanza el valor de PR2 (es decir, cada vez que se completa un periodo). Por ello, aunque CCPR1L puede ser escrito en cualquier momento, el Ciclo de Trabajo solo se actualiza hasta que termina el periodo que está en transcurso. No hay otra manera de escribir al registro CCPR1H, ya que este es un registro de sólo lectura. El número de divisiones que se pueden tener en un Ciclo de Trabajo será 2^n, donde n es el número de bits usados, por lo tanto su duración máxima será: CTPWM=2^n.Tosc.TMR2 Preescaler Sin embargo, dependiendo del valor de Ciclo de trabajo máximo (CT_PWM) deseado, no será posible realizar las 2^n divisiones y por lo tanto no se podrán usar los n bits de resolución. O al revés, si se elige una resolución deseada n no será posible tener cualquier Ciclo de Trabajo máximo (CT_PWM) Deseado. De la ecuación anterior se puede despejar cual es la resolución máxima:
72
ResolucionPWM=log(CTPWM/Tosc.TMR2 Preescaler)/ log (2) Veamos con un ejemplo, si fijamos PR2 en 100 y usamos preescaler 1/4 con un oscilador de 20 MHz, tendremos un PWM de periodo 80.8us. El ciclo de trabajo no puede superar este valor por lo que despejando de las ecuaciones anteriores, CCPR1L:CCP1CON no puede ser mayor a 404. Aquí se ve claramente que no se pueden usar los 10 bits de resolución, sino “8.66” bits. Caso contario el pinRB3/CCP1 nunca será reseteado. En la siguiente tabla se resumen diversas elecciones de resolución n y la correspondiente frecuencia F_PWM máxima, así como el valor dePR2 con el que se logra (para un frecuencia del cristal de 20 Mhz):
Secuencia de configuración del PWM: A continuación se resumen los pasos para realizar la configuración inicial del PWM: Establecer el periodo del PWM escribiendo al registro PR2.
Establecer el Ciclo de Trabajo del PWM escribiendo al registro CCPR1L y a los bits CCP1CON.
Configurar como salida el pin CCP1, limpiando el bit TRISB.
Configurar el preescalador del Timer 2 y habilitar el Timer 2, escribiendo al registro T2CON.
Configurar el módulo CCP1 para operación PWM. Poniendo en alto los bits CCP1CON .
Como ejemplo generaremos dos frecuencias distintas seleccionables mediante la acción de un pulsador conectador a RB0. Una de las señales será de 500Hz y la otra de 2kHz, con ciclo de trabajo de 50%. Con oscilador de 4MHz y preescaler 1/16 tendremos: Señal de 500Hz PR2=124 (0x7C) CCPR1L&2bits=250(0xFA) CCPR1L=0x3E & 2bits=10 Señal de 2kHz PR2=30 (0x1E) CCPR1L&2bits=62 (0x3E) CCPR1L=0x0F & 2bits=10 Hardware necesario:
73
Código:
Veamos otro ejemplo: En este se fija la frecuencia de PWM en 2 kHz y se varía el ciclo de trabajo con 2 pulsadores de control conectados a RB0 y RB1. Solo se modifica en byte alto del CT, ósea el registro CCPR1L con lo que CT se varía de 4 en 4.
74
Hardware necesario:
Código:
75
Registro de desplazamiento 74LS164N En alguna oportunidad hemos hablado de la importancia de los registros de desplazamiento a la hora de controlar displays. Es que gracias a estos integrados se puede evitar al microcontrolador la tarea de actualizar constantemente el estado de los mismos, ya que el registro de desplazamiento se encarga de mantener encendidos los segmentos que sean necesarios. En esta entrega veremos cómo realizar ese control con el popular chip 74LS164. Control de Displays con 74LS164 Aquí realizo un nuevo ejemplo de manejo de varios displays de 7 segmentos. En este caso realizaremos el control de 3 displays con la ayuda del registro de desplazamiento 74LS164. Las ventajas que conlleva el uso de este CI es que no necesitaremos multiplexar la señal enviada a los displays, algo que limita mucho al microcontrolador, ya que debe refrescarlos continuamente. Y, obviamente, la desventaja es que necesitaremos un 74LS164 por cada display. Hardware: (los pines 7 y 14 del 74LS164 van a GND y 5V, y se debe agregar una resistencia a cada línea que une el 74LS164 con el display)
76
Con este circuito se pueden manejar 3 displays + los 3 puntos. El primer bit que ingresa será empujado por los demás. Ejemplo para enviar 12.7:
Código:
77
78
Como hacer Cartel de LEDs 7×32. Con esta entrega finalizamos la segunda parte de este tutorial. A lo largo de 25 entradas hemos visto todo lo necesario para poder programar prácticamente cualquier aplicación utilizando los populares microcontroladores PIC16F84A y PIC16F628A. Nos despedimos con la explicación de lo que para muchos es un proyecto muy interesante: la creación de un cartel de LEDs que pasa mensajes. Cartel de Leds en ASM Para mostrar un ejemplo sencillo vamos a usar una tabla para guardar el mensaje a mostrar y una sola variable (8 bits) para indicar el largo y control de la posición a enviar. Por ello el largo del mensaje estará restringido a 255 bytes que se reducen en 6 por la posición de la tabla en la memoria de programa. La forma de guardar cada letra del mensaje será la siguiente:
Cada byte indica una columna de la letra, donde un 0 es led apagado y 1 led encendido. Nota: Es una forma de hacerlo, la cual la aprendí del amigo BrunoF en uno de sus tantos aportes Luego el mensaje puede ser mayor al cartel utilizado para mostrarlo así que es necesario efectuar un desplazamiento del mensaje sobre el cartel o lo que es lo mismo desplazar el cartel sobre el mensaje. Para ello se utilizará una variable que indica la posición inicial dentro del mensaje, en la cual comienza a mostrarse en el cartel. Como deseamos que el mensaje sea rotativo el problema se presenta en las últimas posiciones donde se debe mostrar la parte final del mensaje y empezar a mostrar el inicio. Lo que hacemos es dividir en 2 la forma de mostrar el mensaje, cuando Posición Inicial + Largo del Cartel sea menor a Largo del mensaje y cuando sea mayor:
79
Bueno, más o menos se ha explicado como vamos a trabajar con el mensaje, ahora se debe entender como va a funcionar el multiplexado de los leds. Vamos a realizar la multiplexación por filas, ósea que vamos a seleccionar una fila y vamos a actualizar sus valores por medio de registros de desplazamientos. El tiempo de actualización no debe superar los 20ms! Para actualizar usaremos una variable que indique que fila ha de actualizarse, por ejemplo para actualizar la fila uno la variable FilaActual será 00000001. Ahora para saber que valores tenemos que mandar a los registros de desplazamiento iremos tomando cada una de las columnas (PosicionEnviar) a actualizar, aremos un AND con FilaActual y determinaremos si enviar un 1 o un 0. ( PosicionEnviar (AND) FilaActual ) = FilaActual?
Si ‐> Enviamos un 1
No‐> Enviamos un 0
Hardware para simulación:
80
Código:
81
82
83
Consideraciones para construcción real: Para la construcción real se debe agregar la parte de “potencia” dado que el PIC y según el registro de desplazamiento que se utilice no son capaces de manejar las corrientes necesarias, para ello se colocan transistores para el control de las filas y se agrega el ULN2803/2003 en serie a los registros de desplazamiento, una forma de hacerlo sería la siguiente:
Y cambiar rutina de efectivizar a:
84
Otra forma:
Otra forma Pero deben tener en cuenta que para encender un led se debe enviar al registro de desplazamiento un cero.
85
Lenguaje ensamblador desde cero Lo primero que hay que saber para este lenguaje, es que cada línea de código, es una instrucción que realiza el CP o un paso que realiza el CP. A diferencia de otros lenguajes, como por ejemplo el C, una línea de instrucción, puede llevar uno o más pasos que el CP debe realizar. Más adelante entenderán el porqué. A esto último dicho, nos explica el porqué de un programa escrito en ASM (assembler) lleva menos línea de instrucción que el mismo programa realizado en otro lenguaje. Esto se traduce que, cuanto menos líneas de código, menor consumo de la memoria de programa. Nota: Entendemos por el mismo código, al realizar un software para realizar una dicha tarea. Una tarea que debe realizar nuestro microcontrolador, puede ser escrito en diferentes lenguajes, y es el programa que se utilizó quien lo traduce a formato hex (1 y 0). Ahora, ¿Qué es el CP? El CP o PC es el Contador de Programa. Es el encargado de leer cada instrucción y realizar la acción solicitada por el software. Para dar un ejemplo un poco más claro, digamos que el CP, es una persona. Esta persona se le da un papel que tiene diferentes tareas a realizar. Estas tareas, están una debajo de la otra y lee línea por línea y hace lo que le dice esa línea. Cada línea, posee una instrucción que se debe hacer. Cuando terminó de hacer esa instrucción, continúa con la línea de abajo. Supongamos que tenemos un papel que dice:
Levantar la mano izquierda. Bajar la mano izquierda. Saltar tres veces en el mismo lugar. Levantar la mano derecha. Saltar una vez en el mismo lugar. Bajar la mano derecha.
El CP, hará esas tareas sin negarse y lo hará fielmente a lo que está escrito. Por lo que, si hace una tarea mal, es porque le pusimos una o más instrucciones mal. Si bien, el CP hará lo que nosotros le pidamos, el CP nos pide que respetemos algunas cosas. Estas cosas, dependerán de cada microcontrolador y que debemos saber para poder programar en forma correcta al PIC.
¿Cómo es la estructura del lenguaje ASM? Es muy fácil, posee 4 columnas bien diferenciadas y que no es problema acordarse. La primera columna se llama ETIQUETA y sirve para darle el nombre a una posición de la memoria del programa al que se necesita apuntar. Los que no se den cuenta, ya lo harán. La segunda columna, se llama INSTRUCCIÓN y lo que justamente hace, es una instrucción a realizar por el CP. La tercera columna, se llama OPERANDO y es el operando de una instrucción, o sea, de la segunda columna. Hay instrucciones que no tienen operando y las veremos más adelante.
Y la cuarta columna, se llama OBSERVACIONES y sirve solo para el programador, o sea, el usuario que está programando el PIC. En otras palabras, a nosotros. Siempre comienza con ; (punto y coma) Si no sirve para el PIC, ¿Para qué complicarla más? Todo lo contrario, es para ayudar al programador de que no se olvide que intentó hacer. Todos los lenguajes, desde los más básicos hasta los más avanzados, tienen esta características ya que en el pondremos que es lo que queremos hacer o explicar el programa. Si bien, estas observaciones se utilizan en la cuarta columna, no es obligación colocarla en esta columna, si no que puede ir al margen de la planilla en dónde estamos programando. Y es aquí dónde explicaremos la porción de una rutina para entenderlo más adelante. Bien, veremos a continuación, como se escriben las columnas: ETIQUETAS
INSTRUCCIÓN
OPERANDO
;OBSERVACIONES
O, podemos verlo así: ;OBSERVACIONES ;OBSERVACIONES ;OBSERVACIONES (y la cantidad que necesitemos) ETIQUETAS
INSTRUCCIÓN
OPERANDO
;OBSERVACIONES
Antes de empezar a ver las instrucciones, debemos concentrarnos en los registros.
¿Qué son los registros? Los registros, son posiciones de memoria el cual se utilizan para ir configurando el pic mientras se corre el programa, cambiar de bancos para acceder a otras partes de memorias, son banderas que nos van diciendo que está pasando con distintas operaciones, son habilitaciones o deshabilitaciones para módulos que trae el PIC. Por ejemplo, conversor Analógico/Digital, PWM. Estos registros, tienen una ancho de bit de acuerdo al PIC, que hay de 8 bit, 16 bit y 32 bit. Excepto la palabra configuración y que la veremos más adelante. El tamaño de los registro depende del tipo de microcontrolador. Cada registro, posee un nombre, y cada bit o un grupo de bit, se puede utilizar para lo descripto anteriormente (habilitar/deshabilitar, etc.). A continuación, veremos una posición de la RAM y sus 4 bancos. Este, es del PIC16F877
Como notarán, en las cuatro columnas, hay nombres. Estos nombres son todos los registros que posee este PIC. Verán que hay registros que se repiten. No quiere decir que están duplicados, triplicados o cuadriplicados, si no que se puede acceder a ellos no importa en qué banco estemos trabajando. (Ya lo entenderán, no se preocupen). Cada vez que se programe el PIC y se necesite trabajar con uno de estos registros, se debe acceder al banco que esté dicho nombre. Es por eso, que esta tabla es muy importante a la hora de trabajar.
Aprovechando que subí esta tabla del pic, cuando de ejemplos de programación, será sobre este pic. Microchip provee en forma gratuita estos Datasheet. Y hay uno por cada PIC. Por lo que si no tienen este Datasheet, descárguenlo de la página de Microchip. Cuando empecemos a programar, lo necesitarán. Uno de los registros muy utilizado, es el registro de trabajo W, el cual, se utiliza para mover un dato/valor de un registro a otro, cargar valores en un registro, y con la ayuda de la ALU puede hacer operaciones matemáticas. Observen con atención el diagrama de bloque dónde se encuentra W
Ahora bien, si W es un registro, ¿dónde está ubicado, ya que en el mapa de memoria no lo
encuentro? El registro W no está implementado en la memoria RAM, ni en la posición 0x00 ni en otra posición. W es un registro independiente que tiene un bus directo con la ALU (un camino privado). La ALU es la única entidad que puede leer o escribir este registro de trabajo. El direccionamiento indirecto hace uso de los registros FSR e INDF. INDF es completamente ajeno a W. Un direccionamiento indirecto usa el registro FSR como apuntador al contenido de otros registros. Cualquier instrucción que hace uso de INDF (0x00) como dirección invoca un direccionamiento indirecto. Los microcontroladores PIC, poseen tres tipos de memorias.
Memoria de Programa: Es la ubicación física dónde se guarda el firmware que hemos creado, o sea, nuestro programa. Y tiene un ciclo de 100.000 de lectura y/o escrituras antes de estropearse y es del tipo FLASH. Memoria de datos de uso general: Es la memoria RAM del PIC. Recordemos, que los registros están sobre la memoria RAM y la memoria de uso general, comienza después de los registros. Memoria de datos EEPROM: Puede almacenar datos más de 40 años sin energía y 1.000.000 de ciclos de escritura y lectura El PIC 16F877, tiene una memoria de programa de 8Kb por un ancho de 14 Byte, o sea que cada posición de memoria tiene 14 bytes (B'11111111111111', o H'3FFF') Cada línea de instrucción ocupa una posición en la memoria de programa, así que, podemos poner hasta 8.192 instrucciones. La RAM de uso general (más los registros) trae 368 bytes con un ancho de 8 bytes (B'11111111', o H'FF'). Esto quiere decir, que tenemos 368 posiciones para nuestro uso. La EEPROM trae 256 bytes con un ancho de 8 bytes. Tenemos 256 posiciones para nuestro uso. Ahora, vamos a estudiar el registro STATUS y luego continuaremos con las instrucciones.
Registro STATUS Si entendieron hasta acá, se acordará que los registros poseen un ancho de 8 bytes. Cada byte, puede contener un 1 o 0. Vemos ahora en detalle el registro STATUS:
Este registro, tiene 3 Bytes dedicado para las operaciones matemáticas, 3 bytes dedicado al cambio de banco de memoria y 2 bytes dedicado para saber qué o quién produjo un Power Up (despertar del micro). Y se puede leer y escribir en él (cambiar datos). Los analizamos desde el más significativo (MSB) hasta el menos significativo (LSB).
BIT 7: Se llama IRP y sirve para el direccionamiento indirecto para cambiar el banco de memoria. 1 = Banco 2 y 3 0 = Banco 0 y 1
BIT 6 y BIT 5 Se llaman RP1 y RP0 respectivamente. Sirve para el direccionamiento directo para cambiar de banco de memoria.
00 = Banco 0 01 = Banco 1 10 = Banco 2 11 = banco 3
BIT 4 Se llama TO (neg). Este bit se utiliza para saber quién despertó al PIC. 1 = Después que despierta (Power up) o por las instrucciones CLRWDT o SLEEP, se pone a 1 este bit. 0 = Se pone a 0 cuando el Watchdog o en castellano perro guardián (WDT) despierta al PIC.
BIT 3 Se llama PD (neg). Este bit se utiliza para saber si el pic estaba durmiendo. 1 = Después de que despierta (Power up) o por la instrucción CLRWDT, se pone a 1 0 = Se pone a 0 cuando se ejecuta la instrucción SLEEP
BIT 2 Se llama Z y al igual que los dos bytes anteriores, es una bandera. Nos indica el resultado de una operación aritmética y lógica. 1 = La operación aritmética o lógica dio como resultado 0 0 = La operación aritmética o lógica no dio como resultado 0
BIT 1 Se llama DC. Digit carry/borrow (dígito llevar/prestar). Es afectado por las instrucciones ADDWF; ADDLW; SUBLW; SUBWF (Para la resta, la polaridad es inversa). 1 = Hubo un acarreo del 4to bit menos significativo al 5to bit. 0 = No hubo un acarreo del 4to bit menos significativo al 5to bit.
BIT 0 Se llama C carry/borrow. Es afectado por las mismas instrucciones que afectan al bit DC. 1 = Hubo un acarreo del bit más significativo (Bit 7) o sea cuando se excede de H'FF' 0 = No hubo acarreo del bit más significativo
Breve introducción de las instrucciones Cada instrucción tiene un ancho de 14 Bits, es por eso que la memoria de programa tiene el mismo ancho. Justamente para poder alojar cada instrucción. Las instrucciones, están divididas en tres grupos. Los cuales son: Byte-Oriented operation (Byte-Orientando a la operación) Cada instrucción de este grupo está compuesta por: OPCODE (Código) DESTINATION (Destino) FILE REGISTER ADDRESS (Dirección del archivo de registro) El OPCODE o código, es el código de cada instrucción y que es única para cada instrucción. Está formada por los bit del 13 al 8.
El DESTINATION o el destino, indica en dónde se va a guardar el dato.Por ejemplo, si hacemos una suma, tenemos dos opciones dónde guardarlo, una puede ser el registro W y la otra opción puede ser otro registro cualquiera o una posición de la RAM. Está formada por el bit 7. La constante que nos indica esto es la letra d. Si esta letra es 0, la operación se guardará en el registro W. En cambio si vale 1, la operación se guardará en el registro o posición de memoria que estemos trabajando al momento de usar una instrucción. Hay instrucciones, como veremos más adelante, que no es necesario indicar dónde queremos guardar la operación, ya que se hace en forma automática. Y hay otras instrucciones que si no se indica el destino, nos puede dar un error al compilar o el compilador lo elegirá el y por ende, nos puede ejecutar mal el programa. Y por último, tenemos el FILE REGISTER ADDRESS que se carga con la dirección del registro a ser guardado. Está formada por los bit 6 al 0. La constante que nos indica esto, es la letra f Bit-Oriented operation (Bit-Orientando a la operación) Cada instrucción de este grupo está compuesta por: OPCODE (Código): igual al primer grupo. Está formado por los bits 13 al 10. BIT ADDRESS (Bit de dirección): Se utiliza para direccionar la operación. Está formado por los bits 9 al 7. Como pueden observar, se sacrificó bit del opcode para dárselo al bit address. La constante que nos indica esto es la letra b. FILE REGISTER ADDRESS (Dirección del archivo de registro): Es igual al primer grupo. Está formado por los bit 6 al 0. Igual que en el primer grupo la constante que nos indica esto es la letra f.
Literal and Control operation (Control y Literal de la operación) Cada instrucción de este grupo, está compuesta por: OPCODE: Es igual que en el primer grupo. Está compuesta por los bits 13 al 8. Excepto para las instrucciones CALL y GOTO que está compuesta por los bit 13 al 11 (prestar mucha atención a esto, cuando veamos estas dos instrucciones entenderán la importancia). LITERAL: Que puede ser un valor, por ejemplo para sumar, para restar, para cargar al registro W, en fin, un número decimal, binario o hexadecimal. O puede ser un valor de dirección a dónde apuntar para las instrucciones CALL y GOTO. Está compuesta por los bits 7 al 0. Excepto para las instrucciones CALL y GOTO que está compuesta por los bit 10 al 0 (prestar mucha atención a esto, cuando veamos estas dos instrucciones entenderán la importancia).
A continuación, vemos las 35 instrucciones agrupadas por los tres grupos:
Si alcanzan a ver en la imagen, verán que algunas instrucciones afectan al registro STATUS y otras no.
Las instrucciones Les voy a arruinar el momento de alegría. Las instrucciones hay que estudiarlas de memoria. Si, leyeron bien, de memoria. Lo que tienen que saber sobre las instrucciones, es como se escriben, que hace cada instrucción y lo más importante que bit del REGISTRO afecta. Vamos a ir viéndolo por orden alfabético. Y otra cosita más, como es de esperarse, están en INGLES o son abreviaturas pero en INGLES. Recordemos que: .123 o D'123' es en decimal; 0x7B o 7Bh o H'7B' es en Hexadecimal; B'01111011' es en binario.
ADDLW Suma un valor designado por el programador al registro W Código: ADDLW
.128
Si W tenía cargado un valor = .5, después de la instrucción W tiene cargado el valor .133 Para recordar, ADD es sumar, L es Literal y W es el registro W Afecta a: Z Se pone a 1 si la operación es 0 DC Se pone a 1 si hubo un acarreo del bit 3 al 4 C Se pone a 1 si hubo desbordamiento, o sea, cuando se supera H'FF'
ADDWF Suma el valor del registro W con el valor de un registro cualquiera. El destino de esta suma, lo elige el programador. Código: ADDWF
TEMP,W
Si W tenía guardado .133 y la posición de la RAM llamada TEMP tenía el valor cargado con .2, W vale .135 y TEMP continúa valiendo .2 Ahora si hubiera puesto así: Código: ADDWF
TEMP, F
TEMP valdría .135 y W valdría .133 Para recordar, F, es File Register Address. NOTA: Para indicar la dirección de dónde se guarda, también se puede poner 0 o 1 en vez de W o F. 0, corresponder guardarlo en el registro W y 1 en el registro TEMP (para este caso). Afecta a: Z Se pone a 1 si la operación es 0 DC Se pone a 1 si hubo un acarreo del bit 3 al 4 C Se pone a 1 si hubo desbordamiento, o sea, cuando se supera H'FF'
ANDWF Realiza la operación AND entre W y un registro designado por el programador. El destino de esta operación lo elige el programador. Código: ANDWF
TEMP, F
Si antes de la instrucción W vale B'11100011' y TEMP vale B'00111010' Después de la instrucción TEMP vale B'00100010' y W vale B'11100011' Afecta a: Z Se pone a 1 si la operación es 0
BCF Pone a 0 el bit de un registro. El bit debe ser indicado por el programador. Ejemplo: Código: BCF
TEMP, 2
Antes de la instrucción TEMP vale B'11111111'. Después de la instrucción TEMP vale B'11111011' Para recordar, Bit Clear es borrar File es archivo o registro No afecta ningún bit del registro Status.
BSF Pone a 1 el bit de un registro. El bit debe ser indicado por el programador: Código: BSF
TEMP, 0
Antes de la instrucción TEMP vale B'01110110'. Después de la instrucción TEMP vale B'01110111' Para recordar, Bit Set es poner a 1 File Archivo o registro No afecta a ningún Bit del registro Status.
BTFSC Salta un línea si el bit de un registro es cero. El bit debe ser indicado por el programador. Ejemplo: Código: BTFSC TEMP, 5 BCF
PORTA, 0
BSF
PORTB, 0
Caso 1: TEMP vale B'00011110'. El CP analizará solo el Bit 5 del registro TEMP, como es 0, salta la instrucción BCF PORTA, 0 y ejecuta la siguiente línea que es BSF PORTB, 0 y continua haciendo la instrucción. Caso 2: TEMP vale B'00111000'. El CP analizará solo el Bit 5 del registro TEMP, como es 1 no salta la instrucción y hará la instrucción BCF PORTA,0 y luego continua con la instrucción BSF PORTB,0 Para recordar Bit Test es chequear File Skip es salto Clear No afecta a ningún Bit del registro Status.
BTFSS Salta una línea si el bit de un registro es 1. EL bit debe ser indicado por el programador. Código: BTFSS TEMP, 3 ADDWF PORTC ANDWF NODO
Caso 1: TEMP vale B'01101100'. El CP analizará solo el Bit 3 del registro TEMP, como es 1, salta la instrucción ADD PORTC y ejecuta la siguiente línea que es ANDWF NODO y continúa haciendo la instrucción. Caso 2: TEMP vale B'11110000'. El CP analizará solo el Bit 3 del registro TEMP, como es 0 no salta la instrucción y hará la instrucción ADD PORTC y luego continúa con la instrucción AND NODO. Para recordar Bit Test es chequear File Skip es salto Set No afecta a ningún Bit del registro Status. Normalmente, continuando las instrucciones BTFSS y/o BTFSC va un GOTO o CALL pero no la he puesto porque aún no se explicaron estas instrucciones.
CALL Se dirige a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para dirigirse a una rutina o tarea. Su principal ventaja es que una vez que finalizó la tarea, vuelve al punto siguiente desde dónde se llamó. Código: CALL
ESC_PORTB
Para recordar, CALL es llamada. No afecta ningún bit del registro Status.
GOTO Se dirige a una dirección de la memoria de programa designado por el programador. En otras palabras, se utiliza para saltar instrucciones que no queremos que se ejecuten. A diferencia de la instrucción CALL, no hay forma de volver cuando se ejecuta la instrucción. Código: GOTO
INICIO
Para recordar GO TO es ir a. No afecta a ningún bit del registro Status.
Extendiendo la explicación… En la siguiente imagen, vemos el diagrama de bloques del PC o CP.
El PC es de 13 bits en este caso(8kwords). 14 son los bits de cada "word" o instrucción que se graban en cada posición de la FLASH (memoria de programa). El PC se reparte en: sus 8 bits de menor peso en el registro PCL, y los 5 restantes en el registro PCLATH. Los PICs al tener un set de instrucciones reducido no puede en una sola instrucción meter todos los bits necesarios para direccionar toda su memoria. EL program counter son 2 registros, el PChigh y PCLow. Cuando haces un CALL o un GOTO, solo se rellenan 11 bits (los 8 del PClow y 3 del PChigh) y los dos restantes los rellenas con el PCLATH (para completar los 13bits). El STACK (pila) tiene toda la dirección, no solo parcial. Si haces un call desde la página 0 a la página 3 y luego un return el código SI volverá a la página 0, pero el PCLATH sigue apuntando a la página 3, entonces si usas otro goto o call, debes tener en cuenta de modificar el PCLATH. Entonces, dijimos que: El PC = ProgramCounter o CP = Contador de Programa, tiene 13 bits; del 0 al 12. Al ejecutar un call o un goto, se copian del 0 al 10, y los otros 2 bits se copian del registro PCLATH. El pclath solo estará allí para esa situación. En un return o retfie la microelectrónica del PIC, pega la dirección del PC que estaba guardada. Lo vemos con un ejemplo 1. STACK = vacío PC = 0x00A0 PCLATH = 0b000011000 Ejecutas un CALL 0x230 2. El STACK tiene en su posición 0 la dirección 0x00A0. PC = 111000110000
3. Se ejecuta la subrutina y en ese punto el PC ya quedó en PC = 111000110111 4. Viene un RETURN. La microelectrónica del PIC copiará el stack tal cual en el program counter + 1 Valor STACK 0x00A0 + 1 --> PC = 0x00A1 5. EL código sigue ejecutándose en la página 0 pero hay que tener en cuenta que el PCLATH apunta a la página 3 por ello si harás otro CALL o GOTO, deberás cambiar de nuevo el PCLATH si la subrutina no está en la página 3. Vamos a entrar a todo detalle en el Program Counter(PC) para que se vayan todas las dudas ya que es muy importante. Vayamos al tema del PC, computed goto(lo que algunos llaman "tabla"), call, returns y goto. El Program Counter (PC) está conformado en esta familia de uC (y refiriéndonos a la familia 16F, las otras poseen más o menos bits implementados) por 13 bits repartidos entre dos registros: PCH y PCL. El PCL es legible/escribible directamente a través del registro físico PCL(valga la redundancia). En cambio, el PCH no es directamente accesible. No puede ser leído, y sólo puede ser grabado mediante un buffer que contiene el valor temporalmente (oh! aquí aparece nuestro famoso PCLATH). Entonces, recordar: El PCLATH es sólo un buffer temporal que almacena los 5 bits de mayor peso del PC para ser escritos cuando se ejecute una instrucción que lo requiera. Ahora, hay dos situaciones posibles en las que el PC debe ser cargado de manera distinta: una es cuando queremos trabajar con tablas y otra cuando realizamos un call o un goto que no esté en el mismo banco.
1era situación: Tabla(Comuted Goto) La tabla es una situación de uso del PC en la que se afecta directamente al registro PCL. Cuando se afecte directamente al PCL mediante una instrucción, es necesario que el usuario asegure que PCLATH tenga sus 5 bits pre-cargados adecuadamente. Hago un ejemplo:
Mal: org 0x000 movlw 0x01 call tabla
org 0x300 tabla addwf PCL,F retlw 0x03 retlw 0x01 retlw 0x0F
Bien: org 0x000 movlw 0x03 movwf PCLATH movlw 0x01 call tabla
org 0x300 tabla addwf PCL,F retlw 0x03 retlw 0x01 retlw 0x0F
Mejor: org 0x000 pageselw tabla movlw 0x01 call tabla
org 0x300 tabla addwf PCL,F retlw 0x03 retlw 0x01 retlw 0x0F
Pageselw es una instrucción del MPASM que genera dos instrucciones: un movlw literal y un movwf PCLATH. El valor del literal es automáticamente seleccionado por el ensamblador según la etiqueta(o posición de memoria) que se le especifique. En el caso anterior pageselw tabla generaría estas dos instrucciones:
movlw 0x03 movwf PCLATH Si no aseguramos que los 5 bits del PCLATH estén correctamente seteados al momento de afectar al PCL mediante alguna instrucción (generalmente es la addwf, pero puede usarse subwf y muchas otras) entonces el programa saltará a una posición indeseada.
2da situación: CALL y GOTO En esta familia de uC, cada instrucción es codificada en 14 bits. En el caso de las instrucciones CALL y GOTO, su estructura es la siguiente: F2 F1 F0 K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0 Donde las F indican cuál instrucción es la que debe ejecutarse (100 para la CALL 101 para la GOTO), y las k corresponden a la dirección a la cual queremos llamar (con un CALL) o saltar (con un GOTO). Aquí se ve claramente un problema. Podemos ver que un CALL o un GOTO sólo almacenan 11 bits de la dirección a la cual debe ir. 11 bits es un máximo 2048 posiciones. ¿Qué pasa cuando un uC posee más de 2k de memoria Flash entonces? Por ejemplo, un 16F877A posee 8k de memoria Flash. ¿Cómo haría para llamar a una subrutina que está más allá de la posición 2047 dela flash? La solución nuevamente se encuentra en el PCLATH(y es nuevamente el usuario el que tiene el deber de pre-cargar el valor adecuado). Entonces, dijimos que el PC contiene 13 bits de longitud. 13 bits son hasta 8kwords(una word es en esta familia un conjunto de 14 bits que conforman una instrucción la cual se aloja en la memoria FLASH del uC). Un CALL o un GOTO sólo contienen los 11 bits de menor peso de la dirección a la cual ir, por lo que los 2 bits restantes deberán ser pre-cargados en los bits 4 y 3 del registro PCLATH por el usuario programador. Cuando se ejecuta una instrucción CALL o GOTO, es imprescindible que el registro PCLATH esté correctamente precargado. La instrucción a la que el uC irá estará conformada por 13 bits y ellos serán: PCLATH, 4 PCLATH, 3 K10 K9 K8 K7 K6 K5 K4 K3 K2 K1 K0 Cabe mencionar que el uC carga a PC con el valor pasado por los 11 bits de K, y a PC con el valor de los bits PCLATH. El registro PCLATH no es modificado de ninguna manera. Sólo se leen esos dos bits. Por ejemplo, en un uC de 8kWords hay 4 páginas. Una página cada 2048 words. Si se está en una página y se quiere ir a otro es necesario precargar antes dichos bits del PCLATH para poder hacerlo. El usuario no debe preocuparse por precargar el PCLATH en dos situaciones: Si el uC no posee más de 2kWords de memoria Flash; O si en el código creado por el usuario, no se utiliza la memoria FLASH más allá de la posición 2047(0x7FF). Si ocurre al menos uno de esos dos casos, es suficiente con asegurar que los bits PCLATH se encuentren ambos en cero. Vamos con un par de ejemplos:
Mal:
Bien:
Mejor:
org 0x000 ;Esto es página0 call cruzo
org 0x000 ;Esto es página 0 movlw 0x08 movwf PCLATH call cruzo
org 0x000 ;Esto es página 0 pagesel cruzo ;automáticamente seleccionar banco call cruzo
org 0x800 ;Esto ya es el página1 cruzo retlw 0xFF org 0x800 ;Esto ya es el página 1 cruzo org 0x800 ;Esto ya es el página 1 retlw 0xFF cruzo retlw 0xFF
Pagesel es una instrucción del MPASM que genera dos instrucciones: un bcf/bsf PCLATH,3 y un bcf/bsf PCLATH,4. El software ensamblador selecciona automáticamente la instrucción bcf o bsf según el banco en el cual se encuentra la etiqueta(o posición de memoria) que se le especifique. En el caso anterior pagesel cruzo generaría estas dos instrucciones: bsf PCLATH, 3 bcf PCLATH, 4 Ya que la subrutina cruzo se encuentra en la página1. Finalmente, cuando se ejecuta una instrucción CALL, se carga en el STACK el valor de la posición actual más 1(es decir, se guarda en el STACK el valor PC+1). Se guardan los 13 bits, por lo que durante las instrucciones RETURN, RETLW y RETFIE no es necesario precargar al PCLATH. Para más información, ver el esquema sección 2.3 del Datasheet de los PIC16F87XA que habla de cómo cargar al PC según cada situación.
CLRF Borra el contenido de un registro seleccionado por el programador. La forma en que lo hace, pone en 0 los 8 bit del registro. Este registro, puede ser cualquiera de la posición de la RAM. Ejemplo: Código: CLRF
PORTB
Antes de la instrucción PORTB vale B'11000111'. Después de la instrucción PORTB vale B'00000000' Para recordar CLeaR es limpio File es archivo o registro. Afecta a:Z Se pone a 1
CLRW Borra al registro W. La forma en que lo hace, pone en 0 los 8 bit del registro. Ejemplo: Código: CLRW
Antes de la instrucción W vale B'00000111'. Después de la instrucción W vale B'00000000'.
Para recordar CLeaR es limpiar Work es trabajo. Afecta a:Z Se pone a 1
CLRWDT Borra al WDT. La forma en que lo hace, pone en 0 al mismo. Ejemplo: Código: CLRWDT
Antes de la instrucción WDT vale B'11111110'. Después de la instrucción vale B'0000000'. Para recordar CLeaR es limpiar Watchdog es perro guardián Timer es contador. Afecta a: TO (neg) Se pone a 1 PD (neg) Se pone a 1
NOTA: El WDT o el contador perro guardián, sirve para destrabar al PIC. Cada vez que se desborda, o sea, cada vez que pasa de H'FF' a H'00', produce un reset, y como es un reset, se dirige a la posición 0h de la memoria de programa. La forma de trabajar con él, es ir poniendo en lugares estratégicos la instrucción ya explicada, de esta manera evitamos el desborde del contador. Si el CP se traba en algún bucle o algo similar, al no limpiar el contador, el WDT desbordará y llevará al CP a la posición 0h de la memoria de programa. Muchas veces se evita de usar esta herramienta por no tener que calcular por todo el programa dónde y cuándo limpiar al WDT. Es recomendable su uso.
COMF Realiza el complemento de un registro. Código: COMF
TEMP, F
Si TEMP tenía guardado B'00111101' luego de ejecutar la instrucción TEMP vale B'11000010'. Nótese, que aquí también podemos elegir el destino y esto nos deja guardarlo en el registro W si así lo requerimos. Para recordar COMplement es complemento File es registro. Afecta a:Z Se pone a 1 si la operación da 0
DECF Decrementa en una unidad, o lo que es lo mismo, resta 1 el contenido de un registro Código: DECF
DECENA, W
Si antes de la instrucción DECENA vale .255, después de la instrucción W vale .254 y DECENA vale .255 Si por el contrario, hubiéramos elegido el destino F, después de la instrucción DECENA vale .254 Para recordar DECrement es decremento File es registro. Afecta a:Z se pone a 1 si la operación es 0
DECFSZ Decrementa en uno, o lo que es lo mismo, resta en 1 el contenido de un registro y cuando este vale 0, el CP salta una instrucción
Código: LOOP DECFSZ GOTO BCF
TEMP
LOOP PORTB, 0
El CP descontará en 1 el registro TEMP y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO LOOP, el cual se dirige de nuevo a la línea LOOP DECFSZ TEMP el cual volverá a descontar en 1 y evalúa el valor, si es cero salta la línea GOTO LOOP y ejecuta la instrucción BCF PORTB. Esta última línea, el programador pondrá la instrucción que necesite ejecutar. Este pequeño programa que acabamos de ver, es un temporizador o un retardo que tardará en salir del bucle dependiendo de la frecuencia de reloj y el valor cargado en TEMP. NOTA: Esta instrucción, también hay que elegirle el destino. En el caso que no se exprese, como en este caso, el MPLAB dará por sentado que el resultado se guardará en el registro F y no en W. Para recordar DECrement es decremento File es registro Skip es salto Zero que es cero. No afecta ningún bit del registro STATUS.
INCF Incrementa en 1, o suma 1, el contenido de un registro elegido por el programador. Código: INCF
INDF, F
Si antes de la instrucción INDF vale H'29', después de la instrucción INDF vale H'2A'. Nótese que también podemos elegir el destino. Si hubiéramos elegido W, después de la instrucción W vale H'2A' y INDF vale H'29'. Para recordar INCrement es incremento File es registro. Afecta a:Z se pone a 1 si el resultado es 0
INCFSZ Incrementa en 1, o suma en 1, el contenido de un registro elegido por el programador y cuando este es 0, el CP salta una instrucción. Código: VOLVER
INCFSZ
CONTADOR
GOTO
VOLVER
INCF
PORTA
El CP incrementará en 1 el registro CONTADOR y evalúa el valor, si no es cero, ejecuta línea siguiente que es GOTO VOLVER, el cual se dirige de nuevo a la línea VOLVER INCFSZ CONTADOR el cual volverá a incrementar en 1 y evalúa el valor, si es cero salta la línea GOTO VOLVER y ejecuta la instrucción INCF PORTA. Esta última línea, el programador pondrá la instrucción que necesite ejecutar. Este pequeño programa que acabamos de ver, es un temporizador o un retardo que tardará en salir del bucle dependiendo de la frecuencia de reloj y el valor cargado en CONTADOR. Normalmente, se utiliza el retardo con DECFSZ pero este también es válido. NOTA: Esta instrucción, también hay que elegirle el destino. En el caso que no se exprese, como en este caso, el MPLAB dará por sentado que el resultado se guardará en el registro F y no en W. Para recordar INCrement es incremento File es registro Skip es salto Zero es cero. No afecta ningún bit del registro STATUS.
IORLW Realiza la operación OR entre W y un literal elegido por el programador. El resultado se guarda en W. La operación es W OR L. Si antes de la instrucción W vale B'01110100' y el literal elegido es B'00011111', después de la instrucción W vale B'01111111'. Para recordar Inclusive es inclusivo OR es la operación binaria OR Literal es literal W es el registro trabajo. Afecta a:Z se pone a 1 si la operación da 0
IORWF Realiza la operación lógica OR entre el registro W y un registro elegido por el programador. La operación es W OR F. Código: IORWF
PORTC, F
Si antes de la instrucción W vale B'01111111' y PORC vale B'00001111' después de la instrucción PORTC vale B'01111111' y W vale B'01111111'. Nótese que podemos elegir el destino y la otra opción, como ya se dieron cuenta por las instrucciones pasadas, puede ser W. Para recordar Inclusive es inclusivo OR es la operación binaria OR W es el registro trabajo y File es registro. Afecta a: Z se pone a 1 si el resultado es 0
MOVLW Carga al registro W con un literal elegido por el programador para luego hacer una operación matemática o moverlo a otro registro como veremos más adelante. Sin duda alguna, una de las instrucciones más usadas en la programación ASM. Código: MOVLW
.255
Si antes de la instrucción W vale .15, después de la instrucción W vale .255. Para recordar MOVe es mover Literal es literal W es el registro trabajo. Como es de esperar, no afecta ningún bit del registro STATUS.
MOVF Mueve el contenido de un registro a otro registro elegido por el usuario. Código: MOVF
RETARDO, W
Si antes de la instrucción W vale H'2A' y RETARDO vale H'FF', después de la instrucción W vale H'FF'. Nótese que aquí podemos elegir el destino, y tenemos la posibilidad de elegir el destino al propio registro RETARDO. Al principio parece innecesario, pero se puede tomar como una verificación, ya que se ve afectado el registro STATUS bit Z. Para recordar MOVe es mover y File es registro. Afecta a:Z Se pone a 1 si la operación es 0 Anteriormente, habíamos dicho que esta instrucción se la puede tomar como verificación, para saber si se guardó con el mismo valor que tenía, el bit Z se pone a 1 si el valor es igual al que tenía cargado.
MOVWF Mueve el contenido del registro W a un registro cualquiera elegido por el programador. Sin duda alguna, esta instrucción, es otra muy usada en la programación ASM
Código: MOVWF
ADCON0
Si antes de la instrucción W vale B'10000001' y ADCON0 vale 0x0, después de la instrucción ADCON0 vale 0x81. Para recordar MOVe es mover W es el registro W y File es registro. No afecta ningún bit del registro STATUS.
NOP No realiza ninguna operación. Solo consume un ciclo de instrucción. Código: NOP
Para recordar No es no y OPeration es operación. No afecta ningún bit del registro STATUS.
RETFIE Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP antes de atender la interrupción. Al mismo tiempo, pone a 1 el bit GIE para activar de nuevo las interrupciones. Código: RETFIE
Para recordar RETurn es retornar From es de la IntErrupt es interrupción. No afecta ningún bit del registro STATUS.
RETLW Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP desde dónde se llamó a la subrutina y al retornar, lo hace con un literal cargado en W especificado por el programador. Esta instrucción, se utilizan en las tablas (para más detalle, ver la explicación del GOTO y CALL). Ejemplo: Código: RETLW
'L'
En este ejemplo, el MPLAB, carga en W el código ASCII correspondiente a la letra L Extendiendo el ejemplo: Código: PAGESELW MOVFW
TABLA contador
CALL
TABLA
CALL
LCD_DATO
;CONFIGURA AL PCLATH PARA VOLVER AL LUGAR CORRECTO ;CARGA A W LA POCICIÓN A LEER EN LA TABLA POR EJEMPLO 3 ;LLAMA A LA RUTINA TABLA ;LLAMA A LA RUTINA PARA MOSTRA AL LCD
NOP ; ; TABLA ADDWF
PCL,F
;SUMA AL PCL CON EL CONTENIDO DE W POR EJEMPLO 3
RETLW
'1'
;RETORNA CON 1 ASCII
RETLW
'2'
;RETORNA CON 2 ASCII
RETLW
'3'
;RETORNA CON 3 ASCII
RETLW
'T'
;RETORNA CON 4 ASCII
Este es un ejemplo sencillo de cómo utilizar RETLW. Para interpretar este código empezamos desde PAGESELW, supongamos que el CP está en esta instrucción (que está explicado que hace) luego pasa a la instrucción MOVFW contadory suponemos que tiene cargado 3 en decimal, por lo que W pasará a tener 3 en decimal. El CP continua con CALL TABLA, el CP saltará por encima a todas las demás instrucciones y se dirige a la etiqueta TABLA y ejecuta la instrucción ADDWF PCL,F En el código hablamos que le suma 3 al PCL, por lo que saltará al RETLW '3' cargando a W con el código ASCII 3. Retorna justo debajo del CALL TABLA, o sea retorna a CALL LCD_DATO y ejecuta la rutina correspondiente, cuando termina, regresa al NOP (que puede ser cualquier instrucción que necesite el programador. Si en cambio, contador hubiera tenido cargado 4 en decimal cuando llegue a la tabla y le sume al PCL este apuntará a RETLW 'T' cargando en W el código correspondiente ASCII. Para recordar RETurn es retornar Literal es literal W es el registro de trabajo W No afecta ningún bit del registro STATUS.
RETURN Carga al CP con el valor de la parte alta de la pila para volver al lugar dónde se encontraba el CP cuando se llamó a la rutina o subrutina. La diferencia con RETLW es que RETURN regresa sin cambiar a W. Este se utiliza para terminar una rutina y no se necesite ningún dato. Por ejemplo en la rutina CALL LCD_DATO no nos sirve que vuelva con ningún valor ya que es una rutina para enviar datos a un LCD, así que esta rutina tendrá implementada RETURN Por ejemplo: Código: RETURN
No afecta ningún bit del registro STATUS Para recordar RETURN es retornar.
RLF Rota hacia la izquierda los bit de un registro seleccionado por el programador. El destino de la operación se puede elegir. Cada rotación equivale a multiplicar por 2 si el bit C del registro STATUS es 0. Código: RLF
PORTC, F
Si antes de la instrucción PORTC vale B'00001000', después de la instrucción vale B'00010000'. Si se hubiera elegido como destino W, PORTC después de la instrucción continua valiendo B'00001000' y W vale B'00010000' Para recordar Rotate es rotar Left es izquierda File es el registro. Afecta a:C se pone a 1 si hubo acarreo
RRF Rota hacia la derecha los bits de un registro seleccionado por el programador. El destino de la operación se puede elegir. Cada rotación equivale a dividir por 2 si el bit C del registro STATUS es 0. Código: RRF
PORTB, F
Si antes de la instrucción PORTB vale B'10000000' después de la instrucción PORTB vale B'0100000'. Si se hubiera elegido como destino W, PORTB después de la instrucción continua valiendo
B'10000000' y W vale B'01000000' Para recordar Rotate es rotar Right es derecha File es el registro. Afecta a:C se pone a 1 si hubo acarreo
Extendiendo la explicación de las instrucciones RRF y RLF A la hora de utilizar estas dos instrucciones, hay que prestarle atención al bit C del registro STATUS. La razón de esto, es porque la rotación se hace a través del bit C. Supongamos que tenemos lo siguiente: BIT C = 0 TEMP = B'00010000' Ejecutamos la instrucción RRF y TEMP vale B'00001000'. O si ejecutamos la instrucción RLF TEMP vale B'00100000' Pero si ahora tenemos: BIT C = 1 TEMP = B'00010000' Ejecutamos la instrucción RRF y TEMP vale B'10001000'. O si ejecutamos la instrucción RLF TEMP vale B'00100001' Vemos como rota los bit dependiendo del valor del bit C. Pero anteriormente, habíamos dicho que estas dos instrucciones afectan al bit C. La actualización del bit C, lo hace después de la rotación. Lo vemos con un ejemplo: Código: MOVLW B’10001001’ MOVWF temp BCF STATUS, C RLF temp, F
; PONEMOS A 0 AL BIT C ; ROTAMOS A LA IZQUIERDA
Al ejecutar este programa, nos dará lo siguientes resultados: TEMP = B'00010010' BIT C = 1 Y para ver la diferencia vemos lo siguiente: Código: MOVLW B’00000001’ MOVWF temp BCF STATUS, C RLF temp, F
; PONEMOS A 0 AL BIT C ; ROTAMOS A LA IZQUIERDA
Al ejecutar este programa, nos dará lo siguientes resultados: TEMP = B'00000010' BIT C = 0
Algo que me había olvidado de mencionar pero que MIGSANTIAGO del foro de TODOPIC estuvo atento es que estás dos instrucciones, nos sirve para enviar datos en forma serial utilizando el bit C que lo veremos más adelante. Recordemos que, para utilizar estas instrucciones para multiplicar o dividir, debemos asegurarnos de que el bit C, esté en 0.
SLEEP Pone al microcontrolador en bajo consumo. Código: SLEEP
Para recordar SLEEP es dormir. Afecta a: TD se pone a 1 PD se pone a 1
SUBLW Resta el contenido de W con un literal de hasta 8 bit (.255). El resultado se guarda en W. Código: SUBLW
.20
Si antes de la instrucción W vale .23 después de la instrucción W vale .3 Para saber si el resultado es negativo o positivo, hay que chequear el bit C del registro Status. Si hay acarreo, el resultado es negativo, y por el contrario, si no hay acarreo es positivo. Para recordar SUBtraction es restar Literal es literal y W es el registro W. Afecta a: Z se pone a 1 si el resultado es 0 DC se pone a 0 si hay acarreo del bit del 4 al 5 bit del registro (recordemos que en la resta, es
distinto a la suma, por eso, se pone a 0 si hubo acarreo). C se pone a 0 si hubo acarreo (recordemos que en la resta, es distinto a la suma, por eso, se pone
a 0 si hubo acarreo).
SUBWF Resta el contenido de un registro seleccionado por el programador con el contenido del registro W. La fórmula es F - W = d. d es la dirección elegida por el programador en dónde se guardará el resultado que puede ser el registro W o el registro elegido por el programador. Código: SUBWF
MINUENDO, W
Si antes de la instrucción W vale .55 y MINUENDO vale .56, después de la instrucción, MINUENDO vale .56 y W vale .1 Para recordar SUBtraction es resta W es el registro W y File es el registro elegido. Afecta a: Z se pone a 1 si el resultado es 0 DC se pone a 0 si hubo un acarreo del 4 bit al 5 bit (recordemos que en la resta, es distinto a la
suma, por eso, se pone a 0 si hubo acarreo). C se pone a 0 si hubo acarreo del 7 bit. (recordemos que en la resta, es distinto a la suma, por eso,
se pone a 0 si hubo acarreo).
SWAPF Intercambia los bits de un mismo registro elegido por el programador. Los 4 bit de menor peso, pasan a ser lo 4 bits de mayor peso, y los 4 bits de mayor peso, pasan a ser los 4 bits de menor peso. El destino puede ser seleccionado. Cabe pensar que puede ser una instrucción de muy poco uso, pero todo lo contrario si se utilizan con las interrupciones. Microchips recomienda su utilización a la hora de salvar el contexto y restaurarlo en una interrupción ya que no modifica el registro STATUS. Cuando trabajemos con las interrupciones, se verá que es muy recomendable salvar el registro STATUS y W en la RAM para luego restaurarlos. Si utilizamos la instrucción MOVF, es afectado el bit Z, perdiendo su estado original en el momento de la interrupción. Esto se soluciona, utilizando la instrucción SWAPF. No se preocupen si no lo entiende por ahora. Lo entenderán cuando veamos ejemplo de interrupciones. Código SWAPF
STATUS, W
Si antes de la instrucción W bale H'55' y el registro STATUS vale B'00100100', después de la instrucción el registro STATUS vale H'24' y el registro W vale B'01000010' Para recordar SWAP es intercambiar NIBBLE es porción File es el registro. No afecta ningún bit del registro STATUS. XORLW Realiza la operación lógica XOR entre un literal o valor y el registro W. El resultado queda guardado en el registro W. Código: XORLW
B'11000101'
Si antes de la instrucción W vale B'11111000', después de la instrucción W vale B'00111101'. Para recordar XOR es la operación lógica XOR Literal es un valor W es el registro W. Afecta a:Z se pone a 1 si la operación es 0
XORWF Realiza la operación XOR entre un registro elegido por el programador y el registro W. La operación es F XOR W = d. El resultado se puede elegir dónde será guardado. Código: XORWF
PORTB, F
Si antes de la instrucción PORTB vale B11111110' y W vale B'00000001', después de la instrucción W vale .1 y PORTB vale B'11111111'. Para recordar XOR es la operación lógica XOR W es el registro W y File es el registro elegido. Afecta a:Z se pone a 1 si la operación es 0 Una vez que vimos todas las instrucciones y asumiendo que ya se la saben de memoria (tiempo tuvieron de sobra) y si no lo han hecho, es hora de hacerlo, empezamos a escribir programas muy sencillos. Pero, ¿cómo? Si se están haciendo esta pregunta, es hora de leer desde el primer mensaje. Tienen que acordarse las posiciones de las cuatro columnas. Recuerden que vamos a escribir los programas en el MPLAB, por lo que si aún no lo tienen, es hora de descargarlo de la página de Microchip en forma gratuita e instalarlo en la PC. Lamentablemente, este programa trabaja solo bajo Windows, por los que tienen Mac o Linux, no lo podrán hacer si no es por un emulador de Windows y aun así, no sé si funcione correctamente. Empezaremos bien desde el principio y para ello, voy a explicar cosas nuevas. Todo programa tiene un encabezado que se repite en cada programa nuevo y que varía según el PIC a utilizar y las
prestaciones del programa a escribir. Muchas de las personas crean plantillas nuevas para ahorrarse el trabajo. Yo soy una de ellas y aquí les doy una plantilla que pueden utilizar sin problemas y modificarlas según su necesidad. He aquí la plantilla que también está adjuntado. LIST P=16F877A INCLUDE __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC ; ;--------------------------------------------------------------;VARIABLES. ;--------------------------------------------------------------; CBLOCK H'20' ENDC ; RESET ORG H'00' GOTO INICIO ORG H'04' ;VECTOR INTERRUPCIÓN INTERRUP ; INICIO ; END
Primera línea: LIST P=16F877A Esta línea, sirve solo para el MPLAB y le indica que el PIC a utilizar es el 16F877A. Se debe actualizar según sea necesario.
Segunda línea: INCLUDE Esta línea sirve a la hora de compilar ya que dentro de este archivo que viene incluido dentro del MPLAB, le indica al ensamblador dónde están ubicados cada registro y el nombre de cada bits del PIC a utilizar así, nos ahorra de hacerlo nosotros mismos. Si desean abrir el archivo, lo pueden hacer con el notepad o cualquier editor de texto. Se debe actualizar según sea necesario.
Tercera línea: __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC Línea muy importante pero se puede omitir. Aquí se configura la palabra de configuración del PIC y que está en el Datasheet del pic correspondiente. En mi caso, yo la omito y luego lo configuro desde el ICPROG a la hora de pasar el archivo .hex al pic. Es por eso que esta línea está incompleta para el pic a utilizar pero completa para el PIC 16F84. Vemos en más detalle esta línea: __CONFIG Directiva para el ensamblador de que debe generar la palabra de configuración correspondiente de acuerdo a:
_CP_OFF la protección de lectura del PIC deshabilitado. _PWRTE_ON Reset de encendido habilitado _WDT_OFF Perro guardián deshabilitado. _XT_OSC Oscilador a cristal. El carácter& sirve para unir una directiva con otra.
Cuarta línea: ; ;--------------------------------------------------------------;VARIABLES. ;--------------------------------------------------------------; Este no es más que un pequeño encabezado. El ensamblador omitirá estas líneas y solo porque empieza con ";". Recuerden que, toda línea que empiece con ";" es comentario y el ensamblador la omitirá. Justo por debajo de este encabezado, ponemos las variables. ¿Aqué le llamo variables? Aposiciones de memorias o nombre de bit que yo quiera darle personalmente. Por ejemplo, dentro del archivo P16F877A.INC a la posición de memoria 0x05 le llama PORTA pero si yo quiero llamarlo PUERTO_A, es aquí dónde le indico al ensamblador. ¿Cómo se hace? Es muy fácil y con la instrucción "EQU" PUERTO_A EQU H’05’ Si al Bit RA6 lo quiero llamar LED6 lo hacemos así: LED6 EQU 6 Supongamos que quiero encender el LED 6 que está en el Puerto a bit 6, y este se activa con un 1 lógico, ponemos esta instrucción: BSF PUERTO_A, LED6 Y el ensamblador lo traducirá así: BSF 0X05, 0X06
Quinta línea: CBLOCK
H'20'
ENDC A estas dos líneas las unifique porque van juntas. Entre ellas van los nombres de cada posición de la RAM que nosotros queramos nombrar, o cada Registro que queramos renombrar. Recuerden que los registros están implementados en la RAM y que la posición de la RAM que puede utilizar el usuario para guardar cosas, empieza desde una posición que está indicada en el Datasheet. Para el caso de 16F877 empieza desde la posición 0x20 como lo ven en la figura siguiente.
La directiva CBLOCK H'20' indica que empezamos a nombrar posiciones de memoria desde la ubicación 0x20. Por ejemplo CBLOCK H’20’ temperatura demora1 demora2 ENDC La posición 0x20 se llama temperatura, la posición 0x21 se llama demora1 y así sucesivamente. También existe otra forma de nombrar o renombrar posiciones de memoria o registro, y ya lo vimos que es con la directiva EQU. Por ejemplo: temperatura EQU H'20' demora1 EQU H'21' demora2 EQU H'22'
Sexta línea: RESET ORG H'00' GOTO INICIO ORG H'04' ;VECTOR INTERRUPCIÓN INTERRUP He unificado estas líneas porque por lo general así se escribe en la mayoría de nuestros programas. Vemos con más detalles estas líneas. RESET ORG H’00’ Aquí es en dónde empieza el CP cuando se enciende al PIC. Y estos se debe, porque cuando hay un reset por el pin MCLR o un reset interno producido por, encendido del PIC o, por ejemplo, del perro guardián, el vector reset se ubica en la posición 0x00 de la memoria de programa. Como pueden apreciar, hemos llamado a la posición 0x00 con el nombre de RESET, ya que la primer columna es una etiqueta. Las etiquetas sirven para nombrar posiciones de memoria de programa. ORG H’00’ indica que se empieza a escribir desde la posición 0x00 por lo que la siguiente instrucción, quedará alojada en la posición 0x00. ORG es una directiva, y solo sirve para el ensamblador. Esta línea, SIEMPRE debe estar y no se puede modificar, salvo la etiqueta. Siguiente línea es GOTO INICIO, esta instrucción queda alojada en la posición 0x00 y es un salto a la etiqueta INICIO. Este salto tiene que estar, porque en las siguientes posiciones de memoria de programa, está la interrupción, externa e interna del PIC. Por eso es necesario hacer el salto. Siguiente línea nos encontramos con ORG H’04’, como se dieron cuenta, es una directiva. Esta posición de memoria, indica el vector de interrupción. Cuando estén habilitadas las interrupciones y una de ella se activa, el CP apuntará aquí, a la posición 0x04. Siguiente línea nos encontramos con la etiqueta INTERRUP. Si trabajamos con las interrupciones, aquí es donde escribiremos lo que necesitamos hacer con ellas.
Séptima línea: ; INICIO ; Vemos la etiqueta INICIO. Aquí vendrá el CP cuando haya un reset gracias al GOTO INICIO ubicado en la posición 0x00 de la memoria de programa. De esta manera, saltamos un montón de instrucciones que no debemos ejecutar antes. Como por ejemplo las interrupciones si hubiese. Por ende, a partir de esta etiqueta, estará nuestro programa principal. Configuraremos los puertos, las interrupciones y empezaremos a darle trabajo al CP.
Octava línea: END Esta es una directiva y solo sirve para el ensamblador indicando que después de esta directiva, se terminó el programa. Instrucciones que estén por debajo de esta línea, no serán tenido encuentra. Y esta directiva es obligatoria colocarla.
View more...
Comments