Beginning Microcontrollers Part 8: Adding a Button to the Microcontroller and Making It Do Something

About: I like robots!

A very simple and easy way to provide for human interaction with the microcontroller is to insert a button into the circuit. We communicate with computers using two main input devices: the mouse and the keyboard. A keyboard is nothing more than a bunch of buttons laid-out to allow the user to input (ASCII) characters to the computer. If you're scratching your head on the ASCII part, don't worry--it just represents the code for each character.

By this point in our journey, you should have already setup your computer with WINAVR (or AVR-GCC for Linux), and be able to program your microcontroller. You should also have a circuit built with an LED plugged into the microcontroller. You also made the LED blink in the previous tutorial.

Adding a button or switch to the circuit enables the microcontroller to receive human input. Other forms of input for microcontrollers include (but are not limited to) a keyboard or a mouse, buttons, switches, audio (through microphones), touch screen displays and digitizers. There are of course many other devices that can provide input to a microcontroller, but these may not all be activated by voluntary human action. I place these other devices into the "sensing" category, as these devices typically sense conditions or events and react accordingly. A few such examples include sensors for tilt (accelerometers), detecting infrared energy or monitoring temperature.

So, here's the skinny on buttons and mechanical switches: they're imperfect! The two families of mechanics and electronics go together like the Montagues and Capulets. That is, they don't! When you push a button, you might expect a clean response electronically. Well, sorry to be the bearer of bad news, but the signal often bounces quite a bit before it settles to its correct voltage level. In this image, I show this phenomenon. If the voltage is set at 5 volts before the button is pressed and then goes to zero volts when the button is pressed, there will be a "bouncing" effect of the voltage between these two values. So then why don't all of our kitchen appliances or our cars exhibit this problem?

As you should be able to see in the image, I have inserted a capacitor between the two pins. This will smooth out the signal. The effect of this capacitor can be seen on an oscilloscope, as demonstrated in the video. But if you don't have an oscilloscope of your own, then you will just have to trust me. Another way we could alleviate this problem is to add a time delay into the program, just after the microcontroller senses the first button press event. However adding a discrete component to a circuit to solve such an electronics problem, is often a better way than adding code to cause a delay--as that code will introduce another potential source of a bug into the program, and will also require more processor time to execute. In additional, this code can also result in the development of other problems as the rest of the code continues to execute.

But what value capacitor should we select? This will ultimately depend on how poorly the button performs regarding this particular problem. Some buttons can display a tremendous bouncing behavior, yet others will have very little. A low capacitor value like 1.0nF (nanofarads) will react very quickly, with little or no effect on the bouncing. Conversely, a higher capacitor value such as 220nF (which is still pretty small in terms of capacitors) will provide a slow transition from the starting to the ending voltage (i.e. 5v to 0v). The transition seen with a 220nF capacity is still pretty fast in a real-world sense however, and thus can be used on poorly performing buttons.

You might have noticed by now that the breadboard has changed somewhat, to give the circuit a cleaner look. The previous wires were too long, and the build environment started getting messy as I added further components to the circuit. Therefore a re-design of the breadboard was in order, so I snapped a second breadboard to the end of the first. But you might be asking--why did I do this if there were sufficient ties remaining at the other end? Well, I did it for neatness, and I also liked where the microcontroller was positioned. You may be able to tell that I've aligned it with the numbers so that I don't need to count pins all the time--I simply let the breadboard numbering tell me where each pin is on the MCU. I also tied all of the positive (+) rails on both boards together, and did the same to all of the negative (-) rails as well. This will allow me to have VCC or GND close by, anywhere on the breadboard.

So then, how do we program the ATmega32 microcontroller (or other MCU that you may be applying to this experiment) to make use of the new button? Well it's really quite straightforward! We only have to add two initializing lines just prior to the infinite loop, and a single condition block within the loop. The two initialization lines added before the loop include one statement to set PINB1 for input by assigning it a "0" like this:

DDRB &= ~(1 << PINB1);

We will also set pin B1 "high," meaning the pin will read 5 volts until the button is pressed; at which time the pin will read zero volts. To set the pin at a high voltage of 5 volts, we add this line of code:

PORTB |= 1 << PINB1;

Within the program, there must be a decision: a decision to run some code when the button is pressed, or run some other code if the button is not pressed. This task is in the form of a conditional statement called an "if else" statement. It does EXACTLY what is says. Just like the English equivalent... if (the button is pressed), jump up and down, else stand on your head. the action "jump up and down" will happen while the button is pressed. But while the button is not pressed, the other action "stand on your head" will happen. The if statement code:

if (bit_is_clear(PINB, 1))

specifies a test for a condition specified inside the parentheses. The name "bit_is_clear" represents a function that takes two arguments. In this case the first argument is PINB, which describes the set of pins we are specifying. The second argument represents which pin we are checking, and in this case we are concerned with pin #1 in the set.

You might be wondering what sorts of things can we put into the code block controlled by the "if" condition? That all depends on what you want your program (and circuit) to do. In this case, as a way to show that this button does something and works, I have the LED blinking slowly (every 100 ms) while the button is not pressed, and blinking faster (every 10ms) while pressed.

Here are the changes made to the previous LED blinking program:

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

int main(void)


DDRB |= 1 << PINB0;
DDRB &= ~(1 << PINB1); PORTB |= 1 << PINB1;

while (1)


PORTB ^= 1 << PINB0;
if (bit_is_clear(PINB, 1)) { _delay_ms(10); //Fast

} else { _delay_ms(100); //Slow, from previous




That is it! There is very little programming required to use a button, LED and make things blink!

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Microcontroller Contest

Participated in the
Microcontroller Contest

First Time Author Challenge

Participated in the
First Time Author Challenge

Be the First to Share


    • Assistive Tech Contest

      Assistive Tech Contest
    • Reuse Contest

      Reuse Contest
    • Made with Math Contest

      Made with Math Contest



    5 years ago on Introduction

    Very nicely done.............Always love the basics ....................