unnecessary delay during debounce

so im getting this unnecessary delay with my debounce. so i have debounced 3 buttons which worked fine looking at the serial monitor but as i combined in with another code i've sensed a delay between the debounce and it feels less sensitive. like i have to press it for longer in order for message that iv set up to say 'debouncing ' to show up on the serial monitor. i looked to see if mistakenly duplicated variables but couldnt seem to find any

#define notPRESSED 0
#define partialPRESS 1
#define normalPRESS 2
#define B1pressed !(PINC & B1)
#define B1released !B1pressed
#define B2pressed !(PINC & B2)
#define B2released !B2pressed
#define B3pressed !(PINC & B3)
#define B3released !B3pressed
#define debounce 500 // debounce duration
#define B1 B00000001
#define B2 B00000010
#define B3 B00000100
#define NUMB0_SEG B00111111
#define NUMB1_SEG B00000110
#define NUMB2_SEG B01011011
#define NUMB3_SEG B01001111
#define NUMB4_SEG B01100110
#define NUMB5_SEG B01101101
#define NUMB6_SEG B01111101
#define NUMB7_SEG B00000111
#define NUMB8_SEG B01111111
#define NUMB9_SEG B01100111
#define LETSEG_A B00111111
#define LETSEG_B B01111100
#define LETSEG_C B00111001
#define LETSEG_D B01011110
#define LETSEG_E B01111001
#define LETSEG_F B01110001
#define HB_PORTB   PORTB
#define HB_DDRB    DDRB
#define HB_PORTB_ST   PORTC
#define HB_DDRB_ST    DDRC
#define HB_DS_POS PB0      //Data pin (DS) pin location
#define HB_SH_CP_POS PB4      //Shift Clock (SH_CP) pin location 
#define HB_ST_CP_POS PC3      //Store Clock (ST_CP) pin location
#define HBDataHigh() (HB_PORTB|=(1<<HB_DS_POS))
#define HBDataLow() (HB_PORTB&=(~(1<<HB_DS_POS)))

void HBInit()
{
  //Make the Data(DS), Shift clock (SH_CP), Store Clock (ST_CP) lines output
  HB_DDRB |= ((1 << HB_SH_CP_POS) | (1 << HB_DS_POS));
  HB_DDRB_ST |= ((1 << HB_ST_CP_POS));
}


//Low level macros to change data (DS)lines


//Sends a clock pulse on SH_CP line
void HBPulse()
{
  //Pulse the Shift Clock

  HB_PORTB |= (1 << HB_SH_CP_POS); //HIGH

  HB_PORTB &= (~(1 << HB_SH_CP_POS)); //LOW

}

//Sends a clock pulse on ST_CP line
void HBLatch()
{
  //Pulse the Store Clock

  HB_PORTB_ST |= (1 << HB_ST_CP_POS); //HIGH
  _delay_loop_1(1);

  HB_PORTB_ST &= (~(1 << HB_ST_CP_POS)); //LOW
  _delay_loop_1(1);
}

void HBWrite(uint8_t data)
{
  //Send each 8 bits serially

  //Order is MSB first
  for (uint8_t i = 0; i < 8; i++)
  {
    //Output the data on DS line according to the
    //Value of MSB
    if (data & B10000000)
    {
      //MSB is 1 so output high

      HBDataHigh();
    }
    else
    {
      //MSB is 0 so output high
      HBDataLow();
    }

    HBPulse();  //Pulse the Clock line
    data = data << 1; //Now bring next bit at MSB position

  }

  //Now all 8 bits have been transferred to shift register
  //Move them to output latch at one
  HBLatch();
}

//module0 variables
bool init_module0_clock;
//module1 variables
bool init_module1_clock;
//module2 variables
bool init_module2_clock;
unsigned char B1_state, B2_state, B3_state; // buttons state variables


void setup() {


  
  HBInit();
  DDRC &= ~B1;
  PORTC |= B1;
  DDRC &= ~B2;
  PORTC |= B2;
  DDRC &= ~B3;
  PORTC |= B3;
  init_module0_clock = true;
  init_module1_clock = true;
  init_module2_clock = true;
  B1_state = notPRESSED;
  B2_state = notPRESSED;
  B3_state = notPRESSED;
  Serial.begin(9600);

}


void loop() {



  
  HBWrite(B10000000);   //Write the data to HB
  _delay_ms(500);
  HBWrite(B00000000);   //Write the data to HB
  _delay_ms(500);
  { // module 0
    static unsigned long module_time, module_delay, debounce_count;
    static bool module_doStep;
    static unsigned char state; // state variable for module 0

    if (init_module0_clock) {
      module_delay = 17;
      module_time = millis();
      module_doStep = false;
      init_module0_clock = false;
      state = 0;
    }
    else {
      unsigned long m = millis();
      if ( ( (long)(m - module_time) ) > module_delay ) {
        module_time = m; module_doStep = true;
      }
      else module_doStep = false;
    }

    if (module_doStep) {
      switch (state) {
        case 0:
          B1_state = notPRESSED;
          if (B1released) state = 0;
          else {
            debounce_count = module_time;
            state = 1;
          }
          break;
        case 1:
          B1_state = partialPRESS;
          if (B1released) state = 0;
          else if ((long)(millis() - debounce_count) < debounce) state = 1;
          else state = 2;
          break;
        case 2:
          B1_state = normalPRESS;
          if (B1released) state = 0;
          else state = 2;
          break;

        default: state = 0; break;
      }
    }
  }

  { // module 1
    static unsigned long module_time, module_delay, debounce_count;
    static bool module_doStep;
    static unsigned char state; // state variable for module 1

    if (init_module1_clock) {
      module_delay = 17;
      module_time = millis();
      module_doStep = false;
      init_module1_clock = false;
      state = 0;
    }
    else {
      unsigned long m = millis();
      if ( ( (long)(m - module_time) ) > module_delay ) {
        module_time = m; module_doStep = true;
      }
      else module_doStep = false;
    }

    if (module_doStep) {
      switch (state) {
        case 0:
          B2_state = notPRESSED;
          if (B2released) state = 0;
          else {
            debounce_count = module_time;
            state = 1;
          }
          break;
        case 1:
          B2_state = partialPRESS;
          if (B2released) state = 0;
          else if ((long)(millis() - debounce_count) < debounce) state = 1;
          else state = 2;
          break;
        case 2:
          B2_state = normalPRESS;
          if (B2released) state = 0;
          else state = 2;
          break;

        default: state = 0; break;
      }
    }
  }
  { // module 2
    static unsigned long module_time, module_delay, debounce_count;
    static bool module_doStep;
    static unsigned char state; // state variable for module 2

    if (init_module2_clock) {
      module_delay = 17;
      module_time = millis();
      module_doStep = false;
      init_module2_clock = false;
      state = 0;
    }
    else {
      unsigned long m = millis();
      if ( ( (long)(m - module_time) ) > module_delay ) {
        module_time = m; module_doStep = true;
      }
      else module_doStep = false;
    }

    if (module_doStep) {
      switch (state) {
        case 0:
          B3_state = notPRESSED;
          if (B3released) state = 0;
          else {
            debounce_count = module_time;
            state = 1;
          }
          break;
        case 1:
          B3_state = partialPRESS;
          if (B3released) state = 0;
          else if ((long)(millis() - debounce_count) < debounce) state = 1;
          else state = 2;
          break;
        case 2:
          B3_state = normalPRESS;
          if (B3released) state = 0;
          else state = 2;
          break;

        default: state = 0; break;
      }
    }
  }

  {
    static char old;
    if (old != B1_state) {
      Serial.print("B1 = "); Serial.println(B1_state ? ((B1_state == 2) ? "pressed" : "de-bouncing") : "not pressed");
      old = B1_state;
    }
  }

  {
    static char old;
    if (old != B2_state) {
      Serial.print("B2 = "); Serial.println(B2_state ? ((B2_state == 2) ? "pressed" : "de-bouncing") : "not pressed");
      old = B2_state;
    }
  }
  {
    static char old;
    if (old != B3_state) {
      Serial.print("B3 = "); Serial.println(B3_state ? ((B3_state == 2) ? "pressed" : "de-bouncing") : "not pressed");
      old = B3_state;
    }
  }
}
_delay_ms(500);

Does this and the second instance in loop() stop anything happening for half a second ? If so, then no wonder that the code is unresponsive

I didn't follow it all but it is certainly an interesting debouncing routine for those 3 buttons.
For debouncing (in a normal, not electrically noisy environment) I'd simply accept that any pulse from a button was related to a button press attempt, accept and act on it, but then refuse to accept further pulses from the same button within, say, 50ms)

if ( buttonA == LOW && millis() - millis_since_last_successful_press_Button_A > 50 ) {
    millis_since_last_successful_press_Button_A  = millis() ;

    //
    //  act on button A
    //

}

Debouncing in software especially if you have multiple inputs just makes code massively bigger and more complicated, and distracts from the real purpose of your program.

Further, as you have seen it makes your program less responsive, because it relies on multiple reads and tests; and also as I have found on real tests, the timing of these (especially if using millis() )is often un-necessarily long.

Published programs almost exclusively use the millis() function, allowing times of up to 50ms and more to eliminate a bounce. For my tests I needed to use micros(). Why? because on test using Millis() I NEVER caught a single bounce.

Whay not just debounce with a capacitor and resisitor? 2 external components per switch vs 2000 extra lines of code - no contest in my book.

johnerrington:
Debouncing in software especially if you have multiple inputs just makes code massively bigger and more complicated, and distracts from the real purpose of your program.

Further, as you have seen it makes your program less responsive, because it relies on multiple reads and tests; and also as I have found on real tests, the timing of these (especially if using millis() )is often un-necessarily long.

Published programs almost exclusively use the millis() function, allowing times of up to 50ms and more to eliminate a bounce. For my tests I needed to use micros(). Why? because on test using Millis() I NEVER caught a single bounce.

Whay not just debounce with a capacitor and resisitor? 2 external components per switch vs 2000 extra lines of code - no contest in my book.

A 2000 line button debounce routine ? ? ?
If you use the technique outlined in post 2, it can be very short and have zero latency.

johnerrington:
Whay not just debounce with a capacitor and resisitor? 2 external components per switch vs 2000 extra lines of code - no contest in my book.

You're doing things wrong.You need only a few lines of extra code for all the switches together (of course you put them in an array so you can conveniently iterate), and hardware debounce only needs an external capacitor. Those caps however will interfere with your readings when your switches are in a matrix.

That said, in many cases you don't need to worry about contact bounce to begin with...

@johnerrington

OK. In the mean time, I've looked in detail at your linked document. It is very nicely presented and does, indeed, also consider the software "Bounce Lock Out" technique, which I referred to by example.

For a manually operated push button in an electrically stable environment I'd need, however, some strong convincing that a hardware solution was appropriate. Also bear in mind that, in mass production of electronic goods, every cent saved on component count can make a difference between profit and loss.

I'd also still consider a 2000 line debouncing algorithm an imposing sight, though.

Have a look at:
https://forum.arduino.cc/index.php?topic=719995.0

This version contains sample code for debouncing any number of buttons and calling different functions depending on which button is pressed:
https://forum.arduino.cc/index.php?topic=720016.0

Nowhere have I managed to use anything close to 2000 lines of code to debounce a few buttons...

I'm still thinking in machine code!

Ok perhaps a little exaggeration.

johnerrington:
Whay not just debounce with a capacitor and resisitor? 2 external components per switch vs 2000 extra lines of code - no contest in my book.

Yuppers, hardware switch debouncing is the route to go.

This cast to 'long' is not only unnecessary, it's a bug.

      unsigned long m = millis();
      if ( ( (long)(m - module_time) ) > module_delay ) {

Debouncing is easier to code, than it is to spell. The main application where a hardware solution is an economically sound design, is with input interrupts, since although you can debounce an ISR in software, you can't stop the ISR from being called on every switch transition, which may be a processor burden. With polling, only one transition has to be handled, all the rest are skipped.

Also I agree that many examples use far too long a time constant. I have had good luck with 10-20 ms. The salient parameter in this case is the expected repetition rate. Of course a 5 second debounce would work, but it would slow the rep rate to 12 key presses per minute. You can only increase the rep rate to the extent that the corresponding debounce period doesn't become too short, and catch switch bounce.

Some programs logic can legitimately tolerate very slow rep rates, because they poll very slowly. These don't need any debounce. But don't confuse those cases with the normal cases where the rep rate can be much higher.

aarg:
although you can debounce an ISR in software, you can't stop the ISR from being called on every switch transition,

Of course you could disable the interrupt in the ISR, and re-enable it when needed (i.e. when your processor goes back to sleep). That's two lines of code. One to disable, one to enable that interrupt.

wvmarle:
Of course you could disable the interrupt in the ISR, and re-enable it when needed (i.e. when your processor goes back to sleep). That's two lines of code. One to disable, one to enable that interrupt.

Would that work ? Interrupts are automatically disabled when entering an ISR and automatically enabled again on exit, so would your explicitly disabled interrupt not also be enabled on exit from the ISR ?

UKHeliBob:
Would that work ? Interrupts are automatically disabled when entering an ISR and automatically enabled again on exit, so would your explicitly disabled interrupt not also be enabled on exit from the ISR ?

Yes. As I understand it. Say, for an external interrupt on a Uno/Nano (ATMEGA328P) etc., simply set the appropriate bit in EIMSK (say INT0) to suspend the interrupt. Do that in the ISR. Then set a timer for the block period (say 100mS). When the timer expires, simply re-enable the interrupt in the loop().

UKHeliBob:

_delay_ms(500);

Does this and the second instance in loop() stop anything happening for half a second ? If so, then no wonder that the code is unresponsive

in his other thread, he has not been able to blink LEDs with BWD. I am guessing that the delay is there to blink LEDs.
as you said, when the program is blocked for a second, the button press has to accommodate.
with the blocking of the two delay()s debounce is not ever going to be a problem.

with the blocking of the two delay()s debounce is not ever going to be a problem.

Yes, but also with long delays, there is the alternate problem of completely missing a key press.