Timer Interrupts on the AVR in C - More Complex Example
Timer Interrupts
If you are just stumbling on this page, you might want to check out the entire series of AVR related programming guides here.
Also make sure that you have read the simple timer interrupts guide here.
Now that you have timer interrupts working, lets get to work making them more useful.
STK500 Setup
For this example, make sure that you have your PORTA jumpered to LEDs, as was discussed in our Port Output guide.
You will also want to have the second serial port called RS232 SPARE connected to your computer, and a terminal program listening to that port. This example uses 19,200 baud.
Volatile Global Variables
If you are going to modify a global variable in your ISR or you are going to modify it in your main loop and then use it in your ISR, you need to declare that variable as volatile, to tell the compiler to not use the optimized value and assume that it hasn't changed.
volatile uint8_t timer0_ticks;
Main Loop
If you want to process code in the foreground, while letting timers do other processes in the background, then put your main code in your main loop. For instance, if you want to respond to SW0 connected to PORTB, you might have the following code in your main loop:
while(true) {
if(bit_is_clear(PINB,PB0)) {
printf("***** Stop poking me! *****\n");
while(bit_is_clear(PINB,PB0)) { }
printf("***** That's better. *****\n");
}
}
Lets look at this block of code really close.
if(bit_is_clear(PINB,PB0)) checks to see if the pushbutton connected to PB0, which is SW0 has been pressed. If it has not been presssed, code continues on. However, if it has been pressed, then the entire if block gets executed.
Inside the if block, we print Stop poking me! at the user, and then enter a while loop that does nothing { } and watches the same bit. As long as the user holds SW0 down, we will never leave that empty { } loop. That's ok, because the timer can still interrupt us to do whatever it has to do, even though we are effectively looping forever.
When the user finally lets go of the button, our second while loops ends and we tell the user "That's better." After that, we jump back to the top of our never ending main loop.
Timer Interrupt Routines
The timer interrupt routines are going to be busy every once in a while doing randome tasks. In this program, we have them doing the following:
- timer1 proudly prints "***** Timer1 Overflow ****" to the screen.
- timer0 increments a global variable called lights and sends it out to PORTA for a nice light show
- timer0 also tracks a value called timer0_ticks and at the correct time prints the number of seconds that have elapsed to the screen
Timers, Tracking Seconds, and printf
This is a large example of code that uses timers to keep track of how many seconds have elapased, and every second prints the current number of seconds to the USART using printf. It also responds to button inputs on SW0 and SW1 in the main loop.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdbool.h>
#define BAUD 19200
#define MYUBRR F_CPU/16/BAUD-1
void usart_init(uint16_t ubrr);
char usart_getchar( void );
void usart_putchar( char data );
void usart_pstr (char *s);
unsigned char usart_kbhit(void);
int usart_putchar_printf(char var, FILE *stream)
static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar_printf, NULL, _FDEV_SETUP_WRITE);
volatile uint8_t timer0_ticks;
volatile uint16_t seconds;
volatile uint8_t lights;
ISR(TIMER1_OVF_vect)
{
printf("***** Timer1 Overflow *****\n");
}
ISR(TIMER0_OVF_vect)
{
lights++;
PORTA=~lights;
timer0_ticks++;
if(timer0_ticks==30){
printf("Its been %u seconds.\n",seconds+1);
}
if(timer0_ticks==61){
timer0_ticks = 0;
seconds++;
seconds++;
printf("Its been %u seconds.\n",seconds);
}
}
int main( void ) {
DDRA = 0xFF;
DDRB = 0x00;
PORTB = 0;
stdout = &mystdout;
usart_init ( MYUBRR );
printf("Ready to rock!\n");
TIMSK=(1<<TOIE0) | (1<<TOIE1);
TCNT0=0x00;
TCCR0 = (1<<CS02) | (1<<CS00);
TCCR1B |= (1 << CS10) | (1 << CS12);
sei();
while(true) {
if(bit_is_clear(PINB,PB0)) {
printf("***** Stop poking me! *****\n");
while(bit_is_clear(PINB,PB0)) { }
printf("***** That's better. *****\n");
}
if(bit_is_clear(PINB,PB1)) {
seconds=0;
timer0_ticks;
}
}
}
void usart_init( uint16_t ubrr) {
UBRRH = (uint8_t)(ubrr>>8);
UBRRL = (uint8_t)ubrr;
UCSRB = (1<<RXEN)|(1<<TXEN);
UCSRC = (1<<URSEL)|(3<<UCSZ0);
}
void usart_putchar(char data) {
while ( !(UCSRA & (_BV(UDRE))) );
UDR = data;
}
char usart_getchar(void) {
while ( !(UCSRA & (_BV(RXC))) );
return UDR;
}
unsigned char usart_kbhit(void) {
unsigned char b;
b=0;
if(UCSRA & (1<<RXC)) b=1;
return b;
}
void usart_pstr(char *s) {
while (*s) {
usart_putchar(*s);
s++;
}
}
int usart_putchar_printf(char var, FILE *stream) {
if (var == '\n') usart_putchar('\r');
usart_putchar(var);
return 0;
}
You can download the complete source code here.
This program compiles to 2,300 bytes (thanks to printf). It generates the following output if left alone:
Ready to rock!
Its been 1 seconds.
Its been 2 seconds.
Its been 3 seconds.
Its been 4 seconds.
Its been 5 seconds.
Its been 6 seconds.
Its been 7 seconds.
Its been 8 seconds.
***** Timer1 Overflow *****
Its been 9 seconds.
Its been 10 seconds.
And if you press and hold SW0 for a second or 2, then release it, you'll get this in your output:
Its been 11 seconds.
***** Stop poking me! *****
Its been 12 seconds.
Its been 13 seconds.
Its been 14 seconds.
***** That's better. *****
Its been 15 seconds.
Meanwhile, the LEDs connected to PORTA are constantly lighting up in a binary counting pattern.
Our next guide is not yet written...
Head back to our index of AVR Guides here.
Efundies.com - Making Electronics Fun
|