[ Log In ]
Thumbnail: quad buffer line driver 74HC126E

Quad Buffer Line Driver (Through Hole)

Image of the Atmega324p


Serial to USB converter with Micro USB cable

USB to Serial Converter

Thumbnail: Crystal Oscillator 18.432 MHz for UART

18.432 MHz Crystal Oscillator 18pf 30ppm

Thumbnail: 22 pF Capacitor

22 pF Multilayer Ceramic Capacitor

Thumbnail: Quartz crystal oscillator - 16 MHz

16 MHz Crystal Oscillator 20 pF Through Hole

Thumbnail: 4x4 keypad top view.

4x4 Keypad with Adhesive Backing

USB AVR programmer

USB AVR Programmer

3 pin slide switch

SPDT Slide Switch 3 pin 30V

Handheld auto range multimeter

Handheld Auto Ranging Digital Multimeter

Skip Navigation Links

Microcontroller - A Beginners Guide - Adding a Button to the Microcontroller and Making it Do Something

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

_delay_ms(100); //Slow, from previous

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