#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2010
    Posts
    6
    Rep Power
    0

    AVR interrupt C code


    Hi,

    I am using an ATmega16 microcontroller to read in the data off a temperature sensor. What I am trying to do now is to slow the output down so that it only displays the temperature say every 5 seconds with a timer interrupt. However, when debugging my code, the while loop inside the main function doesn't run even though the argument inside is true. The program counter just goes back to the start of the main function and never runs the code inside the while loop.

    My code is below:

    Code:
    #include <avr/io.h>
    #include <stdio.h>
    #include <avr/interrupt.h>
    
    volatile int counter = 0;
    volatile unsigned char flag = 0x00;
    static int uart_putchar(char c, FILE *stream);
    static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,_FDEV_SETUP_WRITE);
    static unsigned int temp;
    static double ftemp;
    static double adc_v;
    
    void blink_led(void)
    {
        PORTB=PORTB^0xff;
    }
    
    void timer_init(void)
    {
        SREG |= 0x80; // enable global interrupt 
    
    	TCCR0 = 0x05; // divide timer by 1024,  timer
                      
    
        TIMSK=0x01; // enable timer0 for overflow interrupt mode
    
        sei();     //  Global Interrupt Enable
    }
    
    
    //**********************************************
    //InitADC: initialize analog to digital converter
    //**********************************************
    
    void InitADC(void)
    {
    ADMUX = 0x06;
    ADCSRA = 0xCF;
    
    while (!(ADCSRA & 0x10));
    ADCSRA |= 0x10; //manually clear interrupt flag
    
    }
    
    //***********************************************
    //ReadADC: read analog voltage from ADC converter
    //***********************************************
    unsigned int ReadADC(unsigned char channel)
    {
    unsigned int binary_weighted_voltage, binary_weighted_voltage_low;
    unsigned int binary_weighted_voltage_high;
    
    ADMUX = channel;
    ADCSRA |= 0x4F; //start conversion
    
    while (!(ADCSRA & 0x10));
    ADCSRA |= 0x10; //manually clear interrupt flag
    
    binary_weighted_voltage_low = ADCL;
    binary_weighted_voltage_high = ((unsigned int) (ADCH << 8));
    binary_weighted_voltage = binary_weighted_voltage_low | binary_weighted_voltage_high;
    return binary_weighted_voltage;
    }
    
    //
    //
    void USART_init(void)
    {
      UCSRA = 0x00;
      UCSRB = 0x08;
      UCSRC = 0x86;
    
      UBRRH = 0x00;
      UBRRL = 0x33; //9600 for 8MHz
    
    } 
    
    //
    //
    void USART_tx(unsigned char data)
    {
      while((UCSRA & 0x20) == 0x00){};
      UDR = data;
    }
    
    
    int uart_putchar(char c, FILE *stream)
    {
      if (c == '\n'){
        uart_putchar('\r', stream);
      }
      loop_until_bit_is_set(UCSRA, UDRE);
      UDR = c;
      return 0;
    }
    
    int
    main(void)
    {
    	DDRB=0xff;  // PORTB as output
        PORTB=0x55;
    
        USART_init();
        stdout = &mystdout;
    	InitADC();
    	timer_init();
    
    	printf("\nSystem Initialised\n");
    	
        for(;;){
    	   while(!(flag & 0xff));
    	   temp = ReadADC(0x06); //read in the value from the ADC
    	   adc_v = (temp*5.0/1024); //adjust for 5 Volts reference voltage and 1024 ADC Levels
    	   ftemp = (double) (temp*5.0/1024/10e-3) - 273,15; //convert to degrees Celsius
    	   //Print to screen
    	   printf ("Temperature = %0.1f\n   Voltage = %0.5f",ftemp,adc_v);
    	}
    
    }
    
    
    ISR(TIMER0_OVF_vect)
    {
        /* This interrupt function would be called automatically after every 
    10ms once the timer_init function has been called. */
    
        counter++; /* This variable multiplies with 10ms to generate custom timer. Adjust this as per your need */
    
       if ( counter == 153 )
        {
            /* Call your function here or put the events which you want to be called after every 1 second */
           blink_led();  // for example blink LEDs on PORTB
    	   counter = 0;
    	   flag = 0xff;
        }
    	
    }
    Any hints as to what could be causing this?
    Thanks in advance.
  2. #2
  3. Contributing User

    Join Date
    Aug 2003
    Location
    UK
    Posts
    5,109
    Rep Power
    1803

    Post


    I cannot immediately see your problem, but as I suggested before you would do better posting this on the AVR Freaks forum. There are few embedded systems specialists here, let alone AVR specialists.

    If you are stepping this with the debugger have you configured the debugger to stop the timer while the processor is halted? If not, every time you halt, a timer interrupt will become pending an the code in main may never run while stepping. Moreover if you break in the ISR, an interrupt will become pending and teh ISR may re-enter, eventually overflowing the call stack, and causing a crash.

    The comment for the baud rate suggests you are running at 8MHz; so the Timer 0 overflow will occur at 32.768ms intervals, not 10ms as in the comment - that would explain the value of 153 in your ISR! If you are going to post the code for help, at least keep the comments consistent, since that only adds confusion!

    Any how, you are making your timer far less flexible that necessary. I suggest that you make the 1 second event counter internal to the ISR (if you need it at all), and create a separate continuous counter for timing, as follows:

    Code:
    volatile unsigned char systick = 0 ;
    
    ISR(TIMER0_OVF_vect)
    {
        static counter = 0 ;
    
        tick++ ;
    
        counter++ ;
    
        // every second (approx)...
        if ( counter == 31 )
        {
            blink_led() ;
    	counter = 0;
        }
    }
    Then your timing look can look more like:
    Code:
    for(;;)
    {
        unsigned char start = systick ;
        while( systick - start < 153 ) /* do nothing for 5 seconds (approx)*/ ;
    
        ...
    }
    Note that I have used an unsigned char for systick so that access is atomic. If you need a larger counter (this one is good for intervals of up to ~8.39 seconds), you will need to disable the interrupt to read systick, or use a mechanism where by you must read the same value twice in succession to validate it, for example:
    Code:
    unsigned int getSysTick()
    {
        int now ;
        do
        {
            now = systick ;
        } while( systick != now ) ;
    
        return now ;
    }
    or
    Code:
    unsigned int getSysTick()
    {
        int now ;
    
        TIMSK=0x00; 
        now = systick ;
        TIMSK=0x01; 
    
        return now ;
    }
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Loyal (3000 - 3499 posts)

    Join Date
    May 2004
    Posts
    3,417
    Rep Power
    887
    We do have an embedded systems forum.
    I no longer wish to be associated with this site.

IMN logo majestic logo threadwatch logo seochat tools logo