Helpful Information
 
 
Category: Embedded Programming
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:



#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.

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:



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:


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:


unsigned int getSysTick()
{
int now ;
do
{
now = systick ;
} while( systick != now ) ;

return now ;
}
or


unsigned int getSysTick()
{
int now ;

TIMSK=0x00;
now = systick ;
TIMSK=0x01;

return now ;
}

We do have an embedded systems forum.










privacy (GDPR)