Solo decirte que el programa ni el diagrama de flujo van a funcionar como vos esperas.
Algunos errores.. ORG 5
Nunca pongas ORG 5 o ORG 0x05, en estos pics no va haber problema por que los GOTO ocupan solo 1 posicion de memoria ( 0x4 ) pero podes llegarte a acostumbrar mal, en los PIC18 los GOTO ocupan 2 espacios de memoria ( 0x04 y 0x05 el equivalente a ese caso, aunque los vectores son 0x8 y 0x18), el mismo compilado se va a encarga de acomodarlos asi que mejor ahorranos eso. Es mas que nada un error de aplicacion o de entendimiento de como funciona el programador, no va a cambiar como funciona tu programa.
No un error per se, pero:
CLRF INTCON ;limpiamos todo INTCON por si acaso
BSF INTCON,GIE ;habilitamos interrupciones globales
BSF INTCON,T0IE ;habilitamos la interrupcion por ovverflow del TMR0
BSF INTCON,INTE ;habilita la interruñcion por variacion de un bit de forma externa(boton)
BSF INTCON,RBIE ;interrupcion por cambio de puerto
Asegurate que las interrupciones siempre sea lo ULTIMO a activar y configurar, el bit GIE tambien. que sea el ultimo. Para lo que necesitas hacer aca y como lo queres hacer no es necesario el uso de la interrupcion externa por RB0 ( INTE ) asi que deberias sacarlo.
BTFSS COLUMNA,N_COLUMNA ;Comprueba en que columna estamos
Esta instruccion deberia ser un error. Luego de la coma no se puede poner un registro... Solo una constante que va de 0 a 7.
Consejos:Usa W y F, sacando algunas instrucciones:
MOVF PORTB,0 ;el contenido de portB (entrada)se lo pasas a FILA
INCF TECLA,1 ;tecla +1
RLF FILA,1 ;rota a la izquierda fila
Es mas facil recordar W y F como destino
MOVF PORTB, W ;el contenido de portB (entrada)se lo pasas a FILA
INCF TECLA, F ;tecla +1
RLF FILA, F ;rota a la izquierda fila
La conversion W a 0 y F a 1 estan en el .inc del PIC agregado.
InterrupcionAunque como no tenes nada en LOOP no vas a tener problemas, es bueno que te acostumbres a esto. Por que el dia que quieras agregar algo a LOOP y usar una interrupcion vas a tener problemas. La interrupcion funciona asi:
A la izquierda seria el programa de LOOP, y cuando ocurre una interrupcion este salta al vector de interrupcion y guardando la direccion anterior, cuando se sale se usa la instruccion RETFIE, que funciona como un RETURN y ademas activa de nuevo las interrupciones ( bit GIE ). No existe ningun CALL o GOTO desde la rutina LOOP a la interrupcion, el micro cuando ocurre alguna solo hace eso. Lo que te decia es lo siguiente.
LOOP
BSF STATUS, Z
BTFSS STATUS, Z
BSF PORTA,5
GOTO LOOP
Ese simple ejemplo si no guardas el valor de W y STATUS al entrar a la interrupcion traeria problemas. Sigamos las instrucciones, Primero pongo a 1 Z y luego pregunto si esta en 1. lo normal a pensar es que siempre va a saltar al BSF del PORTA. Pero si tenes una interrupcion y como dije no guardas el valor de W y STATUS, podes tener problemas. Suponete que la interrupcion ocurrio luego del BSF STATUS, Z . Entro a la interrupcion, hizo operaciones cambiando los registros STATUS y poniendo a 0 a Z, cuando sale de la interrupcion vuelve a donde estaba antes, es decir a la siguiente instruccion ( BTFSS ) pero esta ves Z es 0 culpa de tu interrupcion, en este ejemplo es "simple" pero si pensas en una suma por ejemplo en que antes de sumar entra a la interrupcion y le cambia el valor a W, es peor. Hay que evitar eso. Asi que lo primero es poner esos registros de proteccion.
Microchip provee el codigo para esto en sus datasheets, si buscas por W_TEMP lo vas a encontrar. Te quedaria algo asi, simplificandolo un poco:
ISR
MOVWF W_TEMP ; Por si las dudas
SWAPF STATUS, W
CLRF STATUS
MOVWF STATUS_TEMP
BTFSC INTCON, T0IF
GOTO INT_TIMER
BTFSC INTCON, RBIF
GOTO INT_TECLADO
SALIDA_ISR:
SWAPF STATUS_TEMP, W
MOVWF STATUS
SWAPF W_TEMP, F
SWAPF W_TEMP, W
RETFIE
Observa que al entrar a la interrupcion guardo los registros W y STATUS a W_TEMP y STATUS_TEMP. Y al salir hago lo contrario, Por que usar SWAPF ? por que esa instruccion no modifica los valores de las banderas de STATUS!. Observa al final el RETFIE tambien.
Al medio tenes la rutina de interrupcion, como hay 2 interrupciones, debo saber exactamente cual es la que ocurrio, si el teclado o si el Timer. Entonces pregunto por cada flag y actuo segun ello.
¿Entonces que nos queda ver?
Hay varias cosas..
- Timer y su valor de carga / preescaler
- Barrido de teclado ya que es matricial
- Rutina de interrupcion de teclado
El Timer , su valor y su preescaler.Cuando te pregunte sobre las frecuencias no fue coincidencia. Como sabes al Timer hay que cargarlo con un valor y cuando se produce el overflo 0xFF a 0x00 se entra a la interrupcion.
Podria haber calculado para cada preescaler el valor necesario, pero opte por buscar una solucion mas practica.
Use una calculadora online
http://eng-serve.com/pic/pic_timer.htmlAhi carge la frecuencia del oscilador ( 4Mhz = 4000000) y luego seleccione el preescaler en 1:16, fui moviendo la barra hasta encontrar los valores que me dieran las frecuencias correspondientes. No me dieron exactas, ya que para eso deberia complicarlo mas, pero no creo que nadie lo note si tiene +/- 1Hz.
Los valores a pesar que yo ya los mire, te los dejo a vos para que sepas cuales son, asi podes cargar tu DO1,RE,MI,FA,SOL,etc.
Esos valores debes cargarlos luego al timer. La funcion es bastante simple
INT_TIMER
; Tomar valor de un registro que tiene el valor a cargar y pasarlo a W
; De W al registro del TMR0
; Borro flag del TMR0
GOTO SALIDA_ISR
Por que tomar los datos de 1 registro y pasarlo al Timer?, por que vos tenes muchas notas, seria muy complicado tener que crear el codigo para todas. Entonces supongamos que la interrupcion del teclado detecta la tecla y dependiendo del valor de la tecla lo convierte en el valor a cargar en el timer, leugo procede a guardarlo en ese registro. Cuando ocurra la interrupcion del timer a este no le importa la tecla que se presiono, siempre va a cargar del mismo lugar. Y de esa forma simplificas el codigo del Timer.
Los comentarios en el programa que di tiene su equivalente a 1 instruccion ASM cada uno, un MOVF , MOVWF y BCF es cada una de ellas.
Barrido del tecladoYa sigo...