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

4

El problema era virtualmente no descargable, la interrupción que estaba cambiando una variable al azar no lo haría, sin un patrón aparente; la variable dentro del código de interrupción cambiaría pero, fuera de ella, el cambio se revertirá esencialmente al estado anterior. Es una variable regular, por lo que no tiene doble buffer. Marcarlo como volátil no tuvo ningún efecto (además de hacer que el código se ejecute más lento). Ya he resuelto el problema, pero los fenómenos siguen existiendo. Además, mi chip es Atmega644 y el lenguaje es C.

He reducido el problema a un código siguiente: la interrupción se activó durante la ejecución del código que también estaba escribiendo en esa variable. Verificó el estado del pasador y, dependiendo de ello, establecería o eliminaría un poco de esa variable. Resolví el problema poniendo ese código en la interrupción también, lo que dejaba un código que no se puede interrumpir en un solo lugar donde se escribía esa variable, solo se leía.

El código es sustancialmente largo, por lo que solo proporciono fragmentos relevantes (el resto del código ni siquiera trata 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. Funciona de esa manera, pero debería haber funcionado como era. Cambiar una variable es una operación atómica, por lo que no se puede interrumpir en mitad de la acción. Mi conjetura fue que se debió a la magia de optimización, pero entonces volatile habría cambiado el comportamiento. E incluso entonces, incluso sin un marcador volátil, el código puede haber ignorado el cambio la primera vez que se ejecutó (no es crítico para que esté actualizado en tiempo real), pero la siguiente iteración habría reconocido el cambio. Esto me deja perplejo sobre la naturaleza de los fenómenos.

¿Alguien sabe algo al respecto? ¿O es solo un error del compilador?

    
pregunta Raidho Coaxil

2 respuestas

5

Es muy poco probable que hayas descubierto un problema de compilación.

Simple y simple ... muchas modificaciones de variables requerirán que la CPU realice un comportamiento de lectura-modificación-escritura en la ubicación de la memoria. Si su interrupción se produce en medio de ese proceso, tendrá un comportamiento indefinido.

La solución para ello en el código de la línea principal es poner entre paréntesis 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 incorporados tienen macros especializadas que puede invocar para realizar los pasos de inhabilitar / volver a habilitar. A menudo es típico que solo tomen instrucciones de una máquina cada una, por lo que 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 de la línea principal pueda ejecutarse con o sin interrupciones habilitadas de vez en cuando. Este caso también puede surgir si se llama a una función de subrutina o biblioteca desde el contexto de la línea principal y 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)
    
respondido por el Michael Karas
4

¿Por qué dice que es aleatorio o imposible de depurar? Ni siquiera lo veo como un problema de compilación. Es un diseño de software en tiempo real más deficiente.

Tienes un código principal que escribe en una variable. Tienes ISR que escribe en la misma variable. Esto me suena problemático. Desactivas las interrupciones y los bloqueos de código. Tienes un recurso compartido, incluso si es solo una ubicación de memoria. Debe implementar algún tipo de recurso compartido o cambiar el código para eliminar el problema.

Aisló el problema en el código principal, que se interrumpió al escribir en la variable. ISR escribe en la variable, pero el código principal escribe sobre la variable cuando ISR devuelve. Voila, tus datos ISR se pierden. Código secuencial con un ISR que puede ocurrir en cualquier momento.

Muchas soluciones. Más fácil es una interrupción de software. Haga que el programa principal llame a ISR para escribir sus datos. Suponiendo que deshabilites los ISR en ISR.

Personalmente, me gustaría volver a pensar cómo hacer lo que quieres al separar el programa principal de ISR.

    
respondido por el StainlessSteelRat

Lea otras preguntas en las etiquetas