Debouncing multiple buttons using schmitt trigger

I'm not sure I know how to recreate that. This is the final schematic that I have at this moment. I've got my PCB designed as well. Are there any changes I could make to this design to make it more efficient or component/space saving?

|500x302

Thank you all for your time and help btw!

Even if you must use interrupts to handle button presses, maybe because you use delay() statements in your sketch and you need to “jump” over these, you can still use simple software debouncing. In the interrupt service routine, on a valid call, simply set a timer, say: static uint32_t isrLastCalledAtMs = millis() ;

However, if too little time has elapsed since the last valid call and the current call ( say less than 100mS ) then ignore the call because it is a bounce.

@Kuusou, yeah, like that, OVER the supply :) And important, on the actual layout they must be physically close to the chip.

And like runaway_pancake also showed, no real need for R1, R3 and R5.

And you are still drawing GND evil. Okay, a little less evil but still ;) Just flip all the caps! Then the GND can be down and in line! Room enough to do so. And all switches can easily share the GND that way. Same for C1 to C3. I would even have used pins 1-2 on the switch to go to GND just to make it all look way better.

@runaway_pancake, is it just me or am I really missing a pull down in that design?

@runaway_pancake, is it just me or am I really missing a pull down in that design?

Where?

On that INT pin. Nothing is ever pulling it low.

Kuusou: However my particular case (i need you to trust me on this) I need to have the interrupts attached to the buttons. A hardware solution is the only one I have. My program has some 13 different looping functions that I need to be able to switch between whenever a button is pressed.

That's calling for a state machine, with buttons changing between the states. No interrupts needed, assuming you have done your programming properly. It sounds like debouncing is also not needed - if say button2 switches to loop2, bouncing doesn't matter as you're in loop2 already after the first contact is made. Debouncing is only needed when you have to count button presses.

I may be missing something, but that's basically what you're telling here. So all you need is buttons - no caps, no resistors (use the internal pull-ups), no Schmitt trigger.

So yeah, XY problem.

On that INT pin. Nothing is ever pulling it low.

Is that required? It's designated 'RISING' in the sketch, I think that takes care of it.

Yes, it's required :) It only looks for a rising edge, it does nothing to the state of the pin. Just like on any other input, just letting it float WILL include pick up noise thus it will include a rising edge.

You might get away with it because of reverse leakage current through the diodes. But that's in the order of 5 to 10nA for a 1N4148 @ 5V (aka, at best). Times three that might be enough to keep the pin at a LOW state but I would not count on it. Add some noise and you have a trigger for sure.

May I do A wild guess? You said you've used this design. Is the first thing you do in the ISR to check which of the normal pins fired the interrupt? So any false triggering can be hidden by that if you don't see a HIGH pin on those :-X If so, I think you fire that interrupt way more often then you think ;)

runaway_pancake: On that INT pin. Nothing is ever pulling it low.

Is that required? It's designated 'RISING' in the sketch, I think that takes care of it.

Absolutely required, you must never have an undefined input on an interrupt enabled pin, it might fire all the time from capacitive pickup. With your circuit it might never fire as it stays HIGH all the time, or fire continuously, or only at the weekend, whatever.

septillion: @Kuusou, yeah, like that, OVER the supply :) And important, on the actual layout they must be physically close to the chip.

And like runaway_pancake also showed, no real need for R1, R3 and R5.

And you are still drawing GND evil. Okay, a little less evil but still ;) Just flip all the caps! Then the GND can be down and in line! Room enough to do so. And all switches can easily share the GND that way. Same for C1 to C3. I would even have used pins 1-2 on the switch to go to GND just to make it all look way better.

Ok so remove resistors 1, 3, and 5 and replace resistors 2, 4, and 6 with a 20k. Make the caps/switches all share a ground. So how does this look?

|500x303

The other thing I should mention is part of the problem I believe is button glitching, where i'm pressing the button and on release its registering multiple presses, these circuit are suppose to debounce at 1ms but is that enough? It seems to me that I should make the caps larger so as to increase the debounce time to avoid this issue. Does anyone have any thoughts on this? In programming it seems the normal is 50ms debounce but does follow over to hardware debounce?

wvmarle: That's calling for a state machine, with buttons changing between the states. No interrupts needed, assuming you have done your programming properly. It sounds like debouncing is also not needed - if say button2 switches to loop2, bouncing doesn't matter as you're in loop2 already after the first contact is made. Debouncing is only needed when you have to count button presses.

I may be missing something, but that's basically what you're telling here. So all you need is buttons - no caps, no resistors (use the internal pull-ups), no Schmitt trigger.

So yeah, XY problem.

This was my setup before but when the buttons are pressed they sometimes skip functions in the sequence on release, so its registering multiple interrupts extremely quickly. I've tried to debounce them in software but nothing seems to work.

My program has roughly 14 different LED functions. The PCB has three switches for effect, color, and brightness. Using interrupts I can switch between any looping function without delay or change the color or brightness at any point and it works great (except for this button issue). I had tried other programming but there was always an extremely bad delay on the button presses since the function would be looping. I'm also new to programming and self taught so while there may be a better way to do this its the only way i've been able to find that works. I've posted about this on these forums before but as I said, I would just get referred to blink without delay and honestly just couldn't figure it out. I have no idea how to have 14 looping functions within a loop and how to poll for buttons in such a way that they can be responsive. :(

Also another question I have is when designing the PCB do the individual components have to have traces between their GND connecting them or can they all just be left as is? My first PCB I just covered both sides of the PCB in copper as the grounding layer and that seemed to work just fine. I didn't have any ground traces.

To be honest it sounds like you have coding issues...

I controlled 1600 leds with 30 different programs while also monitoring button presses and controlling a character lcd screen and had no need for interrupts...

Qdeathstar: To be honest it sounds like you have coding issues...

I controlled 1600 leds with 30 different programs while also monitoring button presses and controlling a character lcd screen and had no need for interrupts...

As I said before, I'm new to coding and self taught. There are probably better ways to do it but I have no idea how and haven't been able to figure out a better way...

I'm using switch statements for the effects, color, and brightness. The issue is that some of the led effects run for a long time and I can't wait for the loop to finish before exiting and going to the next function/color/brightness

The only thing I can think of is using a interrupt to run every 5-10ms with a timer. It would constantly check for the state of the buttons and if a button has been pressed longer than 50ms then it could increment a counter for the button and switch the effect.

Is this something that could work, would the constant interrupt be noticeable in terms of a particular led effect running? If it is I'm not sure how to exit the function at the time its a valid press without waiting for it to finish. Does anyone have any thoughts or suggestions on this? Is this a better approach?

@septillion, @MarkT I understand your point, makes sense, I'm nodding in agreement. But I aver that I've never had an instance (in several years now) where I had a failure to dec/inc or stop nor any inadvertent dec/inc or stop. I pulled the box off the bike, to see if I'd placed a pulldown without noting it, but it has none. Maybe I'm lucking out on the ragged edge.

@Kuusou - Sorry for the... controversy.

FWIW

byte newLevel = 0;       // comparison
byte Accel_Btn = 4;      // alias - Accelerate btn
byte Decel_Btn = 5;      // alias - Decelerate btn
byte Zeropwr = 6;        // alias - "kill" button
byte motorpin = 11;      // pin alias, PWM to gate/MOT
byte Level [6] = {0,20,40,100,175,250};
volatile boolean Detect = false;  // Interrupt Note 

void setup()
{
  pinMode(motorpin, OUTPUT);
  pinMode(Zeropwr, INPUT);
  pinMode(Accel_Btn, INPUT);
  pinMode(Decel_Btn, INPUT);
  //Serial.begin(9600);
  attachInterrupt(0,Activity,RISING);  // '0' means Ext_Int on D2
}

void loop()
{
  if (Detect == true)
  {
    Decode();
  }
  if (currentLevel != newLevel)
  {
    analogWrite(motorpin,Level[speed_idx]);
    currentLevel = newLevel;
  }
}

void Activity()   // this is the Interrupt !
{
  Detect = true;
}

void Decode()
{
  Detect = false;          // clear out "Detect"
  if (digitalRead(Zeropwr) == HIGH)
  {
    speed_idx = 0;
    newLevel = 0;
    //Display_it();
  }
  else if (digitalRead(Accel_Btn) == HIGH)
  {
    Accelerate();
  }
  else if (digitalRead(Decel_Btn) == HIGH) // Activity detected wasn't Zeropwr or Accel_Btn
  {
    Decelerate();   // default = (A' & Z')
  }
} 

void Accelerate()
{  
  if (speed_idx < 5)
  { 
    speed_idx ++;
  }
  newLevel = Level[speed_idx];
}

void Decelerate()
{
  if (speed_idx >= 1)
  {
    speed_idx --;
  }
  newLevel = Level[speed_idx];
}

Ok guys, i'm trying to change my code to use the watchdog timer interrupt to check the state of my buttons every 15ms. What i'm trying to do is that every 3 interrupts (45ms) if the button in question is still in a high state then the effect will change and execute just like my old code. The issues is i'm getting crazy flashing off my leds, it appears to be rapidly changing the effects but i'm not touching the button.

Here is the code in the ISR i'm using. I'm sure my logic is flawed somewhere but just not sure where.

ISR (WDT_vect) {
  if (digitalRead (EFFECT_BUTTON) == HIGH ) {
    effect_count++;
  } else if (digitalRead (EFFECT_BUTTON) == LOW ) {
    effect_count == 0;
  }
  if (digitalRead (EFFECT_BUTTON) == HIGH && effect_count >= 3) {
    selectedEffect++;
    EEPROM.put(0, selectedEffect);
    asm volatile ("jmp 0"); // force reset of micro controller at line 0
  }
}

You are doing some really crazy, scary, and outright wrong things there.

For starters, the wrong things happen when you hold that button for longer than three WDT intervals - you continue to count after your reset.

That reset is simply the wrong way of doing things.

What you need is state change detection to count (so count when your button GOES down, not when it IS down as you're doing).

I have no idea what you're doing in the various routines or how you even call them so can't comment on that, really.

Your loop() may look a bit like this:

uint32_t lastPressed;
bool previousButtonState;
const uint8_t NPROGRAMS = 3;
uint8_t selectedEffect;

void loop() {
  if (digitalRead(EFFECT_BUTTON) == LOW) { // button is pressed
    if (previousButtonState == HIGH) { // but it was not
      previousButtonState = LOW;
      selectedEffect++; // increase the count.
      if (selectedEffect== NPROGRAMS) {
        selectedEffect= 0;
      }
      EEPROM.put(0, selectedEffect);
    }
      lastPressed = millis(); // record when it happens.
  }
  else if (millis() - lastPressed > 50) { //  Button is unpressed, and it's at least 50 ms since it last got pressed
    prevousButtonState = HIGH;
  }
  switch (selectedEffect) { // Select program.
    case 0:
      effects0();
      break;

    case 1:
      effects1();
      break;

    case 2:
      effects2();
      break;
  }
}

Now as long as you make sure those effectsn() functions return fast you don't need an interrupt, and you can count properly without crazy things like a reset - where did you even find that routine?

And another thing: you're not allowed to use delay() until you know why that is.

wvmarle: And another thing: you're not allowed to use delay() until you know why that is.

You can't use delay in an ISR because it will screw up your time keeping and everything will be thrown off correct?

I see why my LEDS were acting that way. I'm idiot and had my states wrong. Low is when its pressed, not high.

I'm using the reset as a way to exit the function. Many of these functions aren't fast and this is a way to go back to 0, reset all my stuff, use EEPROM to figure out the effect for the switches, and go into the next effect. I see what you're saying though, holding down the button causes rapid switching through the effects.

Kuusou: You can't use delay in an ISR because it will screw up your time keeping and everything will be thrown off correct?

Never even thought of using delay() in an ISR, that's double bad. An ISR should return as fast as possible.

Delay is blocking. Nothing happens during delay. No button reads, nothing. That's why. Write your code without delay() or other blocking loops and you can return to loop() hundreds or many thousands of times a second to read that button.