Supershort pulses when pushing button. Bouncing?

Greetings fellas,

I try to write code that gives me the timing of how I push a button. That means: When I push that button a few times, how many microseconds was it in high state, how long was it in low, then again for how long high, how long low, and so on…

The Problem is, sometimes I get these really short time periods which cant possible be produced by myself. My first idea what the problem could be was “bouncing” of the button, but as I tested for that I couldnt find any clues. Lets dive into it:

Code:

const int buttonPin = 2;     // pushbutton pin
unsigned long buttonHoldStartTime = 0;
const int patternArraySize = 10; 
unsigned long pattern[patternArraySize] = {0};
int i;

void setup() 
{
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() 
{
  
    
  i=0;
  bool lastState = HIGH;

  
  Serial.println("Waiting for input");
  while(digitalRead(buttonPin) == LOW)
  {
    //do nothing, wait for HIGH
  }
    
  buttonHoldStartTime = micros();
  
  while(i < patternArraySize) 
  {
    pattern[i] = micros();
    if(digitalRead(buttonPin) != lastState)
    {
      i++;
      lastState = !lastState;
    }
  }

  Serial.println("Input section finished");
  delay(3000); 

  
     
  Serial.println("raw array:");
  for(i=0; i<patternArraySize; i++) //print raw array with absolute timer values
  {
    Serial.print(i);
    Serial.print(": ");
    Serial.println(pattern[i]);
  }
  

  for(i=patternArraySize-1; i > 0 ; i--)  //calculate how long each state was active (relative timer values)
  {
    pattern[i] = (pattern[i] - pattern[i-1]);
  }
  pattern[0] = (pattern[0] - buttonHoldStartTime);
  
  Serial.println("\nrelativ times:");
  for(i=0; i<10; i++) //print array with relative times
  {
    Serial.print(i);
    Serial.print(": ");
    Serial.println(pattern[i]);
  }

}

Output:

Waiting for input
Input section finished
raw array:
0: 42841832
1: 43602936
2: 43670144
3: 43780912
4: 43780944
5: 43780960
6: 43852712
7: 43954896
8: 43954912
9: 43954944

relativ times:
0: 758424
1: 761104
2: 67208
3: 110768
4: 32
5: 16
6: 71752
7: 102184
8: 16
9: 32

As you can see there are short pulses of 16 or 32 microseconds.

I thought it was bouncing, however when I ran this code:

  while(1)
  {
    Serial.println(digitalRead(buttonPin));
  }

There was no sign of bouncing at all. Clear transitions from 0 to 1 and backwards without any jumping bits.

What I found aswell is that one iteration of my input loop takes exactly 16 microseconds. I did so with the following two code samples:

Measuring the input loop:

     while(1)
     {
        int i=0;
        bool lastState = HIGH;
        unsigned long startTime = 0;
        unsigned long stopTime = 0;

        startTime = micros();  
        if(digitalRead(buttonPin) != lastState)
        {
          i++;
          lastState = !lastState;
        }
        pattern[i] = micros();

        stopTime = micros();
        Serial.println(stopTime-startTime);
     }

Measuring the time of the measuring code alone:

     while(1)
     {
        unsigned long startTime = 0;
        unsigned long stopTime = 0;

        startTime = micros();  
        stopTime = micros();

        Serial.println(stopTime-startTime);
     }

The first example outputs 24 microseconds.
The second one gives 8 microseconds.

Which means that one iteration of my input loop takes 16 microseconds. And that is exactly what I got above in the output of my actual code.
So it seems that sometimes the input-while() is just skipping ahead without listening to my actual button pushes. Like as if it was bouncing.

So, any ideas anyone what causes those short pulses?

Thanks for your help :slight_smile:

Switch bounce is an extremely well known and understood phenomenon. Google it. Yes, it is bouncing. Yes, you can overcome the effect in software with "debouncing", also well known and used for decades.

Serial is

S

L

O

W

Once you've filled up the output buffer then your while(1) loop will execute at the rate that characters are going out. At 9600, that's about 1000 characters per second. But you used println(), so there's extra CR and LF characters. You're getting about 330 samples per second that way. You can't even see millisecond bounces: certainly not microseconds.

Yes, actually reading at delayed intervals is the easiest but the most sluggish way to debounce. Often the leading edge (activation) is the one of interest, not the falling edge (release). So you want to scan for it rapidly and react immediately. Then you ignore the switch for a while.

Okay, thank you guys. I didn’t think about the serial output beeing so slow. That makes sense.

Now before I fix my code, I want to understand what the bouncing actually looks like so I can set the right measures to remove it from my output.

But again the code I wrote to see the button bouncing doesn’t work. Is it even possible to see it without an oscilloscope?

Code:

const int buttonPin = 2;     // pushbutton pin
int i = 1;
const int arraySize = 100;
bool bounce[arraySize]={0};
bool interruptcalled = false;

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(buttonPin), interrupt, FALLING);
}

void loop() 
{
  if(interruptcalled == true)
  {
    for(i=0; i<arraySize; i++)
    {
      Serial.print(i);
      Serial.print(": ");
      Serial.println(bounce[i]);
    }
    interruptcalled = false;
    
    interrupts();   //restart interrupts
  }

}


void interrupt() 
{
  noInterrupts(); //turn off interrupts
  i=0;
  while(i < arraySize)
  {
    bounce[i] = digitalRead(buttonPin);
    i++;
  }
  interruptcalled = true;
}

Output:

0: 0
1: 0
2: 0
3: 0
4: 0
5: 0
6: 0
7: 0
8: 0
9: 0
10: 0
11: 0
12: 0
13: 0
14: 0
15: 0
16: 0
17: 0
18: 0
19: 0
20: 0
21: 0
22: 0
23: 0
24: 0
25: 0
26: 0
27: 0
28: 0
29: 0
30: 0
31: 0
32: 0
33: 0
34: 0
35: 0
36: 0
37: 0
38: 0
39: 0
40: 0
41: 0
42: 0
43: 0
44: 0
45: 0
46: 0
47: 0
48: 0
49: 0
50: 0
51: 0
52: 0
53: 0
54: 0
55: 0
56: 0
57: 0
58: 0
59: 0
60: 0
61: 0
62: 0
63: 0
64: 0
65: 0
66: 0
67: 0
68: 0
69: 0
70: 0
71: 0
72: 0
73: 0
74: 0
75: 0
76: 0
77: 0
78: 0
79: 0
80: 0
81: 0
82: 0
83: 0
84: 0
85: 0
86: 0
87: 0
88: 0
89: 0
90: 0
91: 0
92: 0
93: 0
94: 0
95: 0
96: 0
97: 0
98: 0
99: 0

You need to do some homework about interrupts. You really balled it up. https://www.gammon.com.au/interrupts

Okay, I am very thankful that you are trying to help but I dont understand whats going on.
I did as you suggested and read the site you posted a link to.

Things I see I did wrong:
-Not using volatile variables.
-Stopping interrupts with in an ISR doesnt make sense because they are stopped on entering and restarted once the routine is left.
-Having too much code in the ISR. I knew that one but figured it doesnt matter since my code doesnt do anything else in this example.

I tried some things but still I dont know why its not working. What I would like to see is the bouncing of pin 2 after I pushed the button. In my circuit pin2 has an external pulldown resistor.
I measured that one iteration of the while loop which is reading the button state takes around 8µs so I would assume I should see some bouncing in that time (arraySize * 8µs = 8000µs). But its always just 1s. The ISR call counter in the output tells me though that apperently there is some bouncing happening even though I turn the interrupts off right after the ISR.
This is something I dont understand aswell: Sometimes it skips some values. Also some other times it doesnt finish printing the whole array.

const int buttonPin = 2;     // pushbutton pin
int i = 1;
const int arraySize = 1000;
volatile bool bounce[arraySize]={0};
volatile bool interruptcalled = false;
volatile int ISRcounter = 0;


void observePin()
{
  noInterrupts(); //turn off interrupts
  
  i=0;
  while(i < arraySize)
  {
    bounce[i] = digitalRead(buttonPin);
    i++;
  }
  
  interruptcalled = true;
}

void interrupt() 
{
  ISRcounter++;
  observePin();
}

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(buttonPin), interrupt, RISING);
}

void loop() 
{
  if(interruptcalled == true)
  {
    for(i=0; i<arraySize; i++)
    {
      if(i%40 == 0)
      {
        Serial.println();
      }
      Serial.print(bounce[i]);
    }
    Serial.println();
    Serial.print("ISR call counter: ");
    Serial.println(ISRcounter);
    interruptcalled = false;
    
    interrupts();   //restart interrupts
  }
}
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 1

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 2

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 4

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 5

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 6

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111
ISR call counter: 9

1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
1111111111111111111111111111111111111111
ISR call counter: 10

Are you using a pull up or pull down resistor to hold the pin in the opposite state the button makes it? If the button makes the state high. Use a pull down resistor of 10k

lineman2208:
Are you using a pull up or pull down resistor to hold the pin in the opposite state the button makes it? If the button makes the state high. Use a pull down resistor of 10k

Thanks for your reply!
Yes, I am using a 10k pull down resistor on pin 2. The buttonpush makes it go high.

Your interrupts are messing with the process. When you disable interrupts, your interrupt service routine won’t run again until you re-enable them. However, it remembers if there is an interrupt pending. So that interrupt is handled as soon as you re-enable the interrupts, by which time your switch has debounced big time.

For this test, maybe you should try it without interrupts. Set it up so you’re sure it’s all debounced and stuff and spin waiting for you to push the button. Then run your test exactly the way you did it, by filling your array. Then you can serial print the results, and loop.

A couple of things. digitalRead() is slow, yes, but not that slow. The loop that you timed taking 8 uSec was basically two calls to micros().

You used your global i in two routines. Bad idea.

If you want it to be even faster or more accurate, you can read the PIND pseduo variable directly.

Try this: * Untested Code *

const int buttonPin = 2;     // pushbutton pin  ** This is PIND, bit 2  **
int i;
const int arraySize = 1000;
byte bounce[arraySize];


void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() 
{
  unsigned long timeStamp = millis();

// First, ignore buttons until we're sure it's debounced and not pushed.
  while (millis() - timeStamp < 100)
    if (PIND & 4)                     // Button Pushed
      return;                         // Start over

// OK.  We have gone .1 seconds without any push or bounce.  We should be clear to start our spin wait

  while (!(PIND & 4));    // Spin wait until button push

  for (i = 0; i < arraySize; ++i)
    bounce[i] = PIND;

  for(i=0; i<arraySize; i++)
  {
    if(i%40 == 0)
    {
      Serial.println();
    }
    Serial.print(bounce[i]);
  }
  Serial.println();
  Serial.println();        // Blank line between tests
}

Thanks Jimmus! The last days I spent a few hours getting into this and your hints and code helped alot!
Especially once I understood what the bouncing looks like, it was much easier to debug the interrupt code and then finally solve the original problem.
The button I use has very little bouncing on push (I saw it bounce up to 125µs after push), but a lot longer on release (that was ocassionally up to 5ms and very rarely up to 10ms).

Jimmus:
Your interrupts are messing with the process. When you disable interrupts, your interrupt service routine won’t run again until you re-enable them. However, it remembers if there is an interrupt pending. So that interrupt is handled as soon as you re-enable the interrupts, by which time your switch has debounced big time.

It did mention that in gammons site of course, but logically I didnt get it.
That helped: EIFR = bit (INTF0);

Jimmus:
A couple of things. digitalRead() is slow, yes, but not that slow. The loop that you timed taking 8 uSec was basically two calls to micros().

You used your global i in two routines. Bad idea.

If you want it to be even faster or more accurate, you can read the PIND pseduo variable directly.

Very valuable points! I understand now what you ment. (The 8µs loop of two micros() was just to measure how long the timetaking itself takes, btw. Its explained in the starting post)

First I used your code and adapted it a little. In this version it records the bouncing on button-release:

const int buttonPin = 2;     // pushbutton pin  ** This is PIND, bit 2  **
int i;
const int arraySize = 1000;
byte bounce[arraySize];
int loopCounter;
      unsigned long startTime = 0;
    unsigned long stopTime = 0;

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
  loopCounter = 0;



}

void loop()
{
  


  unsigned long timeStamp = millis();

// First, ignore buttons until we're sure it's debounced and not pushed.
  while (millis() - timeStamp < 100)
    if (!(PIND & 4))                     // Button Pushed
      return;                         // Start over

// OK.  We have gone .1 seconds without any push or bounce.  We should be clear to start our spin wait

  loopCounter++;
  while ((PIND & 4));    // Spin wait until button push

  
  startTime = micros();

  for (i = 0; i < arraySize; ++i)
  {
    bounce[i] = PIND;
    delayMicroseconds(7);  //one iteration of this for-loop takes only about 1.4µs. adding a small delay to be able to see for how long the bouncing continues
  }
    
  stopTime = micros();

    
  for(i=0; i<arraySize; i++)
  {
    if(i%40 == 0)
    {
      Serial.println();
    }

    if(bounce[i] & 4)
    {
      Serial.print("1");
    }
    else
    {
      Serial.print("0");
    }
    
  }
  Serial.println();
  Serial.print(loopCounter);
  Serial.print("  -  ");
  Serial.println(stopTime-startTime);
  Serial.println();        // Blank line between tests
}

Then I fixed my interrupt code. I realized that I had some big flaws in that one before. Again for button release:

const int buttonPin = 2;     // pushbutton pin
const int arraySize = 1000;
volatile bool bounce[arraySize]={0};
volatile bool interruptcalled = false;
volatile int ISRcounter = 0;
volatile int ObserveCounter = 0;    
unsigned long startTime = 0;
unsigned long stopTime = 0;





void interrupt() 
{
  //Serial.println("XXX isr called XXX ");
  ISRcounter++;
  interruptcalled = true;
}

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(buttonPin), interrupt, FALLING);
}

void loop() 
{
    
  if(interruptcalled == true)
  {
    
    int i=0;
    startTime = micros();
    while(i < arraySize)
    {
      bounce[i] = digitalRead(buttonPin);
       delayMicroseconds(20);
      i++;
    }
    stopTime = micros();
    ObserveCounter++;
    
    
    for(int x=0; x<arraySize; x++)
    {
      if(x%100 == 0)
      {
        Serial.println();
      }
      Serial.print(bounce[x]);
    }
    
    Serial.println();
    Serial.print("ISR call counter: ");
    Serial.println(ISRcounter);
    Serial.print("Observe Counter: ");
    Serial.println(ObserveCounter);
    Serial.print("time passed: ");
    Serial.println(stopTime-startTime);

    interruptcalled = false;

    delay(500);
    EIFR = bit (INTF0);
    
    interruptcalled = false;
  }
}

And finally the fixed code of my original problem. All it took was a delay after the button transition. :slight_smile:
Works perfectly now!

const int buttonPin = 2;     // pushbutton pin
unsigned long buttonHoldStartTime = 0;
const int patternArraySize = 10; 
unsigned long pattern[patternArraySize] = {0};
int i;
const int bounceSize=100;
bool bounce[bounceSize] = {0};

void setup() 
{
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
}

void loop() 
{
  
  i=0;
  bool lastState = HIGH;

  
  Serial.println("Waiting for input");
  while(digitalRead(buttonPin) == LOW)
  {
    //do nothing, wait for HIGH
  }
  Serial.println("start input");

    
  buttonHoldStartTime = micros();
  
  while(i < patternArraySize) 
  {
    pattern[i] = micros();
    if(digitalRead(buttonPin) != lastState)
    {
      i++;
      lastState = !lastState;
      delay(20);
    }
  }

  Serial.println("Input section finished");
  delay(3000); 

  
     
  Serial.println("raw array:");
  for(i=0; i<patternArraySize; i++) //print raw array with absolute timer values
  {
    Serial.print(i);
    Serial.print(": ");
    Serial.println(pattern[i]);
  }
  

  for(i=patternArraySize-1; i > 0 ; i--)  //calculate how long each state was active (relative timer values)
  {
    pattern[i] = (pattern[i] - pattern[i-1]);
  }
  pattern[0] = (pattern[0] - buttonHoldStartTime);
  
  Serial.println("\nrelativ times:");
  for(i=0; i<10; i++) //print array with relative times
  {
    Serial.print(i);
    Serial.print(": ");
    Serial.println(pattern[i]);
  }
  
  Serial.println("\n relative time in milliseconds:");
  for(i=0; i<10; i++) //print array. times in milliseconds
  {
    pattern[i] = pattern[i] / 1000;
    Serial.print(i);
    Serial.print(": ");
    Serial.println(pattern[i]);
  }

  Serial.println("\n same but as bars:");
  for(i=0; i<10; i++) //print array. times in milliseconds
  {
    pattern[i] = pattern[i] / 10;
    Serial.print(i);
    Serial.print(": ");
    for(int b=0; b < pattern[i]; b++)
    {
      if(i % 2)
      {//if there is a rest it is uneven
        Serial.print("-");
      }
      else
      {//if there is no rest it is even
        Serial.print("1");
      }
    }
    Serial.println();
  }

}

Thanks again guys. I learned some things :slight_smile: