Variable global AVR modificada por interrupción no persistente

0

Al usar un ATmega328p, tengo un problema cuando activo una interrupción (INT0 o INT1), el código se ejecutará bien (las funciones MusicOnLed y FadeOnLed se ejecutarán), sin embargo, tan pronto como desactivo la interrupción, El programa no hará nada hasta que otra interrupción esté activa.

Se está cambiando el volátil global display_select, ya que permite que esas funciones se ejecuten, pero solo cuando la interrupción está activa. Cuando la interrupción se desactiva, display_select se está cambiando de nuevo a 0xFF, o se está colgando en alguna parte.

Idealmente, la interrupción se dispara, cambia el valor de display_select y luego vuelve a main. En main evalúa display_select, y ve que se cambia (por la interrupción de cambiarlo), y sigue ejecutando ese bucle.

Esto se hizo de esta manera para evitar tener que usar interrupciones anidadas, ya que estaba teniendo problemas con esas.

#define F_CPU 16000000L
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "ADC.c"
#include "serial.c"                 //only needs to be included if using stdio or serial functions

//Preprocessor Macros
#define EQ_RESET 6                  //msgeq-7 reset on portd bit 6
#define STROBE 5                    //strobe pin on portd bit 5
#define SW0 2                       //SW0 on PORTD bit 2 (INT0)
#define SW1 3                       //SW1 on PORTD bit 3 (INT1)
#define SW2 4                       //SW2 on PORTD bit 4 (PCINT20)

#define GREEN OCR1A                 //GREEN LED is PWM Output Compare A for timer1
#define RED OCR1B                   //RED LED is PWM Output Compare B for timer1
#define BLUE OCR2A                  //BLUE LED is PWM Output Compare A for timer2

//Global audioband array
uint8_t AudioLevel[7] = {0};        //init 7 position AudioLevels array to 0

//Globals for storing average output values 
uint8_t out_low;
uint8_t out_mid;
uint8_t out_high;

//Fade delay function declaration
const int led_delay = 50;

//Selector switch global initialized at 0xFF
volatile uint8_t display_select = 0xFF;         

//Function Prototypes
void GetAudioBandLevel(void);                           //Gets audio levels, stores in global AudioLevels array
void MusicOnLed(void);                                  //Music display function on LEDs
void FadeOnLed(void);                                   //Fade display function on LEDs
void StrobeOnLed(uint8_t, uint8_t, uint8_t);            //Strobe display function on LEDs
void IncrementUp(uint8_t, uint8_t);                     //Increments specific LED up by specified delay cycle time (const int led_delay)
void IncrementDown(uint8_t, uint8_t);                   //Increments specific LED down by specified delay cycle time (const int led_delay)
void Init_Timer1_PWM(void);                             //Timer1 for PWM on BASS and MID
void Init_Timer2_PWM(void);                             //Timer2 for PWM on TREBLE
void Init_IO(void);                                     //See actual function for IO Port Description
void SolidColorOnLed(void);                             //Changes between colors on LED

int main(void)
{
    init_ADC();                     //init ADC for channel 0, 8 bit resolution, ADC clock = clk/128
    Init_Timer1_PWM();              //timer1 and 2 as 8 bit output compares
    Init_Timer2_PWM();
    Init_IO();                      //enables things and interrupts 
    sei();                          //enables global interrupts

    while(1)
    {
        while(display_select == 0xFF)
        {
            //do nothing while waiting for an interrupt
        }
        while(display_select == 0x00)
        {
            MusicOnLed();
        }
        while(display_select == 0x01)
        {
            FadeOnLed();    
        }       
    }
}

void IncrementUp(uint8_t led, uint8_t max_val)
{
    uint8_t i = 0;      //LCV

    for(i = 0; i < max_val; i++)
    {
        switch(led)
        {
            case 1: 
                RED     = i;
            break;          
            case 2:
                GREEN   = i;
            break;          
            case 3:
                BLUE    = i;
            break;
        }
        _delay_ms(led_delay);
    }
}

void IncrementDown(uint8_t led, uint8_t max_val)
{
    uint8_t i;      //LCV

    for(i = 0; i < max_val; i++)
    {
        switch(led)
        {
            case 1:
            RED     = max_val - i;
            break;
            case 2:
            GREEN   = max_val - i;
            break;
            case 3:
            BLUE    = max_val - i;
            break;
        }
        _delay_ms(led_delay);
    }
}

void GetAudioBandLevel(void)
{
    uint8_t audio_band = 0;
    DDRD    |=  (1 << STROBE)|(1 << EQ_RESET);              //PORTD bit strobe and reset pins output
    PORTD   &=  ~((1 << STROBE)|(1 << EQ_RESET));           //sets strobe and reset low
    PORTD   |=  (1 << EQ_RESET);                            //reset goes high
    _delay_us(100);                                         //delay 100usec for setup time req
    PORTD   |=  (1 << STROBE);                              //strobe goes high
    _delay_us(50);                                          //strobe delays
    PORTD   &=  ~(1 << STROBE);                             //strobe goes low
    _delay_us(50);                                          //strobe delays
    PORTD   &=  ~(1 << EQ_RESET);                           //reset reset
    PORTD   |=  (1 << STROBE);                              //strobe goes high
    _delay_us(50);              

    for(audio_band = 0; audio_band < 7; audio_band++)
    {
        PORTD   &=  ~(1 << STROBE);             //resets (set strobe pin low (active))
        _delay_us(30);                          //setup time for capture
        AudioLevel[audio_band] = read_ADC();    //reads 8 bit resolution audio level from audio bandpass filter
        PORTD   |=  (1 << STROBE);                  //sets (set strobe pin high again)
        _delay_us(50);                          //not sure if needed - check datasheet
    }
}

void MusicOnLed(void)
{
    GetAudioBandLevel();
    out_low     = (AudioLevel[0] + AudioLevel[1]) / 2;                      // Average of two Lowest Bands
    out_mid     = (AudioLevel[2] + AudioLevel [3] + AudioLevel[4]) / 3;     // Average of three Middle Bands
    out_high    = (AudioLevel[5] + AudioLevel[6]) / 2;                      // Average of two Highest Bands

    uint8_t led_out_low     =   out_low * 1.2;
    uint8_t led_out_mid     =   out_mid * 1.2;
    uint8_t led_out_high    =   out_high * 1.2;

    if(out_low >= 212)
    {
        led_out_low = 255;      //set to max brightness if overflow condition would otherwise occur
    }
    if(out_mid >= 212)
    {
        led_out_mid = 255;      //set to max brightness if overflow condition would otherwise occur
    }
    if(out_high >= 212)
    {
        led_out_high = 255;     //set to max brightness if overflow condition would otherwise occur
    }

    //assign to outputs
    GREEN   = led_out_low;
    RED     = led_out_mid;
    BLUE    = led_out_high;
    //_delay_ms(10);            //old delay before menu implementation
    _delay_ms(1);

}

void Init_IO(void)
{
    DDRB = 0x3F;                                            //portb as output

    DDRD    &= ~(1 << SW0) | ~(1 << SW1) | ~(1 << SW2);     //portd bits 2, 3, and 4 as inputs (pushbuttons)
    PORTD   |= (1 << SW0) | (1 << SW1) | (1 << SW2);        //turn pullups on only on pushbutton input pins

    EICRA   = 0x0;                                          //Enables active LOW triggering on ext. interrupts on INT0 and INT1 pins (SW0 and SW1)
    EIMSK   = 0x3;                                          //Enables external interrupts on INT0 and INT1 (SW0 and SW1)

    /*
    PCMSK2  = 0x10;                                         //Enable interrupts on PCINT20 (SW2)
    PCICR   |= (1 << 2);                                    //Enables interrupts on pin change request 2 reg
    */

    DDRC    = 0x00;                                         //port c as input
    PORTC   = 0x3F;                                         //pullups on
}

void Init_Timer1_PWM(void)
{
    TCCR1A = 0b10100001;        // Timer1 in fast mode PWM
    TCCR1B = 0b00000100;        //
    TIMSK1 = 0b00000110;    
}

void Init_Timer2_PWM(void)
{
    TCCR2A = 0b10100001;        // Timer2 in fast mode PWM
    TCCR2B = 0b00000110;        //
    TIMSK2 = 0b00000100;
}

void FadeOnLed(void)
{
    GREEN   = 0;
    BLUE    = 0;
    RED     = 90;

    //see led_delay for led delay time
    //RED is defined as 1, GREEN as 2, BLUE as 3 when passing values to function
    while (1)
    {
        IncrementUp(2, 90);
        IncrementDown(1, 90);
        IncrementUp(3, 90);
        IncrementUp(1, 90);
        IncrementDown(2, 90);
        IncrementDown(1, 90);
        IncrementDown(3, 90);
        IncrementUp(1, 90);
        IncrementUp(2, 90);
        IncrementDown(2, 90);
    }
}

void StrobeOnLed(uint8_t red, uint8_t green, uint8_t blue)
{
    RED     = red;
    GREEN   = green;
    BLUE    = blue;
    _delay_ms(100);
}

void SolidColorOnLed(void)
{
    RED = 0;
    GREEN = 100;
    BLUE = 0;
}

ISR(INT0_vect)
{
    display_select = 0x00;
}

ISR(INT1_vect)
{
    display_select = 0x01;
}

/*
ISR(PCINT2_vect)        //PCINT20
{
    display_select++;
}
*/

EDIT:

He modificado main () para:

int main(void)
{
    init_ADC();                     //init ADC for channel 0, 8 bit resolution, ADC clock = clk/128
    Init_Timer1_PWM();              //timer1 and 2 as 8 bit output compares
    Init_Timer2_PWM();
    Init_IO();                      //enables things and interrupts 
    sei();                          //enables global interrupts
    display_select = 0xFF;

    while(1)
    {
        switch(display_select)
        {
            case 0x00:
                    MusicOnLed();
                break;
            case 0x01:
                    //FadeOnLed();
                break;
            case 0xFF:
                    RED = 0;
                    GREEN = 50;
                    BLUE = 0;
                break;
        }   

Lo que sucede es que cuando INT0 permanece en el estado bajo (activado), MusicOnLed() sigue funcionando mientras INT0 esté en el estado bajo. Tan pronto como INT0 pasa de bajo a alto (activado a no activado), display_select aún debería ser 0x00 (permitiendo que MusicOnLed() siga funcionando) pero de alguna manera se restablece a 0xFF . Obviamente, esto hace que los GREEN LED se enciendan y los otros LED se apaguen.

EDIT 2

He cambiado el código para sondear la variable display_select durante la ejecución de las funciones FadeOnLed() y MusicOnLed() . Ahora puedo interrumpir las funciones.

También he cambiado los valores de DDRD y PORTD para reflejar lo que he aprendido en línea

Mi nuevo problema es que INT1 siempre parece estar activo. En el simulador, PIND bits 2 y 4 se establecen, mientras que los otros permanecen restablecidos. Me pregunto si esto hace que INT1 se dispare continuamente (por ser una interrupción de nivel).

Consulte las siguientes funciones modificadas:

int main(void)
{
    sei();                          //enables global interrupts
    init_ADC();                     //init ADC for channel 0, 8 bit resolution, ADC clock = clk/128
    Init_Timer1_PWM();              //timer1 and 2 as 8 bit output compares
    Init_Timer2_PWM();
    Init_IO();                      //enables things and interrupts 

    while(1)
    {       
        switch(display_select)
        {
            case 0x00:
                    while (display_select == 0x00)
                    {
                        MusicOnLed();
                    }
                break;
            case 0x01:
                    FadeOnLed();
                break;
            case 0xFF:          //standby glow faint purple
                RED = 1;
                GREEN = 0;
                BLUE = 1;
            break;
        }   
    }
}

void IncrementUp(uint8_t led, uint8_t max_val)
{
    uint8_t i = 0;      //LCV

    while((i < max_val) & (display_select == 0x01))
    {
        switch(led)
        {
            case 1:
            RED     = i;
            break;
            case 2:
            GREEN   = i;
            break;
            case 3:
            BLUE    = i;
            break;
        }
        i++;
        _delay_ms(led_delay);
    }
}
void IncrementDown(uint8_t led, uint8_t max_val)
{
    uint8_t i = 0;      //LCV

    while((i < max_val) & (display_select == 0x01))
    {
        switch(led)
        {
            case 1:
            RED     = max_val - i;
            break;
            case 2:
            GREEN   = max_val - i;
            break;
            case 3:
            BLUE    = max_val - i;
            break;
        }
        i++;
        _delay_ms(led_delay);
    }
}

void Init_IO(void)
{
    DDRB = 0x3F;                                            //portb as output

    SMCR |= (1 << SM0);                                     //Sets sleep mode 'ADC Noise Reduction'

    //DDRD  &= ~(1 << SW0) | ~(1 << SW1) | ~(1 << SW2);     //portd bits 2, 3, and 4 as inputs (pushbuttons)
    DDRD    |= (1 << SW0) | (1 << SW1) | (1 << SW2);        //ddrd set high
    PORTD   |= (1 << SW0) | (1 << SW1) | (1 << SW2);        //turn pullups on only on pushbutton input pins

    EICRA   = 0x0;                                          //Enables active LOW triggering on ext. interrupts on INT0 and INT1 pins (SW0 and SW1)
    EIMSK   |= (1 << INT1) | (1 << INT0);                   //Enables external interrupts on INT0 and INT1 (SW0 and SW1)    

    //PCMSK2    = 0x10;                                         //Enable interrupts on PCINT20 (SW2)
    //PCICR |= (1 << 2);                                    //Enables interrupts on pin change request 2 reg


    DDRC    = 0x00;                                         //port c as input
    PORTC   = 0x3F;                                         //pullups on
}
    
pregunta jfri2

1 respuesta

1

Hay un bucle infinito dentro FadeOnLED() . Una vez que se activa la INT1, tomará su main() dentro de FadeOnLED() y como contiene while(1) no hay forma de salir de ella. Por lo tanto, el flujo principal del programa se atascará dentro del bucle infinito de FadeOnLED() . Después de eso, otras interrupciones pueden interrumpir el trabajo de este bucle infinito (donde se encuentra el flujo principal del programa), pero no saldrán (interrumpirán) de él.

Una posible solución (no muy agradable, pero solo para mostrar la idea) es usar lo siguiente:

void FadeOnLed(void)
{
    GREEN   = 0;
    BLUE    = 0;
    RED     = 90;

    //see led_delay for led delay time
    //RED is defined as 1, GREEN as 2, BLUE as 3 when passing values to function
    while (display_select == 0x01)
    {
        ...
    }
}
    
respondido por el helleye

Lea otras preguntas en las etiquetas