AVR Microcontroller. Toggle LED’s Using a Push Button Switch. Push Button Debouncing.

About: Welcome to FOG channel! "Hobby" "DIY" "Electronics" "Programming" "Video Games" "Media Design" "Digital Art" and more... https://www.y...

In this section, we will learn How to make program C code for ATMega328PU to toggle the status of the three LED’s according to the input from a button switch. Also, we have explored a solutions to the problem of is ‘Switch Bounce‘. As usually, we will assemble the electrical circuit on base of the AVR ATmega328 to check the work of program code.

Teacher Notes

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

Step 1: Writing and Building AVR Microcontroller Application in C Code Using the Integrated Development Platform Atmel Studio 7

If you don’t have Atmel Studio, you should download and install it.

https://www.microchip.com/mplab/avr-support/atmel-studio-7

The first few lines we have some compiler defines.

F_CPU defines the clock frequency in Hertz and is common in programs using the avr-libc library. In this case it is used by the delay routines to determine how to calculate time delays.

#ifndef F_CPU
#define F_CPU 16000000UL // telling controller crystal frequency (16 MHz AVR ATMega328P)
#endif
#include <avr/io.h> // header to enable data flow control over pins. Defines pins, ports, etc. 

The first include file is part of avr-libc and will be used in pretty much any AVR project you work on. io.h will determine the CPU you're using (which is why you specify the part when compiling) and in turn include the appropriate IO definition header for the chip we're using. It simply defines the constants for all your pins, ports, special registers, etc.

#include  <util/delay.h> // header to enable delay function in program

The library util/delay.h contains some routines for short delays. The function we'll be using, is _delay_ms().

We use defines to declare our button and LED's ports and pins. Using the defines statements like this allows us to only need to modify 3 easy-to-find lines if we move the LED to a different I/O pin or use a different AVR.

#define BUTTON1 1 // button switch connected to port B pin 1

#define LED1 0 // Led1 connected to port B pin 0
#define LED2 1 // Led2 connected to port C pin 1
#define LED3 2 // Led3 connected to port D pin 2

The final two define statements setup times, in millisecond, to debounce the switch and the time to wait before allowing another press of the button. The debounce time needs to be adjusted to the time it takes the switch to go from a digital high to a digital low after all the bouncing. The bounce behavior will differ from switch to switch, but 20-30 milliseconds is typically quite sufficient.

#define DEBOUNCE_TIME 25 // time to wait while "de-bouncing" button 
#define LOCK_INPUT_TIME 300 // time to wait after a button press
void init_ports_mcu()
{

This function is called just once in the beginning of our program to initialize input output pins that we will be using.

For the button, we will be using the PORT and PIN registers for writing and reading. With AVRs, we read a pin using it's PINx register and we write to a pin using it's PORTx register. We need to write to the button register to enable the pull-ups.

For the LED we only need to use the PORT register to write to, however, we also need the data direction register (DDR) as the I/O pins are setup as inputs by default.

First, we're setting the LED's I/O pins as an output using it's data direction register.

DDRB=0xFFu; // Set all pins of the PORTB as output.

Next, explicitly set the button pin as an input.

DDRB &= ~(1<<BUTTON1); // Makes first pin of PORTB as Input

Next, the PORTB pins is set high (+5 volt) to turn it on. The output pins is initially high, and since our LED is wired active-high, it will be turned on unless we explicitly turn it off.

And finally, we enable the internal pull-up resistor on the input pin we're using for our button. This is done simply by outputting a one to the port. When configured as an input, doing so results in enabling pull-ups and when configured as an output, doing so would simply output a high voltage.

PORTB = 0xFF; // Set all pins of the PORTB as HIGH. Led is turn on, 
              // also the internal Pull Up resistor of first pin PORTB is enable. 

DDRC=0xFFu; // Set all pins of the PORTC as output.
PORTC=0x00u; // Set all pins of PORTC low which turns it off.
DDRD=0xFFu; //  Set all pins of the PORTD as output.
PORTD=0x00u; // Set all pins of PORTD low which turns it off.
}
unsigned char button_state()
{

This function returns a boolean value indicating whether or not the button was pressed. This is the block of code with is continually being executed in the infinate loop and thus is polling the state of the button. This is also where we debounce the switch.

Now, remember that when we press the switch, the input output pin is pulled to ground. Thus, we're waiting for the pin to go low.

/* the button is pressed when BUTTON1 bit is clear */
if (!(PINB & (1<<BUTTON1))) {

We do so by checking if the bit is clear. If the bit is clear, indicating that the button is depressed, we first delay for the amount of time defined by DEBOUNCE_TIME which is 25ms and then check the state of the button again. If the button is depressed after the 25ms then the switch is considered to be debounced and ready to trigger an event and so we return 1 to our calling routine. If the button is not depressed, we return 0 to our calling routine.

  _delay_ms(DEBOUNCE_TIME);
  if (!(PINB & (1<<BUTTON1))) return 1;
  }
return 0;
}
int main (void)
{

Our main routine. The main function is unique and set apart from all other functions. Every C program must have exactly one main() function. main is where the AVR starts executing your code when the power first goes on, so it's the entry point of the program.

unsigned char n_led = 1; // initially  LED number is on now<br>

Call of the function to initialize I/O pins being used:

init_ports_mcu();

infinite loop where our program runs:

while (1)
{

When button_state returns one indicating that the button was pressed and debounced, then toggling the current status of LED's in turn according to the n_led parameter.

if (button_state()) // If the button is pressed, toggle the LED's state and delay for 300ms (#define LOCK_INPUT_TIME)
{
   switch(n_led){
   case 1:
     PORTB ^= (1<<LED1);
     PORTC ^= (1<<LED2);
   break;

These statements uses C bitwise operators. This time it's using the exclusive OR operator. When you XOR the PORT with the bit value of the bit you want to toggle, that one bit is changed without effecting the other bits.

    case 2:
      PORTC ^= (1<<LED2);<br>      PORTD ^= (1<<LED3);
    break;
    case 3:
      PORTD ^= (1<<LED3);
      PORTB ^= (1<<LED1);
      n_led=0; // reset LED number
    break;
    }
    n_led++; // next LED is turn on
    _delay_ms(LOCK_INPUT_TIME); 
   }
 }
 return (0);
}

So now, when you run this program, you should be able to press the push-button to LED's are toggling. Due to our delay defined by LOCK_INPUT_TIME, you can press and hold the button which will cause the LED's to turn off and on at a consistant rate (little more than every 275ms).

Programming is complete.

Next step is building the project and programming hex file into the microcontroller using the avrdude program.

You can download main.c file with program in c code:

Step 2: Transfering the HEX File of Program Into the Flash Memory of Chip

Download and install AVRDUDE. The latest version available is 6.3: Download the zip file

First, copy the hex file of program in to AVRDUDE directory. In my case it is ButtonAVR.hex

Then, type in DOS prompt window the command: avrdude –c [name of programmer] –p m328p –u –U flash:w:[name of your hex file].

In my case it is: avrdude –c ISPProgv1 –p m328p –u –U flash:w:ButtonAVR.hex

This command writes hex file to the microcontroller’s memory.

Watch the video with a detailed description of the microcontroller flash memory burning:

Microcontroller flash memory burning...

Ok! Now, the microcontroller works in accordance with the instructions of our program. Let's check it out!

Step 3: Hardware Switch Debouncing

In addition to Software switch debouncing we can use hardware switch debouncing technique. The basic idea behind such technique is to use a capacitor to filter out quick changes in the switch signal.

What value capacitor should be 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.0 nanofarads will react very quickly, with little or no effect on the bouncing. Conversely, a higher capacitor value such as 220 nanofarads (which is still pretty small in terms of capacitors) will provide a slow transition from the starting to the ending voltage (5 volt to 0 volt). The transition seen with a 220 nanofarads capacity is still pretty fast in a real-world sense however, and thus can be used on poorly performing buttons.

Step 4: Electrical Circuit

Connect components in accordance with schematic diagram.

Plug power and it is working!

Be the First to Share

    Recommendations

    • CNC Contest

      CNC Contest
    • Make it Move

      Make it Move
    • Teacher Contest

      Teacher Contest

    Discussions

    0
    None
    uuu000

    Question 5 months ago on Introduction



    How to add another led
    and how to write it ?
    #define LED1 0 // Led1 connected to port B pin 0 (PORTB0)
    #define LED2 1 // Led2 connected to port C pin 1 (PORTC1)
    #define LED3 2 // Led3 connected to port D pin 2 (PORTD2)