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 » Turn on LED with Button won't work

March 25, 2011
by lnino
lnino's Avatar

Hi at all.

After I have compelted my Nerdkits Guide I tried to start a new small project, but i got stuck.

Here is what I tried: I have two LEDs on my Breadboard. The green one turns on and off all the time, because its in the while(1). The red one should be turned on when I push a button. And here is my problem: The red Led won't turn on. Something wrong with my interrupt? Because when I push the button the green led freezes for a short time.

The components are connected on my breadboard like this:

The green LED is connected with the cathode to GND and with anode to Pin 27 (PC4) of the mCU. The red LED is connected with the cathode to GND and with anode to Pin 26 (PC3) of the MCU. The button(included in the nerdkit) is connected with C to GND and NO to Pin 24 (PC1) of the MCU.

(If this is to few, I can't make a photo of my breadboard)

Can anybody figure out what I have made wrong?

Thanks for the help.

Here is my source code:

// for NerdKits with ATmega168

#define F_CPU 14745600

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

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

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

void setup_pin_change_interrupt(void);

int main(void)
{
    DDRC = 0b00011000;//Config PC3 and PC4 as outputs (for LEDs)
    PORTC |= 0b00000010;//enable pull up resistor on PC1 (for a Button)
    setup_pin_change_interrupt();//setup the interrupts
    sei();//enable global interrupts

  while(1)//blink the led on PC4
    {
        PORTC |= 0b00010000;
        delay_ms(1000);
        PORTC &= ~(0b00010000);
        delay_ms(1000);
    }
}

void setup_pin_change_interrupt(void)
{
    PCMSK1 = (1<<PCINT9);//Un-mask PCINT9
    PCICR = (1<<PCIE1);//Enable the PORTC pin change interrupt
}

ISR(PCINT0_vect) //ISR
{
    if((PORTC & 0b00001000) == 0) {PORTC |= 0b00001000;}//if LED is off, turn it on (PC3).
    else {PORTC &= ~(0b00001000);}//If LED is on, turn it off (PC3).
}
March 25, 2011
by Noter
Noter's Avatar

Maybe you should debounce your switch or change the logic in the interrupt from toggling to just be on if the switch is closed, off if the switch is open. Also, your code would read easier if you used (1<<PC3) or _BV(PC3) instead of binary constants.

March 25, 2011
by Ralphxyz
Ralphxyz's Avatar

Noter, where does that _BV() syntax come from? I know it comes from a C library as a Macro but where, and do you have any reference tutorials on using that/those macros?

Ralph

March 25, 2011
by bretm
bretm's Avatar

It comes from an internal header in avr-libc that gets pulled in when you reference io.h. It means Bit Value and _BV(x) is the same as (1<<(x))

March 25, 2011
by Noter
Noter's Avatar

Some people think it's a bad idea to use _BV but I like it. If I ever need it on another platform and it's not in the system include files, I'll just define it in somewhere in my code like this.

#ifndef _BV
#define _BV(n) (1<<n)
#endif

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=48246&start=0

March 25, 2011
by lnino
lnino's Avatar

@ Notar:

Can you give me an example of your idea? I am not that familiar with this area.

March 25, 2011
by Noter
Noter's Avatar

I didn't run it so don't hold it against me if it doesn't work but this is the general idea. See if the led lights and goes out with the button.

// input PC1 has button to GND, pull-up enabled.
ISR(PCINT0_vect) 
{
    if(PORTC & _BM(PC1)){       // non-zero is true, pull-up is giving a 1
        PORTC &= ~_BM(PC3);     // turn off PC3 led (the button is open)
    }else{
        PORTC |= _BM(PC3);      // turn on PC3 led (the button is closed)
    }
}

As for debouncing a switch, search the forum there are previous posts with examples.

March 25, 2011
by bretm
bretm's Avatar

PCINT0_vect corresponds to PORTB. To catch PORTC changes use PCINT1_vect (I think, don't have datasheet in front of me).

In Noter's example, use PINC to read port C pins, not PORTC. But PCINT0_vect was probably your problem.

I wouldn't attach a pin-change interrupt to a button that didn't have hardware debouncing. The button will bounce many times and generate many interrupts, possible overlapping and missing up your logic. Either use software debouncing without pin-change interrupts, or hardware debouncing with or without interrupts.

March 25, 2011
by lnino
lnino's Avatar

Thanks bretm. That was the mistake.

I have two additional questions:

1) The button won't react good on my pushes. I have read that you have to put a delay on the button for this reason. But it won't work well. How can I put it in my code to have a working button?

2) If I put a second and a third button on my breadboard, which should do completely different things, like turn on different LEDs. Button 1 turns on LED1(red), Button 2 turns on LED2(green) and so on.

How can I make a selection in my code to find out which button has been pressed and which reaction has to be done?

I have realized that some time ago on a atxmega128, but how can I use this method on my atmega168?

Here is my old code from my atxmega128:

ISR (PORTH_INT0_vect)
{
    unsigned char cKeyIn = PORTH_IN;    // Read Buttonports (8 Bit!!)

    switch(cKeyIn){

        case 0b1111110:     // Button on PH0
            reset_program();
            break;

        case 0b1111101:     // Button on PH1
                     // do something

        {
March 26, 2011
by leedawg
leedawg's Avatar

I dont think I totally understand your second question? Are you just simply trying to set up multiple switchs to turn on and off different LEDs attatched to the microcontroller? Or are you trying to read the state of each of these switchs? In any case I use if statements and variables to turn on and off things I find it works pretty well. As for question #1 I can help you with that. I had the same problem the button did not seem to work very well when I pressed it so I added a variable and put it out to the LCD screen to see what was going on and it turns out the interupt was firing about 30 or 40 times for each button press just simply due to the bouncing of the contacts inside the switch. It does not feel like it bounces but it does when your talking about the MCU running at 14.74 mhz.

To solve this problem I found some by noter actually that uses a timer to control the debounce it works very well. Here is the code that I used.

    volatile int button_disable;

    ISR(PCINT1_vect){
      if (button_disable==0) {  // only handle if zero, otherwise in debounce period
        button_disable=102;  // start debounce period ~ .2 sec
        if(!(PINC & (1<<PC5))){ // pin goes low on push
          <button processing code>
          }
        }
      }
    }

    SIGNAL(SIG_OVERFLOW0) {
      // decrement the push button debounce countdown
      if (button_disable>0) {
        button_disable--;
        }
    }

    in your init routine...
     // set timer for button debounce
     // Timer0 internal 14745600/256/256 =  225mhz interrupts
     TCCR0B = (1<<CS02);
     TIMSK0 = (1<<TOIE0);

This should straighten out the problems your having in your first question.

Here is an example of how I turn things on and off however. I would define a variable in your init main() function such as toggle. Then set up a while statement that uses that variable and use your external interrupt to increment that variable from 0 to 1 and then simply have a line that resets the variable back to zero if it is greater than 1. So here is example of what im talking about.

volatile uint8_t toggle;

    int main(){

        while(1)

                while (toggle == 1)
                {

                //Code that you want to toggle on and off. 
                    if (toggle >1) {// this resets toggle value to 0 once you have incremented beyond one with your interrupt 
    toggle=0;
    }
            }
        }  
    }

Of course you would need to add to this code so that your interupt command would say increment the variable toggle up each time you have a button push and presto you are now toggling your code on and off.

Alternatively you could use if statements one for on and one for off to turn the led on and off based on what you have incremented the variable to.

for example

if(toggle <=0) {
    // do something here 
    }

if(toggle >=1) {
    // do something here 
    }

I hope this helps a little bit with both questions. Just ask on here and search I to just a few months ago was completely mystified on how to make most things happen in the avr. Just dont give up and keep trying things ive learned tons by doing that and am just starting to feel comfortable with the basics. Good luck.

Lee

March 26, 2011
by lnino
lnino's Avatar

what i have meant with my second question is the following. i want to have for example 10 buttons. every button has a different job. button one should display a 1 on the display. button 2 should display a 2 on the display. and so on. how can i realize that? thank you for your help.

March 26, 2011
by leedawg
leedawg's Avatar

oh well that should be pretty easy to do. You just want each button to write a number to the display.

I would just simply define a variable then of whatever your liking is. And then just write a bunch of if statments that say if button 1 is pressed set variable equal to the number you want on the display. Then add a line at the end of the loop that will write out that variable that you are changing each time to the LCD.

SO something kinda like this.

int main(){

lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

uint32_t button ; // define the variable that you are then changing with your buttons.
DDRC &= ~(1<<PC5) & ~(1<<PC4) & ~(1<<PC3); //Define inputs
PORTC |= (1<<PC5) | (1<<PC4) | (1<<PC3); //Pull up resistors on

while(1) {

if(!(PINC & (1<<PC5))) {
button=1;
}

if(!(PINC & (1<<PC4))) {
button=2;
}

if(!(PINC & (1<<PC3))) {
button=3;
}

fprintf_P(&lcd_stream, PSTR("%2.2f Button Value"), (double) button);

}
}

Now this is most of the code but it might not run because I may have made a mistake but you get the idea. You will of course need to put in your calls for all the necessary libraries and what have you so that it all compiles the right way and if I made some typos or did not define something properly your compiler will let you know but this is how I would accomplish what you were asking in your last post.

Lee

March 26, 2011
by Ralphxyz
Ralphxyz's Avatar

You probable will want to use a switch/case statement instead of 10 if/else statements though the if/else will work.

Google C switch.

Ralph

March 27, 2011
by lnino
lnino's Avatar

Thanks for your replies.

If I want additional to the output on my display that every button turns on a different LED.

Can I solve this challenge also completely in the while(1) situation(like leedawg did), or do I have to use interrupts?

Might this be a right thought?

// PCINT0 for PORTB Ports
ISR (PCINT0_vect) {     
unsigned char cKeyIn = PORTB;    // Read Buttonports (8 Bit!!)       
switch(cKeyIn){           
case 0b1111110:     // Button on PB0             
// turn on LED1            
break;

case 0b1111101:     // Button on PB1                    
// turn on LED2          
}

// PCINT1 for PORTC Ports
ISR (PCINT1_vect) {     
unsigned char cKeyIn = PORTC;    // Read Buttonports (8 Bit!!)       
switch(cKeyIn){           
case 0b1111110:     // Button on PC0             
// turn on LED3

case 0b1111101:     // Button on PC1                    
// turn on LED4          
}

Sorry for this post, but I can't test it because I am on the way and I write this post with my mobile phone.

I hope to get close my computer the next days to realise a programm included with you thoughts and help.

March 27, 2011
by leedawg
leedawg's Avatar

Again I dont think I understand your question. Are you saying you want to switch an LED on in addition to displaying which LED should be switched on, on your LCD screen? If this is the case you could simply just add the code to switch the pin on which ever LED you have connected to it under the respective if statments in your code or as ralph said a switch case setup.

So you would just simply add something like

if(!(PINC & (1<<PC5))) {
button=1;
PORTC |= (1<<PC3);
}

So this would then output the number 1 to your variable set as button and in addition do that switch on pin PC3 on the microcontroller and you would have some particular led attatched to that pin.

Hope that answers your question.

Lee

March 28, 2011
by lnino
lnino's Avatar

Hi leedawg,

thank you for your reply.

I think now I can handle it.

You can realise this challenge in the while(1) and as a interrupt.

What would be the advantage/disadvantages of the while(1) version and the advantage/disadvantages of the interrupt version?

Sorry for my english, but this is not my native language.

Thanks a lot.

March 28, 2011
by Ralphxyz
Ralphxyz's Avatar

One of the advantages to using interrupts is that your mcu can be doing other things while waiting to be interrupted.

Doing all of your processes in the while() dedicates processor resources to "polling" your button.

Both methods can be effective and useful and have had lots of deep discussions on the web.

I am sure there are other explanations from people who actually know what they are talking about.

Your English is fine and you are most welcome to practice here.

Ralph

March 29, 2011
by lnino
lnino's Avatar

Hi Ralph,

thanks for you reply and your nice words.

I was now able to realize my challenge. Now the button is doing what i want when I press it and I can go forward with my intention.

Post a Reply

Please log in to post a reply.

Did you know that signed numbers need to be sign-extended when chaging variable sizes? Learn more...