Hola a todos.
He diseñado (es un decir

) un terminal para el control de entradas de claves.
Utilizo un PIC16f873, TECLADO 4X4 Y lcd 16X2.
Proton+ 2.1.3 y una interrupción basada en en TMR1 para el teclado. Como no me permite adjuntos (no sé por qué

os pongo al final el fuente. Lo siento.
Todo funciona perfectamente excepto que no puedo conseguir lo siguiente:
Cuando entro en modo ARMED (o sea, alarma armada) disparo una serie de sonidos temporizados de forma que indiquen que se debe salir pero no he sido capaz de lograr que mientras suena los pitidos se pueda introducir una clave para desarmar la alarma.
¿Alguien me puede ayudar?
Gracias anticipadas de un novato,
"*******************************************************************************
" TERMINAL REMOTO
" PROGRAMA: BETA.BAS
" PROPOSITO: Actuar como una llave electrónica.
"*******************************************************************************
include "LT16F873.INC" " Variables genéricas configuración básica
"==== VARIABLES ================================================================
"---- Lógicas ----
DIM TRUE AS 1
DIM FALSE AS 0
DIM ON AS 1
DIM OFF AS 0
"---- Aplicación ----
DIM MODE AS BYTE " MODO ACTUAL (STANDBY, ARMED o ALARM)
DIM STANDBY AS 255 " MODO STANDBY (Modo NORMAL sin ALARMA)
DIM ARMED AS 254 " MODO ARMED (Modo ALARMA ARMADA)
DIM ALARMON AS 253 " MODO ALARMON (Modo ALARMA DISPARADA)
DIM SUBMODE AS BYTE " SUBMODO ACTUAL (PSWENTRY)
DIM WAITING AS 250 " SUBMODO WAITING (Esperando primera tecla)
DIM PSWENTRY AS 251 " SUBMODO PSWENTRY (Entrada de claves)
DIM TEMP AS BYTE
"---- Alias de Puertos ----
DIM GRNLED AS PORTC.0 " Alias del LED verde (STANDBY)
DIM REDLED AS PORTC.1 " Alias del LED rojo (ARMED)
DIM SPEAKER AS PORTC.2 " Alias del altavoz
"---- Teclado -----
DIM KEY AS BYTE " Almacena la tecla pulsada
DIM LAST_KEY AS BYTE " Valor de la última tecla en el último barrido
DIM NEW_KEY AS BIT " Flag para indicar cuando se ha leído una nueva tecla
DIM NO_KEY AS 255 " Valor que indica que no se ha pulsado tecla
DIM KEY_STATUS AS BIT " Flag para indicar control de tecla correcto
"---- Temporización ----
DIM INPUT_TMR AS WORD " Temporizador entrada de tecla
DIM INPUT_TMR_RUNNING AS BIT " Flag de funcionamiento del temporizador de tecla
DIM INPUT_TMR_VALUE AS 300 " Centésimas de segundo entre teclas
" ---- Claves ----
DIM PSW_POINTER AS BYTE " Puntero al siguiente carácter de la clave
DIM MAX_NO_NOS AS 5 " Número de caracteres en cada clave
DIM PSW_STR[MAX_NO_NOS] AS BYTE " Cadena en la que se almacena la clave
DIM PSW_ENTERED AS BIT " Flag que indica que se ha introducido una clave correcta
DIM PSW_TYPE AS BYTE " Flag de tipo de clave 0: Normal, 1: Master
DIM PSW_OK AS BIT " Flag que indica que una clave es correcta
DIM PSW_ENTRY AS 0 " Submodo ENTRADA DE CONTRASEÑA (En STANDBY, ALARMON y ALARM)
DIM NEWPSW_ENTRY AS 1 " Submodo ENTRADA NUEVA CONTRASEÑA (Sólo en Modo STANDBY)
DIM ATTEMPTS AS BYTE " Contador de intentos
DIM MAX_NO_ATT AS 3 " Número máximo de intentos de introducción clave
DIM NO_PSWS AS 2 " Número de claves almacenadas
DIM STD_PSW_ADDR AS MAX_NO_NOS " La dirección de inicio de la clave normal
DIM HIDE_PSW AS 1 " Flag ocultación de la clave (pone asteriscos)
"---- Lectura/Escritura EEPROM -----
DIM EEPSW_ADDRESS AS BYTE " Dirección de la EEPROM
DIM EEPSW_DATA AS BYTE " Byte transferido
DIM OFFSET AS BYTE " Offset
"---- Control de interrupción ----
DIM GIE AS INTCON.7
DIM TIMER1REG AS TMR1L.WORD
SYMBOL FUDGE_FACTOR = 7
SYMBOL TMR1_VAL =((65536)-(XTAL*2500))+FUDGE_FACTOR " Cálculo de los valores
" de offset para una interrup-
" ción de 100Hz. Puede utilizar-
" se como una frecuencia de flash
" para LEDs o para el SPEAKER.
ON_INTERRUPT INTERRUPT_ROUTINE " Rutina en caso de interrupción
GOTO START " Salto de todas las subrutinas
"==== INICIALIZACION ===========================================================
START:
T1CON = %00000000 " Configuración de Tmr1 para tener un prescaler 1:1 y actuar como un temporizador
PIR1.0 = 0 " Borrado del flag de interrupción de Tmr1
INTCON = %11000000 " Global and Peripheral interrupts on
PIE1.0 = 1 " Enable Tmr1 as peripheral interrupt source
T1CON.0 = 1
CLEAR
trisa = %11111100
CLS
SOUND SPEAKER, [100, 20, 110, 20] " Beeps al encendido
PRINT AT 1,1, "TRI v.BETA "
PRINT AT 2,1, " "
DELAYMS 500
CLS
GOSUB SETMODE_STANDBY
GOSUB SETSUBMODE_WAITING
GOTO MAIN_LOOP
"*******************************************************************************
"**** PROGRAMA PRINCIPAL *******************************************************
"*******************************************************************************
" Llama al barrido de teclado.
" Espera a la pulsación de una tecla antes de iniciar la recogida de datos.
" Cuando se ha introducido la clave completa entonces la verifica y decide
" la acción a realizar
" Tambien permite la cancelación de la clave que se está introduciendo
"-------------------------------------------------------------------------------
MAIN_LOOP:
GOSUB KEY_SCAN " SE BARRE EL TECLADO Y SE REGRESA
" Solo para depuración
"IF KEY <> 255 THEN
"PRINT AT 2,1," "
"PRINT AT 2,1," KEY: ", KEY " Para caracteres alfanuméricos
"PRINT AT 2,10,"@KEY: ", @KEY " Para caracteres numéricos
"ENDIF
" HASTA AQUI PUEDE LLEGARSE CON UNA TECLA PULSADA O NO (NO_KEY).
" En cualquiere de los casos se verifica. Con la llegada de la primera tecla
" numérica se entra en el modo PSWENTRY y se controla que en el caso de que
" entre una y otra tecla se superre el límite de tiempo dado por INPUT_TMR_VALUE
" automáticamente se suspende la entrada y se pasa al modo STANDBY
IF SUBMODE = WAITING THEN " SI, se está en SUBMODO WAITING
GOSUB CHKNUMKEY " Verificación de tecla numérica (1ª tecla)
IF KEY_STATUS = TRUE THEN " SI, la tecla es correcta, por tanto,
GOSUB SETSUBMODE_PSWENTRY " entramos en MODO PSW_ENTRY
NEW_KEY = TRUE " Flag para indicar que la tecla es nueva
GOSUB LOAD_INPUT_TIMER " y hay que cargar el límite de tiempo del temporizador
GOSUB PSW_ACC " ACC contraseña continúa añadiendo tecla
ENDIF
ELSE " NO, ahora se está en MODO PSWENTRY
IF INPUT_TMR_RUNNING = FALSE THEN " Si, se ha superado el límite de tiempo
" para la introducción de la tecla siguiente
GOSUB MODE_RETURN " Se retorna al Modo actual
GOTO MAIN_LOOP " y se entra en el bucle principal
ENDIF
GOSUB PSW_ACC " ACC contraseña continúa añadiendo tecla
ENDIF
" CONTROL ENTRADA CLAVE.
" HASTA AQUI PUEDE LLEGARSE CON UNA CLAVE INTRODUCIDA O NO
IF PSW_ENTERED = TRUE THEN " SI, se han introducido todos los números
GOSUB PSW_COMPARE " entonces comparar con las claves
PSW_ENTERED = FALSE " se BORRA EL FLAG
IF PSW_OK = TRUE THEN " SE HA ENCONTRADO UNA CLAVE
IF PSW_TYPE = 0 THEN " Clave normal y MODOS STANDBY O ARMED
GOTO NORMAL_ENTRY_ROUTINE " Entrada de Clave Normal
ENDIF
IF PSW_TYPE = 1 AND MODE = STANDBY THEN " Clave "MASTER" y MODO STANDBY
" (Sólo en modo STANDBY)
GOTO MASTER_ENTRY_ROUTINE " Entrada de Clave "Master"
"PENDIENTE DE DESARROLLO
ENDIF
GOSUB CLEAR_PSW_BUFFER " Ouesta a 0 del buffer de claves
ELSE
PRINT AT 1,20,DEC ATTEMPTS " Para caracteres numéricos
GOSUB CLEAR_PSW_BUFFER
IF ATTEMPTS > MAX_NO_ATT - 1 THEN " SUPERADO MAX. NUM. DE INTENTOS
GOSUB SETMODE_ALARMON " SE PASA A ALARMA
ENDIF
ENDIF
ENDIF
goto MAIN_LOOP
"==== RUTINAS ==================================================================
"==== MODOS ====
"---- STANDBY ----
SETMODE_STANDBY:
MODE = STANDBY
GOSUB SETSUBMODE_WAITING
"LOW GRNLED
HIGH GRNLED
LOW REDLED
PRINT AT 1,1, "ALARMA DESARMADA ","#", DEC ATTEMPTS
PRINT AT 2,1, " "
IF ATTEMPTS = 0 THEN GOSUB MODE_ENTRY_SOUND
RETURN
"---- ARMED ----
SETMODE_ARMED:
MODE = ARMED
GOSUB SETSUBMODE_WAITING
LOW GRNLED
HIGH REDLED
PRINT AT 1,1, "ALARMA ARMADA ","#", DEC ATTEMPTS
PRINT AT 2,1, "SALGA AHORA "
IF ATTEMPTS = 0 THEN GOSUB MODE_ENTRY_SOUND
TEMP = 0
Repeat " Crea un bucle
SOUND SPEAKER, [105,10] "PITIDOS INICIALES
Inc TEMP
DELAYMS 500
Until TEMP = 25 " Para al llegar a 25
PRINT AT 1,1, "ALARMA ARMADA "
PRINT AT 2,1, "SALGA INMEDIATAMENTE"
TEMP = 0
Repeat " Crea un bucle
SOUND SPEAKER, [105,10] "PITIDOS FINALES
Inc TEMP
Delayms 100
Until TEMP = 50 " Para al llegar a 50
PRINT AT 1,1, "ALARMA ARMADA "
PRINT AT 2,1, " "
RETURN
"---- ALARMON ----
SETMODE_ALARMON:
MODE = ALARMON
GOSUB SETSUBMODE_WAITING
HIGH GRNLED
HIGH REDLED
ATTEMPTS = 0 " AQUI NO IMPORTA CONTROLAR LOS INTENTOS
" LA ALARMA YA SE HA DISPARADO
PRINT AT 1,1, " ¡ A L A R M A !","#", DEC ATTEMPTS
PRINT AT 2,1, " "
RETURN
" ---- MODE_ENTRY_SOUND ----
MODE_ENTRY_SOUND:
SOUND SPEAKER, [110, 20, 100, 20] " Beep confirmación entrada en modo
RETURN
"==== SUBMODOS ====
"---- WAITING ----
" Espera la llegada de la primera tecla numérica pulsada
SETSUBMODE_WAITING:
SUBMODE = WAITING
PRINT $FE, $0C " Deshabilita cursor
GIE = 0 " Deshabilita interrupciones globales
WHILE GIE = 1 : GIE = 0 : WEND " y se asegura de que lo están
INPUT_TMR_RUNNING = FALSE
GIE = 1 " Habilita las interrupciones globales otra vez
"ATTEMPTS = 0 " No utilizar, hay que controlar los intentos
PRINT AT 2,1, " "
GOSUB CLEAR_PSW_BUFFER " Siempre parece ser necesario
RETURN
"---- PSWENTRY ----
" Se entra inmediatamente despues de la primera tecla pulsada y espera las restantes
SETSUBMODE_PSWENTRY:
SUBMODE = PSWENTRY
PRINT AT 2,1, "CLAVE: "
RETURN
"---- MODE_RETURN ----
" Retorno al modo actual
MODE_RETURN:
IF MODE = STANDBY THEN
GOSUB SETMODE_STANDBY " Se retorna al MODO STANDBY
ENDIF
IF MODE = ARMED THEN
GOSUB SETMODE_ARMED " Se retorna al MODO ARMED
ENDIF
IF MODE = ALARMON THEN
GOSUB SETMODE_ALARMON " Se retorna al MODO ALARMON
ENDIF
RETURN
"==== TECLADO ====
"---- Barrido ----
KEY_SCAN:
KEY = INKEY " Barrido del teclado
KEY = LOOKUPL KEY, [1,2,3,"A",4,5,6,"B",7,8,9,"C","*",0,"#","D",NO_KEY]
" Si no se detecta pulsación de tecla se borra el flag NEW_KEY.
IF KEY = NO_KEY THEN
NEW_KEY = TRUE
GOTO KEY_EXIT
ENDIF
" Es necesario prevenir que la mísma tecla sea leída dos veces.
" Se consigue mediante una rutina tipo cerrojo.
IF NEW_KEY = TRUE THEN " Si es la primera vez que pulsa
IF KEY <> LAST_KEY THEN GOSUB BEEP " esta tecla, se aplica antirrebote
ENDIF
KEY_EXIT:
LAST_KEY = KEY " Salvar el valor de la tecla
RETURN
"---- Verificación tecla numérica ----
" Se verifica si se ha pulsado una tecla numérica
" Si es así se utiliza.
CHKNUMKEY:
KEY_STATUS = FALSE " Precondición del resultado
IF NEW_KEY = TRUE THEN " Si la pulsación es nueva (distinta de la anterior)
IF KEY >= 0 THEN " Si estamos en el rango de 0 a 9
IF KEY <= 9 THEN
KEY_STATUS = TRUE " ES UNA TECLA NUMERICA
GOSUB BEEP
NEW_KEY = FALSE " Confirma que la tecla ha sido leída
RETURN
ENDIF
ENDIF
ENDIF
RETURN
"---- Borra el "buffer" de la clave y resetea el puntero ----
CLEAR_PSW_BUFFER:
FOR PSW_POINTER = 0 TO MAX_NO_NOS " Rutina para borrar la matriz de la clave
CLEAR PSW_STR[PSW_POINTER] " Borra el "buffer" de la clave
NEXT
CLEAR PSW_POINTER " Puesta a 0 del puntero
RETURN
"==== TEMPORIZACION ====
"---- Carga el temporizador de entrada de tecla ----
LOAD_INPUT_TIMER:
GIE = 0 " Desconecta las interrupciones globales
WHILE GIE = 1 : GIE = 0 : WEND " y se asegura de que lo están
INPUT_TMR = INPUT_TMR_VALUE " Recarga el Temporizador de entrada de tecla
INPUT_TMR_RUNNING = TRUE " y se asegura de que está en marcha
GIE = 1 " Vuelve a conectar las interrupciones globales
RETURN
"---- Acumulación Clave ----
PSW_ACC:
GOSUB CHKNUMKEY " Verifica teclas entre 0 y 9
IF KEY_STATUS = TRUE THEN " ¿Es una de las teclas 1 a 9?
GOSUB LOAD_INPUT_TIMER " SI, recarga temporizador entrada de tecla
PSW_STR[PSW_POINTER] = KEY " Se salva el número introducido en una matriz
INC PSW_POINTER " Incrementa el puntero
GOSUB DISPLAY_PSW
IF PSW_POINTER > MAX_NO_NOS-1 THEN " ¿Se han superado el máximo número de caracteres?
PSW_ENTERED = TRUE " SI, entonces tenemos una clave completa
GOSUB DISPLAY_PSW
PRINT $FE, $0C
ENDIF
ENDIF
RETURN
"---- Comparación de claves ----
PSW_COMPARE:
EEPSW_ADDRESS = 0
REPEAT " Verifica las claves
PSW_OK = TRUE " Se necesita precargar este flag para que la rutina funcione
CLEAR OFFSET " Se pone a 0 el offset
REPEAT " Bucle a través de todos los caracteres del número
EEPSW_DATA = EREAD EEPSW_ADDRESS + OFFSET " Primero leer FIRST READ IN THE EEPROM STORE PASSWORD
IF PSW_STR[OFFSET] <> EEPSW_DATA THEN " despues comparar con el de la clave
PSW_OK = FALSE " Este carácter no es igual, por tanto, continuar con el siguiente
ENDIF
INC OFFSET " Incrementa el offset
UNTIL OFFSET = MAX_NO_NOS
IF PSW_OK = TRUE THEN " ¡¡¡CORRECTO!!!
ATTEMPTS = 0 " Lo primero es poner a 0 el contador de intentos
PSW_TYPE = EEPSW_ADDRESS / MAX_NO_NOS " Su valor indica cuál de las dos claves es la correcta, o sea, normal= o master=1
"print at 1,1, " "
"print at 1,1, "PASSWORD ", @PSW_TYPE
RETURN
ENDIF
EEPSW_ADDRESS = EEPSW_ADDRESS + MAX_NO_NOS
UNTIL EEPSW_ADDRESS = MAX_NO_NOS * NO_PSWS
GOSUB ERROR_PSW
RETURN " Llegados aquí es porque no se han encontrado igualdades
ERROR_PSW:
INC ATTEMPTS "Se incrementa el contador de intentos
IF ATTEMPTS

AND MODE <> ALARMON THEN
PRINT AT 2,1, "QUEDAN ", DEC (3- ATTEMPTS), " INTENTOS "
SOUND SPEAKER, [60, 10, 80, 30] " Beep confirmación entrada en modo
ENDIF
GOSUB MODE_RETURN " Se retorna al Modo actual
GOSUB SETSUBMODE_WAITING " Se pasa al submodo WAITING
RETURN
"---- Clave normal ----
NORMAL_ENTRY_ROUTINE:
IF MODE = STANDBY THEN
GOSUB SETMODE_ARMED " Si se estaba en STANDBY, se pasa a ARMED
GOTO END_NER
ENDIF
IF MODE = ARMED OR MODE = ALARMON THEN GOSUB SETMODE_STANDBY " Si se estaba en ARMED, se pasa a STANDBY
END_NER:
PSW_OK = FALSE " Se pone a 0 el Flag de PASWORD OK
"ATTEMPTS = 0 " Se pone a 0 el CONTADOR DE INTENTOS
GOTO MAIN_LOOP
RETURN
"---- Clave "master" ----
MASTER_ENTRY_ROUTINE:
"SUBMODE = NEW_PWRD_ENTRY " Indica que se está en ese submodo
"GOSUB MODE_CONTROL " !!!LED FLASH RATE
GOSUB LOAD_INPUT_TIMER " Carga el temporizador de entrada
MASTER_ENTRY_LOOP:
GOSUB KEY_SCAN " Barrido del teclado
GOSUB PSW_ACC " Se acumula la clave
IF INPUT_TMR_RUNNING = FALSE THEN " ¿El temporizador ha rebasado?
GOSUB SETMODE_STANDBY " SI, regreso a modo STANDBY
GOTO MAIN_LOOP " y regreso al bucle principal
ENDIF
IF PSW_ENTERED = TRUE THEN " ¿Introducidos todos los números?
GOSUB PSW_COMPARE " SI, entonces compara la clave
PSW_ENTERED = FALSE " Borrar el flag
IF PSW_OK = FALSE THEN " Proceder sólo si es una nueva clave
CLEAR OFFSET
REPEAT " ESCRIBE LA NUEVA CLAVE EN LA EEPROM
EWRITE STD_PSW_ADDR + OFFSET,[PSW_STR[OFFSET]]
INC OFFSET
UNTIL OFFSET = MAX_NO_NOS
ENDIF
GOSUB SETMODE_STANDBY " Se regresa al modo STANDBY
GOTO MAIN_LOOP " y al bucle principal
ENDIF
GOTO MASTER_ENTRY_LOOP " Vuelta otra vez al bucle
"---- Visualización de la Clave ----
DISPLAY_PSW:
IF KEY<>255 THEN
PRINT $FE , $0F
CURSOR 2,PSW_POINTER + 15
IF HIDE_PSW = 0 THEN
PRINT DEC PSW_STR[PSW_POINTER-1] " Show the contents of each element.
ELSE
PRINT "*" " Visualiza "*" en lugar de dígitos
ENDIF
ENDIF
RETURN " ALL DONE SO RETURN
"---- Rutina de interrupción servicio temporizador ----
INTERRUPT_ROUTINE:
CLEAR T1CON.0 " PARO TMR1
TIMER1REG = TIMER1REG + TMR1_VAL " CARGA TMR1
SET T1CON.0 " ARRANQUE TMR1
IF INPUT_TMR_RUNNING = TRUE THEN " ¿Esta en marcha?
DEC INPUT_TMR " Poner en decimal
IF INPUT_TMR = 0 THEN INPUT_TMR_RUNNING = FALSE " Flag de llegada a 0.
ENDIF
CLEAR PIR1.0 " CLEAR TMR1 INTERRUPT FLAG
CONTEXT RESTORE " END OF INTERRUPT ROUTINE
"---- BEEP ----
BEEP:
SOUND SPEAKER, [120,1]
RETURN
"==== RUTINAS ==================================================================
END
"==== RUTINAS ==================================================================
"---------------------------------------------------
" CONSTANTES POR DEFECTO
"---------------------------------------------------
EDATA 1,4,4,7,9 " Clave NORMAL por defecto
EDATA 1,2,3,4,5 " Clave MASTER por defecto