Home Projects AVR Programming Guides Electrical Fundamentals Insteon Saving Electricity

Recent Updates

EasyGrow Greenhouse
Follow our adventure of assembling an 8x12 EasyGrow Greenhouse.
AVR Programming Guides
We have some nice AVR code samples to share. Lots of AVR goodies like timers, interrupts, and pin I/O for the beginner.
Watts Tables
Use these tables to help you determine how much various items in your house cost you by the day, month, and year.
Saving Electricity
A list of a variety of ways to help you save money by learning how much it costs to run things.
What is a Kilowatt Hour
Saving money on your electric bill starts with understanding what a kilowatt hour is.
How To Measure Watts
Learn a variety of ways to measure watts in your house.
Read Your Power Meter
By reading your power meter you can get a good idea of how much various electrical devices cost you.
Electronics Fundamentals
Learn some basic electronics fundamentals and see if you enjoy the field of electronics.














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.

// these values are changed in an ISR, so they must be declared as volatile

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) {

    // check to see if the user has pressed SW0 down

    if(bit_is_clear(PINB,PB0)) {

        // if they have, yell at them

        printf("***** Stop poking me! *****\n");

        // wait for button to be let go, software debounce

        // this loop can be interupted by the timers

        while(bit_is_clear(PINB,PB0)) { }

        // when they finally let go, thank them for their generosity

        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.

/********************************************************************************
Includes
********************************************************************************/

#include <avr/io.h>

#include <avr/interrupt.h>
#include <stdio.h>
#include <stdbool.h>

 

/********************************************************************************
Macros and Defines
********************************************************************************/

#define BAUD 19200
#define MYUBRR F_CPU/16/BAUD-1

 

/********************************************************************************
Function Prototypes
********************************************************************************/

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)

 

/********************************************************************************
Global Variables
********************************************************************************/

static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar_printf, NULL, _FDEV_SETUP_WRITE);

 

// these values are changed in an ISR, so they must be declared as volatile
volatile uint8_t timer0_ticks;
volatile uint16_t seconds;
volatile uint8_t lights;

 

/********************************************************************************
Interrupt Routines
********************************************************************************/
// timer1 overflow
ISR(TIMER1_OVF_vect) {
    printf("***** Timer1 Overflow *****\n");
}

// timer0 overflow
ISR(TIMER0_OVF_vect) {
    // lets make the PORTA lights do something every timer tick
    lights++;
    PORTA=~lights;

    // 61 ticks = 2 seconds @ 8.0MHz
    timer0_ticks++;
    if(timer0_ticks==30){
        // fake it, notice the printf is printing seconds+1
        printf("Its been %u seconds.\n",seconds+1);
    }

    if(timer0_ticks==61){
        timer0_ticks = 0;
        // till you make it, now we increment seconds twice
        seconds++;
        seconds++;
        printf("Its been %u seconds.\n",seconds);
    }
}

 

/********************************************************************************
Main
********************************************************************************/

int main( void ) {

    // Configure PORTA as output
    DDRA = 0xFF;

    // Configure PORTB as input
    DDRB = 0x00;

    // make sure it is high impedance and will not source
    PORTB = 0;

    // setup our stdio stream
    stdout = &mystdout;

    // fire up the usart
    usart_init ( MYUBRR );

    printf("Ready to rock!\n");

    // enable timer overflow interrupt for both Timer0 and Timer1
    TIMSK=(1<<TOIE0) | (1<<TOIE1);

    // set timer0 counter initial value to 0
    TCNT0=0x00;

    // start timer0 with /1024 prescaler
    TCCR0 = (1<<CS02) | (1<<CS00);

    // lets turn on 16 bit timer1 also
    TCCR1B |= (1 << CS10) | (1 << CS12);

    // enable interrupts
    sei();


    while(true) {

        // check to see if the user has pressed SW0 down

        if(bit_is_clear(PINB,PB0)) {

            // if they have, yell at them

            printf("***** Stop poking me! *****\n");

            // wait for button to be let go, software debounce

            // this loop can be interupted by the timers

            while(bit_is_clear(PINB,PB0)) { }

            // when they finally let go, thank them for their generosity

            printf("***** That's better. *****\n");

        }

 

        // check if the user has pressed SW1 down
        if(bit_is_clear(PINB,PB1)) {
            // clear seconds and the timer0_ticks
            seconds=0;
            timer0_ticks;
        }

    }

}

 

/********************************************************************************
usart Related
********************************************************************************/

void usart_init( uint16_t ubrr) {

    // Set baud rate

    UBRRH = (uint8_t)(ubrr>>8);
    UBRRL = (uint8_t)ubrr;

    // Enable receiver and transmitter

    UCSRB = (1<<RXEN)|(1<<TXEN);

    // Set frame format: 8data, 1stop bit

    UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

void usart_putchar(char data) {

    // Wait for empty transmit buffer

    while ( !(UCSRA & (_BV(UDRE))) );

    // Start transmission

    UDR = data;
}
char usart_getchar(void) {

    // Wait for incoming data

    while ( !(UCSRA & (_BV(RXC))) );

    // Return the data

    return UDR;
}

unsigned char usart_kbhit(void) {

    //return nonzero if char waiting polled version

    unsigned char b;
    b=0;
    if(UCSRA & (1<<RXC)) b=1;
    return b;
}

void usart_pstr(char *s) {

    // loop through entire string

    while (*s) {
        usart_putchar(*s);
        s++;
    }
}

 

// this function is called by printf as a stream handler

int usart_putchar_printf(char var, FILE *stream) {

    // translate \n to \r for br@y++ terminal

    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
 
Saturday, 04-Feb-2012 16:08:39 PST