LIST P=PIC16F84A #INCLUDE <P16F84A.INC> ; Pic a usar ; Lista de etiquetas de microchip ; Configuración opciones de hardware para la programación CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC ; Valores de constantes: FRAC_INI D'12' ; Constante para inicio cuenta de fracciones de ; segundo SEGS_INI D'196' ; Constante para inicio cuenta de segundos MINS_INI D'196' ; Constante para inicio cuenta de minutos HORS_INI D'232' ; Constante para inicio cuenta de horas ADJMIN D'9' ; Número de "frac_sec" que se necesita sumar cada ; minuto para ajustar el tiempo ADJHOR D'34' ; Número de "frac_sec" que se necesita restar cada ; hora para ajustar el tiempo ADJDIA D'6' ; Número de "frac_sec" que se necesita sumar cada ; 24 horas para ajustar el tiempo ;Ajustes: ; Un "frac_sec" es aproximadamente 1 / 244 s ; 1 MHz / 16 = 62.500 Hz ; 62.500 Hz / 256 = 244,140625 Hz ; T = 0,004096 s ; 0,004096 s * 244 = 0,999424 s; dif 1 segundo = -0,000576 s ; 1 "minuto" = 0,999424 s * 60 = 59,96544 s ; 60 s - 59,96544 s = 0,03456 s ; 0,03456 s / 0,004096 s = 8,4375 ; 1 "minutoadj" = 59,96544 s + (0,004096 s * 9) = 59,96544 s + 0,036864 s = 60,002304 s ; 1 "hora" = 60,002304 s * 60 = 3600,13824 s ; 3600 s - 3600,13824 s = -0,13824 s ; -0,13824 s / 0,004096 s = -33,75 s ; 1 "horaadj" = 3600,13824 s - (0,004096 s * 34) = ; = 3600,13824 s - 0,139264 s = 3599,998976 s ; 24 "horas" = 3599,998976 s * 24 = 86399,975424 s ; 86400 s - 86399,975424 s = 0,024576 s ; 0,024576 s / 0,004096 s = 6 ; 24 "horasadj" = 86399,975424 s + 0,004096 s * 6 = 86399,975424 s + 0,024576 s = 86400 s ; Activación de RB1-3 para las entradas de los pulsadores PULSADOR B'00001110' ; RB1, RB2 y RB3 ; Asignación de banderas. Los pulsadores activos proporcionan un "1" CHG H'03' ; Indica que se ha activado un pulsador o que es ; necesario actualizar los valores de la hora que tienen ; que mostrarse en los displays PSEG H'04' ; Pulsador A, modo segundero. PMIN H'05' ; Pulsador B, avance rápido minutos. PHOR H'06' ; Pulsador C, avance rápido horas. P_ON H'07' ; Un pulsador ha sido activado DSPOFF B'11111111' ; Displays apagados DISPLAY DECENA MIN / DECENA SEG -> RA,2-1 \ / 18 - RA,1 <- DISPLAY UNIDAD HR DISPLAY UNIDAD MIN / UNIDAD SEG -> RA,3-2 17 - RA,0 <- DISPLAY DECENA HR NC - 3 16F84A 16 - XT MCLR/ - 4 15 - XT GND - 5 14 - Vcc PUNTO dp -> RB,0-6 13 - RB,7 -> SEGMENTO g PUL A (SEG) / SEGMENTO a -> RB,1-7 12 - RB,6 -> SEGMENTO f PUL B (MIN) / SEGMENTO b -> RB,2-8 11 - RB,5 -> SEGMENTO e PUL C (HOR) / SEGMENTO c -> RB,3-9 10 - RB,4 -> SEGMENTO d PORTA, control displays 7 segmentos de cátodo común PORTB, segmetos de los displays, led separadores, pulsadores como entrada El pulsador A (conectado a RB1) muestra el segundero en tanto permanezca presionado. El pulsador B (conectado a RB2) avanza rápidamente los minutos. El pulsador C (conectado a RB3) avanza rápidamente las horas. ; Mapa de activación de segmentos para los displays (PORTB) ; a ; ========= ; ; f b ; g ; ========= ; ; e c ; ; ========= # p ; d ; gfedcbap CERO H'7E' ; 01111110 U H'0C' ; 00001100 DOS H'B6' ; 10110110 TRES H'9E' ; 10011110 CUATRO H'CC' ; 11001100 CINCO H'DA' ; 11011010 SEIS H'FA' ; 11111010 ETE H'0E' ; 00001110 OCHO H'FE' ; 11111110 NUEVE H'DE' ; 11011110 SEGM_OFF equ H'00' ; Todos los segmentos apagados. Separador entre horas y minutos apagado (RB0). ; Posición de memoria de variables INICIO Configuración OPTION: OPTION_REG = 10000011 Configuración PUERTOS: TRISA = 0000 0000 TRISB = 0000 0000 PORTA = DSPOFF (1111 1111) PORTB = 0000 0001 Inicialización de variables: TMR0 = 1 display = 1111 1110 (decena de hora) digito1 = CERO digito2 = CERO digito3 = CERO digito4 = SEGM_OFF banderas = 0000 0000 Configurar puertos como salidas, blanquear display RB Pull Up desconectadas TMR0 en modo temporizador (se utilizan los pulsos de reloj internos, Fosc/4) Preescaler 1:16 Bits PORTA como salidas Bits PORTB como salidas Puerto A apaga los displays Con Puerto B todos los segmentos apagados. Separador entre horas y minutos encendido (RB0). Pone 01h en TMR0 Inicia display seleccionando decena de hora Los valores para digito1, digito2, digito3 y digito4 permitirán que desde el primer momento aparezcan las 0:00 en el display. ; Las variables de tiempo comienzan con un número que permite contar y ajustar el tiempo ; Por ejemplo la variable "segundos" se inicia con 196 decimal, para que después de 60 ; incrementos de 1 segundo se produzca un 0 (196 + 60 = 256 -> 0) frac_sec H'0C' ; Fracciones de segundo (1/244) segundos H'0D' ; Segundos minutos H'0E' ; Minutos horas H'0F' ; Horas conta1 H'10' ; Variable 1 para bucle contador ; display H'11' ; Indicador de display que debe actualizarse digito1 H'12' ; Display unidad de minuto / unidad de segundo digito2 H'13' ; Display decena de minuto / decena de segundo digito3 H'14' ; Display unidad de hora digito4 H'15' ; Display decena de hora banderas H'16' ; Banderas; 3-CHG, 4-PSEG, 5-PMIN, 6-PHOR, 7-P_ON Variables de tiempo: frac_sec = FRAC_INI (12d) segundos = SEGS_INI (196d) minutos = MINS_INI (196d) horas = HORS_INI (232d) PRINCIPAL Pag. 2
PRINCIPAL TMR0 cuenta libremente para no perder ciclos de reloj escribiendo valores TMR0_LLE Incremento de TMR0 TMR0 se va incrementando líbremente con la señal de reloj a 1.000.000 MHz / 16 = 62.500 Hz TMR0=0 Se comprueba el bit Z de STATUS TMR0 se ha desbordado y se han contado 256 * 16 = 4096 ciclos de reloj, (4,096 ms) La frecuencia es: 62.500 Hz / 256 = 244,140625 Hz frac_sec = frac_sec + 1 Se añade 1 a frac_sec frac_sec comienza por 12, hasta desbordarse cuenta 244 Se activa separador horas-minutos (RB0) Restaura la variable frac_sec para la próxima vuelta frac_sec = 0 frac_sec = 0, se ha contado 1 segundo (0,999424 s) RB0 = 1 frac_sec = FRAC_INI (12d) Comprueba variables pulsadores COMPROBAR_PUL El programa pasa por aquí cada 4,096 ms, esto es unas 244 veces por segundo No hay pulsadores activados P_ON = 1 Incrementar segundos, minutos y horas Ajustes cada minuto, hora y 1/2 dia CHG se pone a 1 Se ha activado un pulsador pero no es PSEG, debe ser PMIN o PHOR INC_HORA PSEG = 0 Pag. 7 Si está pulsado PSEG, (Pul A) se mostrarán los segundos en el display COMPROBAR_CHG Se comprueba el estado de CHG por si se ha activado algún pulsador o es necesario actualizar los valores de la hora que tienen que mostrarse en los displays Se actualiza hora, displays y pulsadores cada 4,096 ms (244 veces por segundo) Puesta en hora PMIN (Pul B) avanza los minutos PHOR (Pul C) avanza las horas Pag. 6 PONER_RELOJ Si no se han activado pulsadores ni ha cambiado la hora se salta a DISPLAY_PUL, que principalmente refresca uno de los displays cada vez que se accede a ella y escanea pulsadores. PSEG = 1 COMPROBAR_SEG Se comprueba si se activo el pulsador de segundos (Pul A) para mostrar los segundos en el display digito2 = 0 digito3 = 0 digito4 = 0 digito1 = second SEGS_INI (196d) Se mostrarán los segundos en el display de minutos Se guarda temporalmente el número de segundos en digito1 Resto de variables digit a 0 Se guardan la hora y los minutos para su tratamiento digito3 se utiliza temporalmente para almacenar la hora, (Si la hora es 255, digito3 sería 255 232 = 23 digito1 se utiliza temporalmente para almacenar los minutos OBTENER_H_M digito3 = horas HORS_INI (232d) digit 1 = minutos MINS_INI (196d) Pag. 4 DIV_DIGITOS Divide los segundos o los minutos y las horas en digitos independientes, ejemplo, [14] lo pasa a [1]-[4] Pag. 5 CONVER_COD_7S Convierte cada digito (digito1, digito2, digito3 y digito4) en valores para los segmentos del display Pag. 3 DISPLAY_PUL Se borran los bits de flag para actualizar su estado Escanea pulsadores, si alguno está activado se pone a 1 la bandera que le corresponda así como P_ON y CHG Muestra los digitos correspondientes a los segundos o a los minutos y horas en el display que corresponda. Cada display se actualiza cada 244,14 Hz / 4 = 61,04 Hz.
DISPLAY_PUL Se borran los bits de flag para actualizar su estado Escanea pulsadores, si alguno está pulsado se pone a 1 el pulsador que le correspoda así como "P_ON" y "CHG" Muestra los digitos correspondientes a los segundos o a los minutos y horas en el display que corresponda. banderas = 0000 0000 PORTA = DSPOFF (1111 1111) w = SEGM_OFF XOR PORTB w = w AND B'11111110' TRISB = PULSADOR (0000 1110) Se borran los bits de flag para actualizar su estado Apagar los segmentos respetando separador horas-minutos (RB0) Se apagan los displays Se configuran los bits 1, 2 y 3 de PORTB como entrada Se almacena el estado de los pulsadores en var RB1 = 1 COMPROBAR_PSEG COMPROBAR_PMIN PSEG= 1 P_ON = 1 w = SEGM_OFF XOR PORTB w = w AND B'11111110' Este código copia los bits del literal SEGM_OFF que se quieran en PORTB, Se copiaran aquellos bits de SEGM_OFF cuya posición coincida con un 1 en la máscara que se utiliza con la función AND y se respetaran los valores de los bits PORTB que coincidan con un 0 en la máscara. En este caso SEGM_OFF = 00H y en la operación AND se utiliza B'11111110' con lo que en PORTB se respetará el valor de RB0 y se pondrán a cero el resto de bits. RB2 = 1 PMIN= 1 P_ON = 1 En nuestro caso se podría simplificarse el proceso, eliminando la primera XOR pero entonces no se podría trabajar con otros posibles valores de SEGM_OFF Ejemplos: Si PORTB es RB3 = 1 COMPROBAR_PHOR PHOR= 1 P_ON = 1 w = SEGM_OFF XOR PORTB: 0000 0000 --------------- w = w AND B'11111110': 1111 1110 -------------- TRISB = 0000 0000 ACTIVAR_SEGM Puerto B como salida -------------- 0000 0000 Display es XXXX XXX0 Display es XXXX XX0X Display es XXXX X0XX Se determina que display debe actualizarse, es decir, que dato debe presentarse en el puerto B y se establece el siguiente display w = digito4 w = digito3 w = digito2 Si PORTB es 1000 1111 w = SEGM_OFF XOR PORTB: 0000 0000 1000 1111 --------------- 1000 1111 w = w AND B'11111110': 1000 1111 1111 1110 -------------- 1000 1111 -------------- 0000 0001 Display es XXXX 0XXX w = digito1 w = w XOR PORTB w = w AND B'11111110' Bit 7 de frac_sec = 0 Se entregar el valor de w en el puerto B respetando el valor de RB0 RB0 = 0 Se apagan los puntos de separación. Se activó en INICIO y se activa cada vez que frac_sec se hace 0. Para rotar el display a la próxima posición se utiliza el siguiente código: rlf display,f ; Rota display 1 bit a la próxima posición bsf display,0 ; Asegura un 1 en la posición más baja de display (luego se hará 0 si es necesario) btfss display,4 ; Comprueba si el último display fué actualizado bcf display,0 ; Si lo fué, se vuelve a habilitar el primer display PORTA = display Rota display a siguiente posición Se habilita el display correspondiente Cada display se enciende con una cadencia de 244 Hz / 4 = 61 Hz En la variable display se va desplazando un cero a la izquierda. Sólo se tendrán en cuenta los 4 bits menos significativos La variable display va cambiando: 1111 1101 1111 1011 1111 0111 1110 1110 1101 1101 1011 1011 0111 0111 1110 1110 Sólo valen los 4 bits menos significativos PRINCIPAL Pag. 2
DIV_DIGITOS Divide los segundos o los minutos y las horas en digitos independientes, ejemplo, [14] lo pasa a [1]-[4] digito4 = 0 digito2 = 0 conta1 = 2 FSR = digito1 Se ponen a cero las posiciones de las decenas para el caso de que no se incrementen Bucle para convertir cada número (segundos o minutos y horas) Dirección de digito1 en FSR para usar INDF La primera vez, FSR = digito1 (minutos o segundos) y la segunda vez FSR = digito3 (horas) Se vuelve a comprobar si es necesario sumar uno a la decena cada vez que ésta se ha incrementado Este LOOP se utiliza primero para los minutos o los segundos y después para las horas LOOP INC_DECENAS INDF = INDF - 10 Averiguar cuantas decenas hay en el número. El número menos diez en cada bucle. Incf FSR,F Incf INDF,F Decf FSR,F El puntero apunta a la primera posición de las decenas Se añade 1 a las decenas Se restaura el valor de INDF para la próxima resta hasta que se termine CARRY = 1 Se comprueba "CARRY", que se pone a 1 si en la resta no se ha producido llevada Si C = 1 se añadirá 1 a la posición de las decenas Este LOOP se utiliza para las horas después de trabajar con los minutos o los segundos INDF = 10 + INDF C = 0, no se incrementan las decenas y se suma 10 para restaurar las unidades LOOP2 FSR = digito3 PROX_NUM conta1 = conta1-1 Próximo número: Primero ha sido segundos o minutos y luego horas conta1 = 0 CONVER_COD_7S Pag. 5
CONVER_COD_7S Convierte cada dígito a código 7 segmentos para los displays FSR = digito1 conta1 = 4 Coloca la dirección del primer digito (digito1) en FSR Prepara la variable conta1 para el bucle de los 4 displays PROX_DIGITO w = INDF Obtener el valor de la variable "digito" actual Pag. 8 CODIGO_7S LLamar a la rutina de conversión a código 7 segmentos INDF = w FSR = FSR + 1 conta1 = conta1-1 Colocar en la variable "digito" actual el código 7 segmentos devuelto Incremente INDF para el próximo "digito" Se resta 1 a conta1 conta1 = 0 Permitir que conta1 de sólo 4 vueltas BORRAR_CERO digito4 = 0? Si hay un cero en el display de las decenas de hora no se muestra (borrado de los ceros a la izquierda) digito4 = SEGM_OFF Si está pulsado PSEG no se muestra nada en el display de la posición de la unidad de hora. Contando con BORRAR_CERO, esto significa que sólo se mostrarán los segundos. BORRAR_CERO_SEG PSEG = 1 digito3 = SEGM_OFF DISPLAY_PUL Pag. 3
PONER_RELOJ Puesta en hora de horas y minutos segundos = SEGS_INI (196d) Inicia los segundos cuando se pone el reloj en hora PONER_MINUTOS Comprobar si se ha pulsado PMIN (Pulsador minutos) PMIN = 1 Avance rápido del tiempo cuando se ajustan minutos frac_sec = 175 Incrementar los minutos frac_sec = 175d minutos = minutos + 1 minutos = 0 Iniciar minutos si al incrementar se han desbordado minutos = MINS_INI PONER_HORAS Comprobar si se ha pulsado PHOR (Pulsador horas) PHOR = 1 Avance rápido del tiempo cuando se ajustan horas frac_sec = 127d Incrementar las horas frac_sec = 127d horas = horas + 1 horas = 0 horas = HORS_INI (232d) OBTENER_H_M Pag. 2
INC_HORA Incrementar segundos, minutos y horas Ajustes cada minuto, hora y 24 horas Se especifica que se ha producido un cambio segundos = segundos + 1 Como ha pasado un segundo se incrementa segundos segundos = 0 segundos = SEGS_INI (196d) frac_sec = frac_sec ADJMIN (9d) minutos = minutos + 1 Se ha desbordado "segundos" y se reestablece el valor inicial de segundos para la próxima vuelta Se resta 9 a frac_sec cada minuto para los ajustes de tiempo El minuto será 9 frac_sec más largo. Se añade 1 minuto minutos = 0 minutos = MINS_INI (196d) frac_sec = frac_sec + ADJHOR (34d) horas = horas + 1 Se reestablece el valor inicial de minutos para la próxima vuelta Se suma 34 frac_sec a cada hora para los ajustes de tiempo La hora será 34 frac_sec más corta Se añade 1 hora horas = 0 horas = HORS_INI (232d) frac_sec = frac_sec ADJDIA (6d) Se reestablece el valor inicial de horas para la próxima vuelta Se resta 6 a frac_sec cada 24 horas para los ajustes de tiempo Cada 24 horas se añadirán 6 "frac_sec" COMPROBAR_CHG Pag. 2
SUBRUTINAS CODIGO_7S Devuelve el código 7 segmentos addwf PCL,F CERO U DOS TRES CUATRO CINCO SEIS ETE OCHO NUEVE Devuelve en el acumulador el valor de la constante CERO a NUEVE según el valor que se hubiese colocado en w