;*************************************************************
; RELOJ DE 24 HORAS
;*************************************************************
;
; Programa original: CLOCK54.ASM
; Dan Matthews
; Microchip Technology Inc.
; 16 de enero de 1997
; [URL="http://www.microchip.com"]www.microchip.com[/URL], AN590.PDF
; Revisión: RELOJ24H.ASM
; Emilio Toboso
; I.E.S. "La Fuensanta" Córdoba
; Abril de 2005
; Programa para PIC16F84A
; Frecuencia de reloj: 4 MHz
; Instrucción: 1Mz = 1 us
; Perro Guardián: OFF
; Tipo de Reloj: XT
; Protección de código: OFF
; POR: ON
;
;*************************************************************
; DESCRIPCIÓN DEL PROGRAMA
;*************************************************************
;
; Este programa funciona en un PIC16F84A y se trata de un reloj
; de 24 horas que muestra horas y minutos.
; Dispone de dos displays LED para las horas y otros dos para los
; minutos.
; Para programar la hora se dispone de un pulsador de avance rápido
; de horas y de otro para avance rápido de minutos.
; También se dispone de un tercer pulsador que se utiliza para mostrar
; los segundos mientras permanezca pulsado.
;
; DESCRIPCIÓN DEL HARDWARE
;
; VISUALIZACIÓN:
;
; La hora se muestra mediante cuatro displays de 7 segmentos de cátodo
; común multiplexados. Los segmentos de cada display se unen y estan
; controlados a través de resistencias por salidas del PIC. Cada cátodo
; común de cada display es controlado por el PIC a través de un transistor.
; El reloj presenta las horas y minutos por dos puntos (88:88). Los
; segmentos se asignan al puerto B.
; Los dos puntos se realizan mediante dos LED en serie con una resistencia
; y se conectan a RB0.
; Los segmentos de los displays, de "a" hasta "f" se asignan a las salidas
; de RB1 a RB7.
; Los cuatro cátodos comunes se controlan mediante el puerto A a través de
; los transistores.
; RA0 controla la decena de hora, RA1 la unidad de hora; RA2 la decena de
; minuto y RA3 la unidad de minuto.
;
; PULSADORES:
;
; Sobre los tres pulsadores:
; Pulsador avance rápido de horas conectado a RB3
; Pulsador avance rápido de minutos conectado a RB2
; Pulsador para mostrar los segundos conectado a RB1
;
; Debido a que todas las patillas E/S (salvo RA4) están utilizadas,
; se debe comprobar el estado de los pulsadores estableciendo como salidas
; o como entradas alternativamente las patillas a las que están conectados.
; Los pulsadores utilizan el Puerto B que cambiará a entrada durante un
; instante para comprobar el estado de los pulsadores.
;
;
;
;
;
;
;
;
;
;
;
;
;*************************************************************
; PATILLAS
;*************************************************************
; ___________
; 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.
;
;*************************************************************
LIST P=PIC16F84A ; Pic a usar
#INCLUDE <P16F84A.INC> ; Lista de etiquetas de microchip
;
;*************************************************************
; Configuración opciones de hardware para la programación
__CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC
;
;**************************************************************
; Lista de constantes y variables para el uso del programa
;**************************************************************
;
; Valores de constantes ***************************************
;
FRAC_INI equ D'12' ; Constante para inicio cuenta de fracciones de segundo
SEGS_INI equ D'196' ; Constante para inicio cuenta de segundos
MINS_INI equ D'196' ; Constante para inicio cuenta de minutos
HORS_INI equ D'232' ; Constante para cuenta de horas
ADJMIN equ D'9' ; Número de "frac_sec" que se necesita sumar cada minuto
; para ajustar el tiempo
ADJHOR equ D'34' ; Número de "frac_sec" que se necesita restar cada hora
; para ajustar el tiempo
ADJDIA equ 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 equ B'00001110' ; RB1, RB2 y RB3
;
; Asignación de banderas. Los pulsadores activos proporcionan un "1"
CHG equ 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 equ H'04' ; Pulsador A, modo segundero.
PMIN equ H'05' ; Pulsador B, avance rápido minutos.
PHOR equ H'06' ; Pulsador C, avance rápido horas.
P_ON equ H'07' ; Un pulsador ha sido activado
;
DSPOFF equ B'11111111' ; Displays apagados (PORTA)
;
; Mapa de activación de segmentos para los displays (PORTB)
; a
; =========
; | |
; f | | b
; | g |
; =========
; | |
; e | | c
; | |
; ========= # p
; d
;
; gfedcbap
CERO equ H'7E' ; 01111110
UNO equ H'0C' ; 00001100
DOS equ H'B6' ; 10110110
TRES equ H'9E' ; 10011110
CUATRO equ H'CC' ; 11001100
CINCO equ H'DA' ; 11011010
SEIS equ H'FA' ; 11111010
SIETE equ H'0E' ; 00001110
OCHO equ H'FE' ; 11111110
NUEVE equ 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 ************************************
; 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 equ H'0C' ; Fracciones de segundo (1/244)
segundos equ H'0D' ; Segundos
minutos equ H'0E' ; Minutos
horas equ H'0F' ; Horas
conta1 equ H'10' ; Variable 1 para bucle contador
;
display equ H'11' ; Indicador de display que debe actualizarse
digito1 equ H'12' ; Display unidad de minuto / unidad de segundo
digito2 equ H'13' ; Display decena de minuto / decena de segundo
digito3 equ H'14' ; Display unidad de hora
digito4 equ H'15' ; Display decena de hora
banderas equ H'16' ; Banderas; 3-CHG, 4-PSEG, 5-PMIN, 6-PHOR, 7-P_ON
;
;**************************************************************
ORG 0x00 ;Vector de Reset
goto INICIO
org 0x05 ;Salva el vector de interrupción
;**************************************************************
; SUBRUTINAS
;**************************************************************
CODIGO_7S ; Devuelve el código 7 segmentos
addwf PCL,F
retlw CERO
retlw UNO
retlw DOS
retlw TRES
retlw CUATRO
retlw CINCO
retlw SEIS
retlw SIETE
retlw OCHO
retlw NUEVE
;**************************************************************
;
;
;
;
;
;
;
;
;
;
;**************************************************************
; Comienzo del programa
;**************************************************************
;
INICIO
; Configurar puertos como salidas, blanquear display
bsf STATUS,RP0 ; Activa el banco de memoria 1.
movlw B'10000011' ; Configuración del registro Option, RB Pull Up desconectadas
movwf OPTION_REG ; TMR0 en modo temporizador (uso de pulsos de reloj internos, Fosc/4)
; prescaler TMR0 a 1:16
movlw B'00000000'
movwf TRISA ; Pone todas las patillas del puerto A como salidas
movwf TRISB ; Pone todas las patillas del puerto B como salidas
bcf STATUS,RP0 ; Activa el banco de memoria 0.
;
; Establecer estados iniciales de las salidas
movlw DSPOFF
movwf PORTA ; Apaga los displays
movlw B'00000001' ; Todos los segmentos apagados. Separador
movwf PORTB ; entre horas y minutos encendido (RB0).
;
; Inicialización de variables:
movlw H'01'
movwf TMR0 ; Pone 01h en TMR0
movlw B'11111110'
movwf display ; Inicia display seleccionando decena de hora
movlw CERO
movwf digito1 ; Aparecerá un "0" en el display unidad de minutos
movwf digito2 ; Aparecerá un "0" en el display decena de minutos
movwf digito3 ; Aparecerá un "0" en el display unidad de hora
;movlw SEGM_OFF ======================= EDITADO =======================
movwf digito4 ; No aparecerá nada en el display decena de hora
movlw B'00000000'
movwf banderas ; Coloca todas las banderas a 0
;
; Inicia las variables de tiempo
movlw FRAC_INI
movwf frac_sec ; 12
movlw SEGS_INI
movwf segundos ; 196
movlw MINS_INI
movwf minutos ; 196
movlw HORS_INI
movwf horas ; 232
;
;**************************************************************
PRINCIPAL ; Rutina principal cíclica
;**************************************************************
;
; Esperar al desbordamiento de TMR0
TMR0_LLENO
; 4 MHz -> 1 MHz
; 1.000.000 Hz / 16 = 62.500 Hz
; 62.500 Hz / 256 = 244,140625 Hz -> 4,096 ms
movf TMR0,W
btfss STATUS,Z ; TMR0 cuenta libremente para no perder ciclos del reloj
; escribiendo valores
goto TMR0_LLENO
;
; Se ha desbordado TMR0 y se han contado 256.
; Tarda en desbordarse 4.096 ciclos de reloj, 4,096 ms
incfsz frac_sec,F ; Se añade 1 a frac_sec
goto 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 ha desbordado frac_sec y se han contado 244 "frac_sec", 1 segundo.
; Tarda en desbordarse 4.096 ciclos de reloj, 4,096 ms * 244 = 999,424 ms
; Al no consegirse exactamente 1 segundo sino 0,999424 s, luego se necesitan ajustes
bsf PORTB,0 ; Se activa separador horas-minutos
movlw FRAC_INI
movwf frac_sec ; Restaura la variable frac_sec para la próxima vuelta
;
COMPROBAR_PUL ; Comprueba variables pulsadores
btfss banderas,P_ON ; Si no se ha pulsado nada, se pasa a INC_HORA
goto INC_HORA ; Incrementa segundos, minutos y horas. Ajustes y “CHG” a 1
btfsc banderas,PSEG ; Comprobar si se ha pulsado PSEG (Pulsador segundos)
goto INC_HORA ; Se ha pulsado PSEG, (Pul A) y se mostrarán
; los segundos en el display
;
PONER_RELOJ
movlw SEGS_INI ; 196d
movwf segundos ; Inicia los segundos cuando se pone el reloj en hora
;
PONER_MINUTOS
btfss banderas,PMIN ; Comprobar si se ha pulsado PMIN (Pulsador minutos)
goto PONER_HORAS ; No se ha pulsado PMIN, ir a comprobar estado de PHOR
movlw H'AF' ; 175d
movwf frac_sec ; Avance rápido del tiempo cuando se ajustan minutos frac_sec = 175
incfsz minutos,F ; Incrementar los minutos
goto PONER_HORAS
movlw MINS_INI
movwf minutos ; Iniciar minutos si al incrementar se han desbordado
;
PONER_HORAS
btfss banderas,PHOR ; Comprobar si se ha pulsado PHOR (Pulsador horas)
goto OBTENER_H_M ; No se ha pulsado PHOR, no se cambian las horas
movlw H'7F' ; 127d
movwf frac_sec ; Avance rápido del tiempo cuando se ajustan horas frac_sec = 127
incfsz horas,F ; Incrementar la hora
goto OBTENER_H_M
movlw HORS_INI
movwf horas ; Iniciar la hora si al incrementar se han desbordado
goto OBTENER_H_M
;
INC_HORA ; Incrementar segundos, minutos y horas
; Ajustes cada minuto, hora y 24 horas
bsf banderas,CHG ; Se especifica que se ha producido un cambio
;
incfsz segundos,F ; Como ha pasado un segundo se incrementa "segundos"
goto COMPROBAR_CHG
movlw SEGS_INI ; Se ha desbordado "segundos" y se reestablece el valor inicial
movwf segundos ; de "segundos" para la próxima vuelta
;
movlw ADJMIN ; Se resta 9 a "frac_sec" cada minuto para los ajustes de tiempo
subwf frac_sec,F ; El minuto será 9 "frac_sec" más largo
;
incfsz minutos,F ; Se añade 1 minuto
goto COMPROBAR_CHG
movlw MINS_INI ; Se ha desbordado "minutos" y Se reestablece el valor inicial
movwf minutos ; de "minutos" para la próxima vuelta
;
movlw ADJHOR ; Se suma 34 a "frac_sec" cada hora para los ajustes de tiempo
addwf frac_sec,F ; La hora será 34 "frac_sec" más corta
;
incfsz horas,F ; Se añade 1 hora
goto COMPROBAR_CHG
movlw HORS_INI ; Se ha desbordado "horas" y se reestablece el valor inicial
movwf horas ; de "horas" para la próxima vuelta
movlw ADJDIA ; Se resta 6 a "frac_sec" cada 24 horas para los ajustes de tiempo
subwf frac_sec,F ; Cada 24 horas se añadirán 6 "frac_sec"
;
; 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)
COMPROBAR_CHG
btfss banderas,CHG ; Si no se han activado pulsadores ni ha cambiado la hora
goto DISPLAY_PUL ; se salta a DISPLAY_PUL, que principalmente refresca uno de los
; displays cada vez que se accede a ella y escanea pulsadores.
;
COMPROBAR_SEG ; Se comprueba si se activo el pulsador de segundos (Pul A)
; para mostrar los segundos en el display
btfss banderas,PSEG
goto OBTENER_H_M ; No estaba pulsado PSEG
movlw H'00' ; Se mostrarán los segundos en el display de minutos
movwf digito2 ; Variables "digito2" a 0
movwf digito3 ; Variables "digito3" a 0
movwf digito4 ; Variables "digito4" a 0
movlw SEGS_INI
subwf segundos,W
movwf digito1 ; Se guarda temporalmente el número de segundos en “digito1”
goto DIV_DIGITOS
;
OBTENER_H_M
movlw HORS_INI
subwf horas,W
movwf digito3 ; La variable digito3 almacena temporalmente el valor para las horas
movlw MINS_INI
subwf minutos,W
movwf digito1 ; La variable digito1 almacena temporalmente el valor para los minutos
;
DIV_DIGITOS ; Divide los segundos o los minutos y las horas en dígitos independientes
; ejemplo, [14] lo pasa a [1]-[4]
movlw H'00'
movwf digito4 ; Se ponen a cero las posiciones de las decenas
movwf digito2 ; para el caso de que no se incrementen
movlw H'02'
movwf conta1 ; Bucle para convertir cada número (segundos o minutos y horas)
movlw digito1 ; Dirección de digito1 en FSR para usar INDF
movwf FSR ; La primera vez, FSR = digito1 (minutos o segundos) y la segunda vez FSR = digito3 (horas)
goto LOOP
;
LOOP2 ; Este LOOP se utiliza para las horas después de trabajar con los minutos o los segundos
movlw digito3
movwf FSR
;
LOOP ; Este LOOP se utiliza primero para los minutos o los segundos y después para las horas
movlw D'10' ; Averiguar cuantas "decenas" hay en el número
subwf INDF,F ; En cada LOOP restar 10 al número
btfsc STATUS,C ; Se comprueba "C", que se pone a 1 si en la resta no se ha
; producido llevada
goto INC_DECENAS ; C = 1 por lo que se añade 1 a la posición de las decenas
addwf INDF,F ; C = 0, no se incrementan las decenas y se suma 10 para restaurar
; las unidades
goto PROX_NUM
;
INC_DECENAS
incf FSR,F ; El puntero apunta a la primera posición de las decenas
incf INDF,F ; Se añade 1 a las decenas
decf FSR,F ; Se restaura el valor de INDF para apuntar al número
goto LOOP ; para la próxima resta hasta que se termine
; Con "goto LOOP" se vuelve a comprobar si es necesario
; sumar uno a la decena cada vez que esta se ha incrementado
;
PROX_NUM ; Próximo número, primero ha sido segundos o minutos y luego horas
decfsz conta1,F
goto LOOP2
;
CONVER_COD_7S ; Convierte cada dígito a código 7 segmentos para los displays
movlw digito1
movwf FSR ; Coloca la dirección del primer digito (digito1) en FSR
movlw H'04'
movwf conta1 ; Prepara la variable conta1 para el bucle de los 4 displays
;
PROX_DIGITO
movf INDF,W ; Obtener el valor de la variable "digito" actual
call CODIGO_7S ; LLamar a la rutina de conversión a código 7 segmentos
movwf INDF ; Colocar en la variable "digito" el código 7 segmentos devuelto
incf FSR,F ; Incremente INDF para el próximo "digito"
decfsz conta1,F ; Permitir que conta1 de sólo 4 vueltas
goto PROX_DIGITO
;
BORRAR_CERO ; Si hay un cero en el display de las decenas de hora
; no se muestra (borrado de los ceros a la izquierda)
movlw CERO
subwf digito4,W
btfss STATUS,Z
goto BORRAR_CERO_SEG
;movlw SEGM_OFF ; =================================================
movlw CERO ; EDITADO =================================================
movwf digito4
;
BORRAR_CERO_SEG
btfss banderas,PSEG ; Si está pulsado PSEG no se muestra nada en el display de la
goto DISPLAY_PUL ; posición de la unidad de hora.
movlw SEGM_OFF ; Contando con BORRAR_CERO, esto significa que sólo se
;movlw CERO ; EDITADO =================================================
movwf digito3 ; mostrarán los segundos.
movwf digito4 ; ====== EDITADO ======.
;
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 dígitos correspondientes a los segundos o a
; los minutos y horas en el display que corresponda.
;
movlw B'00000000'
movwf banderas ; Se borran los bits de flag para actualizar su estado
;
; Apagar los displays
movlw DSPOFF
movwf PORTA
;
; Apagar los segmentos respetando separador horas-minutos
movlw SEGM_OFF ; Respeta valor RB0
xorwf PORTB, w
andlw B'11111110' ; Poner "1" el la posición del bit a copiar
xorwf PORTB, f
;
; Configurar los bits 1, 2 y 3 de PORTB como entrada
bsf STATUS,RP0 ; Activa el banco 1.
movlw PULSADOR
movwf TRISB ; Se configuran los bits 1, 2 y 3 de PORTB como entrada
bcf STATUS,RP0 ; Activa el banco 0.
nop ; Las instrucciones "nop" pueden no ser necesarias.
nop ; En principio proporcionan el tiempo suficiente para que los
nop ; estados anteriores de las salidas se actualicen a través de
nop ; las resistencias de 10K (y de las de 820 ohm si está activado
nop ; algún pulsador) antes de leer las patillas del puerto.
;
COMPROBAR_PSEG ; Se comprueba Pulsador PSEG
btfss PORTB,1
goto COMPROBAR_PMIN
bsf banderas,CHG
bsf banderas,PSEG
bsf banderas,P_ON
;
COMPROBAR_PMIN ; Se comprueba Pulsador PMIN
btfss PORTB,2
goto COMPROBAR_PHOR
bsf banderas,CHG
bsf banderas,PMIN
bsf banderas,P_ON
;
COMPROBAR_PHOR ; Se comprueba Pulsador PHOR
btfss PORTB,3
goto ACTIVAR_SEGM
bsf banderas,CHG
bsf banderas,PHOR
bsf banderas,P_ON
;
ACTIVAR_SEGM ; Se coloca en PORTB el valor para los segmentos del display actual
bsf STATUS,RP0 ; Activa el banco 1.
movlw H'00'
movwf TRISB ; Puerto B como salida
bcf STATUS,RP0 ; Activa el banco 0.
;
; Se determina que display debe actualizarse, es decir, que dato debe
; presentarse en el puerto B y se establece el siguiente display
btfss display,0 ; Si es el primer display (decena de hora) tomar digito4
movf digito4,W
btfss display,1 ; Si es el segundo display (unidad de hora) tomar valor digito3
movf digito3,W
btfss display,2 ; Si es el tercer display (decena de min/seg) tomar valor digito2
movf digito2,W
btfss display,3 ; Si es el cuarto display (unidad de min/seg) tomar valor digito1
movf digito1,W
;
; Entregar el valor en puerto B y respetar valor RB0
xorwf PORTB, w
andlw B'11111110' ; Poner "1" el la posición del bit a copiar
xorwf PORTB, f
;
btfsc frac_sec,7 ; Establecer el separador de horas y minutos a un 50%
bcf PORTB,0 ; del ciclo (1/2 segundo encendido, 1/2 segundo apagado)
;
movf display,W ; Tomar el valor del display que debe habilitarse
movwf PORTA ; Cada display se “enciende” con una cadencia de 244 Hz / 4 = 61 Hz
; Cada display se “enciende” con una cadencia de 244 Hz / 4 = 61 Hz
; En este momento están encendidos los segmentos correspondientes
;
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 fue actualizado
bcf display,0 ; Si lo fue, se vuelve a habilitar el primer display
; 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
;
goto PRINCIPAL ; Volver a realizar todo el proceso
;
END