ARM Architecture

Introduction to Microcontrollers – Buttons and Bouncing


Fast Hyperlinks

What Is A Button?

To your {hardware}, that’s.  As mentioned in Introduction to Microcontrollers – Extra On GPIO, a button (or key, or change, or any type of mechanical contact) is usually hooked as much as a microcontroller in order to generate a sure logic stage when pushed or closed or “energetic,” and the other logic stage when unpushed or open or “inactive.”  The energetic logic stage might be both ‘0’ or ‘1’, however for causes each historic and electrical, an energetic stage of ‘0’ is extra frequent.  Right here is the fundamental button connection to a GPIO enter, which we noticed within the earlier tutorial chapter:

When the change is open, the pullup or pulldown causes the GPIO enter pin to see the inactive logic state, however when the change is closed it overrides the resistor and causes the GPIO enter pin to see the energetic logic state.  As a reminder, drawing ‘A’ reveals an active-high button, whereas drawing ‘B’ reveals an active-low button.

What Does A Button Look Like?

To your code, that’s.  The best option to signify a button is as a boolean, or as a single zero or non-zero byte.  One button, one byte.  Name this Methodology 1, or the Boolean-per-Button technique.  It is easy as a result of there is no such thing as a masking required, both in setting the variable or in studying it.  The disadvantage of this method is that it makes it troublesome to verify for all of the totally different button prospects in your system (16 buttons = 16 bytes to check).

One other option to signify a button is as a boolean which is is one little bit of a byte or 16-bit phrase.  Now you possibly can have a single byte or phrase that may maintain the state of as much as 8 or 16 fully impartial buttons (or 32 if you wish to go to a 32-bit worth).  This button mixture is straightforward to course of in a “change” assertion, and it additionally supplies the power to verify for a number of simultaneous button pushes.  By “impartial” buttons we imply buttons that may be pressed and acknowledged whatever the state of any of the opposite buttons.  This example or requirement is definitely far more frequent for switches than for buttons, however bear in mind we’re treating switches and buttons and keys all the identical right here.  Name this Methodology 2, or the Bit-per-Button technique.

Lastly, one can signify the state of a number of non-independent buttons as a single worth.  Take a calculator keyboard for instance.  With a calculator there’s in all probability just one keypress allowed at any given time, so a calculator with 20 or 30 buttons can signify all of the potential legitimate button pushes with a single variable that may maintain 20 or 30 values.  On this case, 30 buttons might be represented by solely 5 bits, for the reason that buttons should not impartial and thus most (and even all) combos of a number of buttons are unlawful.  Name this Methodology 3, or the Single-Worth technique.

For essentially the most half, all of the examples on this tutorial part will signify buttons in accordance with strategies 2 or 3 – that’s, as an 8 or 16-bit variable that both represents a number of impartial buttons or that represents a novel worth amongst non-independent buttons.

Button Debouncing, Revisited

You might bear in mind in Introduction to Microcontrollers – Extra On GPIO that we noticed some scary scope photos of change bounces.  Each system that makes use of any form of mechanical change should take care of the difficulty of debouncing.  The important thing activity is to guarantee that one mechanical change or button motion is just learn as one motion by the microcontroller, despite the fact that the microcontroller will usually be quick sufficient to detect the undesirable change bounces and deal with them as separate occasions.  Bouncing might be eradicated by particular ICs or by RC circuitry, however generally debouncing is completed in software program as a result of software program is “free.” (a software program particular person ought to at all times carry tomatoes to throw at this level)

Scary photos of change bouncing, redux:

Switch Bouncing

The important thing to debouncing is to ascertain a minimal criterion for a legitimate button push, one that may be applied in software program.  This criterion should contain variations in time – two button presses in 20ms have to be handled as one button occasion, whereas two button presses in 2 seconds have to be handled as two button occasions.  So what are the related occasions we have to take into account?  They’re these:

  • Bounce time:  most buttons appear to cease bouncing inside 10ms
  • Button press time: the shortest time a consumer can press and launch a button appears to be between 50 and 100ms
  • Response time: a consumer notices if the system response is 100ms after the button press, however not whether it is 50ms after

Combining all of those occasions, we will set a couple of objectives

  • Ignore all bouncing inside 10ms
  • Present a response inside 50ms of detecting a button push (or launch)
  • Have the ability to detect a 50ms push and a 50ms launch

The best debouncing technique is to look at the keys (or buttons or switches) each N milliseconds, the place N > 10ms (our specified button bounce higher restrict) and N <= 50ms (our specified response time).  That is the strategy we used within the final chapter with our instance cyclic govt program STM32_LCD2.  We then have three potential outcomes each time we learn a button:

  1. We learn the button within the strong ‘0’ state
  2. We learn the button within the strong ‘1’ state
  3. We learn the button whereas it’s bouncing (so we are going to get both a ‘0’ or a ‘1’)

Outcomes 1 and a couple of pose no issues, as they’re what we might at all times prefer to occur.  Final result 3 additionally poses no downside as a result of throughout a bounce both state is appropriate.  If we now have simply pressed an active-low button and we learn a ‘1’ because it bounces, the following time via we’re assured to learn a ‘0’ (bear in mind, the following time via all bouncing could have ceased), so we are going to simply detect the button push a bit later.  In any other case, if we learn a ‘0’ because the button bounces, it’s going to nonetheless be ‘0’ the following time in spite of everything bouncing has stopped, so we’re simply detecting the button push a bit earlier.  The identical applies to releasing a button.  Studying a single bounce (with all bouncing over by the point of the following learn) won’t ever give us an invalid button state.  It is solely studying a number of bounces (a number of reads whereas bouncing is going on) that can provide invalid button states reminiscent of repeated push alerts from one bodily push. 

So if we assure that each one bouncing is completed by the point we subsequent learn the button, we’re good.  Nicely, virtually good, if we’re fortunate…

Noise, We Do not Have No Stinking Noise!

Do not we?  Nicely, perhaps we do not.  Or perhaps we do.  Typically.  Microcontrollers usually dwell amongst high-energy beasts, and sometimes management the beasts.  Excessive vitality units make electrical noise, generally nice quantities {of electrical} noise.  This noise can, on the worst potential second, get into your delicate button-and-high-value-pullup circuit and act like an actual button push.  Oops, missile launched, sorry!

If the noise is simply too intense we can’t filter it out utilizing solely software program, however will want {hardware} of some type (or perhaps a redesign).  But when the noise is just occasional, we will filter it out in software program with out an excessive amount of trouble.  The trick is that as an alternative of relating to a single button ‘make’ or ‘break’ as legitimate, we insist on N contiguous makes or breaks to mark a legitimate button occasion.  N will likely be an element of your button scanning charge and the quantity of filtering you need to add.  Greater N offers extra filtering.  The best filter (however nonetheless an enormous enchancment over no filtering) is simply an N of two, which suggests evaluate the present button state with the final button state, and provided that each are the identical is the output legitimate.

Word that now we now have not two however three button states: energetic (or pressed), inactive (or launched), and indeterminate or invalid (in the course of filtering, not but filtered).  Usually we will deal with the invalid state the identical because the inactive state, since we care generally solely about after we go energetic (from no matter state) and after we stop being energetic (to inactive or invalid).  With that simplification we will take a look at easy N=2 filtering (representing buttons utilizing Methodology 2), studying 4 buttons wired active-low to AVR PC0-PC3:

u8 filter_buttons_N2(void)
{
  static u8 last_buttons = 0;  // as much as 8 buttons, all inactive
  u8 raw_buttons;
  u8 filtered_buttons;
  
  raw_buttons = ~PINC & 0x0f;  // learn 4 buttons (PINC is active-low), energetic state="1"
  filtered_buttons = raw_buttons & last_buttons;  // N=2 filtering
  last_buttons = raw_buttons;  // save for subsequent filtering
  return filtered_buttons;     // solely energetic if energetic this time and final time
}

The perform filter_buttons_N2() have to be referred to as no extra usually than our debounce time (10ms).  Usually it will be referred to as each 10-25ms.  Every time it reads the 4 active-low buttons on PINC, inverts them to active-high, and filters them by ANDing them with the final studying.  Provided that each readings had a given button excessive (pressed) will the end result be excessive (pressed).  Word that we will filter as much as 8 buttons at a time, one per bit place, with this technique (16 if we used u16 button variables).

To increase to larger filtering (bigger N), needless to say the filtering method basically entails studying the present button state after which both counting or reseting the counter.  We rely if the present button state is similar because the final button state, and if our rely reaches N we then report a legitimate new button state.  We reset the counter if the present button state is totally different than the final button state, and we then save the present button state as the brand new button state to match in opposition to the following time.  Additionally observe that the bigger our price of N the extra usually our filtering routine have to be referred to as, in order that we get a filtered response inside our specified 50ms deadline.  So for instance with an N of 8 we ought to be calling our filtering routine each 2 – 5ms, giving a response time of 16 – 40ms (>10ms and <50ms).

The small print of the filtering depend upon whether or not the worth to be filtered is an easy binary (a single button), or is a multi-value variable such because the state of a button matrix.  If the worth is an easy binary, we will do some methods with bit-shifting to search out our N consecutive states.  For simplicity, allow us to first select N to be 8 or 16 or 32 – that’s, the variety of bits in a u8, u16 or u32.  Let’s select a u8 for this instance.  Now, each time we pattern the button state, we merely shift that state right into a u8 “accumulator” variable.  Thus the u8 variable will keep the final 8 button samples.  When the variable comprises 0b00000000 then we now have 8 contiguous 0s, and we now have a legitimate filtered ‘0’ output.  Equally, when the variable comprises 0b11111111 then we now have a legitimate filtered ‘1’ output.  Some other worth is indeterminate.  So all we now have to do is shift the present state into the variable and evaluate for 0 or ~0 (do not forget that the ‘~’ operator is the bitwise inversion operator).  Thus ~0 is ~0b00000000 is 0b11111111.  OK, to be extra right, (u8)~0 is 0b11111111.  Since we are actually utilizing a complete byte (or phrase) to signify one button, it is a variant of Methodology 1.  On this case our button is assumed to be active-low, wired to PC3.

u8 filter_button_N8(void)
= 1;          // button on PC3 is energetic (low), shift in a 1
  return button_acc;          // 0 is inactive, ~0 is energetic, others indeterminate
//
//return (button_acc == ~0) ? 1 : 0; // alternately, return 1 for energetic, 0 in any other case
//

However I Need An N Of 5!

N values of 8, 16 or 32 are particularly simple to take care of, however they might not slot in together with your timing constraints; particularly, your timer tick charge.  That is OK, since different N values are additionally useable.  For instance, let’s select an N of 5.  Meaning we need to use the final 4 inputs together with the present enter in our filtering.  If we have been to make use of the strategy described above, we’d have a legitimate filtered ‘0’ with the byte 0bxxx00000, and a legitimate filtered ‘1’ with the byte 0bxxx11111.  Hmm, these ‘x’s are an issue, however not an enormous one.  We’ve two approaches to take care of them.  We are able to both simply AND off these bits and evaluate to 0b00000 or 0b11111, or we will stuff in additional bits each time we learn the button state and proceed to match with 0 or ~0 to search for a legitimate filtered enter.  Utilizing the latter method in our N=5 instance, as an alternative of including a single bit into the accumulating variable, we’ll add 4 bits – 0b0000 if the present button state is ‘0’, or 0b1111 if the present button state is ‘1’.  The overall rule is so as to add in 8-N+1 bits of all 0s or all 1s.  This even works for N=1 – we’d then add 0b11111111 or 0b00000000, which is simply the 0 or ~0 that we’ll evaluate with.

By the way in which, there is a easy option to outline the values we are going to AND or OR to our accumulator variable, based mostly on the worth of N.  That is one software of the helpful sample (1<<X)-1, which can give a price or masks of all ‘1’s within the rightmost (least important) X bits.

#outline BUTTON_VAL ((1<<(8-N+1)-1)  // for N=5, that is 0b00001111

Now our enter and filtering routine appears like this:

#outline N           5
#outline BUTTON_VAL  ((1<<(8-N+1))-1)  // for N=5 that is 0b00001111

u8 filter_button_N5(void)
= BUTTON_VAL; // button on PC3 is energetic (low), load in 1s
  else
    button_acc &= ~BUTTON_VAL;  // button is inactive (excessive), load in 0s
  return button_acc;          // 0 is inactive, ~0 is energetic, others indeterminate
//
// return (button_acc == ~0) ? 1 : 0; // alternately, return 1 for energetic, 0 in any other case
//

Increasing This Filter Methodology To A number of Impartial Buttons

As you possibly can see above, the bit-shift filter technique can solely take care of one button at a time (in our examples, we hardwired that button to be PC3).  In fact within the typical case we would like a extra basic potential to filter a number of buttons.  To do that we will create appropriate knowledge buildings that encapsulate all the mandatory knowledge for a given button, and cross within the related construction to a filter program for every button.

typedef struct 
{
  u8  b_num;  // the bit place of this button on our button port
  u8  b_acc;  // the button accumulator
  u8  b_val;  // the boolean button val (energetic or different)
} B_DATA;

u8 filter_button_N8(B_DATA * p_bd)
= 1;           // button on PC3 is energetic (low), shift in a 1
  return p_bd->b_acc;         // 0 is inactive, ~0 is energetic, others indeterminate
  //
  //p_bd->b_val = (p_bd->b_acc == ~0) ? 1 : 0;
  //return (p_bd->b_val; // alternately, return 1 for energetic, 0 in any other case
  //

Right here we’re nonetheless hard-wiring the button port (PINC), however not the bits throughout the port.  We are able to now keep an array of B_DATA buildings, one for every button, and filter all of the buttons in a loop.

#outline NUM_B 6
B_DATA Button_data[NUM_B]  // components have to be initialized earlier than use

void filter_all_buttons_N8(void)
{
  u8 i;
  
  for (i = 0; i < NUM_B; i++)
    filter_button_N8(&Button_data[i]);  // outcomes are in b_acc and b_val for every i
}

We are able to additionally accumulate all of our filtered buttons right into a button byte or phrase (Methodology 2) if we like.  Right here we now have altered filter_button_N8() to return a ‘1’ if the filtered button is energetic and a ‘0’ in any other case, to make our bit shifting simpler:

u8 filter_button_N8(B_DATA * p_bd)
= 1;           // button on PC3 is energetic (low), shift in a 1
  p_bd->b_val = (p_bd->b_acc == ~0) ? 1 : 0;
  return p_bd->b_val;         // return 1 for energetic, 0 in any other case


u8 filter_all_buttons_N8(void)
{
  i8 i;
  u8 b;
  u8 buttons = 0;

  for (i = NUM_B - 1; i >= 0; i--)  // discover reverse rely order, to accomodate left shift
   b;          // b have to be 0 or 1
  
  return buttons; 
}

When Does A Button Press Finish?

It is fairly clear when a button press begins, however when does it finish?  Think about an ideal button – no bouncing, no noise.  This button is in a merchandising machine, and urgent the button as soon as is meant to trigger the machine to ship one can of Tazmanian Cola:

// assume buttons filtered and checked each 50ms
u8 buttons = filter_buttons();  // GPIO, filtering particulars do not matter

if (buttons == DISPENSE_TASMANIAN_COLA_BUTTON)
  dispense_tasmanian_cola();

So what number of cans of Tasmanian Cola will you get?  Belief me, you had higher be very thirsty if you happen to’re utilizing this merchandising machine,  since you’ll get 20 cans per second for so long as you maintain down the button!

The issue is that the software program is trying to see if the button is pressed (wanting on the button state), when it ought to be trying to see if the button has been pressed as soon as ( a button occasion).  The distinction is essential.  The button state simply is what it’s, however a button occasion have to be consumed when used in order to not deceive the code into considering the a number of button occasions have occurred.  On this, buttons (and keys) are totally different from switches, the place it’s typically the state that one is all for.

To show button pushes into button occasions we have to assume when it comes to button flags.  A flag will likely be set when a button push happens, and cleared when that button push is consumed – that’s, when acted on by the software program.  Word rigorously that the flag is just not set at any time when the button is discovered to be pushed (energetic), however solely when the button is first pushed (first goes energetic – the “vanguard” of the button push).  We’ll revisit this restriction after we take a look at auto-repeating, however for now we are going to maintain to this restriction.  However for now:

  • Flag is ready when button goes from inactive or indeterminate, to energetic
  • Flag is cleared when software program consumes – that’s, acts upon – the button

Let’s assume a byte of filtered buttons utilizing Methodology 2:

//
// detect button occasions
//
u8 buttons;             // uncooked filtered buttons
u8 new_buttons;         // all buttons that simply went from 0 to 1
u8 last_buttons = 0;    // final uncooked energetic buttons
u8 button_flags = 0;    // all buttons which have gone energetic

buttons = get_filtered_buttons();
new_buttons = buttons & ~last_buttons;  // 1s for every button that went from 0 to 1
button_flags |= new_buttons;        // add all new button pushes to present pushes
last_buttons = buttons;             // for subsequent time via

//
// devour a button occasion
//
if (button_flags & (1<<MY_BUTTON))  // detect MY_BUTTON occasion
{
  button_flags &= ~(1<<MY_BUTTON);  // devour MY_BUTTON occasion
  ..course of MY_BUTTON..
}

Buttons In Mixture

To date we now have been button occasion knowledge as being Methodology 1 or Methodology 2 knowledge (that’s, impartial booleans), however that is usually not the case.  Many occasions, reminiscent of with a keypad or keyboard, we would like a single code or worth to signify all the buttons being pressed.  This additionally makes debouncing simpler as a result of we’re simply debouncing one worth at a time, not N totally different buttons.

For now let’s simply assume a 16-button keypad and a scanning routine that returns a uncooked worth from the button or buttons being pushed on the keypad.  Let’s additionally specify an instance encoding:

  • 0:    1
  • 1:    2
  • 2:    3
  • 3:    4
  • 4:    5
  • 5:    6
  • 6:    7
  • 7:    8
  • 8:    9
  • 9:    10
  • EQUAL:  11
  • CLEAR: 12
  • +:    13
  • -:    14
  • *:    15
  • /:    16
  • No key pushed: 0

So these are the values returned by read_keypad() under, and thus by filter_keypad().  It is a fairly easy calculator keypad, with out, for instance, any decimal functionality, however it’s going to exhibit the strategies.  We’ll stipulate some perform read_keypad() which reads the keypad buttons and returns one of many values specified above.  Now how will we debounce this?  We’ll search for N contiguous an identical keypad values, simply as we now have been doing to date.  Solely the strategy will likely be a bit totally different.

u8 filter_keypad(void)
{
  static u8 last_val = 0;           // 0 means no buttons pushed
  static u8 rely = 0;              // debounce filter rely
  u8 val;                           // uncooked val this time
  u8 filtered_val = 0;              // our filtered return val
  
  val = read_keypad();              // get uncooked keypad worth
  if (val != 0)                     // have a keypad button push
  {
    if (val == last_val)            // we're filtering this val
    {
      if ((rely != 0) && (--count == 0))  // proceed filtering & verify if achieved
      {
        filtered_val = val;         // discovered sufficient consecutive values to return as legitimate
      }
    }
    else
    {
      rely = FILTER_COUNT;         // begin filtering a brand new val
    }
  }
  last_val = val;
  return filtered_val;
}

As with all the opposite button filtering strategies, this technique will likely be referred to as as soon as each filtering interval, which is likely to be 1-10ms.  The primary verify (if (val != 0)) checks for any button pushes.  If none, the no-button-value of 0 is returned.  The second verify appears to see if the brand new worth is similar because the final worth.  Whether it is then we’re within the midst of filtering the worth.  If not (if a brand new val is detected) the filter rely is loaded to its full filter worth and filtering begins for the brand new worth.  The third verify (if we’re within the midst of filtering) appears to see if the filter rely has simply been decremented to zero.  If that’s the case, the code experiences this worth because the filtered val, however is not going to proceed to report it in subsequent passes.

This technique presents a couple of bonuses that is probably not readily obvious.  Within the first place, it returns true keypad occasions.  It’ll return the filtered keypad worth solely as soon as (when rely decrements from 1 to 0), as is desired, and won’t proceed to return it as buttons stay held down.  Within the second place, and associated to the primary, it permits us so as to add auto-repeat very simply, as we are going to see within the subsequent tutorial chapter.  Additionally within the subsequent tutorial chapter we are going to take a look at matrixed buttons and methods to scan the matrix to learn them.

You may additionally like… (promoted content material)

Leave a Reply

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

Back to top button