Entendiendo los campos de clase volátiles en los programas AVR C ++

3

Tengo cierta confusión sobre qué miembros declarar inestable en un programa que estoy escribiendo en C ++ para un microcontrolador AVR, con interrupciones. Cuando está claro C tiene sentido, el compilador no sabe que las ISR de interrupción pueden modificar las variables en cualquier momento, por lo que esas variables deben declararse volatile . Pero tengo una arquitectura parecida a la siguiente (ejemplo destilado):

class FifoBuffer {
    public: 
    int Size;
    int BeginIndex;
    void Push() {
        Size++;
        // ...
    } 
}

class RadioInterface {
    public: 
    FifoBuffer RxBuffer;
    void HandleInterrupts() {
        RxBuffer.Push( 123 );
        // ...
    }
    int GetPacket() {
        RxBuffer.Pop();
        // ...
    }
};

RadioInterface g_Radio;

ISR( INT0_vect ) {
    g_Radio.HandleInterrupts();
};

void main() {
    while ( true ) {
        g_Radio.GetPacket();
    }
}

Mi pregunta básica: ¿qué necesita ser volatile aquí?

Cuando el ISR llama a RadioInterface :: HandleInterrupts (), ¿estamos de nuevo en un contexto seguro y no volátil que el compilador puede descubrir y optimizar? ¿O necesito responder el árbol de llamadas y descubrir todo lo que podría ser tocado desde main() y desde ISR(...) y hacerlo volátil?

    
pregunta QuadrupleA

2 respuestas

4

Es esencialmente el mismo problema que escribir un programa de multiproceso, excepto que no puedes usar mutexes para proteger tus secciones críticas. Todo lo que se puede tocar desde más de un contexto debe marcarse como volátil. Pero puede que no sea suficiente: cualquier patrón de lectura-modificación-escritura debe ser analizado. Es al menos asimétrico; El controlador de interrupciones no puede ser interrumpido por la ejecución principal, solo al revés.

Hay dos técnicas habituales para garantizar resultados confiables:

A) Inhabilite las interrupciones mientras trabaja en las variables compartidas, luego vuelva a habilitarlas. Aumentará la interrupción de la latencia en el peor de los casos.

B) Evite tener variables escritas tanto por el controlador de interrupciones como por el "hilo" principal. Cada variable está escrita por uno u otro. Por ejemplo, use un búfer circular como FIFO: hay un puntero de lectura, que solo se ve alterado por main, y un puntero de escritura, que solo está alterado por el controlador de interrupción.

La investigación de estructuras de datos "sin bloqueo" también puede ser informativa.

    
respondido por el pjc50
5

Simplemente quisiera señalar que pjc50 respondió una pregunta diferente, relacionada con la sincronización y las operaciones atómicas, que es completamente ortogonal a la cuestión de si las variables deben marcarse como volatile para el compilador.

volatile no es diferente en C ++ que en C: el compilador no tiene conocimiento global sobre cómo se usan las variables, y en general no puede descubrir por sí mismo cuáles pueden compartirse entre varios subprocesos. Tenga en cuenta que los módulos utilizados en estos subprocesos pueden compilarse por separado.

    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas