Button debouncing

I have tried debouncing buttons using a delay, however it doesn’t seem to work.
I am making a lap time where the user presses a button:

//					STOPWATCH
printf("7 clicks in total\n");
res = f_open(&fl1, "0:rotor.txt", FA_CREATE_ALWAYS | FA_WRITE); //open file to write to
   if (res != FR_OK) { 
      printf("file open failed, reason: %d\n", res); 
   } 
// *****initiating timings******************************** /
 while (PINA&(1<<button));  // loop here until first button press

    timer_tick=0;// here is where we start timing
    _delay_ms(10);  // delay for 10 ms to debounce button press
    while (!(PINA&(1<<button)));  // loop here until button is released
    _delay_ms(10);  // delay for 10 ms to debounce button release

   //cumulative timings. j is 0 - 5 because first press is above
   for (j=0;j<6;j++){

    while (PINA&(1<<button));  // loop here until second button press
    rotor[j] = (double)timer_tick/1000;      //get the value of the millisecond counter
    _delay_ms(10);  // delay for 10 ms to debounce button press
	printf("[%d] time: %f\n",j+1, rotor[j]);
 while (!(PINA&(1<<button)));  // loop here until button is released
    }
printf("storing data\n");
printf("%.3f, %.3f, %.3f, %.3f, %.3f, %.3f\n\n", rotor[0], rotor[1], rotor[2], rotor[3], rotor[4], rotor[5]);

quite simple really. timer2 is setup in CTC mode to interupt every millisecond.
However you can see my delays, but they dont over come the problem.
I read around and an integration method is often popular:


#define DEBOUNCE_TIME		0.3
#define SAMPLE_FREQUENCY	10
#define MAXIMUM			(DEBOUNCE_TIME * SAMPLE_FREQUENCY)

/* These are the variables used */
unsigned int input;       /* 0 or 1 depending on the input signal */
unsigned int integrator;  /* Will range from 0 to the specified MAXIMUM */
unsigned int output;      /* Cleaned-up version of the input signal */

/* Step 1: Update the integrator based on the input signal.  Note that the
integrator follows the input, decreasing or increasing towards the limits as
determined by the input state (0 or 1). */

  if (input == 0) 

    {
    if (integrator > 0)
      integrator--;
    }
  else if (integrator < MAXIMUM)
    integrator++;

/* Step 2: Update the output state based on the integrator.  Note that the
output will only change states if the integrator has reached a limit, either

0 or MAXIMUM. */

  if (integrator == 0)
    output = 0;
  else if (integrator >= MAXIMUM)
    {
    output = 1;
    integrator = MAXIMUM;  /* defensive code if integrator got corrupted */
    }

How would you implement this instead of the delay’s to debounce the buttons?
I assume the above should be a function of its own that I call instead of the delay?

Hello,

The way we would detect a debounced button press is the way we actually did implement it in the Orangutan Pushbuttons section of the Pololu AVR Library. Basically, yes, there is a single function that we call repeatedly, and it returns true once for each debounced button press.

You might want to take a look at our code or just use it.

-Paul

Hi Paul, yeah those functions look like what Im after, can I just download the library and call them?
My code waits for a button press to begin the timing, for each ‘lap’, instead of my delay do I just call the function in that library? Is that sort of how it works?

What if I want to hold a button down fo a length of time?

Hello,

You are welcome to use the library in your own projects. I do not know what kind of a board you are working with, so I cannot promise you that it will be easy. But you can try following our instructions.

Did you read the explanation of how the function works? You are supposed to call it repeatedly in your main loop, and it should just return true once for each press, no matter how long you hold down the button down.

-Paul

So assuming I am using a pololu board…
I am running this code:

 while (PINA&(1<<button));  // loop here until first button press

    timer_tick=0;// here is where we start timing
    _delay_ms(10);  // delay for 10 ms to debounce button press
    while (!(PINA&(1<<button)));  // loop here until button is released
    _delay_ms(10);  // delay for 10 ms to debounce button release

   //cumulative timings. j is 0 - 5 because first press is above
   for (j=0;j<6;j++){

    while (PINA&(1<<button));  // loop here until second button press
    rotor[j] = (double)timer_tick/1000;      //get the value of the millisecond counter
    _delay_ms(10);  // delay for 10 ms to debounce button press
	printf("[%d] time: %f\n",j+1, rotor[j]);
 while (!(PINA&(1<<button)));  // loop here until button is released
    }

I.e it is a lap timer storing 6 lap timings. press once to start, then 6 more times, and between clicks it stores the current time in seconds. However currently at each click I have a debouncing issue. I have put in the delays to try and deal with it but it doesnt solve the problem, so what Im not sure of is where I would call your functions…
D’ya get me?

Hello,

I have not tried this, but I think that entire code could be replaced by something like this:

  while (!get_single_debounced_button_press(...));  // loop here until first button press
  timer_tick=0;

   //cumulative timings. j is 0 - 5 because first press is above
   for (j=0;j<6;j++) {
    while (!get_single_debounced_button_press(...));  // loop here until next button press
    rotor[j] = (double)timer_tick/1000;      //get the value of the millisecond counter
    printf("[%d] time: %f\n",j+1, rotor[j]);
  }

Every time you press the button, that function will return true exactly once and advance to the next stage of the loop.

-Paul

By the way, which Orangutan are you using?

-Paul

Well I was using the 128 model, canr remember its exact name, but now I have a mega1284 on a breadboard. Great thanks Paul, is there a way though to time how long a button has been held down for? Because that returns a 1 if the button is pressed regardless of how long for - that’s not for the code u edited already, its for something else, just wondered whether there is another function that would allow for that.
Im not using the entire Pololu library with my project, all I would need is the debounce routine. Below is the routing that detects a button has been pressed, although it is part of a class. Not having used it before Im not entirely sure the best way to simplify it so that I could make it a function of its own that I could use in my project.
I dont have the button definitions as the pololu library does - Im waiting for a button press like so:

while (PINA&(1<<button));

I think from your last post I should do something like this (ive defined ‘button’ as PA0):

while (getSingleDebouncedPress(PINA&(1<<button)));

??? Then that calls the function below? Im not sure about unsigned char buttonsDown = BUTTONS_DOWN;
I think therefore I also would not need to call init(); and where does millis(); come from?

unsigned char getSingleDebouncedPress(unsigned char buttons)
{
	static unsigned char state = 0;
	static unsigned long prevTimeMillis = 0;
	static unsigned char mask = 0;
	
	unsigned char buttonsDown = BUTTONS_DOWN;
	unsigned long timeMillis = millis();

	switch (state)
	{
		case 0:
			if (~buttonsDown & buttons)				// if one of the specified buttons is up
			{
				mask = ~buttonsDown & buttons;		// mask becomes all of masked up buttons
				prevTimeMillis = timeMillis;
				state = 1;
			}
			break;
			
		case 1:
			if (timeMillis - prevTimeMillis >= 15)	// if 15 ms or longer has elapsed
			{
				if (~buttonsDown & mask)			// and if a masked button is still up
				{
					state = 2;						// proceed to next state
					mask = ~buttonsDown & mask;		// new mask becomes all of masked up buttons
				}
				else
				{
					state = 0;						// go back to previous (initial) state
				}
			}
			break;
			
		case 2:
			if (buttonsDown & mask)					// if a masked button is now down
			{
				state = 3;							// proceed to next state
				mask = buttonsDown & mask;			// new mask becomes all of masked down buttons
				prevTimeMillis = timeMillis;
			}
			else if (mask != (~buttonsDown & buttons))	// if our mask becomes inaccurate
			{
				state = 0;							// go back to the initial state
			}
			break;
			
		case 3:
			if (timeMillis - prevTimeMillis >= 15)	// if 15 ms or longer has elapsed
			{
				if (buttonsDown & mask)				// and if a masked button is still down
				{
					state = 0;						// next state becomes initial state
					return buttonsDown & mask;		// return masked down buttons
				}
				else
				{
					state = 2;						// go back to previous state
				}
			}
			break;
	}
	
	return 0;
}

Hello,

I have moved your post into the General Discussion section. Please do not post to a “Pololu Robotics and Electronics Product Support” section of our forum when you are not asking for support on a Pololu product!

Anyway, in your previous code you had something called “timer_tick”, that you should probably use instead of millis(). And you will probably not need init(), which takes care of Orangutan-specific initialization, but it is defined in the same file in case you are curious.

Overall, it sounds like you really don’t understand the code, so I recommend that you just get an actual Orangutan and try it out there, before making modifications.

-Paul