Changing a global variable in C

你离开我真会死。 提交于 2019-12-24 02:04:24

问题


I am running a C program on an AVR chip. Whenever a serial signal is heard, it runs the serial interrupt ISR (USART_RX_vect). In this method it should turn on change to = 1;. Then in my main while loop, it should clear the LCD and display it and then set change = 0 again.

This is to stop it continually doing the calulations, and displaying the result on the LCD a million times a minute..

However, when the interrupt method changes the change variable to 1, it does not seem to change it "globally" and in the main method it is always 0..

There is a bit of stuff in here that is for debugging purposes.

/* LCD DEFINES */
#define LED PB5
#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)

/* UART SERIAL DEFINES */
#define F_CPU 16000000UL
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

#define STARTCHAR 'R'
#define ENDCHAR 'E'

char reading;
char inputBuffer[12];
char readStatus;
uint8_t position;
int change;

char output;
int result;

struct Axis
{
    uint8_t axisNumber;
    uint16_t position;
    uint16_t oldPosition;

} axis1, axis2, axis3;


/* SETUP UART */

void USART_Init( unsigned int ubrr)
{
   /*Set baud rate */
   UBRR0H = (unsigned char)(ubrr>>8);
   UBRR0L = (unsigned char)ubrr;

  /*Enable receiver and transmitter */
   UCSR0B = (1<<RXEN0)|(1<<TXEN0);

   /* Set frame format: 8data, 2stop bit */
   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}

void USART_Transmit( unsigned char data )
{
    UDR0 = data;
}

unsigned char USART_Receive( void )
{
   return UDR0;
}

/*****************************************************************/

int main(void)
{
    /* INITALISE SERIAL */
    USART_Init(MYUBRR);

    /* Turn on Receive Complete Interrupt */
    UCSR0B |= (1 << RXCIE0);

    /* Turn On GLobal Interrupts */
    sei();

    position = 0;
    change = 0;

    /* Initialise LCD */
    lcd_init(LCD_DISP_ON);  /* Initialize display, cursor off. */
    lcd_clrscr();
    lcd_puts("READY");

    //Turn on LED 13
    set_output(PORTB,LED);
    output_low(PORTB,LED);

    while (1)               /* Loop forever */
    {
        if (change == 1)
        {
            //If not reading, display the result on the LCD display.
            axis1.position  = (inputBuffer[0]<< 8) | inputBuffer[1];
            axis2.position  = (inputBuffer[2]<< 8) | inputBuffer[3];
            axis3.position  = (inputBuffer[4]<< 8) | inputBuffer[5];

            char axis1Printout[12];
            char axis2Printout[12];
            char axis3Printout[12];

            sprintf(axis1Printout,"%u ", axis1.position);
            sprintf(axis2Printout,"%u ", axis2.position);
            sprintf(axis3Printout,"%u ", axis3.position);

            char output[40] = "";
            strcat(output, axis1Printout);
            strcat(output, axis2Printout);
            //strcat(output, axis3Printout);

            lcd_clrscr();  /* Clear the screen*/
            lcd_puts(output);
            _delay_ms(300);
            change = 0;
        }
    }
}

/* INTERRUPTS */

ISR (USART_RX_vect)
{
    change = 1;
    unsigned char input = USART_Receive();

    if (input == 'R')
    {
        readStatus = 0; //Reading
        position = 0;
    }
    else if ((input != 'E') && (position < 12) && (position > -1))
    {
        inputBuffer[position] = input;
        position++;
    }
    else if (input == 'E')
    {
        readStatus = 1; //Stop Reading
        position = -1;
        output_high(PORTB,LED);
    }
}

回答1:


You need to declare change using the volatile keyword:

volatile int change;

This tells the two 'threads' (main execution loop and your ISR code) to not 'cache' the value in a register, but always retrieve it from memory.

Edit: There's another problem with the code - in your main loop, by the time you set changed to 0, you may have already had another interrupt which should have triggered your loop to run again. The easy-but-not-guaranteed fix is to immediately set changed to 0 straight after you check it. The proper way would be to use a lock - but depending on your situation, the first option might do.




回答2:


Make the variable declaration volatile to ensure that a changed value is written imediately to the variable in memory.




回答3:


An object shared by an interrupt handler and the application code should be qualified as volatile in the declaration.

Without the qualifier, the implementation can assume the object cannot change unexpectedly in the application code and can cache the variable (in a register for example) for optimizations while executing the application code.



来源:https://stackoverflow.com/questions/9228436/changing-a-global-variable-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!