Lesson 6. Treating button’s contacts bounce. Interface of communication between program blocks.

Arduino lessons

In this lesson we will learn how to handle the button signal to eliminate switch bounce.

Previous lesson     List of lessons     Next lesson

In the previous lesson, we wrote a simple program for controlling the LED using the button. The button is pressed - the LED is lit, is free - not lit. Probably, you decided that the button is a very simple component and it's not difficult to work with it. We read the state of the button and did something. But let's write another program for controlling the LED.

 

LED control program.

The program must change the state of the LED each time the button is clicked, i.e. turn it on and off. In this program it is necessary:

  • read the state of the button;
  • compare it with the previous state;
  • if the previous state was free and the current state is pressed, inverts the LED status.

Those. The program must catch the front of the button or the signal state drop. There are two signal edges:

  • from high to low (--_);
  • from low to high (_--).

Pressing the button will correspond to the signal drop from high to low. We should detect this event.

// sketch_6_1 lesson 6
// Each time the button is clicked, inverts the status of the LED
// Works incorrectly due to bounce contacts

#define LED_PIN 13 // the number of the LED pin is 13
#define BUTTON_PIN 12 // the number of the pin for the button is 12

boolean buttonState; // button state
boolean buttonPrevState; // previous state of the button
boolean ledState; // LED status

void setup() {
  pinMode(LED_PIN, OUTPUT); // determine pin 13 (LED) as output
  pinMode(BUTTON_PIN, INPUT_PULLUP); // define pin 12 (button) as input
}

void loop() {
  buttonState = digitalRead(BUTTON_PIN); // write the state of the button to the variable buttonState

  if ( (buttonPrevState == HIGH) && (buttonState == LOW) ) {
    // the previous state of the button is free, and the current state is pressed
    ledState = ! ledState; // inversion of the LED state
    digitalWrite(LED_PIN, ledState); // write the state of the LED from the variable to the output
  }
  buttonPrevState = buttonState; // previous state of button = current
}

The detection of the event occurs in the construction

if ( (buttonPrevState == HIGH) && (buttonState == LOW) )

&& is a logical AND, produced over conditional expressions. As a result of its application, the condition will be worked out true if both expressions are true. In this case, the block of the if statement will be executed if the previous state of the button = HIGH and the current state of the button = LOW are simultaneously. Those. if the button was free in the previous check, and now it is pressed.

This condition inverts the state of the LED.

In principle, a simple program. But upload it to the Arduino board and check how it works.

It works incorrectly. Sometimes, at the push of a button, it inverts the state of the LED, sometimes it does not. Often the LED flickers when pressed. This is because the signal from the button is not at all like we imagine it.

 

Switch bounce.

Button contacts are mechanical elements. When closed, they bounce off each other, touch each other with irregularities, surfaces covered with oxides, dirt, etc. All this leads to a transient process, called switch bounce. The diagram of the button signal for closing and opening looks like this.

Switch bounce

The transient process usually lasts a few milliseconds. Therefore, for each press, the button generates many signal edges, and on each front, our program inverts the state of the LED. Those. The LED indicates an even or odd number of fronts in the button signal.

Obviously, it is necessary to programmatically process the button's signal to eliminate the bounce phenomenon of the contacts. There is still a problem - it is electromagnetic interference. If the button is connected by long wires, then short electromagnetic impulses can appear in the signal, which will lead to additional false alarms.

A robust program must necessarily handle signals from buttons and mechanical sensors.

 

How to deal with contact bounce?

There are several ways to handle switch bounce. Described below - one of the most reliable. I'll talk about one more method in the next lessons.

So. The solution is quite obvious. We must not react to frequent switching of the signal. The button can not be pressed for a time, for example, 0.0001 sec. So this is a bounce. It is necessary to wait, when during a certain time the state of the button will be stable, and only then make a decision. And for frequent signal switching, the program should not react.

 

Programm for debounce of button contacts.

Let's write such a program. To be closer to practical programming, select the blocks in the program and try to properly design the interface between them.

There are ready-made functions for processing signals from buttons. You call the function with the pin number as an argument, and it returns
debouncing the pin state.

But debouncing operation takes a considerable time, no less than the duration of the transient, usually 10 ms. And all this time the program will wait for the result of the function. But there can be several buttons. The program, in addition to reading the state of the buttons, still has something to do, and not all processes can be interrupted for a long time.

So let's try to process the state of the buttons in a parallel process. Let's make the first step to multitasking. While to conditional multitasking.

Let's single out the processing of the button signal into a separate program block. Let's put a condition that this block should be regularly called with a period, for example, 2 ms. To communicate with other software modules, we will create global variables that determine the state of the button. Thus, the button signal will be continuously processed in a parallel process, and in any part of the program you can learn about the state of the button by checking these variables.

The program consists of two main blocks located in the infinite cycle loop():

  • button signal processing unit;
  • LED control unit.

What we might need as a result of processing the button? What variables to create? As a rule, only two signs are needed:

  • One shows the current status of the button (pressed or free).
  • The second informs that the button was clicked (there was a signal drop from a high level to a low one).

We set global variables. Global because, it is not known in which part of the program we want to control the state of the button.

boolean flagPress = false; // sign that the button is pressed
boolean flagClick = false; // sign of pressing the button (front)

The signal processing unit of the button signal is called with a period of 2 ms. To count the steady state of the signal, you need a button state counter and a constant that specifies this time.

byte buttonCount = 0; // counter of button state acknowledgments
#define TIME_BUTTON 12 // steady state of the button (* 2 ms)

The functions of the program block are that it produces the following signs:

  • flagPress = true if the button is pressed;
  • flagPress = false if the button is pressed;
  • flagClick = true, if there was an event, the button was pressed. This sign should be reset after processing the event. In the processing unit, it can only be set.

If to call such a block regularly with a period of 2 ms, the signs will correspond to the current state of the button. They can be used by other program blocks anywhere. Thus, we can implement parallel execution of tasks.

The sketch of the program, built on this principle.

// sketch_6_2 lesson 6
// Each click of the button inverts the status of the LED

#define LED_PIN 13 // the number of the LED pin is 13
#define BUTTON_PIN 12 // the number of the pin for the button is 12

// variables and constants for processing the button signal
boolean flagPress = false; // sign that the button is pressed
boolean flagClick = false; // sign of pressing the button (front)
byte buttonCount = 0; // counter of button state acknowledgments
#define TIME_BUTTON 12 // steady state of the button (* 2 ms)
boolean ledState; // LED state variable

void setup() {
  pinMode(LED_PIN, OUTPUT); // determine pin 13 (LED) as output
  pinMode(BUTTON_PIN, INPUT_PULLUP); // define pin 12 (button) as input
}

// an infinite loop with a period of 2 ms
void loop() {
  // button signal processing unit
  // when the flagPress = true button is pressed
  // flagpress = false when the button is free
  // when the button was clicked the flagClick = true

  if ( flagPress == (! digitalRead(BUTTON_PIN)) ) {
    // flagPress flag = current button state
    // (inverse since the active state of the LOW button)
    // ie. the state of the button remained the same
    buttonCount = 0; // reset the button state acknowledgments counter
  }
  else {
    // flagPress flag not = current button state
    // button state changed
    buttonCount ++; // +1 to the button state counter
    if ( buttonCount >= TIME_BUTTON ) {
      // the state of the button did not fall for the specified time
      // the state of the button became stable
      flagPress = ! flagPress; // inversion of the state sign
      buttonCount = 0; // reset the button state acknowledgments counter

        if ( flagPress == true ) flagClick = true; // sign of pressing the button (front)
    }
  }

  // LED control unit
  if (flagClick == true) {
    // there was a click of the button
    flagClick = false; // reset the front sign of the button
    ledState = ! ledState; // inversion of the state of the LED
    digitalWrite(LED_PIN, ledState); // output the LED status
  }
  delay(2); // 2 ms delay
}

In the button state processing block, the following occurs:

  • If the current state of the button matches flagPress sign, nothing is done. Only the state acknowledgments counter is reset.
  • If the states of the button and the flag are different, the counter begins to count the acknowledgment time. Any return to the button state and flagPress equality resets the counter.
  • If the signal state is stable for the number of cycles defined by the TIME_BUTTON constant, flagPress sign is inverted.
  • In this case, flagClick - the sign of the signal edge is formed, if the button is pressed. When the button becomes free, this flag is not generated.

The LED control unit, I think, should not be explained.

The blocks are included in an infinite loop with a period of 2 ms. This statement is not entirely correct, but let's not discuss it yet. The main thing is that the button signal processing unit is called regularly with a period of about 2 ms.
Download the program to the Arduino controller, check that it works steadily. False positives do not arise.

In the TIME_BUTTON constant, the steady state of the button signal is set to 24 ms. I recommend choosing this time 20-30 ms.
To test the signal processing algorithm, increase the value of the TIME_BUTTON constant to 250 (time 500 ms). You will see that when the button is pressed quickly (less than 0.5 sec), the LED does not change its state. This means that the steady state acknowledgment algorithm works correctly.

In this lesson, a practical module is developed to eliminate the button bounce. You can use it in your programs. In the next lesson, I'll talk about classes in the Arduino programming language, we'll create the button as an object.

Previous lesson     List of lessons     Next lesson

Leave a Reply

Your email address will not be published. Required fields are marked *