OxDEAD Unicornz

Have you ever seen so many?

ATmega8 Blinking LED and Interrupts

Several days ago I decided to clean up drawers in my desk at home and, of course, found several artifacts: old Univercity notes, Parker pen I was presented by my sister several years ago and some ATmega8A AVR microcontrollers.

I had several vacation days and caught a cold. So I couldn’t help but installed Atmel Studio and CodeVisionAVR to play with LEDs a bit.

There is far more information/examples for gcc-avr compiler/Atmel Studio than for CodeVisionAVR compiler. That’s why I wrote code in Atmel Studio.

I’d rather use only Atmel Studio, or, even better some Linux soft like avrdude but I have an old and poorly supported AVR910 programmer based on STK500 as far as I remember. The only program I was able to flash controllers with AVR910 was CodeVisionAVR.

Here is very good starting guide.

Let’s start

I’ve decided to extend common “Hello world” microcontroller project (blining LEDs) with interrupt usage.

The goal is to have blinking LED, which can be switched on or off immediately, at any time on its cycle. That is why interrupts are utilized. Timings are not critical, so we’ll use internal 8 MHz oscillator and change microcontroller fuses configuration respectively.

You can find poorly drawn project circuit below:

Blocking capacitor is 10 uF because it’s the only capacitor I had, don’t mind. Button SW connected to 4th pin of controller, it’s the first external interrupt pin.

10 kOmh resistor is a pull-up resistor for reset pin (here should be logical 1 unless we want to reset microcontroller). 470 Omh resistor limits current via LED.

Project code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#define F_CPU 8000000UL // Set microcontroller frequency to 8 MHz

#include <avr/io.h>
#include <inttypes.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define BUTTON_PORT PORTD
#define BUTTON_PIN PIND
#define BUTTON_BIT PD2
#define BUTTON_DDR DDRD

#define LED_PORT PORTB
#define LED_BIT PB0
#define LED_DDR DDRB

#define DEBOUNCE_TIME 25
#define LOCK_INPUT_TIME 250

volatile uint8_t led_is_on = 0; // volatile and global variable - can be overridden in interrupt call 

void init_io()
{
        LED_DDR |= _BV(LED_BIT); // set LED_BIT as a digital output
        LED_PORT |= _BV(LED_BIT); // LED is off initially
        BUTTON_DDR &= ~_BV(BUTTON_BIT); // set BUTTON_BIT as a digital input
        BUTTON_PORT |= _BV(BUTTON_BIT); // turn on pull-up resistor
        
        MCUCR &= ~(_BV(ISC00)|_BV(ISC01)); // clear ISC00 and ISC01 bits so interrupt is triggered on low level
        GICR |= _BV(INT0);  // enable int0 interrupt
        
        sei(); // enable interrupts
}

ISR(INT0_vect)
{
        if (bit_is_clear(BUTTON_PIN, BUTTON_BIT))
        {
                _delay_ms(DEBOUNCE_TIME);
                if (bit_is_clear(BUTTON_PIN, BUTTON_BIT))  // debounce pushed button
                {
                        if (led_is_on == 0)  // override LED state variable
                        { 
                                led_is_on = 1;
                        }
                        else
                        {
                                led_is_on = 0;
                                LED_PORT |= _BV(LED_BIT);   // Immediately switch off LED
                        }
                        _delay_ms(LOCK_INPUT_TIME);  // add lock time so LED state is not changed several times while key is pressed
                }
        }
                
}

int main(void)
{
        init_io();  // configure input/output pins and interrupt
        
        while(1)
    {
                uint8_t SaveSREG = SREG;   // save interrupt flag. Actually, disabling interrupts it's an overkill in this particular case as led_is_on variable is 1 byte long, and read operation is atomic. Interrupts should be disabled if you're performing non-atomic operation like read operation of 16 or 32 bit integer in 8-bit microcontroller.
                cli();   // disable interrupts
                
        if (led_is_on == 1) 
                {
                        SREG = SaveSREG;   // restore the interrupt flag
                        LED_PORT &= ~_BV(LED_BIT);  // LED on
                        _delay_ms(200);
                        LED_PORT |= _BV(LED_BIT);   // LED off
                        _delay_ms(200);
                }
                else
                {
                        SREG = SaveSREG;   // restore the interrupt flag
                }
    }
}

I’ve changed frequency of internal oscillator from 1 to 8 MHz. Here is a good fuse calculator. Fuse is treated as “programmed” if its value is “0”. The issue is that different programs show “0” state in different ways and you may program your microcontroller to use external clock source. Check cheatsheet and docks, maybe you should start from here You can check correct fuses config for this particular case in CodeVisionAVR below:

Here is how the circuit actually works: