Autor Tema: Problemas con Proteus, interrupciones y Overflows!  (Leído 7114 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Problemas con Proteus, interrupciones y Overflows!
« en: 21 de Octubre de 2013, 23:50:22 »
Buenas noches a todos!

Estoy trabajando en un proyecto en Proton IDE 2.0.0.8 + Proteus 8, y la verdad que me tiene mal ya, je.
El PIC que utilizo es un 16F876A, y lo estoy programando en BASIC.
El proyecto consiste en hacer un contador, que posea  6 displays 7-segmentos para mostrar el valor de cuenta. (Así me lo pidieron, no es algo sobre lo que pueda decidir). Además, debe hacer algunas funciones extra, como levantar una salida al llegar a determinado valor seteado.

Para esto escribí un programa bastante sencillo, que ejecuta un bucle principal para hacer el multiplexado, y se encarga de modificar el valor de la cuenta mediante interrupciones.
Mientras el código del programa no superaba determinada cantidad de líneas, andaba "relativamente" bien. Con esto quiero decir que el programa ejecutaba las funciones que debía, pero al visualizarlo en el Proteus, los LEDs se veían parpadear. (Digamos que el multiplexado no era tan efectivo. Pero al intentar disminuír el tiempo de Delay entre bucle y bucle, directamente no hacía nada. Sólo mostraba 000000 todo el tiempo). Por esto, creí que el problema estaba en que el Proteus no se aguantaba la simulación, y lo dejé ahí.

Ahora bien, el problema que me surgió fue al aumentar la cantidad de líneas de código. Al superar las 2000 (aprox.), comienza a arrojar fallas del tipo:
STACK OVERFLOW.
En uno de los hilos del foro leí que una recomendación era colocar el segmento de manejo de interrupciones inmediatamente después de las variables. Lo hice y me apareció:
[PIC 16CORE]PC=0x014D. Stack underflow executing RETFIE Instruction

También leí que puede deberse a que haya muchas operaciones en el segmento de las interrupciones, y que eso produce el desbordamiento de la pila. Probé sacar todas las operaciones, sólo levantar una flag que utilizara después, durante el bucle principal... pero no. Siguió teniendo los mismos problemas.
No termino de entender la causa del problema. Si es algo en la forma de atender las interrupciones, o a lo mejor el código.

Les agradecería algún comentario sobre la forma de resolver las interrupciones y el bucle principal. Sólo para saber si es factible, o si de entrada ya viene mal encarado. Si esto está bien, entonces tendré que revisar el resto del código.
 A continuación les dejo lo más importante del código. (Si quieren el archivo completo, lo subo, pero en principio no quiero aburrirlos con 600 líneas de potenciales errores).

Gracias desde ya por tomarse el tiempo en leer esto.

Código: [Seleccionar]
Device=16F876A
 Config XT_OSC, PWRTE_ON, CPD_OFF, WDT_OFF, BODEN_OFF, LVP_OFF
;XT_OSC              - oscilador exterior
;PWRTE_ON            - Reset de inicio conectado
;CPD_OFF             - Codigo de proteccion eeprom desactivado
;WDT_OFF             - Perro guardian desconectado
;BODEN_OFF           - No actua el reset por bajada de tension
;LVP_OFF             - programacion de bajo voltaje desconectado

Xtal=4
All_Digital = TRUE
INTCON=  %10011000     'Habilito las interrupciones globales, y en particular la interrupción por Hardware (RB0)                 
CMCON  = 7             'Desactivo los comparadores A/D
TRISA  = %000000       '1=ENTRADA    0=SALIDA
TRISB  = %11110111     'PORTB son todas entradas, excepto RB3, que es la salida del relé
TRISC  = %00000000     'PORTC son todas salidas
OPTION_REG = %01101000

Symbol INTF   = INTCON.1         ' RB0 External Interrupt Flag.
Symbol INTE   = INTCON.4         ' RB0 External Interrupt Enable.
Symbol GIE    = INTCON.7         ' Global Interrupt Enable.
Symbol INTEDG = OPTION_REG.6     ' Flag = 0 int. por flanco bajada. Flag = 1 int. por flanco subida.
Symbol RBIF = INTCON.0           ' RB Port CHANGE Interrupt Enable
On_Interrupt GoTo InterrupEncoder   ' Interrupción por Hardware (es la más rápida).

GIE    = 1                       ' Activa interrupciones generales.
INTE   = 1                       ' Activa la interrupción externa RB0/INT.
INTEDG = 1                     ' Hace que las interrupciones se habiliten por flanco de subida.                       
All_Digital = TRUE

Bucle_General:
If presionado==0 Then      'Mientras no haya interrupciones
'--------------------------- Inicio Bucle Display cuenta------------------------------------------------
Digito= Dig Contador1,i             'tomo el valor i-ésimo de la variable Contador1
'Convierto de decimal a 7-seg, dígitos ánodo común
Select Digito
Case 0
PORTC=$C0
Case 1
PORTC=$F9
Case 2
PORTC=$A4
Case 3
PORTC=$B0
Case 4
PORTC=$99
Case 5
PORTC=$92
Case 6
PORTC=$82
Case 7
PORTC=$F8
Case 8
PORTC=$80
Case 9
PORTC=$90
End Select

Select i              'Multiplexo los transistores
Case 0
PORTA=%011111
If Contador1<0 Then
PORTC=$BF
End If
Break
Case 1
PORTA=%101111
Case 2
PORTA=%110111
Case 3
PORTA=%111011
Case 4
PORTA=%111101
Case 5
PORTA=%111110
End Select

If temporizaMODO=1 Then      'Seccion para contar que MODO esté presionado durante x tiempo
    If PORTB.4==1 Then           ' sin por ello dejar de mostrar el display
    tiempo=tiempo+1
    Else
    tiempo=0
    temporizaMODO=0
    End If
        If tiempo==20 Then
        GoTo Configuracion
        End If
End If

i=i+1
If i==6 Then            'Cuando mira el 6to dígito, recomienza
i=0
End If

If Contador1>setpoint Then        'Prende el relé si se supera la cuenta
PORTB.3=1
Else
PORTB.3=0
End If

If PORTB.2==1 Then     
GoTo Contador
End If

DelayMS 5
'--------------------------- Fin Bucle Display cuenta------------------------------------------------


Y la foto del circuito:
http://imageshack.us/photo/my-images/12/dz61.jpg/


Saludos!

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #1 en: 22 de Octubre de 2013, 16:54:31 »
Estuve mirando un poco más el problema. A lo mejor este thread debería ir en el subforo Proteus (hasta ayer no lo había visto...  :oops:). Si es así, por favor alguien avíseme, así lo posteo donde va. Resumo un poco lo que concluí hasta ahora:

- -Cuando la rutina de manejo de interrupciones la pongo al final de todo, no tira el problema de "Stack underflow executing RETFIE Instruction", el cual SÍ tira cuando muevo la misma rutina, y la pongo arriba de todo, inmediatamente a continuación de la declaración de variables. No termino de entender la causa de esto, pero por lo menos no molesta en las simulaciones.

-Al ejecutar el código con "pocas líneas" (<2000 'program words'), funciona correctamente. (Al menos, no tira errores al simularlo).

-Cuando uso el código completo, y lo simulo, me genera una seguidilla de errores:
1) Varios "Stack overflow pushing return address of interrupt"
2) seguidos por algunos Stack overflow executing CALL instruction
3) y finalmente algunos Stack UNDERflow esecuting RETURN instruction.
Estos últimos se repiten varias veces.


Lo que me queda claro... es que hay stack overflow....  :(

Esta es la rutina de manejo de interrupciones:

Código: [Seleccionar]
'Manejo de interrupciones
InterrupEncoder:             
    Context Save              ' Salva el contexto de los registros antes de operar con la interrupción.               
    If INTF==1 Then'Si la interrupción fue por pulso en el encoder
'--------------------------- Interrupción de Encoder ------------------------------------------------   
    If PORTB.0 = 1    Then    ' Si RB0 se puso en 1 (flanco de subida),
       INTEDG  = 0            ' entonces activar la siguiente interrupción por flanco de bajada.
       If PORTB.1 = 1 Then    ' Si RB1 está en 1,
          Inc Contador1       ' entonces incrementar el contador .
       EndIf
    Else                      ' Si RB0 se puso en  0 (flanco de bajada), 
       INTEDG  = 1            ' entonces activar la siguiente interrupción por flanco de  subida.
       If PORTB.1 = 1 Then    ' Si RB1 está en 1,
          Dec Contador1       ' entonces decrementar el contador .
       EndIf
   INTF = 0                  ' Borra el "flag" de la interrupción RB0/INT para poder permitir la sig interrupción cuando pase.
    EndIf
     
'--------------------------- Interrupción de Encoder ------------------------------------------------

'--------------------------- Decodificador de Switches -------------------------------------------   
    ElseIf RBIF==1 Then 'Si la interrupción se produjo por algún otro de los pulsadores.
    'Averiguo si se seteó algún valor.
    PuertoBactual=PORTB
    PinCambiado=PuertoBactual^PuertoBanterior
    If PinCambiado.4==1 Then          'Esto implementado con los ELSE if, establece las jerarquías
    secuencia=4      'Secuencia de Modo
    ElseIf PinCambiado.5==1 Then
    secuencia=5
    ElseIf PinCambiado.6==1 Or PinCambiado.7==1 Then
    secuencia=6
    End If
    presionado=1 'Seteo variable estableciendo que algo se presionó
    RBIF=0       'Borro la flag de interrupción
    PuertoBanterior=PuertoBactual   'Guardo el valor anterior del puerto
 '--------------------------- Decodificador de Switches -------------------------------------------
    End If     
    Retfie
    'Context Restore           ' Restablece el contexto de los registros tal como estaban antes de la interrupción.
   ' Resume

La idea es utilizar dos tipos de interrupciones:
INTF (pulsos en RB0, por parte de un encoder)
RBIF (pulsos en RB7 a RB4, por parte de diferentes pulsadores)

Dependiendo el pulsador, debe levantar una variable que haga algo diferente en cada caso.

Quisiera su opinión sobre la forma en que está manejado esto. No termino de entender por qué se produce el stack overflow, dependiendo de la cantidad de líneas del programa. Cualquier sugerencia es bienvenida.
Desde ya, gracias por leerlo!

Saludos

Martín

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #2 en: 25 de Octubre de 2013, 13:57:28 »
Estuve investigando un poco y encontré lo siguiente, a lo mejor le sirve a alguien para una situación similar:

Mi programa estaba escrito con varios GoTos. Esta instrucción, utilizada como:

GOTO k,

simplemente carga la constante k (de 9 bits), en el registro PC (el Program Counter, que direcciona).

El problema que aparece con esto es que la constante k es de sólo 9 bits, mientras que el registro PC es de 11 bits. Los dos bits faltantes, (bits 9 y 10 del PC) son tomados respectivamente de los bits de selección de página PA0 y PA1 de la palabra de estado (STATUS). (Por lo menos para los 16F8x, no sé para otros PICS)

Esta "forma rara" de direccionar hace que la memoria de programa aparezca como dividida en páginas de 512 posiciones. Entonces uno debe tener en cuenta que antes de ejecutar una instrucción GOTO es posible que haya que programar los bits PA0 y PA1.


Estos 11 bits del PC, te permiten direccionar una memoria de programa de 2K. Yo creo que acá estaba mi problema. Al momento de sobrepasar los 2k de líneas de código, el programa cargaba una dirección mayor a la que podía manejar en el PC, por lo tanto desbordaba el puntero, y me indicaba a cualquier lado.  :?

Lo que hice hasta ahora fue sacarle todas las instrucciones posibles a las interrupciones (prácticamente lo único que hacen es levantar flags). Por otro lado, disminuí bastante las líneas de código, utilizando subrutinas (con GOSUB y RETURN en lugar de GOTO).

Por lo pronto el programa me funciona. Lo que sucede es que al "ahorrar espacio", nuevamente tengo menos de 2k de program words usadas.
Me parece una solución bastante mediocre. Probablemente, lo que tendría que hacer es:
Conociendo las direcciones exactas en las que arranca cada sección, programar los bits SATUS.0 y STATUS.1 para que al hacer el salto vaya a donde debe.

Si alguien tuvo una experiencia similar (la lógica me dice que debe ser un problema bastante común, por lo menos con los PICS 16F8x.... no?), le agradezco cualquier comentario o sugerencia.

Saludos!

Martín
 

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #3 en: 25 de Octubre de 2013, 14:31:24 »
Hola.

Este tema se ha tratado varias veces en el foro y hay un tutorial de Leon_PIC en donde tratamos este tema.

El datasheet de tu micro lo dice en la sección 2.3:

Citar
All PIC16F87XA devices are capable of addressing a continuous 8K word block of program memory. The CALL and GOTO instructions provide only 11 bits of address to allow branching within any 2K program memory page. When doing a CALL or GOTO instruction, the upper 2 bits of the address are provided by PCLATH<4:3>. When doing a CALL or GOTO instruction, the user must ensure that the page select bits are programmed so that the desired program memory page is addressed. If a return from a CALL instruction (or interrupt) is executed, the entire 13-bit PC is popped off the stack. Therefore, manipulation of the PCLATH<4:3> bits is not required for the RETURN instructions (which POPs the address from the stack).

Cada página tiene un tamaño de 2048 (0x800) instrucciones. Si estás programando en BASIC, debería ser el compilador quien se encargue de generar el código correcto para que no tengas problemas.

Para que quede claro, GOTO k permite 11 bits para cargar la dirección destino. Los otros 2 bits restantes provienen de los bits PCLATH<4:3>. No tienen nada que ver los bits de selección de página del registro STATUS. No confundamos. El PCLATH está relacionado a la memoria de programa(FLASH), mientras que los bits STATUS<RP1:RP0> están relacionados a la memoria RAM.

La memoria de programa está paginada cada 2KWords (2048 instrucciones de programa).
La dirección final (13 bits) que se carga al hacer un CALL o GOTO provienen de:
los 2 bits de mayor peso provienen del valor actual de los bits PCLATH<4:3> y los 11 de menor peso se cargan desde la instrucción GOTO/CALL.

Y qué pasa con el RETURN/RETFIE/RETLW también tengo que tener respecial cuidado antes de retornar de una subrutina?
NO. El STACK de estos micros es de 8 niveles y cada uno de 13  bits. Es decir que cuando haces un CALL, se guardan TODOS los 13 bits donde debe retornar el PC cuando se ejecute el RETURN/RETFIE/RETLW.

Ahora, en cuanto a la memoria RAM:
Está seccionada en BANCOS, cada uno de 0x80 registros (128 Bytes).
La posición de memoria sobre la cual se trabaja (9 bits) provienen de:
los 2 bits de mayor peso provienen de STATUS<RP1:RP0> y los 7 de menor peso se cargan desde el valor de la instrucción mísma.

Si por ejemplo, hago:
Código: ASM
  1. BCF       STATUS,RP1
  2. BSF       STATUS,RP0
  3. MOVF    0x20,W
estaré leyendo la dirección 0xA0 y copiando su valor a W.

La fórmula para calcular la dirección final de memoria a afectar es:

((RP1 * 2) + RP0) * 0x80 + dirección cargada en la instrucción.

En el ejemplo anterior sería entonces:

((0 * 2) + 1) * 0x80 + 0x20 = 0xA0

Eso es para direccionamiento DIRECTO.

Para direccionamiento indirecto, mediante los registros INDF y FSR:

Como el registro FSR es quien contiene la posición de memoria a leer/escribir indirectamente, y los registros del uC son de 8 bits, en este caso el FSR puede contener un valor entre 0 y 255 (un total de acceso de 256 bytes). Nuevamente estos micros poseen más de 256 bytes de RAM, por lo que aparece un registro de apoyo como bit de mayor peso ( bit 8 ) para poder direccionar entre los bancos de memoria. Dicho bit es el STATUS,IRP.

Entonces la posición de memoria a acceder indirectamente proviene de:
el bit de mayor peso proviene del valor del bit STATUS,IRP y los 8 bits de menor peso del valor de FSR.

Ejemplo:

Código: ASM
  1. bcf      STATUS,IRP
  2. movlw 0x20
  3. movwf FSR
  4. movf   INDF,W
W contendrá el valor de la posición de memoria 0x20.

Ahora, si hiciese:

Código: ASM
  1. bsf      STATUS,IRP
  2. movlw 0x20
  3. movwf FSR
  4. movf   INDF,W
W en este caso contendrá el valor de la posición de memoria 0x120.


Saludos!
« Última modificación: 25 de Octubre de 2013, 15:09:49 por BrunoF »
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #4 en: 25 de Octubre de 2013, 15:01:36 »
Gracias, BrunoF! Muy clara tu respuesta!

Sí, ya sospechaba que debía ser un tema trillado, pero no sabía cómo buscarlo, ya que creía que el problema era con las interrupciones.
Tenés razón, estaba mezclando los tantos con lo de la ram y la flash.

Ahora, una consulta. Por lo que estoy viendo, mi compilador NO se está encargando de realizar este direccionamiento.
Nunca había tenido un problema así, pero tampoco había programado antes en BASIC.

Por eso, consulto: ¿Hay algo que se deba configurar al inicio, o alguna otra cosa que posteriormente pueda afectar este funcionamiento? A lo mejor metí la pata de alguna manera, o me faltó alguna palabra de configuración...
Gracias de nuevo por tu respuesta.

Saludos

Martín

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #5 en: 25 de Octubre de 2013, 15:14:24 »
Hola,

no programé nunca un PIC en BASIC así que sinceramente no puedo decirte con certeza. Sí encontré esto:

Citar
7.5. Life After 2K

Yes, there is life after 2K using the PICBASIC PRO™ Compiler.

PICmicro MCUs have a segmented code space. PICmicro MCU instructions in 14-bit core parts such as Call and Goto only have enough bits within them to address 2K of program space. To get to code outside the 2K boundary, the PCLATH register must be set before each Call or Goto.

PBP automatically sets these PCLATH bits for you. There are a few restrictions imposed, however. The PICBASIC PRO™ library must fit entirely into page 0 of code space. Normally this is not an issue as the library is the first thing in a PICBASIC PRO™ program and the entire library is smaller than 2K. However, attention must be payed to this issue if additional libraries are used.

Assembly language interrupt handlers must also fit into page 0 of code space. Putting them at the beginning of the PICBASIC PRO™ program should make this work. See the upcoming section on assembly language for more information.

The addition of instructions to set the PCLATH bits does add overhead to the produced code. PBP will set the PCLATH bits before any Call or Goto instruction on 12-bit core PICmicro MCUs with more than 512 words of code space, 14-bit core devices with more than 2K of code space and PIC17Cxxx devices with more than 8K of code space.

There are specific PICBASIC PRO™ instructions to assist with the 2K issues.

BRANCHL was created to allow branching to labels that may be on the other side of a 2K boundary. If the PICmicro has 2K or less of program space, BRANCH should be used as it takes up less space than BRANCHL. If the microcontroller has more than 2K of code space, and you cannot be certain that BRANCH will always act within the same page, use BRANCHL.

The assembler may issue a warning that a page boundary has been crossed. This is normal and is there to suggest that you check for any BRANCHes that may cross a page boundary.

Fuente: http://melabs.com/resources/pbpmanual/7_0.htm

Que puede te sea útil, ya que parece mencionar el uso de una isntrucción especial para que el compilador te genere los valores correctos del PCLATH antes de saltar.

Saludos.
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #6 en: 25 de Octubre de 2013, 15:41:49 »
Qué grande! Por lo que estoy leyendo, es exactamente lo que pasa.

Muchas gracias por tu ayuda con esto!!  ((:-)) ((:-))

Cuando tenga alguna novedad, lo posteo acá.

Saludos!

Martín

Desconectado jansuini

  • Moderador Local
  • PIC24F
  • *****
  • Mensajes: 566
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #7 en: 25 de Octubre de 2013, 17:41:58 »
Estimados:
El manual que comenta Bruno pertenece al Pic Basic Pro y Marcunni24 comenta que compiló con Proton que es un compilador basic totalmente distinto ,asi mismo ,el manejo de los stack hasta donde recuerdo ambos compiladores lo hacen automáticamente .
Si tengo un rato veo de compilar con proton a ver que me dice.-
Saludos
Jorge

Desconectado marcunni24

  • PIC10
  • *
  • Mensajes: 10
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #8 en: 28 de Octubre de 2013, 16:22:20 »
Jorge, buenas tardes
Gracias por tu respuesta. Lo que decís es cierto, ese manual era para el PBP, y no para el Protón. Sin embargo, llego a la conclusión de que el problema es el mismo. Al pasar las 2k líneas de código, y ejecutar un salto, el compilador no está pudiendo direccionar al banco correspondiente (modificando los bits de PCLATH), por lo que el programa va a donde no debe.
Hasta ahora le escapé al problema provisionalmente, acortando el código, usando subrutinas, manejando las interrupciones al inicio del programa, y demás. Ahora la stack funciona correctamente, sin over o underflows.

Pero me parece que cuando vuelva a superar los 2048, va a reaparecer el problema. No sé si esto es un problema del compilador, o si existe algún Fusible especial que se deba configurar al inicio.
Gracias por los datos!

Saludos
Martín

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Problemas con Proteus, interrupciones y Overflows!
« Respuesta #9 en: 28 de Octubre de 2013, 17:43:42 »
Hola!

Puedo garantizarte que fusibles no hay. Es así la arquitectura de esa familia de micros. Debería haber alguna solución simular para Protón. Te pido disculpas por la confusión, pero no utilizo Basic para programar.

Saludos!
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.