NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Understanding Interrupts

May 10, 2010
by avicell
avicell's Avatar

There are too many variables for me to keep straight about interrupts, and I'd like to ask the community to help me understand.

My project is the traffic light one, and I am attempting to use the interrupt feature for when the 'walk' button is pressed. Whenever the button is pressed, a logic 1 goes to PB1 (verified with multimeter). My aim is to have the MCU recognize a logic 1 on PB1 as an interrupt trigger, which will then enable the 'walk' function (that consists of a white LED going on for 3 seconds, then blinking for 3 seconds. The program will then restart). Now I know that I can just poll the pin for a logic 1, but I am trying to learn interrupts.

Here is my code (note, that each different LED state is handled by a 'condition', commented below. Also, the temp sensor feature - which will ultimately sense for a car on the side street, and the LCD display functionality is currently disabled, to hone in on my de-bugging) Thanks in advance for all the help!:

/* trafficlight.c
Bruce Clark */

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>
#include <inttypes.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

void config() {
    /* Set Port C as output (LEDs)
    PC0 = Gs; PC1 = Ys; PC2 = Rs;
    PC3 = Gm; PC4 = Ym; PC5 = Rm; */
    DDRC |= ((1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5));

    /* set up Port B: 
    PB1 = push button; PB2 = temp sensor; PB5 = Walk LED (white) */
    DDRB &= (~(1<<PB1) & ~(1<<PB2) | (1<<PB5));
    PORTB |= (1<<PB1) | (1<<PB2); //internal pull-up resistors

    //Set up interrupts
    PCMSK0 |= (1<<PCINT1);
    EICRA |= (1<<ISCI1) | (1<<ISCI0);
}

void adc_init() {
    ADMUX = 0;
    ADCSRA = ((1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0));
    ADCSRA |= (1<<ADSC);
}

uint16_t adc_read() {
    while(ADCSRA & (1<<ADSC)) {
    }

    uint16_t result = ADCL;
    uint16_t temp = ADCH;

    result = result + (temp<<8);
    ADCSRA |= (1<<ADSC);

    return result;
}

double sampleToFahrenheit(uint16_t sample) {
    return sample * (5000.0 / 1024.0 / 10.0);
}

/*void temp() {

    uint16_t last_sample = 0;
    double this_temp;
    double temp_avg;
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);

    uint8_t j;

    for(j=0; j<100; j++) {
        last_sample = adc_read();
        this_temp = sampleToFahrenheit(last_sample);

        temp_avg = temp_avg + this_temp/100.0;
    }

    lcd_home();
    lcd_write_string(PSTR("ADC: "));
    lcd_write_int16(last_sample);
    lcd_write_string(PSTR(" of 1024  "));
    lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
    lcd_write_data(0xdf);
    lcd_write_string(PSTR("F      "));
    //printf_p(PSTR("%.2f degrees F/r/n"), temp_avg);
}*/

/*Conditions: (if not stated, all other LEDs = 0)
    Condition 1: Gm = 1; Rs = 1;
    Condition 2: Ym = 1; Rs = 1;
    Condition 3: Gs = 1; Rm = 1;
    Condition 4: Ys = 1; Rm = 1;
    Condition 5: Rs = 1; Rm = 1;
    Condition 6: Rs = 1; Rm = 1; W = 1;
    Condition 7: [Condition 5 + delay(.5) + Condition 6 + delay(.5)] x 3;
    */

void Cond1() {
    PORTC = (1<<PC3) | (1<<PC2);
    PORTB = 0;
    delay_ms(12000);
}

void Cond2() {
    PORTC = (1<<PC4) | (1<<PC2);
    PORTB = 0;
    delay_ms(3000);
}

void Cond3() {
    PORTC = (1<<PC0) | (1<<PC5);
    PORTB = 0;
    delay_ms(6000);
}

void Cond4() {
    PORTC = (1<<PC1) | (1<<PC5);
    PORTB = 0;
    delay_ms(3000);
}

void Cond5() {
    PORTC = (1<<PC5) | (1<<PC2);
    PORTB = 0;
}

void Cond6() {
    Cond5();
    PORTB |= (1<<PB5);
}

void Cond7() {
    int i;

    Cond6();
    delay_ms(3000);
    for(i=0; i<3; i++) {
        Cond6();
        delay_ms(500);
        Cond5();
        delay_ms(500);
    }
}

ISR(PCINT0_vect) {

    if (PORTB |= (1<<PB1)) {
        Cond7();
    }
}

int main() {

    sei();
    config();

    while(1) {

        Cond1();
        Cond2();
        Cond5();
        delay_ms(500);
        Cond3();
        Cond4();
        Cond5();
        delay_ms(500);
    }

    return 0;
}
May 11, 2010
by avicell
avicell's Avatar

OK, I figured it out.... It's amazing what 10 hours away from the problem does!

I needed the code: PCICR |= (1<<PCIE0) PCMSK0 |= (1<<PCINT1);

Now only problem is I have the white LED on PB5 (output), and the push button on PB1 (input). I don't have my bit-masking proper to get the desired functionality of "When you push the button, all else stops and the 'walk' sequence begins. Then proceed back where you were". The program is acting erratically with the walk sequence happening a few times in a row.

May 11, 2010
by Ralphxyz
Ralphxyz's Avatar

Hey avicell, I am trying to learn C programming and interrupts can I ask you some questions.

Can you post your complete code?

What is the tempsensor code doing?

Thanks, Ralph

May 11, 2010
by hevans
(NerdKits Staff)

hevans's Avatar

Hi avicell,

How is your push button wired up? I noticed in your interrupt handler you have

if(PORTB |= (1<<PB1))

Which is going to check for the pin being high. It looks like you set up the pullup resistor to pull your pin high, and if you have your button set up correctly it should pull the pin low when you push it. You want your interrupt to fire on the high to low transition so I think you want to check for the pin being low in that if statement. If this was a perfect button it shouldn't make a difference, but you will typically get more than one transition every time you push the button due to mechanical bouncing. Try checking for a low state of the pin and let us know if that fixes the issue.

Humberto

May 11, 2010
by avicell
avicell's Avatar

Humberto -

Your suggestion was spot on! One line of code changed and swapping out a wire to ground was all it needed! I have the C node of the button on PB1, NO to +5V, NC to ground, and whenever I press the button, the 'walk' function begins.

Still have a little de-bugging to do since the walk function happens 2x in a row, but that shouldn't be a problem. Next task up is to figure out PWM.

Thanks again.

Post a Reply

Please log in to post a reply.

Did you know that you can generate hundreds of volts AC from your microcontroller with a little bit of circuitry? Learn more...