Developing in C for the ATmega328P: Struct - buttons[]

3 minute read

Where I describe the struct, buttons[] and how to use it to debounce and check for button presses.

Introduction

Describing buttons[] is slightly different than describing a function such as analogWrite() and digitalRead() as buttons[] is a struct. The goal of this entry is to provide sufficient instruction as to how to use buttons[]. If you wish to understand more as to how to debounce a button, please review the links mentioned at the end of this entry.

A struct in C is a multi-type array. It allows you to use different types of variables (int, char, or float, etc) in singularly named entity. This provides a much more simpler interface than having to refer to three different arrays due to their different types. You can also have, some to all of the types be the same, however, they are grouped under one name. Again, this simplifies programming and documentation.

In this case, buttons[] is a struct, which provides the ability to easily add a button type. When that type is given an Uno pin number, it will return a debounced pressed value if it detects a press.

Documentation

Steps to get you started

(Optional) In Library/button.h, change the number of buttons to be used, by changing the number after MAX_BUTTONS. It is currently set to 2.

#define MAX_BUTTONS 2
  1. In your main.c file, provide includes for both button.h and sysclock.h and declare the button type.
#include "sysclock.h"
#include "button.h"

extern button buttons[MAX_BUTTONS];
  1. Define the buttons. Note: PinMode() is still required, which allows the user to define INPUT_PULLUP or INPUT, for the desired mode.
# define buttons (inside of main)
buttons[0].uno = 2;
buttons[1].uno = 4;
pinMode(buttons[0].uno, INPUT_PULLUP);
pinMode(buttons[1].uno, INPUT_PULLUP);
  1. In your main function, before you check for button presses, have the statement init_sysclock_2();
init_sysclock_2();

One step to use: Remember buttons[] allows for multiple buttons, so you need to provide a subscript to determine which button was pressed. In the example below, we are checking button[0] or the one attached to pin 2.

if (buttons[0].pressed) {
...code once button is pressed
}

Full Working Example

In other words, here is a full working model for two buttons (examples/button/). Make sure all highlighted lines are in your main.c file.:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include "uart.h"
#include "sysclock.h"
#include "button.h"
#include "pinMode.h"

extern button buttons[MAX_BUTTONS];

int main (void)
{
    init_serial();
    puts("Testing Button Presses");
    uint8_t count[MAX_BUTTONS] = {0};

    buttons[0].uno = 2;
    pinMode(buttons[0].uno, INPUT_PULLUP);
    buttons[1].uno = 4;
    pinMode(buttons[1].uno, INPUT_PULLUP);

    init_sysclock_2 ();

    for (;;)  
    {
        for (uint8_t i=0; i < MAX_BUTTONS; i++) 
        {
            if (buttons[i].pressed) 
            {
                count[i] += 1;
                printf("Button %u was pressed, %u times.\n", i, count[i]);
            }
        }
    }
    return 0;
}

More Detailed Analysis

Why all this work to check if a pin is HIGH or LOW? Because buttons bounce.

Elliot Williams wrote a great two part series on debouncing buttons, part one and part two. What made the series even more interesting were the wealth of comments (over 200!) as to was this the right way or not.

He’s not the only one…Adafruit in Python, or a Arduino Library, or a Hardware approach to debouncing, and a two part treatise on debouncing by Jack Gansslepart one: describes the problem and part two: solutions. All of these authors are attempting to solve one of the most common issues we face in embedded programming, bouncing buttons.

I took Elliot’s solution and implemented it in the buttons function. I use Timer/Counter 2 (Uno PWM pins 11 and 3) to provide a 10ms bounce clock signal. [This means you can’t use the PWM functions on pins 3 and 11, if you use this approach. You are able to use pins 3 and 11, digital read and write functionality.]

For the detail on how the debounce solution works, I ask you to read Elliot’s column and in particular the section THE ULTIMATE DEBOUNCER(TM). To his solution, I added the ability to check multiple buttons. In my testing, 2-4 buttons work well with this approach, however, I wouldn’t use this functionality for a matrix of 8 or more buttons (keyboard). As the number of buttons increase, the interrupt will perform too much processing and the button debounce code will degrade.

Comments powered by Talkyard.