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 » pin change interrupts

January 18, 2012
by Gil
Gil's Avatar

does the ISR() go ouside of main() or inside? and, if my pins are set as output, and set high, will the interrupt fire with a switch pulling output to ground (low) or does the interrupt read hoe the pin is set, high or low. thanks

January 18, 2012
by Gil
Gil's Avatar

this is the code i am have problems with. i can't get the interrupts to work properly. can someone give me some insight as to what i am doing wrong. or just comments on programming. this is a freestyle try at blinking leds at different freq. using the dip_arithmatic breadbord layout from the nerdkit. i know i messed up the DDRC and set them to outputs but i had it working like this using if-elses before i tried to use interrupts. the program basically flashing leds at different frequencys and only allows one switch to be on. the switch pulls the output low and PINC was still reading correctly in my pervious version so i don't know if its interrupt problem or what. first try at using pin change int. and thought i had it figured out until i read the data sheet and some tri-state junk thru me for a loop. help please thanks

// gil's blinking led with interrupts 6 position switch

#define F_CPU 14745600

#include <inttypes.h>
#include <avr/io.h>
#include <math.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

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

//asigning pin read variables

volatile uint8_t a1;
volatile uint8_t a2; 
volatile uint8_t a3; 
volatile uint8_t a4; 
volatile uint8_t a5; 
volatile uint8_t a6;

//pin definitions
//pb1=led anode

//sets pc0 thru pc5 as inputs

    // interrupt service routine
    // sets values od a1..a6 to memory

    ISR(INT1_vect) {
    delay_ms(50);

    a1 = (~(PINC) & (1<<PC0)) >> PC0;
    a2 = (~(PINC) & (1<<PC1)) >> PC1;
    a3 = (~(PINC) & (1<<PC2)) >> PC2;
    a4 = (~(PINC) & (1<<PC3)) >> PC3;
    a5 = (~(PINC) & (1<<PC4)) >> PC4;
    a6 = (~(PINC) & (1<<PC5)) >> PC5;

    }

    int main() {

    //pin definitions
    //pb1=led anode
    //sets pc0 thru pc5 as inputs
    // read pins hi 1 lo 0, swith open the pull-up resistor works and result is HI
    // CLOSED "ON" connects the pin to grd and pulls the result to LO

    DDRC |= (1<<PC0);
    DDRC |= (1<<PC1);
    DDRC |= (1<<PC2);
    DDRC |= (1<<PC3);
    DDRC |= (1<<PC4);
    DDRC |= (1<<PC5);

    //sets the pull up resistor for input pins
    PORTC |= (1<<PC0);
    PORTC |= (1<<PC1);
    PORTC |= (1<<PC2);
    PORTC |= (1<<PC3);
    PORTC |= (1<<PC4);
    PORTC |= (1<<PC5);

    //  led as output
    //  pb1=led anode
    DDRB |= (1<<PB1);

    lcd_init();
    lcd_home();

    sei();   //enable interrupts

    PCICR |= (1<<PCIE1);  //sets the C pins (group 1 interrupts)

    // sets the pin numbers for interrupts
    PCMSK1 |= (1<<PCINT8);
    PCMSK1 |= (1<<PCINT9);
    PCMSK1 |= (1<<PCINT10);
    PCMSK1 |= (1<<PCINT11);
    PCMSK1 |= (1<<PCINT12);
    PCMSK1 |= (1<<PCINT13);

    while ((a1+a2+a3+a4+a5+a6) > 1)   {

            lcd_home();
            lcd_write_string(PSTR("single switch only  "));     
            }

    while ((a1+a2+a3+a4+a5+a6) == 0)      {

            lcd_home();
            lcd_write_string(PSTR("turn on a switch    "));         
            }

    float time;
        time = (a1*1000 + a2*500 + a3*100 + a4*50 + a5*10 + a6);

        lcd_home();
        lcd_write_string(PSTR("frequency is: "));
        lcd_write_int16(1/(time/1000));
        lcd_write_string(PSTR(" Hz "));
        lcd_line_two(); 
        lcd_write_string(PSTR("thanks"));

    while ((a1+a2+a3+a4+a5+a6) == 1)  {

    //turn on led
     PORTB |= (1<<PB1);

     //delay 
     delay_ms(time);

     //turn off led
     PORTB &= ~(1<<PB1);

     //delay 
     delay_ms(time);
     }

  return 0;
}
January 19, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

The ISR() macro defines a function that gets jumped to when the interrupt fires, so it should be defined outside your main method. The interrupt will fire whenever the pin changes state, so you will have to check the current state of the pin within the interrupt handler, this will tell you if you just transitioned from low to high, or from high to low.

Humberto

January 19, 2012
by Gil
Gil's Avatar

i dont know if i care what state the pin is in. the code i posted does not work, and i can't figure out why. I think I have boiled my problems down to a few simple questions. Your help is appreciated.

do pin change interrupts work on a pin enabled as an input or output and configured as an interrupt? Or are the pin options limited to input or output or pin change interrupt. if the later is the case then my program is way off base.

thru the course of experimenting i set what i thought should be input pins to output high instead of input with pull up resistor. to my surprise it worked just fine. this was on a similar program with no interrupts. so my next question.

is there any difference between a pin set as output, set high, switch pulls it to gnd, and I read PINnx as 1 or 0 (verses) set pin as input, set pull up resistor, switch pulls to gnd, and I read PINnx as 1 or 0. They both seem the same to me.

Thanks gil

January 19, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Gil,

I think your confusion is in the difference between an input pin and an output pin. These two modes for the pins are very different.

When a pin is set as an output pin, your chip is going to electrically drive the voltage of the pin to either a logic high, or a logic low voltage.

When a pin is set as an input, it is in a high impudence mode and the pin is not actively driven to any voltage by the chip itself. This allows the external circuitry you build to set the voltage, and then you can read the value inside your code. Enabling a pull up resistor in this mode only enables a resistor that lightly pulls up the voltage when nothing else is driving it so that it has a defined state.

Enabling a pull up resistor on an input pin is very different than configuring an output pin to be a logic low. In your case above you are configuring your pins as output pins and then setting them to be a logic high. This means the chip will actively drive these pins up to 5V. If you are connecting these pins directly to GND these two driving voltages are fighting each other (essentially you have a short circuit, that is being limited by the current limit on the atmega168).

Generally when using interrupts you want to configure your pins as input pins, because you are going to be letting outside circuitry define the voltage level of the pins.

Hope that makes sense.

Humberto

January 19, 2012
by Gil
Gil's Avatar

your help is greatly appreciated. when i get home tonight i'll take another crack at it. one last question. if i configure a pin as output and put a pin change interrupt on that pin. will it read the voltage on the pin or the logic being sent to the pin. case in point - output pin set high pulled low and high by switch (open and to gnd.) does the interrupts see only the high logic on the pin and never fire?

thanks again for your time. your awsome and you know that.

January 19, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Gil,

I think what you are asking is, will a pin change interrupt fire whenever a pin changes state, even if it is an output pin. I believe the answer to that is yes, it will. However the case of an output pin changing state should only ever be triggered from inside your code, because if you have set a pin as an output pin only the chip should drive its voltage.

The case you describe of having an output pin be high and then "driven" low by something else is not a valid circuit, and you can't do it. You can think of an output pin almost like a battery. When you set an output pin high it is a voltage source at 5V. Connecting that to GND and trying to drive it to 0 will result in a short.

Humberto

January 19, 2012
by Gil
Gil's Avatar

i understand what you are saying and i admit that this is all very new to me. the code below runs fine and i accidentally configured the input pins as output and switched them to ground and read the pin, and it works, but it seems to violate what you said in the post. it is wire just like the dip_arithmetic example. thanks for your patenince (where is the spell check on this thing)

// gil's blinking led with 6 position switch

#define F_CPU 14745600

#include <inttypes.h>
#include <avr/io.h>
#include <math.h>
#include <stdio.h>
#include <avr/pgmspace.h>

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

//asigning pin read variables

uint8_t a1;
uint8_t a2; 
uint8_t a3; 
uint8_t a4; 
uint8_t a5; 
uint8_t a6;

//pin definitions
//pb1=led anode

int main() {

    //sets pc0 thru pc5 as inputs
    DDRC |= (1<<PC0);
    DDRC |= (1<<PC1);
    DDRC |= (1<<PC2);
    DDRC |= (1<<PC3);
    DDRC |= (1<<PC4);
    DDRC |= (1<<PC5);

    //sets the pull up resistor for input pins
    PORTC |= (1<<PC0);
    PORTC |= (1<<PC1);
    PORTC |= (1<<PC2);
    PORTC |= (1<<PC3);
    PORTC |= (1<<PC4);
    PORTC |= (1<<PC5);

    //  led as output
    DDRB |= (1<<PB1);

    lcd_init();
    lcd_home();

    // read pins hi 1 lo 0, swith open the pull-up resistor works and result is HI
    // CLOSED "ON" connects the pin to grd and pulls the result to LO

    while (1) {
    top:
    a1 = (~(PINC) & (1<<PC0)) >> PC0;
    a2 = (~(PINC) & (1<<PC1)) >> PC1;
    a3 = (~(PINC) & (1<<PC2)) >> PC2;
    a4 = (~(PINC) & (1<<PC3)) >> PC3;
    a5 = (~(PINC) & (1<<PC4)) >> PC4;
    a6 = (~(PINC) & (1<<PC5)) >> PC5;

    if ((a1+a2+a3+a4+a5+a6) > 1)      {

            lcd_home();
            lcd_write_string(PSTR("single switch only  "));
            goto top;
            }

    else if ((a1+a2+a3+a4+a5+a6) == 0)    {

            lcd_home();
            lcd_write_string(PSTR("turn on a switch    "));
            goto top;
            }

        float time;
        time = (a1*1000 + a2*500 + a3*100 + a4*50 + a5*10 + a6);

        lcd_home();
        lcd_write_string(PSTR("frequency is: "));
        lcd_write_int16(1/(time/1000));
        lcd_write_string(PSTR(" Hz "));
        lcd_line_two(); 
        lcd_write_string(PSTR("thanks"));
        //lcd_line_three();
        //lcd_write_int16(a3);

    //turn on led
     PORTB |= (1<<PB1);

     //delay 
     delay_ms(time);

     //turn off led
     PORTB &= ~(1<<PB1);

     //delay 
     delay_ms(time);
     }

  return 0;
}
January 20, 2012
by hevans
(NerdKits Staff)

hevans's Avatar

Hi Gil,

I'm not entirely sure why its working. In the end this is a real system, and the voltage at your pin will be something (its just not very well defined what it is). It's quite possible that enabling the pin change interrupt changed this behavior, but I haven't convinced myself of that with anything in teh datasheet. At the end of the day, tying to drive a pin to two different voltages shouldn't happen under normal circumstances.

Humberto

Post a Reply

Please log in to post a reply.

Did you know that the microcontroller's crystal oscillator can be used to keep accurate time? Learn more...