4 votos

La interrupción no cambia la variable, ¿cómo?

El problema era prácticamente imposible de depurar, la interrupción que cambiaba una variable al azar no lo hacen, sin patrón aparente; dentro de la variable de código de interrupción cambiaría pero fuera de ella el cambio se revierte esencialmente al estado anterior. Es una variable normal, por lo que no tiene doble búfer. Marcarla como volátil no tuvo ningún efecto (además de hacer que el código se ejecute más lentamente). Ya he solucionado el problema, pero el fenómeno sigue ahí. Además, mi chip es Atmega644 y el lenguaje es C.

He reducido el problema al siguiente código: la interrupción se disparaba durante la ejecución de un código que también escribía en esa variable. Comprobaba el estado del pin, y dependiendo de ello ponía o quitaba un bit de esa variable. Resolví el problema poniendo ese código en la interrupción también, lo que dejó el código interrumpible sin un solo lugar donde se escribiera esa variable - sólo se leía.

El código es sustancialmente largo, por lo que sólo proporciono fragmentos relevantes (el resto del código ni siquiera se ocupa de las variables relacionadas):

#define BIT(x) (1<<(x))
#define BITSET(x,y) ((x)|=(y))
#define BITCLR(x,y) ((x)&=~(y))

//Interrupt snippet:
if ( PINB & BIT(PINB0) )
    BITSET( display, BIT(DSPL_PWR) );
if ( PINB & BIT(PINB1) )
    BITSET( display, BIT(DSPL_ACT) );

//Main code:
if ( PIND & BIT(PIND2) )
    BITSET( display, BIT(DSPL_ACC) );
else
    BITCLR( display, BIT(DSPL_ACC) );

<...>
leds_display = 0x00;
switch ( roll )
{
    case 0:
        leds_display |= display & BIT(DSPL_PWR);
        break;
    //etc.
}
PORTA = leds_display;

Sin embargo, eso no es más que una muleta-arreglo. Funciona así, pero debería haber funcionado así. Cambiar una variable es una operación atómica, por lo que no se puede interrumpir a mitad de la acción. Mi suposición era que se debía a la magia de optimización, pero entonces volátil habría cambiado el comportamiento. E incluso entonces, incluso sin el marcador volátil, el código podría haber ignorado el cambio la primera vez que se ejecutó (no es crítico que esté actualizado en tiempo real), pero la siguiente iteración habría reconocido el cambio. Esto me deja perplejo sobre la naturaleza del fenómeno.

¿Alguien sabe algo al respecto? ¿O es sólo un error del compilador?

1 votos

Código, por favor...

2 votos

El cambio de una variable no es ciertamente atómico.

2 votos

Sin código, todo ese montón de palabras de ahí arriba es inútil. Puede ser cualquier cosa.

5voto

Bernd Puntos 61

Es muy poco probable que haya descubierto un problema del compilador.

Simple y llanamente.....muchas modificaciones de variables requerirán que la CPU realice un comportamiento de lectura-modificación-escritura a la ubicación de memoria. Si tu interrupción llega en medio de ese proceso tendrás un comportamiento indefinido.

La solución para esto en el código principal es poner entre corchetes la modificación de la variable con una secuencia como...

disable interrupts

modify variable

re-enable interrupts

Tenga en cuenta que la mayoría de los compiladores de C embebidos tienen macros especializadas que puede invocar para realizar los pasos de desactivación/reactivación. A menudo es típico que estos sólo toman una instrucción de máquina cada uno y por lo tanto la sobrecarga es muy baja.

A veces es necesario realizar un esquema de protección de código algo más robusto en los casos en que el código del programa principal puede ejecutarse con o sin interrupciones habilitadas de vez en cuando. Este caso también puede surgir si una subrutina o función de biblioteca es llamada tanto desde el contexto de línea principal como desde el contexto de interrupción - y la función llamada necesita protección de código variable. En estos casos es común tener que implementar como:

save current interrupt enabled state (most often to stack)

disable interrupts

modify variable

restore current interrupt enable state (most often from stack)

3 votos

Creo que quieres decir que "es muy poco probable que hayas descubierto un problema del compilador"

0 votos

He probado a desactivar las interrupciones temporalmente, pero eso ha hecho que la interrupción temporizada no se dispare. Es decir, sabía que eso iba a pasar pero lo intenté de todas formas y efectivamente pasó. En vez de fallar al cambiar una variable, la interrupción no se disparaba del todo. No es una opción.

0 votos

@NickJohnson - Gracias por señalar mi "no" extraviado. Se ha eliminado.

4voto

¿Por qué dices que es aleatorio o imposible de depurar? Ni siquiera lo veo como un problema del compilador. Es más bien un mal diseño de software en tiempo real.

Tienes un código principal que escribe en una variable. Tienes un ISR que escribe en la misma variable. Esto me parece problemático. Desactivas las interrupciones y el código se bloquea. Tienes un recurso compartido, incluso si es sólo una ubicación de memoria. Tienes que implementar alguna forma de compartir recursos o cambiar el código para eliminar el problema.

Has aislado el problema en el código principal, que se interrumpió mientras se escribía en la variable. El ISR escribe en la variable, pero el código principal escribe sobre la variable cuando el ISR regresa. Voilà, los datos del ISR se pierden. Código secuencial con un ISR que puede ocurrir en cualquier momento.

Muchas soluciones. La más fácil es una interrupción por software. Hacer que el programa principal llame al ISR para escribir sus datos. Asumiendo que deshabilitas los ISRs en el ISR.

Personalmente, me replantearía cómo hacer lo que quieres separando el programa principal del ISR.

0 votos

No se me pasó por la cabeza que una sola operación de escritura de 8 bits no sería atómica, y no se necesita ninguna medida de concurrencia para las operaciones atómicas, y por eso no había ninguna.

5 votos

Pero no estás haciendo una sola escritura de 8 bits. Tus macros BITSET y BITCLR utilizan |= y &=, y ambas requieren lectura-modificación-escritura como escribió Michael en su respuesta.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X