Instituto Politécnico Nacional Centro de Estudios científicos y Tecnológicos 9: “Juan de Dios Bátiz” Microelectrónica programable Practica 2: “MULTIPLEXADO “MANEJO DE UN TECLADO MATRICIAL DE 4X4”. (ACTUALIZACIÓN DEL TIEMPO). Alumno: Martinez Flores Rodrigo Aldebarán Profesor: Jesús Alberto Olivares Vargas Grupo: 6IM1 Objetivo: El alumno comprende el funcionamiento de un teclado matricial de 4x4, además de aprender a manipular el mismo. También adquiere el conocimiento para desarrollar un programa que permita manipular el tiempo de un reloj de tiempo real. Desarrollo Teórico de la practica 2: “MULTIPLEXADO “MANEJO DE UN TECLADO MATRICIAL DE 4X4”.(ACTUALIZACIÓN DEL TIEMPO). Un teclado matricial es un dispositivo que agrupa varios pulsadores y permite controlarlos empleando un número de conductores inferior al que necesitaríamos al usarlos de forma individual. Podemos emplear estos teclados como un controlador para un autómata o un procesador como Arduino. Estos dispositivos agrupan los pulsadores en filas y columnas formando una matriz, disposición que da lugar a su nombre. Es frecuente una disposición rectangular pura de NxM columnas, aunque otras disposiciones son igualmente posibles. Los teclados matriciales son frecuentes en electrónica e informática. De hecho, los teclados de ordenador normales son teclados matriciales, siendo un buen ejemplo de teclado matricial con disposición no rectangular. Una de las desventajas de usar un teclado matricial es que pueden causar problemas cuando se pulsa más de una tecla simultáneamente. Este es uno de los motivos por el que los teclados de ordenador usan una disposición no rectangular, agrupando ciertas teclas en circuitos diferentes (Ctrl, Alt, Shift...). ¿Cómo funciona un teclado matricial? Como ya mencioné anteriormente, un teclado matricial agrupa los pulsadores en filas y columnas formando una matriz, lo que permite emplear un número menor de conductores para determinar las pulsación de las teclas. La siguiente imagen muestra, a modo de ejemplo, una disposición rectangular de 4x4. Para detectar la pulsación de una tecla actuaremos de forma similar a la lectura simple de un pulsador. En resumen, ponemos a tierra un extremo del pulsador, y el otro lo conectamos a una entrada digital con una resistencia de pull-up. Para leer todas las teclas tendremos que hacer un barrido por filas. En primer lugar ponemos todas las filas a 5V, y definimos todas las columnas como entradas con resistencia de pull-up. Progresivamente ponemos una fila a 0V, y leemos las entradas de la columna. Una vez realizada la lectura volvemos a ponerla a 5V, pasamos a la siguiente fila, y volvemos a realizar el progreso hasta recorrer todas las filas. Para detectar NxM pulsadores necesitamos sólo N+M conductores. Por tanto ahorro de conductores es superior cuanto más grandes sean N y M, y más parecidos entre sí. (ej: 16 pulsadores en 2x8 necesitan 10 conductores, y en 4x4 sólo 8 conductores.) La mayor desventaja de la disposición matricial es que pueden dar problemas al detectar la pulsación de múltiples teclas de forma simultánea. Interrupciones Una interrupción es una suspensión temporal de la ejecución de un proceso, para pasar a ejecutar una subrutina de servicio de interrupción, la cual, por lo general, no forma parte del programa, sino que pertenece al sistema operativo o al BIOS. Una vez finalizada dicha subrutina, se reanuda la ejecución del programa. Las interrupciones son generadas por los dispositivos periféricos habilitando una señal del CPU (llamada IRQ del inglés "interrupt request") para solicitar atención de este. Por ejemplo. cuando un disco duro completa una lectura solicita atención al igual que cada vez que se presiona una tecla o se mueve el ratón. La primera técnica que se empleó para esto fue el polling, que consistía en que el propio procesador se encargara de sondear los dispositivos periféricos cada cierto tiempo para averiguar si tenía pendiente alguna comunicación para él. Este método presentaba el inconveniente de ser muy ineficiente, ya que el procesador consumía constantemente tiempo y recursos en realizar estas instrucciones de sondeo. El mecanismo de interrupciones fue la solución que permitió al procesador desentenderse de esta problemática, y delegar en el dispositivo periférico la responsabilidad de comunicarse con él cuando lo necesitara. El procesador, en este caso, no sondea a ningún dispositivo, sino que queda a la espera de que estos le avisen (le "interrumpan") cuando tengan algo que comunicarle (ya sea un evento, una transferencia de información, una condición de error, etc.). Con lo citado anteriormente se puede aseverar que las interrupciones dentro de la programación son software. Las interrupciones se utilizan: a) En la detección de eventos catastróficos para el sistema, tales como las fallas súbitas en las fuentes de alimentación. Por medio de una interrupción el microprocesador puede ser alertado a tiempo de preservar la información vital y desconectar los dispositivos que puedan dañarse. b) En dispositivos donde el microprocesador no necesita obtener información con mucha frecuencia. Por ejemplo, en sistemas de alarma, ya que el microprocesador no sabe cuándo va a aparecer una situación que requiera sus servicios. El dispositivo genera una interrupción cuando llega el momento, y mientras tanto el microprocesador puede ejecutar otras tareas. c) En la atención a dispositivos periféricos que son mucho más lentos que el microprocesador: teclados, pantallas, impresoras. En vez de que el microprocesador los espere, es más eficiente que los dispositivos lo interrumpan cuando tengan algo nuevo que enviar o estén listos para recibir. d) En el servicio de periféricos de alta velocidad (discos) o dispositivos para los cuales sea muy difícil mantener la información hasta el momento en que el microprocesador la solicite. Las interrupciones principalmente se clasifican en 2 tipos inhibibles y no inhibibles. Cuando se activa una entrada de INTERRUPCIÓN NO INHIBIBLE, el microprocesador siempre es interrumpido, es decir, la señal de interrupción es aceptada bajo cualquier condición, es por esto que las interrupciones no inhibibles son las mas apropiadas para manejar eventos catastróficos. Por otro lado, cuando se activa una entrada de INTERRUPCION INHIBIBLE, el microprocesador reconoce la interrupción solamente si esa entrada se encuentra habilitada. Las entradas de interrupción inhibibles se habilitan o inhabilitan bajo el control del programa. Si la entrada está inhabilitada, el microprocesador ignora la interrupción. En el caso particular del PIC 16F877A al producirse una interrupción, el PIC salta automáticamente a la dirección (0x0004) del vector de interrupción de la memoria de programa y ejecuta la porción de programa, correspondiente a la atención de la interrupción, hasta encontrar la instrucción RETFIE. Al encontrar dicha instrucción, abandona la interrupción y retorna a la posición de memoria del programa principal desde la que salto al producirse la interrupción. La familia PIC16F87XA tiene hasta 14 fuentes de interrupción. Posee un registro de control, INTCON que permite la habilitación de interrupciones y el manejo de los flags. La habilitación general se activa mediante el bit GIE (INTCON<7>), el cual es desactivado en el Reset, por lo tanto, hay que habilitarlo por programa. Timer 0 Los Timer o temporizadores son módulos integrados en el Pic que permite realizar cuentas tanto de eventos internos como externos. Cuando la cuenta es interna se habla de temporización (cuenta pulsos de reloj) y cuando la cuenta es externa se habla de contador. El bloque funcional Timer 0 es un contador (registro) de 8 bits, incrementado por hardware y programable. La cuenta máxima es de 255 (el incremento es constante e independiente). Los registros implicados en la configuración del TIMER 0 son los siguientes: • • • OPTION_REG: Configura el “hardware” del TIMER 0 o WDT. INTCON: Permite trabajar con la interrupción del TIMER 0. TRISA: Habilita el pin RA4 del puerto a. El tiempo de desbordamiento del Timer 0 se calcula según la siguiente ecuación. Tint = (256- Carga TMR0) *P.TCM Donde TCM es el ciclo de máquina que se puede calcular mediante la ecuación: T CM = (1/FOSC)*4 Tiempo mínimo. Si tmr0 = 255 y P = 2. Tint = (256- 255) * 2 * 0.000001 = 2 micro segundos. Tiempo máximo. Si tmr0 = 0 y P = 256. Tint = (256- 0) * 256 * 0.000001 = 65.5 mili segundos. LCD de 16 Caracteres por una línea Sigla del inglés Liquid Cristal Display, 'representación visual por cristal líquido', sistema que utilizan determinadas pantallas electrónicas para mostrar información visual. Cada píxel de un LCD típicamente consiste en una capa de moléculas alineadas entre dos electrodos transparentes, y dos filtros de polarización, los ejes de transmisión de cada uno que están (en la mayoría de los casos) perpendiculares entre sí. Sin cristal líquido entre el filtro polarizante, la luz que pasa por el primer filtro sería bloqueada por el segundo (cruzando) polarizador. La superficie de los electrodos que están en contacto con los materiales de cristal líquido es tratada a fin de ajustar las moléculas de cristal líquido en una dirección en particular. Este tratamiento suele ser normalmente aplicable en una fina capa de polímero que es unidireccionalmente frotada utilizando, por ejemplo, un paño. La dirección de la alineación de cristal líquido se define por la dirección de frotación. Antes de la aplicación de un campo eléctrico, la orientación de las moléculas de cristal líquido está determinada por la adaptación a las superficies. En un dispositivo twisted nematic, TN (uno de los dispositivos más comunes entre los de cristal líquido), las direcciones de alineación de la superficie de los dos electrodos son perpendiculares entre sí, y así se organizan las moléculas en una estructura helicoidal, o retorcida. Debido a que el material es de cristal líquido birrefringente, la luz que pasa a través de un filtro polarizante se gira por la hélice de cristal líquido que pasa a través de la capa de cristal líquido, lo que le permite pasar por el segundo filtro polarizado. La mitad de la luz incidente es absorbida por el primer filtro polarizante, pero por lo demás todo el montaje es transparente. Cuando se aplica un voltaje a través de los electrodos, una fuerza de giro orienta las moléculas de cristal líquido paralelas al campo eléctrico, que distorsiona la estructura helicoidal (esto se puede resistir gracias a las fuerzas elásticas desde que las moléculas están limitadas a las superficies). Esto reduce la rotación de la polarización de la luz incidente, y el dispositivo aparece gris. Si la tensión aplicada es lo suficientemente grande, las moléculas de cristal líquido en el centro de la capa son casi completamente desenrolladas y la polarización de la luz incidente no es rotada ya que pasa a través de la capa de cristal líquido. Esta luz será principalmente polarizada perpendicular al segundo filtro, y por eso será bloqueada y el pixel aparecerá negro. Por el control de la tensión aplicada a través de la capa de cristal líquido en cada píxel, la luz se puede permitir pasar a través de distintas cantidades, constituyéndose los diferentes tonos de gris. El efecto óptico de un dispositivo twisted nematic (TN) en el estado del voltaje es mucho menos dependiente de las variaciones de espesor del dispositivo que en el estado del voltaje de compensación. Debido a esto, estos dispositivos suelen usarse entre polarizadores cruzados de tal manera que parecen brillantes sin tensión (el ojo es mucho más sensible a las variaciones en el estado oscuro que en el brillante). Estos dispositivos también pueden funcionar en paralelo entre polarizadores, en cuyo caso la luz y la oscuridad son estados invertidos. La tensión de compensación en el estado oscuro de esta configuración aparece enrojecida debido a las pequeñas variaciones de espesor en todo el dispositivo. Tanto el material del cristal líquido como el de la capa de alineación contienen compuestos iónicos. Si un campo eléctrico de una determinada polaridad se aplica durante un período prolongado, este material iónico es atraído hacia la superficie y se degrada el rendimiento del dispositivo. Esto se intenta evitar, ya sea mediante la aplicación de una corriente alterna o por inversión de la polaridad del campo eléctrico que está dirigida al dispositivo (la respuesta de la capa de cristal líquido es idéntica, independientemente de la polaridad de los campos aplicados) Es un dispositivo LCD con un solo renglón de 16 caracteres, pero electrónicamente está dividido en dos secciones de 8 caracteres. Cada una de estas secciones se maneja como si fuese un renglón de 8 caracteres, es decir, si se desea escribir en la primera sección debe enviarse antes el comando es 80H y para escribir en la segunda sección, C0H. Descripción de los pines. 1.- GND Señal de referencia a tierra. 2.- VCC. Voltaje de alimentación 5 VCD. 3.- Vo. 4.- R/S. 5.- R/W. 6.- E. 7.- D0. 8.- D1. 9.- D2. 10.- D3. 11.- D4. 12.- D5. 13.- D6. 14.- D7. 15.- LED 16.- LED Voltaje de contraste. Modo de operación “0” lógico comandos o “1” lógico datos. “0” lógico LCD en modo escritura, “1” lógico LCD en modo lectura. Pin utilizado para ingresar a la LCD un comando o dato. Bit 0 bus de datos. Bit 1 bus de datos. Bit 2 bus de datos. Bit 3 bus de datos. Bit 4 bus de datos. Bit 5 bus de datos. Bit 6 bus de datos. Bit 7 bus de datos. Para inicializar la LCD se tienen que enviar los siguientes comandos. Colocar la LCD en modo comandos pin R/S en “0” lógico. Primer comando Segundo comando Tercer comando Cuarto comando Quinto comando 38h. 01h. 06h. 0Ch. 80h. Los dígitos en la memoria de la LCD se encuentran en las siguientes direcciones. Dig 1 Dirección 80h. Dig 2 Dirección 81h. Dig 3 Dirección 82h. Dig 4 Dirección 83h. Dig 5 Dirección 84h. Dig 6 Dirección 85h. Dig 7 Dirección 86h. Dig 8 Dirección 87h. Dig 9 Dirección C0h. Dig 10 Dirección C1h. Dig 11 Dirección C2h. Dig 12 Dirección C3h. Dig 13 Dirección C4h. Dig 14 Dirección C5h. Dig 15 Dirección C6h. Dig 16 Dirección C7h. Funcionamiento del hardware En la imagen podemos apreciar como el microcontrolador PIC16F877A está conectado directamente a la pantalla LCD mediante el puerto C, por otro lado, el teclado matricial también se conecta directamente al PIC mediante el puerto B. La ventaja de usar una LCD es que en cuestión de hardware se simplifica mucho, sin embargo, el software es más complicado. El funcionamiento del sistema desarrollado es el siguiente, el PIC tiene conectado conectado en su puerto C a las 8 terminales del LCD que controlarán los segmentos, este puerto es el encargado de enviar la información al display, permitiendo saber cuál es el mensaje que queremos mostrar, la LCD en sí ya es un circuito integrado que posee sus propios elementos, esto nos permite simplificar el circuito. Al display se conectan otros elementos, por ejemplo, el VCC, que da alimentación al display, el VSS que es la tierra del display y el VEE que permite modular la intensidad de los leds del display, motivo por el cual hay una resistencia variable conectada a esa terminal, a mayor resistencia menor será la intensidad de luz de la iluminación del panel, a menor resistencia la iluminación será más intensa. El teclado matricial se conecta directamente al puerto B del PIC, de los pines RB0 a RB3 se encargan de controlar las entradas del teclado matricial, mientras que de los pines RB4 a RB7 controlan las salidas, ahí es donde se ve rflejado el estado de las teclas, y de esta manera podremos saber que tecla se esta presionando. Funcionamiento del software Como se menciono anteriormente, el hardware del sistema es muy reducido y simple, pero en consecuencia a esto el software es mucho más complejo. La imagen anterior muestra cómo se establece la dirección de diferentes registros que se utilizarán, el contador 1, 2 y 3, aparte de las constantes para las unidades y decenas en el contador, también se declaran los buffers, esto solamente es declarar la posición en la que se encuentra el registro, mas no el valor que va a guardar. En este paso se definen las constantes que emplearemos durante el desarrollo del programa, entre estas destacan las teclas del teclado matricial de 4x4, eso nos permitirá saber que tecla se está seleccionando de cada fila, los valores se repiten para cada fila. Posterior a esto se definen las banderas de los registros Ahora debemos de asignar los valores en binario para definir cuales puertos del PIC actuaran como entradas o salidas, en este caso solo utilizaremos el puerto b como entrada y salida, es decir de RB0 a RB3 actuaran como salidas, mientras que de RB4 a RB7 serán entradas. Del puerto C se utiliza únicamente como salida, un pin del puerto A también será utilizado como salida. Para el puerto A utilizaremos 2 pines, los cuales utilizaremos para controlar el RS y E de la pantalla LCD. Vamos a utilizar los pines RA0 Y RA1, por lo que al definir el valor en binario del puerto pondremos ‘111100’ de tal forma que los dos primeros pines (del menos al más significativo) serán salidas, mientras que los otros 4 serán salidas ya que no los vamos a utilizar. Para el puerto B utilizaremos todos los pines, pero los primeros 4 los definiremos como salidas para asignar el valor de la fila que queremos leer, mientras que los 4 últimos serán entradas, que nos permitirán leer la columna en la que se está presionando la tecla. Podemos ver en la imagen que se define el valor del puerto como ‘11110000’. Podemos apreciar como el puerto C en la parte inferior tiene la instrucción de definir las constantes PROGC como ‘00000000’, lo que quiere decir que funcionarán como salidas. El puerto C y D no se utilizan, por lo que sus valores del puerto se definen como entradas, tal como se puede apreciar en la siguiente imagen. Para el puerto A utilizaremos 2 pines, los cuales utilizaremos para controlar el RS y E de la pantalla LCD. Vamos a utilizar los pines RA0 Y RA1, por lo que al definir el valor en binario del puerto pondremos ‘111100’ de tal forma que los dos primeros pines (del menos al más significativo) serán salidas, mientras que los otros 4 serán salidas ya que no los vamos a utilizar. Para el puerto B utilizaremos todos los pines, pero los primeros 4 los definiremos como salidas para asignar el valor de la fila que queremos leer, mientras que los 4 últimos serán entradas, que nos permitirán leer la columna en la que se está presionando la tecla. Podemos ver en la imagen que se define el valor del puerto como ‘11110000’. Podemos apreciar como el puerto C en la parte inferior tiene la instrucción de definir las constantes PROGC como ‘00000000’, lo que quiere decir que funcionarán como salidas. El puerto C y D no se utilizan, por lo que sus valores del puerto se definen como entradas, tal como se puede apreciar en la imagen anterior. Una vez fueron definidos los puertos, se escriben las subrutinas de vector reset y vector de interrupción. En el vector de interrupción se definen los valores de registros que nos permitirán llevar a cabo el conteo mediante hardware. El vector de interrupción antes de realizar la cuenta de los registros hace un respaldo de todos los valores encontrados en algunos registros especiales, como el STATUS, el PCLATH, y el W. En la segunda parte de esta subrutina se restauran los valores iniciales y se regresa al programa principal, gracias al principio de pila que se definió en la parte teórica. Posteriormente se escribe la subrutina de interrupciones, en esta subrutina se incrementa el contador de milisegundos, aparte de que se comprueba que el valor del registro se haya alcanzado, esta subrutina nos permitirá conocer el momento en el que se completó el segundo. Se definen valores de los prescaladores para conseguir el tiempo que queremos. Esta subrutina se complementa con la que se definió anteriormente. Posteriormente está la subrutina de inicio del PIC, en este se definen el valor de algunos registros y banderas. Se cargan a los registros especiales que manejan el comportamiento de los pines los estados de cada uno de los puertos, salidas y entradas. En el programa principal tenemos algunas subrutinas que es importante definir, la primera subrutina es “prog_ini”, esta llama a la subrutina de inicio que se definió anteriormente. La segunda subrutina es la “ini_LCD” esta permite cargar los valores necesarios a la LCD para inicializarla y para que funcione correctamente, es importante llevar a cabo esta subrutina para poder visualizar correctamente los datos en la LCD. Empieza el loop principal y tenemos varias subrutinas, la subrutina “muestra time”, seguido de la subrutina encargada de contar el tiempo. Tenemos la subrutina nueva de “barre teclado” y otra subrutina nueva llamada “muestra tecla. Subrutina de muestra de tiempo, nos permite visualizar los valores en el display en formato militar, un reloj centrado en el que tenemos formato de 24 horas, separadas por dos puntos “:”. Podemos ver como muestra las variables “dechor”, “unihor”, “decmin”, etc. Estas variables hacen referencia a las decenas de horas, unidades de horas y así sucesivamente hasta las unidades de segundo. Tenemos la subrutina de barrido de teclado, esta nos permitirá leer que tecla fue presionada, este valor de la tecla será guardado en el registro ‘var_tecopri’, el cual utilizaremos para comprobar que teclas fueron presionadas en otras subrutinas. Posteriormente tenemos la subrutina de actualización de tiempo, esta nos permite modificar el tiempo utilizando el teclado matricial una vez que fue presionada la tecla “A” del teclado matricial, después de esto debemos ingresar los valores al teclado y una vez tengamos el tiempo actualizado presionamos la tecla “B” para continuar con el conteo. Después tenemos la subrutina que revisa la tecla, esta se encarga de comprobar que la tecla presionada no sea la “A”, para que una vez sea presionada se inicie la subrutina de actualización de tiempo. Después de esta subrutina sigue una subrutina de espera de int. De 1 segundo, esta permite utilizando el TMR0 hacer la cuenta de 1 segundo para el reloj. Después de esta subrutina de retardo tenemos la subrutina encargada de contar en el reloj, apoyándose de la subrutina de retardo de 1 segundo que se definió anteriormente. Tenemos finalmente varias subrutinas, entre las que se encuentran subrutinas de inicio del LCD, y varias subrutinas de retardo de milisegundos. Desarrollo practico de la practica 2: “MULTIPLEXADO “MANEJO DE UN TECLADO MATRICIAL DE 4X4”.(ACTUALIZACIÓN DEL TIEMPO). Mediciones realizadas con voltímetro en el bus de datos del PIC 16F877 A Se tienen valores lógicos de 0 y 1. Puerto A del microcontrolador Se observa que la terminal RS toma valores lógicos de 0 y 1. Mientras que la terminal Enable siempre presenta 5 volts, para que se pueda activar la LCD. Puerto B del microcontrolador Cuando recibe un 0 lógico nos refleja un valor de 0 volts, mientras que cuando recibe un 1 arroja un valor de 5 volts. Mediciones con osciloscopio en el bus de datos Mediciones realizadas con el osciloscopio en el puerto A Diagrama esquematico de la practica 2: “MULTIPLEXADO “MANEJO DE UN TECLADO MATRICIAL DE 4X4(ACTUALIZACIÓN DEL TIEMPO).” Diagrama de Flujo Codigo Fuente de la practica 2: “MULTIPLEXADO “MANEJO DE UN TECLADOMATRICIAL DE 4X4”.(ACTUALIZACIÓN DEL TIEMPO). ;INSTITUTO POLITECNICO NACIONAL. ;CECYT 9 JUAN DE DIOS BATIZ. ; ;PRACTICA 1 ;TIMER 0 “MANEJO DE UNA PANTALLA LCD” ; ;GRUPO: 6IM1 . ; ;INTEGRANTE: Martinez Flores Rodrigo Aldebaran ; ;Este programa nos permite manipular un reloj de tiempo real mostrado en una LCD de 16x1 mediante un teclado matricial de 4x4 ;------------------------------------------------------------------------------------------------------------------list p=16f877A; ;#include "C:\Program Files (x86)\Microchip\MPASM Suite\P16F877A.INC"; #include "C:\Program Files (x86)\Microchip\MPASM Suite\P16F877A.INC"; ;Bits de configuracion. __config _XT_OSC & _WDT_OFF & _PWRTE_ON & _BODEN_OFF & _LVP_OFF & _CP_OFF; ALL ;------------------------------------------------------------------------------------------------------------------; ;fosc=4 MHz. ;Ciclo de trabajo del PIC = (1/fosc)*4 = 1 µs. ;t int:= (256 - R) * (P) * ((1/3579545)*4) = 1.0012ms // Tiempo de interrupcion ; R = 249, P = 128 ;Frec int = 1 / t int = 874 Hz. ;------------------------------------------------------------------------------------------------------------;Definicion de las variables del programa en RAM. resp_w equ 0x20; resp_status equ 0x21; res_pclath equ 0x22; res_fsr equ 0x23; presc_1 equ 0x24; .001 100 5 presc_2 equ 0x25; t int = t intb * presc_1 * presc_2 banderas equ 0x26; cont_milis equ 0x27; select_time equ 0x28; uniseg equ 0x29; decseg unimin decmin unihor dechor cta_24 Var_teclado Var_tecopri Contador1 Contador2 Contador3 bannar banperr test temporal_num equ 0x30; equ 0x31; equ 0x32; equ 0x33; equ 0x34; equ 0x35; equ 0x35; equ 0x36; equ 0x37; equ 0x38; equ 0x39; equ 0x40; equ 0x41; equ 0x42; equ 0x41; ;Constantes. M N L equ equ equ .3; .255; .255; No_haytecla equ 0XF0; Tec_1 Tec_2 Tec_3 Tec_div Tec_4 Tec_5 Tec_6 Tec_B Tec_7 Tec_8 Tec_9 Tec_c Tec_clear Tec_0 Tec_igual Tec_suma equ equ equ 0XE0; 0XD0; 0XB0; equ equ equ equ equ equ equ equ equ equ equ equ equ 0X70; 0XE0; 0XD0; 0XB0; 0X70; 0XE0; 0XD0; 0XB0; 0X70; 0XE0; 0XD0; 0XB0; 0X70; ;------------------------------------------------------------------------------------------------------------;Banderas del registro banderas ban_int equ .0; El valor .0 es asignado a bandera int, bandera utilizada para comunicar la subrutina de interrupcion con el programa principal sin_bd1 equ .1; sin_bd2 equ .2; sin_bd3 equ .3; sin_bd4 equ .4; sin_bd5 equ .5; sin_bd6 equ .6; sin_bd7 equ .7; bannab equ .7; banperb equ .7; ;------------------------------------------------------------------------------------------------------------- ; Asignación de los puertos de I/0. ; Puerto A. RS_LCD equ .0;Pin de la salida modo comandos o modo datos de la LCD Enable_LCD equ .1;Pin de salida de Enable LCD Sin_UsoRA2 equ .2;sin uso Sin_UsoRA3 equ .3;sin uso Sin_UsoRA4 equ .4;sin uso Sin_UsoRA5 equ .5;sin uso progA ;Puerto B. Act_Ren1 Act_Ren2 Act_Ren3 Act_Ren4 Col_1 Col_2 Col_3 Col_4 progb ;Puerto C. Bit_D0LCD Bit_D1LCD Bit_D2LCD Bit_D3LCD Bit_D4LCD Bit_D5LCD Bit_D6LCD equ b'111100';Def. la config. de los bits del pto. a. equ .0;Pin de salida para activar el renglón 1 del teclado. equ .1;Pin de salida para activar el renglón 2 del teclado. equ .2;Pin de salida para activar el renglón 3 del teclado. equ .3;Pin de salida para activar el renglón 4 del teclado. equ .4;Pin de entrada para leer el código de la tecla oprimida. equ .5;Pin de entrada para leer el código de la tecla oprimida. equ .6;Pin de entrada para leer el código de la tecla oprimida. equ .7;Pin de entrada para leer el código de la tecla oprimida. equ b'11110000'; // Programación inicial del puerto B. equ equ equ equ equ equ equ .0;Bit 0 de datos o comandos para la LCD .1;Bit 1 de datos o comandos para la LCD .2;Bit 2 de datos o comandos para la LCD .3;Bit 3 de datos o comandos para la LCD .4;Bit 4 de datos o comandos para la LCD .5;Bit 5 de datos o comandos para la LCD .6;Bit 6 de datos o comandos para la LCD Bit_D7LCD progc ;Puerto D. Sin_UsoRD0 Sin_UsoRD1 Sin_UsoRD2 Sin_UsoRD3 Sin_UsoRD4 Sin_UsoRD5 Sin_UsoRD6 Sin_UsoRD7 progD ; Puerto E. Sin_UsoRE0 Sin_UsoRE1 Sin_UsoRE2 progE equ .7;Bit 7 de datos o comandos para la LCD equ b'00000000'; // Programación inicial del puerto C como equ equ equ equ equ equ equ equ .0;sin uso .1;sin uso .2;sin uso .3;sin uso .4;sin uso .5;sin uso .6;sin uso .7;sin uso equ B'11111111';Programación inicial del puerto D como equ equ equ .0;sin uso .1;sin uso .2;sin uso equ B'111';Programación inicial del puerto E como ;-----------------------------------------------------------------------------------------------------------------------------------------------------;==================== ;== Vector Reset == ;==================== org 0x0000; vec_reset clrf PCLATH; Borra el contenido del registro pclath. goto prog_prin; Salto incondicional a la etiqueta prog_prin ;-----------------------------------------------------------------------------------------------------------------------------------------------------;============================== ;== Vector de Interrupcion == ;============================== org 0x0004; Las instrucciones del código fuente que siguen a esta directiva se ensamblan a partir de la posición indicada por 0x0004 vec_int movwf resp_w; respalda el estado del registro w movf status,w; movwf resp_status; respalda banderas de la alu clrf status; movf pclath,w; movwf res_pclath; clrf pclath; movf fsr,w; movwf res_fsr; btfsc intcon,tmr0if; call rutina_int; sal_int movlw .131; movwf tmr0; movf res_fsr,w; movwf fsr; movf res_pclath,w; movwf pclath; movf resp_status,w; movwf status; nop; retfie; ;------------------------------------------------------------------------------------------------------------;===================================== ;== Subrutina de Interrupciones ==== ;===================================== rutina_int incf cont_milis,f; incf presc_1,f; movlw .100; xorwf presc_1,w; btfsc status,z; goto sig_int; goto sal_rutint; sig_int clrf presc_1; incf presc_2,f; movlw .10; xorwf presc_2,w; btfss status,z; goto sal_rutint; clrf presc_1; clrf presc_2; sal_rutext bsf banderas,ban_int; sal_rutint bcf intcon,t0if; return; ;------------------------------------------------------------------------------------------------------------;============================================== ;== Subrutina de Inicio de regreso del PIC == ;============================================== prog_ini bsf status,rp0; Ponte en el banco 1 de RAM. movlw 0x02; Asigna un prescalador de 8 al tmr0 y deshabilita pull-up movwf option_reg ^0x80; movlw proga; movwf trisa ^0x80; movlw progb; movwf trisb ^0x80; movlw progc; movwf trisc ^0x80; movlw progd; movwf trisd ^0x80; movlw proge; movwf trise ^0x80; movlw 0x06; Configura puertos A y E como pines digitales movwf adcon1 ^0x80; bcf status,rp0; Regresa al banco 0 de RAM. movlw 0xa0; Borra las banderas y habilita las interrupciones globales e interrupcion de tmr0 por desbordamiento movwf intcon; movlw .131; movwf tmr0; clrf banderas; clrf portc; movlw 0x03; movwf porta; movlw 0x0F; movwf portb; clrf resp_w; inicializa la variable a 0 clrf resp_status; clrf res_pclath; clrf res_fsr; clrf presc_1; clrf presc_2; clrf banderas; clrf cont_milis; clrf uniseg; clrf decseg; clrf unimin; clrf decmin; clrf unihor; clrf dechor; clrf cta_24; clrf select_time; clrf var_Tecopri; clrf banperr; return; ;------------------------------------------------------------------------------------------------------------;========================== ;== Programa Principal == ;========================== prog_prin call prog_ini; call ini_lcd; llamada a la subrutina de inicializacion de la LCD loop_prin call muestra_time; call count_time1; call barre_teclado; call muestra_tecla; goto loop_prin; ;------------------------------------------------------------------------------------------------ ;------------------------------------------------------------------------------------------------------------;====================================== ;== Subrutina de muestra de tiempo == ;====================================== muestra_time call esp_int; bcf porta,RS_LCD; movlw 0x80; movwf portc; call pulso_enable; bsf porta,RS_LCD; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw 0x30; addwf dechor,w; movwf portc; call pulso_enable; movlw 0x30; addwf unihor,w; movwf portc; call pulso_enable; movlw ':'; movwf portc; call pulso_enable; movlw 0x30; addwf decmin,w; movwf portc; call pulso_enable; movlw 0x30; addwf unimin,w; movwf portc; call pulso_enable; movlw ':'; movwf portc; call pulso_enable; movlw 0x30; addwf decseg,w; movwf portc; call pulso_enable; movlw 0x30; addwf uniseg,w; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; movlw ' '; movwf portc; call pulso_enable; return; ;------------------------------------------------------------------------------------------------ ;-------------------------------------------------------------------------------------------------- ;====================================== ;== Subrutina de barrido de teclado == ;====================================== barre_teclado bcf bannar,bannab; bsf portb,Act_Ren4; nop; bcf portb,Act_Ren1; movf portb,w; movwf Var_teclado; movlw 0xF0; andwf Var_teclado,f; movlw No_haytecla; subwf Var_teclado,w; btfsc status,Z; goto sig_Ran2; movlw Tec_7; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec7 movlw Tec_8; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec8; movlw Tec_9; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec9; movlw Tec_div; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tecdiv; sig_Ran2 bsf portb,Act_Ren1; nop; bcf portb,Act_Ren2; movf portb,w; movwf Var_teclado; movlw 0xF0; andwf Var_teclado,f; movlw No_haytecla; subwf Var_teclado,w; btfsc status,Z; goto sig_Ran3; movlw Tec_4; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec4; movlw Tec_5; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec5; movlw Tec_6; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec6; movlw Tec_B; subwf Var_teclado,w; btfsc status,Z; goto Fue_TecB; sig_Ran3 bsf portb,Act_Ren2; nop; bcf portb,Act_Ren3; movf portb,w; movwf Var_teclado; movlw 0xF0; andwf Var_teclado,f; movlw No_haytecla; subwf Var_teclado,w; btfsc status,Z; goto sig_Ran4; movlw Tec_1; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec1; movlw Tec_2; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec2; movlw Tec_3; subwf Var_teclado,w; btfsc status,Z; goto Fue_Tec3; movlw Tec_C; subwf Var_teclado,w; btfsc status,Z; goto Fue_TecC; sig_Ran4 bsf portb,Act_Ren3; nop; bcf portb,Act_Ren4; movf portb,w; movwf Var_teclado; movlw 0xF0; andwf Var_teclado,f; movlw No_haytecla; xorwf Var_teclado,w; btfsc status,Z; goto Fue_Tecnada; movlw Tec_clear; xorwf Var_teclado,w; btfsc status,Z; goto Fue_TecClear; movlw Tec_0; xorwf Var_teclado,w; btfsc status,Z; goto Fue_Tec0; movlw Tec_igual; xorwf Var_teclado,w; btfsc status,Z; goto Fue_Tecigual; movlw Tec_suma; xorwf Var_teclado,w; btfsc status,Z; goto Fue_Tecsuma; Fue_Tecnada movlw ' '; movwf Var_tecopri; bsf bannar,bannab; goto sal_barretec; Fue_Tec0 movlw .0; movwf Var_tecopri; goto sal_barretec; Fue_Tec1 movlw .1; movwf Var_tecopri; goto sal_barretec; Fue_Tec2 movlw .2; movwf Var_tecopri; goto sal_barretec; Fue_Tec3 movlw .3; movwf Var_tecopri; goto sal_barretec; Fue_Tec4 movlw .4; movwf Var_tecopri; goto sal_barretec; Fue_Tec5 movlw .5; movwf Var_tecopri; goto sal_barretec; Fue_Tec6 movlw .6; movwf Var_tecopri; goto sal_barretec; Fue_Tec7 movlw .7; movwf Var_tecopri; goto sal_barretec; Fue_Tec8 movlw .8; movwf Var_tecopri; goto sal_barretec; Fue_Tec9 movlw .9; movwf Var_tecopri; goto sal_barretec; Fue_Tecdiv movlw 'A'; movwf Var_tecopri; goto sal_barretec; Fue_TecB movlw 'B'; movwf Var_tecopri; goto sal_barretec; Fue_TecC movlw 'C'; movwf Var_tecopri; goto sal_barretec; Fue_Tecsuma movlw '+'; movwf Var_tecopri; goto sal_barretec; Fue_TecClear movlw '*'; movwf Var_tecopri; goto sal_barretec; Fue_Tecigual movlw '='; movwf Var_tecopri; sal_barretec return; ;-------------------------------------------------------------------------------------------------;============================================ ;== Subrutina para actualizar el tiempo == ;============================================ actualizar nop; call ret_255ms call ret_255ms call ret_255ms repetir call barre_teclado; btfsc bannar,bannab; goto repetir; bcf banperr,banperb; call compvr0; call compvr1; call compvr2; btfss banperr,banperb; goto repetir; bcf banperr,banperb; movf Var_tecopri,w; movwf dechor; call muestra_time; call ret_40ms call ret_40ms repetir1 call barre_teclado; btfsc bannar,bannab; goto repetir1; bcf banperr,banperb; movlw .2; xorwf dechor,test; btfss status,z; goto todas; goto ceroatres; todas call compvr0; call compvr1; call compvr2; call compvr3; call compvr4; call compvr5; call compvr6; call compvr7; call compvr8; call compvr9; goto salir2; ceroatres call compvr0; call compvr1; call compvr2; call compvr3; goto salir2; salir2 btfss banperr,banperb; goto repetir; bcf banperr,banperb; movf Var_tecopri,w; movwf unihor; call muestra_time; call ret_40ms call ret_40ms repetir2 call barre_teclado; btfsc bannar,bannab; goto repetir2; bcf banperr,banperb; call compvr0; call compvr1; call compvr2; call compvr3; call compvr4; call compvr5; btfss banperr,banperb; goto repetir2; bcf banperr,banperb; movf Var_tecopri,w; movwf decmin; call muestra_time; call ret_40ms call ret_40ms repetir3 call barre_teclado; btfsc bannar,bannab; goto repetir3; bcf banperr,banperb; call compvr0; call compvr1; call compvr2; call compvr3; call compvr4; call compvr5; call compvr6; call compvr7; call compvr8; call compvr9; btfss banperr,banperb; goto repetir3; bcf banperr,banperb; movf Var_tecopri,w; movwf unimin; call muestra_time; call ret_40ms call ret_40ms repetir4 call barre_teclado; btfsc bannar,bannab; goto repetir4; bcf banperr,banperb; call compvr0; call compvr1; call compvr2; call compvr3; call compvr4; call compvr5; btfss banperr,banperb; goto repetir4; bcf banperr,banperb; movf Var_tecopri,w; movwf decseg; call muestra_time; call ret_40ms call ret_40ms repetir5 call barre_teclado; btfsc bannar,bannab; goto repetir5; bcf banperr,banperb; call compvr0; call compvr1; call compvr2; call compvr3; call compvr4; call compvr5; call compvr6; call compvr7; call compvr8; call compvr9; btfss banperr,banperb; goto repetir5; bcf banperr,banperb; movf Var_tecopri,w; movwf uniseg; call muestra_time; call ret_40ms call ret_40ms actualizar1 call barre_teclado; movlw 'B'; xorwf Var_tecopri,w; btfsc status,Z; goto salir; goto actualizar1; salir return; ;-------------------------------------------------------------------------------------------------;-------------------------------------------------------------------------------------------------;====================================== ;== Subrutina que comprueba validez == ;====================================== compvr0 ;bsf banperr,banperb; movlw .0; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr1 ;bsf banperr,banperb; movlw .1; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr2 ;bsf banperr,banperb; movlw .2; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr3 ;bsf banperr,banperb; movlw .3; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr4 ;bsf banperr,banperb; movlw .4; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr5 ;bsf banperr,banperb; movlw .5; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr6 ;bsf banperr,banperb; movlw .6; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr7 ;bsf banperr,banperb; movlw .7; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr8 ;bsf banperr,banperb; movlw .8; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; compvr9 ;bsf banperr,banperb; movlw .9; xorwf Var_tecopri,test; btfsc status,Z; goto activar; goto salir1; activar bsf banperr,banperb; salir1 bcf status,z; return; ;-------------------------------------------------------------------------------------------------;-------------------------------------------------------------------------------------------------;==================================== ;== Subrutina que revisa la tecla == ;==================================== muestra_tecla movlw 'A'; xorwf Var_tecopri,w; btfsc status,Z; goto actualizar; ;bcf porta,RS_LCD; A este no se le quita el comentario*** ;movlw 0x01; ;movwf portc; ;call pulso_Enable; ;bsf porta,RS_LCD; ;goto sal_muestec; sal_muestec return; ;-------------------------------------------------------------------------------------------------- esp_int ;=============================================== ;== Subrutina de espera de int. de 1 segundo == ;=============================================== nop; btfss banderas,ban_int; goto esp_int; bcf banderas,ban_int; return; ;-----------------------------------------------------------------------------------------------;========================================================= ================= ;========= Subrutina que cuenta el tiempo ======== ;========================================================= ================= count_time1 incf uniseg,f; Incrementa en 1 la cuenta de las unidades de segundo. movlw .10; subwf uniseg,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las uni. de seg. que es 9. goto exit_count1; Regresa al programa principal si es que aun no se llega a 9. clrf uniseg; Si se paso a XX-XX-X9 reinicia la cuenta desde 0. incf decseg,f; movlw .6; subwf decseg,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las dec. de seg. que es 5. goto exit_count1; Regresa al programa principal si es que aun no se llega a 5. clrf uniseg; clrf decseg; Si se paso a XX-XX-59 reinicia la cuenta desde 0. incf unimin,f; Incrementa en 1 la cuenta de las unidades de minuto. movlw .10; subwf unimin,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las uni. de min. que es 9. goto exit_count1; Regresa al programa principal si es que aun no se llega a 9. clrf uniseg; clrf decseg; clrf unimin; Si se paso a XX-X9-59 reinicia la cuenta desde 0. incf decmin,f; movlw .6; subwf decmin,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las dec. de min. que es 5. goto exit_count1; Regresa al programa principal si es que aun no se llega a 5. clrf uniseg; clrf decseg; clrf unimin; clrf decmin; Si se paso a XX-59-59 reinicia la cuenta desde 0. incf cta_24; movlw .24; subwf cta_24,w; btfss status,z; goto exit_count1; incf unihor,f; movlw .10; subwf unihor,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las uni. de hor. que es 9. goto exit_count1; Regresa al programa principal si es que aun no se llega a 9. clrf uniseg; clrf decseg; clrf unimin; clrf unihor; Si se paso a X9-59-59 reinicia la cuenta desde 0. incf dechor,f; movlw .3; subwf dechor,w; btfss status,Z; Compara si es que ya se llego al numero maximo de las dec. de hor. goto exit_count1; Regresa al programa principal si es que aun no se pasa a 1. clrf uniseg; clrf decseg; clrf unimin; clrf decmin; clrf unihor; . exit_count1 return; ;-----------------------------------------------------------------------------------------------;================================================ ;== Subrutina de inicialización de la LCD ==== ini_lcd ;================================================ bcf porta,RS_LCD; coloca la lcd en formato comandos movlw 0x38; Carga con 38 el registro w movwf portc; Mueve el 38 al bus de datos que está en el puerto c call pulso_enable; Genera pulso eneable para que capture y ejecute el dato movlw 0x01; movwf portc; call pulso_enable; movlw 0x06; Borra todos los caracteres de la LCD y posiciona el cursor en el digito 1 movwf portc; call pulso_enable; movlw 0x0c; movwf portc; call pulso_enable; movlw 0x80; Direccion del digito 1 en la LCD movwf portc; call pulso_enable; bsf porta,RS_LCD; coloca el LCD en modo datos return; ;----------------------------------------------------------------------------------------;================================================ ;== Subrutina de Retardo de medio segundo ==== ;================================================ pulso_enable bcf porta,Enable_LCD; call retardo_1ms; bsf porta,Enable_LCD; call ret_40ms; return; ;----------------------------------------------------------------------------------------;================================================ ;== Subrutina de Retardo de 10 milisegundos ==== ;================================================ retardo_1ms clrf cont_milis; esp_int1ms movlw .10; subwf cont_milis,w; btfss status,Z; goto esp_int1ms; return; ;----------------------------------------------------------------------------------------;================================================ ;== Subrutina de Retardo de 40 milisegundos ==== ;================================================ ret_40ms clrf cont_milis; esp_int40ms movlw .40; subwf cont_milis,w; btfss status,Z; goto esp_int40ms; return; ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------------------------------------------------------;================================================ ;== Subrutina de Retardo de 255 milisegundos ==== ;================================================ ret_255ms clrf cont_milis; esp_int255ms movlw .255; subwf cont_milis,w; btfss status,Z; goto esp_int255ms; return; ;------------------------------------------------------------------------------------------------------------End Conclusión Esta practica introdujo un nuevo dispositivo electrónico muy importante, estoy hablando del teclado matricial, la importancia de este dispositivo radica en que nos permite interactuar de manera directa con el sistema, en el caso particular de nuestra práctica, nos permite manipular y editar la hora del reloj de tiempo real. El hecho de incluir este nuevo sistema implica a nivel de software realizar los ajustes necesarios para el correcto funcionamiento de este, estos ajustes principalmente se ven reflejados en la subrutina de barrido de teclado, esta subrutina nos permite realizar un sondeo para saber con exactitud que tecla fue la que se pulso, además da validez al dato ingresado en el teclado. Una vez más, esta práctica nos mostró que entre más se simplifique el hardware más complejo será el desarrollo a nivel de software.