TODOPIC
Microcontroladores PIC => Todo en microcontroladores PIC => Mensaje iniciado por: qrppablo en 17 de Diciembre de 2014, 22:14:57
-
YA SOLUCIONÉ EL PROBLEMA, HABIA COMETIDO UN ERROR CON LAS FLAGS. POR FAVOR NO CONTESTAR, GRACIAS!
Buenas, soy nuevo en el foro y tengo un problema que paso a detallar:
Estoy tratando de modificar un programa en asm para el PIC 16F877A que tiene interrupciones con contador, funciona de esta manera: Cada veinte interrupciones se guardan en EEPROM una serie de bytes, cuando termina, se reinicia el contador y asi sucesivamente. Es una radio, la frecuencia del display se graba mas o menos cada 3 segundos en EEPROM para que cuando se apague y se vuelva a encender aparezca en el display la frecuencia que se había usado antes del apagado.
El codigo es el siguiente:
T1CON_INIT EQU 0x31 ; Timer1 - Prescale of 8 and enable the timer
INTCON_INIT EQU 0xC0 ; Interrupt Control- Enable Global and Peripheral
COUNT_OF_INTS EQU 20 ; Interrupts before saving
int_counter ; Used to count number of interrupts
;
;
ORG 0x0000
reset_entry
goto Start ; Jump around the band table to main program
ORG 0x0004
interrupt_entry
goto interrupt ; Go to interrupt routine
;
;
Start
movlw count_of_ints ; Get number of interrupts before saving
movwf int_counter ; Initialize the counter
bcf PIR1,TMR1IF ; Reset the Timer 1 interrupt flag
clrf TMR1L ; Set Timer 1 Low and
clrf TMR1H ; Timer 1 High to maximum count
Bank1
bsf PIE1,TMR1IE ; Enable Timer 1 overflow interrupt
Bank0
movlw INTCON_INIT ; Get GIE and PEIE bits and enable
movwf INTCON ; Global and Peripheral interrupts
movlw T1CON_INIT ; Get T1CKPS1, T1CKPS0 and TMR1ON bits
movwf T1CON ; and enable Timer 1 with prescale of 8
;
;
interrupt
movwf save_w ; Store W in save_w
swapf STATUS,w ; Move STATUS to W
clrf STATUS ; Force to Bank 0
movwf save_s ; Store STATUS in save_s
movf FSR,w ; Move FSR into W
movwf save_fsr ; Save FSR
clrf T1CON ; Stop the timer by clearing T1CON
bsf int_flags,T1_INT ; Set the flag for poll_encoder
bcf PIR1,TMR1IF ; Reset the interrupt flag
clrf TMR1L ; Reload Timer 1 Low
clrf TMR1H ; and Timer 1 High
movlw T1CON_INIT ; Restart Timer 1 with prescale = 8
movwf T1CON ; Timer 1 control
movf save_fsr,w ; Get saved FSR
movwf FSR ; Restore the FSR
swapf save_s,w ; Get saved status
movwf STATUS ; Restore STATUS register (now in pre-interrupt Bank)
swapf save_w,f ;
swapf save_w,w ; Restore W register
retfie
;
;
process_int
decfsz int_counter,f ; Decrement the interrupt counter
goto int_exit ; Not zero, exit
; Guarda freq actual en EEPROM
call VFOA_save
movlw count_of_ints ; Initialize the counter
movwf int_counter ; to start over
int_exit
bcf int_flags,T1_INT ; Reset the interrupt flag bit
goto proc_int_rtn ; Back to poll_encoder
Se carga el numero de veces que tienen que realizarse las interrupciones (20) en la variable int_counter, cada vez que se realiza una interrupción, en la rutina process_int se le resta un bit a int_counter, 20 veces hasta llegar a 0, cuando se llega a 0 se llama a la rutina de guardado de los bytes que equivalen a la frecuencia que muestra el display (no puse esta rutina aqui porque creo que no viene al caso). Al terminar de guardar se vuelve a cargar el 20 en int_counter y se reinicia el conteo y asi sucesivamente. En otra rutina en loop va esta otra parte del codigo:
btfsc int_flags,T1_INT ; Has a Timer 1 interrupt occurred
goto process_int ; Yes, go process it
proc_int_rtn ; Return from interrupt processing
Que es el que va a process_int cada vez que se produce una interrupción.
El problema es que cuando quiero grabar en EEPROM otros datos no puedo, no en la rutina de interrupción, si no por medio de una rutina que se acciona al presionar un botón, supongo que no se puede porque las interrupciones estan activadas? y además de eso cada vez que el conteo llega a 0 se llama a una rutina para grabar en EEPROM?
Ya probé tratando de desactivar las interrupciones pero no se si lo hago mal, ya que cada vez que quiero guardar algún dato en EEPROM no se guarda nada ni tampoco lee nada.
La manera en que intenté desactivar las interrupciones fueron las siguientes:
bcf PIE1,TMR1IE <- Primero probé con poner a 0 TMR1IE justo antes de llamar a guardar en EEPROM, desactiva las interrupciones pero no guarda ni lee.
clrf T1CON <- Despues probé con detener el timer justo antes de llamar a guardar en EEPROM, desactiva las interrupciones pero tampoco guarda ni lee.
Los direccionamientos en EEPROM son correctos pero no logro entender por que no guarda ni lee nada... :5] :5] :5]
Resumiendo: necesito frenar las interrupciones para poder grabar manualmente en EEPROM ya que con las interrupciones habilitadas no logro hacerlo.
Agradezco su ayuda.
YA SOLUCIONÉ EL PROBLEMA, HABIA COMETIDO UN ERROR CON LAS FLAGS. POR FAVOR NO CONTESTAR, GRACIAS!
-
Primero voy a expresar mi opinion pero obviamente esto depende de vos.
Comenzamos con que estas haciendo algo mal de concepto que es escribir seguido en una EEPROM, recorda que tiene una cantidad limitada de escrituras, la forma en que queres hacerlo se puede hacer de otra forma, puede ser sensando la tension de entrada y determinando cuando falte asi se ejecuta un codigo que guarde en EEPROM, pero mientras este "encendida" o conectada solo use la RAM. Tambien si no queres ir o no queres preocuparte por el hardware podes hacer lo mismo pero preguntar si la frecuencia cambio (el valor a guardar con una referencia que tengas en RAM), asi reducirias la cantidad de grabaciones a la EEPROM. De todas formas si ocurre que en esos 3 segundos, cambias de frencuencia y se corta la alimentacion, puede que no llegues a grabar la frecuencia nueva por que no llego a tu interrupcion numero 30. Tambien podes hacer que solo grabe a la EEPROM cuando se dejo mas de 3 segundos en una frecuencia, y obviamente grabas 1 sola ves. Eso depende totalmente de vos es solo mi opinion.
Por otra parte. Tenes el codigo incompleto o solo pusiste una parte?
Al fin de la bandera Start no veo que dirija a otra seccion, sino que el micro seguiria ejecutando lo de la bandera interrupt, es decir faltaria un goto ... Pero no se si es que esta incompleto el codigo.
Y creo que lo interesante seria ver como es que estas guardando los datos a la EEPROM, en especial como seteas la direccion/datos etc. Ademas para desactivar las interrupciones simplemente deshabilita la interrupciones globales (GIE,INTCON).
En el datasheet hay un codigo para la EEPROM, es ese el que estas usando (Pag 43) ? ( En ese desactiva las interrupciones cuando debe )
Escribir:
BSF STATUS, RP1 ;
BSF STATUS, RP0 ;Bank 3
BTFSC EECON1, WR ;Wait for
GOTO $-1 ;write to finish
BCF STATUS, RP0 ;Bank 2
MOVF ADDR, W ;Address to
MOVWF EEADR ;write to
MOVF VALUE, W ;Data to
MOVWF EEDATA ;write
BSF STATUS, RP0 ;Bank 3
BCF EECON1, EEPGD ;Point to Data memory
BSF EECON1, WREN ;Enable writes
;Only disable interrupts
BCF INTCON, GIE ;if already enabled,
;otherwise discard
MOVLW 0x55 ;Write 55h to
MOVWF EECON2 ;EECON2
MOVLW 0xAA ;Write AAh to
MOVWF EECON2 ;EECON2
BSF EECON1, WR ;Start write operation
;Only enable interrupts
BSF INTCON, GIE ;if using interrupts,
;otherwise discard
BCF EECON1, WREN ;Disable writes
Leer:
BSF STATUS, RP1 ;
BCF STATUS, RP0 ;Bank 2
MOVF ADDR, W ;Write address
MOVWF EEADR ;to read from
BSF STATUS, RP0 ;Bank 3
BCF EECON1, EEPGD ;Point to Data memory
BSF EECON1, RD ;Start read operation
BCF STATUS, RP0 ;Bank 2
MOVF EEDATA, W ;W = EEDATA
-
Que tal KILLERJC, gracias por tu respuesta.
Al codigo lo tengo entero, solamente que es bastante extenso y puse solamente las partes que creo que incumben al problema. Entiendo que escribir seguido en una EEPROM sea un error porque tiene un número limitado de escrituras y también repito que estoy tratando de modificar un código que descargue de internet y todo eso ya estaba escrito y funciona de esa manera, estoy tratando de entender como funcionan las interrupciones y como escribir y leer en/desde EEPROM. Pasar todo a RAM es el doble de trabajo ya que tendría que modificar mucho el programa (que llegado el momento seguramente lo haré ya que me interesa).
He probado también desabilitar las interrupciones globales INTCON y GIE antes de escribir en EEPROM pero tampoco guarda ni lee.
De esta manera graba lo descrito en el mensaje anterior (pongo el codigo en segmentos):
ORG 0x2100
DATA ref_osc_0
DATA ref_osc_1
DATA ref_osc_2
DATA ref_osc_3
; Freq de inicio VFO-A | EEADR 0x04
DATA 0x20 ; 17 meters
DATA 0xB2
DATA 0x13
DATA 0x01
; Freq de inicio VFO-B | EEADR 0x08
DATA 0xC0 ; 40 meters
DATA 0xCF
DATA 0x6A
DATA 0x00
; Freq guardado temp de RIT | EEADR 0x0C
DATA 0x40
DATA 0x77
DATA 0x1B
DATA 0x00
; Freq guardado de RIT TX | EEADR 0x10
DATA 0x00
DATA 0x3F
DATA 0xAB
DATA 0x01
; VFO X start up bit | EEADR 0x14
DATA 0x00
; La frecuencia se guarda en cuatro bytes, por ejemplo en EEADR 0x04, 0x05, 0x06, 0x07
; y lo hace de esta manera
VFOA_save
movlw 0x04
movwf ee_addr ; ee_addr = 0x04
movf ee_addr,w ; Carga dirección a guardar
Bank2
movwf EEADR ; En EEADR
Bank0
movf freq_0,w ; Record the first
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_1,w ; Record the second
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_2,w ; Record the third
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_3,w ; Record the fourth
Bank2
movwf EEDATA ; osc
call write_EEPROM
;
;
write_EEPROM
Bank3
btfsc EECON1,WR ; Wait for write to finish
goto $-1 ; Jump back one
bcf EECON1,EEPGD ; Point to Data Memory
bsf EECON1,WREN ; Enable Write
movlw 0x55 ; Write 0x55 and 0xAA to EEPROM
movwf EECON2 ; control register2, as required
movlw 0xAA ; for the write
movwf EECON2 ;
bsf EECON1,WR ; Set WR to initiate write
bit_check
btfsc EECON1,WR ; Has the write completed?
goto bit_check ; No, keep checking
bcf EECON1,WREN ; Clear the EEPROM write enable bit
Bank2
incf EEADR,f ; Increment the EE write address
Bank0
return ; Return to the caller
; con incf EEADR,f se asegura de guardar los 4 bytes que conforman la frecuencia en las direcciones de EEPROM 0x04, 0x05, 0x06, 0x07
Hasta ahi funciona perfecto.
El problema es cuando desactivo las interrupciones o el timer e intento guardar cuando presiono un botón, el codigo para eso es este:
RIT
btfss PORTC,RIT_State ; Is RIT Line low? (RIT_State corresponde al PORTC,6 donde está conectado el botón y está declarado al inicio)
goto RIT_on ; Yes, select RIT Mode
return ; No, continue
; Yes, restore pre RIT base frequency
movlw 0x0C
movwf ee_addr ; ee_addr = 0x0C
movf ee_addr,w ; Carga dirección a leer
Bank2
movwf EEADR ; En EEADR
Bank0
call read_OSC
movf EEDATA,w
Bank0
movwf freq_0 ; Primer byte - LSB
call read_OSC
movf EEDATA,w
Bank0
movwf freq_1 ; Proximo byte
call read_OSC
movf EEDATA,w
Bank0
movwf freq_2 ; Proximo byte
call read_OSC
movf EEDATA,w
Bank0
movwf freq_3 ; Ultimo byte - MSB
return
RIT_on
; Save current base frequency
bcf INTCON,GIE ; Desactivo interrupciones
bcf INTCON,PEIE ;
movlw 0x0C
movwf ee_addr ; ee_addr = 0x0C
movf ee_addr,w ; Carga dirección a guardar
Bank2
movwf EEADR ; En EEADR
Bank0
movf freq_0,w ; Record the first
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_1,w ; Record the second
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_2,w ; Record the third
Bank2
movwf EEDATA ; osc
call write_EEPROM ; byte
movf freq_3,w ; Record the fourth
Bank2
movwf EEDATA ; osc
call write_EEPROM
return
En este caso, cuando se presiona el boton se pone el PORTC,6 en LOW, se debería guardar la frecuencia en las direcciones 0x0C, 0x0D, 0x0E, 0x0F.
Esto permite hacer una sintonía fina en la radio y cambiar la frecuencia. Cuando el botón se suelte, se devuelve la frecuencia guardada en las direcciones anteriores a freq_0, freq_1, freq_2 y freq_3 que conforman la frecuencia que se ve en el display, para volver a la frecuencia anterior a la sintonía fina, la cuestión es que el grabado no se produce. Como lo se? porque para probarlo desactivo las interrupciones pero no las vuelvo a activar cuando suelto el botón. Esto permite que cuando suelto el boton y vuelvo a cambiar de frecuencia, al volver a presionar el botón la frecuencia vuelve a ser la misma de antes. Para graficarlo, por ejemplo sería asi:
Frecuencia con el PORTC,6 en low y con interrupciones activas:
14.010.000 khz
Frecuencia con el PORTC,6 en high y sin interrupciones:
14.010.000 khz
Si cambio de frecuencia por ejemplo con el PORTC,6 en high a 14.015.000 khz y suelto el botón, la frecuencia vuelve a ser 14.010.000 khz (Perfecto, pareciera que se había guardado)
Vuelvo a cambiar de frecuencia, por ejemplo a 14.020.000 khz.
Al presionar el botón la frecuencia debería ser 14.020.000 khz pero NO, sigue siendo 14.010.000 porque los bytes no se guardan y cada vez que cambio de frecuencia y presiono el botón, la misma vuelve a ser 14.010.000 khz.
Perdon si se hace muy extenso esto pero no tengo otra manera mas simple de explicarlo.
La rutina para leer de EEPROM es la misma que uso para leer la frecuencia que se guarda con las interrupciones al prender la radio:
read_OSC
Bank3
bcf EECON1,EEPGD ; Point to Data Memory
bsf EECON1,RD ; Request the read
Bank2
incf EEADR,f ; Increment the read address
return ; Return to the caller