Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Optimal debouncing time in Ergodox EZ #910

Closed
okapies opened this issue Nov 27, 2016 · 13 comments
Closed

Question: Optimal debouncing time in Ergodox EZ #910

okapies opened this issue Nov 27, 2016 · 13 comments

Comments

@okapies
Copy link

okapies commented Nov 27, 2016

I have investigated how to configure debouncing time in Ergodox EZ, and found a comment saying "no sense to have DEBOUNCE higher than 2".

I also found, however, the disabled default DEBOUNCE setting is 5 instead of 2. Which one is the correct?

@ezuk
Copy link
Contributor

ezuk commented Nov 28, 2016

The latter is correct -- I feel it should be around 5.

@okapies
Copy link
Author

okapies commented Nov 29, 2016

Hmm, thanks!

IMO, the debouncing should be enabled by default, why is it disabled?

@fredizzimo
Copy link
Contributor

Disclaimer This answer is written from a purely theoretical point of view, and I have not verified what I say in practice.

I'm also assuming that the following statement is correct, but someone with an Ergodox EZ should be able to confirm by enabling the DEBUG_MATRIX_SCAN_RATE macro, which should print the number of scans per second.

On Ergodox matrix scan rate is relatively low, because of slow I2C.
Now it's only 317 scans/second, or about 3.15 msec/scan.
According to Cherry specs, debouncing time is 5 msec.

But before going further @okapies, you say that the default debounce is disabled, how did you come to that conclusion? It's defined as 5 in keyboards/ergodox/ez/config.h

First let's define what debouncing is, since it's often perceived as something complex and almost magical, while it's in fact a very simple concept.

Debouncing is nothing more than a method of removing the effect of electrical noise caused by mechanical switches. Or in even more practical term a way to make sure that you don't see double keystrokes when you press and release keys.

There are a couple of causes for this, and I believe the first one is the only valid one, the second one probably only happens during a huge solar storm or similar, nevertheless debouncing typically takes care of both cases.

  1. When you press or release the switch, the actual physical contact won't immediately be reliable. In some cases the physical contact actually bounces a bit on the surface, so that's where the term bounce comes from. This causes the electrical signal to be unstable and rapidly switches between contact and no contact. So if there's no filtering you would see a rapid succession of key presses for the key that you are pressing or releasing. Different types of switches can have different behaviour, but usually there's always a small time window where the signal is unreliable.
  2. There can be some electromagnetic interference (EMI), which causes the signal to be reported wrongly. You would probably see this as keys being pressed randomly, even if you don't touch them.

So to make things really simple, if you see key presses that you don't want, you need debouncing.

The debouncing algorithm for the Ergodox EZ is simple. Each time it see a change in the pressed keys (including bouncing signal), it sets the debouncing variable to the debouncing time. If the state of the matrix is the same during the next scan, it decreases the variable. When it goes to zero it sets the matrix and the key press can be observed.

There's a slight bug in the code, it actually waits for DEBOUNCE - 1 matrix scans, and I believe that if you set it to 0, no key presses would ever be observed. There's also in insignificant additional delay of one micro second (which could be removed, since the scan rate is so low), when the keyboard is in the debouncing state.

So in even more simple terms it does this.
Report a change in the keyboard state once the signal has been stable for 3.15 ms * (DEBOUNCE -1), or in the case of the default 5 for 3.15 * (5 - 1) = 12.6 ms

As you can see the DEBOUNCING configuration affects the responsiveness of the keyboard, a higher value will make everything less responsive and introduce latency. For Cherry MX, with the default setting it would mean that the maximum latency is 12.6ms + 5ms = 17.6 ms, but the minimum would be close to 12.6 ms, so it typically varies between those two. If you wonder how it could reach the minimum, it's quite simple. Let's assume that the switch is better than the specs, so the actual debounce time is less than 3.15ms. In this case, if you press it just after the previous scan, then there's 3.15 ms for it to stabilize, and the matrix code would only read the stable values.

For really high debouncing values, there's another effect. The keyboard can start to ignore key presses, since it would consider real key presses as just noise.

So let's look at the recommended value of two
The recommended value of two, with the bug, would mean that we require two scans to report the same value to consider it a key press. If we assume there's no EMI, then every time we see a change in the matrix then it means that a key is being pressed and released, it can't spuriously happen. We also know that the maximum debounce is 5ms for Cherry MX, and I believe it's the same for almost all other switch types.

So let us consider a key press, with the initial state of not pressed and we run the first scan immediately when the key is pressed.

  • If the first scan reports true, the second true, then the third one has to happen outside the 5 ms delay and also report true. The earliest time the second scan could happen is 3.15 ms since the key was pressed, and the third will happen at the earliest 6.30 ms afterwards, so the value is always stable. So we report the change 3.15 ms after it happened
  • If the first reports true, the second false, then third and forth has to report true, so the change is reported 9.45 ms after it happens.
  • If the first scan reports false, the second reports false (still no change reported), then the third and the forth has to return true and we detect the change 9.45ms after the key press
  • If the first scan reports false, the second reports true, then the third has to report true, and the change reported 6.30 ms afterwards

So it shouldn't be possible for any false reports there. If the first scan is run a little bit later, it would just add 0 - 3.15ms extra latency, but the end results will be the same

In other words two is theoretically optimal

A practical way of finding the optimal value

  1. Start by the minimum value 1, which means no debouncing (with the bug)
  2. If you see double key presses increase the value

You will end up with the optimal value for your switches. There's no need to go beyond what works, it only adds additional latency. This way of doing it would also make sure everything works in case you have constant EMI.

I'm not sure how @ezuk came to his conclusion, but in my opinion 5 is way too high.

A small possible improvement to the algorithm
If we assume that there's no EMI, then we could actually report the value as soon as we detect a change. But then after that we should ignore any changes for 5ms. This would reduce the latency to something between 0-3.15ms. We could also do the processing per switch instead of for the whole keyboard.

The reason why we could do like that is that any change we see after a confirmed state has to be real. But changes within 5ms of that could be caused by bounces, so they should be ignored. After 5ms we should again have a stable signal, and we can report any changes that are seen again.

@okapies
Copy link
Author

okapies commented Dec 6, 2016

@fredizzimo: Thanks so much for your very very detailed explanation! Now I fully understand how the debouncing works (and its side-effect) and how to configure DEBOUNCE. I'll give it a try.

But before going further @okapies, you say that the default debounce is disabled, how did you come to that conclusion? It's defined as 5 in keyboards/ergodox/ez/config.h

Wow! Oh, my Buddha. You're absolutely right. It seems that the C parser installed on my head is contaminated by Python-like syntax and ignores the "#"-prefixed line as a comment. 😅

@piotr-dobrogost
Copy link

piotr-dobrogost commented Dec 8, 2016

There's a slight bug in the code, it actually waits for DEBOUNCE - 1 matrix scans

I think there's no bug. DEBOUNCE is described this way:

This constant define not debouncing time in msecs, but amount of matrix scan loops which should be made to get stable debounced results.

and this is exactly what happens. When DEBOUNCE == 1 we treat readout of each scan as stable so the amount of matrix scan loops which should be made to get stable debounced results is indeed 1. When DEBOUNCE == 2 there are two scans made before we treat the readout as stable and so on.

, and I believe that if you set it to 0, no key presses would ever be observed.

Setting number of scans to 0 makes no sense as clearly you need at least 1 scan to observe any change.

In other words two is theoretically optimal

I don't think so.
The optimal value is the least number of consecutive scans spanning the 5ms time period (maximum period of instability of switch declared by the producer). As the time between two consecutive scans is 3.15ms the optimal value is 3 because 2 scans span the period of 3.15ms which is smaller than 5ms and 3 scans span the period of 2*3.15ms which is greater than 5ms.

For Cherry MX, with the default setting it would mean that the maximum latency is 12.6ms + 5ms = 17.6 ms, but the minimum would be close to 12.6 ms, so it typically varies between those two.

I think the above calculation is wrong.
To calculate the maximum latency we have to think about the worst case scenario and this is when a key is pressed right after readout (scan) was made. In this case we are left waiting for the next scan almost 3.15ms and yet another (3-1)*3.15ms for the next 3 scans which we need as established above. This gives 3.15ms + 2*3.15ms = 9.45ms maximum latency.
The best case scenario is when a key is pressed right before readout (scan) was made. In this case we need (3-1)*3.15ms for 3 consecutive readouts (scans) before we get stable/debounced state which gives 6.30ms minimum latency.

If this reasoning is ok then I think the comment in code should be expanded to include some of the above reasoning and the DEBOUNCE set to optimal value. To make it clear where does this value come from this should not be magic constant but the result of computation with the number of scans per second (317[1/s]) and switch's bounce time (0.005[s]) as the input.

@fredizzimo
Copy link
Contributor

fredizzimo commented Dec 9, 2016

I think there's no bug.

Well, I'm pretty sure that it was originally planned to represent milliseconds an that the comment was added later. That's because the scan loop used to wait for 1 millesecond, at least for most other keyboards. And of course after @IBNobody's latest changes it really means ms . But the other reason why I believe it's a bug is what Hasu said here

(1) I intended 5ms but my code seems to wait only 4ms

Anyway, let's get to the real discussion.
First lest's define T as the time you press or release a key, so by definition we have this, when you press a key.
Pressed state is false when t < T
Pressed state is random when T <= t < T + 5, this is the definition of bouncing
Pressed state is true when t >= T + 5

And when you release a key

Pressed state is true when t < T
Pressed state is random when T <= t < T + 5, this is the definition of bouncing
Pressed state is false when t >= T + 5

First let me explain why two is optimal.

With the debouncing, we want to prevent the situation where we read the wrong value, after we have read the correct one. As you already mentioned we have at most two scans inside the interval T <= t < T + 5, so let's analyse the three possible cases for those first (for keypresses, but you can reverse the logic for releases), the value inside the parenthesis is a known value, it can't be anything else.
We have the following cases

 t < T      t >= T    t >= T + 5
   A       B      C       D       E
(false), true,  true,  (true), (true) # Press is reported at C
(false), true,  false, (true), (true) # Press is reported at E
(false), false, false, (true), (true) # Press is reported at E
(false), false, true,  (true), (true) # Press is reported at D

As you can see there's no way to read a false after having read two consecutive trues, so the debouncing does it's job. Also if either D or E, is false, it can only mean that we have released the key, so we are allowed to report a release. Also note that only the second case would report a doubled keypress without debouncing.

The other case that we have is when we only have one scan inside the interval, then we have the following cases

 t < T   t >= T   t >= T + 5
   A       B      C       D       E
(false), true,  (true), (true), (true) # Press is reported at C
(false), false, (true), (true), (true) # Press is reported at D

Again, you can see that there's no reason to go higher. Is there any case that I'm missing?

For the latency calculations, I was calculating with the default of 5, while you seem to be using 3. But anyway for minimum we seem to agree as following your formula, (5-1) * 3.15 ms = 12.6 ms

But for the maximum, I think we both are wrong, it's even higher. The worst case situation occurs when a switch is just below the specs, and we read a wrongly bounced value at close to 5 ms after T, lets say at T + 4.99ms for this example.
We have the following reads, again using a keypress as an example, just reverse true and false for releases

T + 1.84 ms (4.99 - 3.15) - true or false does not matter
T + 4.99 ms - false, which means that debouncing is set to 0
T + 8.14 ms - true, deboucing is set to 5 and --debouncing (the condition for the check) is 4
T + 11.29 ms - true, --debouncing = 3
T + 14.44 ms - true, --debouncing=2
T + 17.59 ms - true, --debouncing=1
T + 20.74 ms - true, --debouncing=0, finally we are allowed to report the press

So the maximum latency is 20.74 ms for the example above

So we have these formulas for
minimum latency (DEBOUNCE - 1) * 3.15 ms
maximum latency 5 ms + DEBOUNCE * 3.15 ms

So with a debounce of 5
minimum latency = (5 - 1) * 3.15ms = 12.6 ms
maximum latency = 5 ms + 5 * 3.15 ms = 20.75 ms

For a debounce of 2
minimum latency = (2 - 1) * 3.15 ms = 3.15 ms
maximum latency = 5 ms + 2 * 3.15 ms = 11.3 ms

And for the best case optimized algorithm, which I described in my previous message would have a
minimum latency = (1 - 1) * 3.15 ms = 0 ms
maximum latency = 5 ms + 1 * 3.15 ms = 8.15 ms
Additional lag would only happen when you attempt to press or release the next key.

@piotr-dobrogost
Copy link

piotr-dobrogost commented Dec 9, 2016

You specify 3 periods ( t < T, t >= T, T >= T + 5) but 5 columns (A-E). I don't know how to match one with the other.
Also, might it be that we differ in what we think of when we're talking about optimal number of scans to perform debounce? When I say 3 I mean all scans, including the first one which registered a change in matrix state. In other words we register the change in scan n and then we have to wait yet another 2 scans (n+1, and n+2) to be sure the value is stable (debounced). Maybe your 2 refers to subsequent scans only (which come after the first one which had registered change) in which case we agree?

@fredizzimo
Copy link
Contributor

fredizzimo commented Dec 9, 2016

There's a typo, T>=T+5 should be t >=T+5, and it applies to columns, D and E, in the first table, and C, D and E in the second.

No, Im talking about all the required scans needed too, or the value to set for DEBOUNCE. We only need to read the same new state two times in a row in order to be sure that we never read an invalid value again. The third value we read has to be the correct one, so once we have read the same new state twice, we can assume that the third one will be too.

Lets take an example, if we are detecting a keypress, it's not possible to read "press, press, release", unless we really have a real keypress followed by a release. The read of release would happen after T+5, which is not physically possible unless the key is physically released. In that case we are of course allowed to report a press followed by release, but only after seeing two consecutive release states, after the two press states.

@piotr-dobrogost
Copy link

(...) once we have read the same new state twice, we can assume that the third one will be too.

What makes you think so? Two consecutive scans span period of 3.15ms. If key was released immediately before the first scan then both scans take place during the 5ms period of signal instability so we could get false value of readout in both scans, namely the value for a pressed key although the key had been released immediately before the first scan. Only in the subsequent, 3rd scan we are guaranteed to obtain true value as this scan takes place at least 2*3.15ms=6.30ms after the key had been released so after the 5ms period of signal instability.

@fredizzimo
Copy link
Contributor

What makes you think so? Two consecutive scans span period of 3.15ms
Because the third scan happens at least 5 ms after the key was physically pressed or released, and has to read the real value.

Since you are talking about key release, I assume that you are talking about the following case

You have a key pressed, and the keyboard currently reports that that's the case. You then release it.

So, if both scans reads the state wrongly
press, press -> that doesn't do anything since the key is already pressed
If only the second is wrong
release, press -> that doesn't do anything, since there wasn't two consecutive releases
If only the third is wrong
press, release -> That doesn't do anything either since there's only one release
If we read correctly
release, release -> then it correctly reports that the key has been released

So, I don't see how we get a false value with a DEBOUNCE value of 2 here.

Maybe I'm still misunderstanding something, could you show a concrete example with timings to show how we the wrong state can be reported?

For example
Initial matrix state pressed
The users press the key at at 0 ms
Read press at 1 ms
Read release at 4.15 ms
Read press at 7.3 ms (this is the only possible value, since we have bounced over 5 ms)
Read press at 10.45 -> Will report that the key has been pressed (two consecutive pressed reads)

So what I'm searching for is a similar example, but where we get the wrong report with a DEBOUNCE value of only 2, and a value of 3 would fix the situation.

@apocalypsemeow
Copy link

I have been using an Atreus 62 and realized thanks to this thread that my spacebar chattering issue is not due to switches (replaced it three times still some chatter), but rather due to EMI. The default setting in the config.h file was 5, so I just upped it to 6 and it went from infrequent chatter to NONE so far. I know this is different than the ergodox EZ, but it shows 5 was not too high in this situation.

I still think fredizzimo's advice is the best: start at 1 and constantly up the debounce until there is no chatter. So helpful for me!

@ghost
Copy link

ghost commented Mar 12, 2017

Ergodox works fine with DEBOUNCE 1 for me

@drashna
Copy link
Member

drashna commented Mar 25, 2018

This has been further improved in #2022 and #2023.

@drashna drashna closed this as completed Mar 25, 2018
fd0 added a commit to fd0/ergodox-tmk that referenced this issue Jun 25, 2018
BlueTufa pushed a commit to BlueTufa/qmk_firmware that referenced this issue Aug 6, 2021
* Clicking on QMK logo to jump to repo

 - idea from Mechmerlin

* Add title hover

* Adjust timing for localStorage tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants