Author Topic: Cherry MX debounce times  (Read 44710 times)

0 Members and 1 Guest are viewing this topic.

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Cherry MX debounce times
« on: Sun, 14 April 2013, 16:16:39 »
Hey all, this is a topic that I dealt with a few weeks ago as I put a keyboard together.  I wanted to poll the "maker" community here to see if we could get a consensus as to the best times for debouncing a Cherry MX switch.

I searched around a bit and I read that Soarer claims a 5ms debounce period is standard from Cherry.  That's pretty authoritative as far as I'm concerned, but I didn't find any supporting documentation for that claim.  Searches also revealed claims of as much as 20ms (!).

On my firmware, I'm using a 4ms for activation and 6ms for deactivation.  It is working well on my boards with Cherry MX brown.  I type at around 80wpm.

So, chime in if you have experience!

(1) What do you like for a debounce period?
(2) Do you like to be conservative or fast?
(3) Do you use the same for activation and deactivation?
(4) Do you find that switch type makes a difference?  For example, might the linear switches require less debounce or more?  What about clicky switches?

My software is very flexible, so I will probably experiment a bit when I have time to see how fast I can go before I get double hits and chatter.  I have a feeling that my current setting is probably pretty good, though.

Offline hasu

  • Posts: 3471
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Cherry MX debounce times
« Reply #1 on: Tue, 16 April 2013, 21:04:08 »
Hey all, this is a topic that I dealt with a few weeks ago as I put a keyboard together.  I wanted to poll the "maker" community here to see if we could get a consensus as to the best times for debouncing a Cherry MX switch.

I searched around a bit and I read that Soarer claims a 5ms debounce period is standard from Cherry.  That's pretty authoritative as far as I'm concerned, but I didn't find any supporting documentation for that claim.  Searches also revealed claims of as much as 20ms (!).
You can find MX spec in this page and it says 5ms for bounce time.
http://www.cherrycorp.com/english/switches/key/mx.htm

And Alps spec also says 5ms.
http://smartdata.usbid.com/datasheets/usbid/2000/2000-q2/5454_31.pdf

I believe you can safely use 5ms for debouncing time if you experience bouncing with this it must be defect of the switch you should replace it.


Quote
On my firmware, I'm using a 4ms for activation and 6ms for deactivation.  It is working well on my boards with Cherry MX brown.  I type at around 80wpm.
Just curious. Why do you use different value?


Quote
(1) What do you like for a debounce period?
(2) Do you like to be conservative or fast?
(3) Do you use the same for activation and deactivation?
(4) Do you find that switch type makes a difference?  For example, might the linear switches require less debounce or more?  What about clicky switches?
(1) I intended 5ms but my code seems to wait only 4ms :)
(2) conservative. But 5ms enough, I think.
(3) same. I didn't even come up with using different.
(4) It seems cherry brown tends to be bouncy than black from simple test, but completely unsure.

Today, I saw bouncing waves of some kind of switches around with scope. Just for fun, not science. I'm totally a noob for EE and oscilloscope.
Switches are not mounted plate, PCB or case. The test circuit is comprised of just 3.0V battery and 10K resistor and tested switch.


Brown cherry(from 2000).


The worst case on Black cherry(with Finger flick)


Omron switch with from 1990, very very bouncy, though I don't know this is normal for them. Note that time scale.


Green Alps(from 1987-90?). Very impressive. Very very clean wave form. I couldn't find almost symptom of bouncing.


The worst case of green Alps I could make up with Finger Flick. Wow, still very clean. Look and compare with Cherry Black.


From this result I can recommend vintage Alps for serious gamers? :D
« Last Edit: Tue, 16 April 2013, 21:05:47 by hasu »

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Re: Cherry MX debounce times
« Reply #2 on: Wed, 17 April 2013, 07:18:49 »
Wow this is great stuff!  Wish I had a scope...

I can't believe I missed that bounce spec on the MX sheet.  It's right at the top! >.<


Quote
On my firmware, I'm using a 4ms for activation and 6ms for deactivation.  It is working well on my boards with Cherry MX brown.  I type at around 80wpm.
Just curious. Why do you use different value?


Well, my code supports different values for each direction, and I was initially playing around with them quite a bit.  I guess I just happened to settle on those numbers.  My reasoning was that I wanted activate to be a little faster.

Offline n0rvig

  • Posts: 355
Re: Cherry MX debounce times
« Reply #3 on: Thu, 18 April 2013, 21:42:32 »
From this result I can recommend vintage Alps for serious gamers? :D

ha! totally, I can now imaging the most hardcore keyboard crew valuing that pure ALPS waveform above all else.

Offline metalliqaz

  • * Maker
  • Thread Starter
  • Posts: 4951
  • Location: the Making Stuff subforum
  • Leopold fanboy
Re: Cherry MX debounce times
« Reply #4 on: Sat, 20 April 2013, 12:20:19 »
Only makes a difference if the controller is souped up to do super fast denounce and use USB 1ms polling rate (or PS2, ick)

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #5 on: Sat, 20 April 2013, 14:01:09 »
Dunno why you say "if the controller is souped up"... that is on topic here!

Debounce time is (or should be) extended by any noise the controller sees - so the Cherry black finger-flick could take a ms or two more than the software's debounce time, depending on exactly when the CPU scans it.

All depends how you implement your debouncing... I'm writing some code for individual switch debounce (as opposed to batch) using bit-shifting. When it's working I could play with different values on make and break quite easily, but I'm not sure I see the benefit :) (Apart from my 'turbo' mode, but that's a different matter... and not EMI immune).

If the scan rate is low, you end up needing a longer debounce time just to get enough scans to have good confidence anyway... so a typical crappy keyboard scanning at 125Hz with batch debounce could be using a debounce of 10s of ms! (e.g. 3 scans, 8ms apart -> 16 to 24ms).  Reducing that doesn't need 1ms interval USB - that would just help as well in reducing overall latency, and more importantly would be a more consistent delay. Scanning at 1000Hz, however, gives 5 scans for 5ms, and that's plenty. What gamer wouldn't want 19ms advantage?!
« Last Edit: Sat, 20 April 2013, 14:03:43 by Soarer »

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #6 on: Sun, 21 April 2013, 03:51:08 »
@hasu
Thank you so much for documenting and sharing your experiment. I'm impressed with the ALPS sub-millisecond physical bouncing! Some gaming brand should use that as the marketing appeal for their ALPS keyboards :p


As for the software debouncing, I was a bit surprised that no one mentioned the other algorithm which validates the input when the last n reads are the same (instead of using a fixed time window). Here you can see a better explanation.

Because it is time independent, I think it can achieve faster debouncing timings (provided the switch is not in poor conditions) but it will be inconsistent by design (each input may take different deboucing timings). But if it's fast enough (anything below 10ms?), that inconsistency won't be percieved. Plus, it is wear-proof; for example, the switch in bad conditions that fails at the fixed time algorithm (because it stays, say, 10ms physically bouncing and the fixed time is only 5ms) will pass in this algorithm no problem, taking just the time needed to debounce the input.

I don't know if that's practical for the mechanical keyboard world though (but it is making me want to let the laziness aside and assemble my Phantom to try to implement this method :)))
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #7 on: Sun, 21 April 2013, 07:13:08 »
I did - at least I mentioned that debounce time should be extended by noise. ;)

In effect last n reads is a time interval, since the reads occur at regular intervals.

I'm using something like DebounceSwitch2() from this article, except using 8-bit values instead of 16-bit, which still allows up to 7 readings required to be the same before a key event is produced.

Simplified version for a single key, looking for 5 readings to be the same...

Code: [Select]
uint8_t debounce_buffer = 0xFF;                            // history of last 8 readings

void scan(void)
{
    debounce_buffer = (debounce_buffer << 1) | read_key(); // shift and put latest reading in least significant bit
    uint8_t v = debounce_buffer & 0b00111111;              // mask with one more bit than the debounce 'time'
    if ( v == 0b100000 ) {                                 // check for change from unpressed (1) to pressed (0)
        key_down();                                        // produce down event
    } else if ( v == 0b011111 ) {                          // check for change from pressed (0) to unpressed (1)
        key_up();                                          // produce up event
    }
}

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #8 on: Sun, 21 April 2013, 13:42:48 »
Nice! Efforts like that helps to kill my super laziness and inspires me to do stuff :)

In effect last n reads is a time interval, since the reads occur at regular intervals.

It depends on the physical bouncing, which is not necessarily the same at all times (a switch in poor conditions takes, say, an average of 11 cycles until it stabilizes; a switch in good conditions takes, say, an average of only 5 cycles until it stabilizes).
« Last Edit: Sun, 21 April 2013, 13:47:09 by F u r u y á »
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #9 on: Sun, 21 April 2013, 14:37:36 »
Well yeah, it's not a fixed time interval from when the noise starts - that would simply be doing it wrong (strong word... perhaps 'chancing it' would be better). So those that know don't think to mention it, and those that don't know have dodgy debouncing! So when someone who knows mentions debounce time, it usually means the time of stability required after the noisy part - and the software debounce time could be different to the switches debounce time, it could even be less :D
« Last Edit: Sun, 21 April 2013, 14:40:56 by Soarer »

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #10 on: Sun, 21 April 2013, 14:48:35 »
?? I didn't understand your text :confused:

Yeah, the terminology is a bit too loose. So let t_d (deboucing time) be the time interval between a key press (finger crossing the activation point) and the stable debounced output signal. With that definition what I was saying is that t_d for the last n reads method is variable by design; whilst t_d for the fixed time method is fixed by design.
« Last Edit: Sun, 21 April 2013, 14:52:44 by F u r u y á »
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #11 on: Sun, 21 April 2013, 15:31:38 »
You understand... you know what you're talking about :)

My only disagreement is in terminology/expression, since reads and time are interchangeable, given a fixed read rate. So what you're trying to say there is actually that there's a difference between starting to count after the first change seen, versus starting to count after the last change seen. In the latter case, the total time for debouncing is higher, but the debounce time is the same - it's just being used in a different way.


Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #12 on: Sun, 21 April 2013, 16:17:26 »
Now I understand your text better :) But what I'm trying to say is (for the last n reads method):

In the latter case (count after the last change seen, I'll call it t_d_last), the time will always be the n you've specified in the software times the sampling period. So I think it's not useful because it does not provide any new information.
  • t_d_last = n * sampling period
    (no matter how old or new the switch is!)

In the former case (count after the first change seen, I'll call it t_d_first), the time may not necessarily be the same. Let t_b be the physical bouncing time. Then:
  • t_d_first = t_b + t_d_last
    (t_b is different every time, that's why I say that this method has variable timing by design)

What we can perceive is the lag between the instant our fingers cross the activation point and the instant the controller provides the stable (debounced) output, which is t_d_first. If t_d_first is big and inconsistent, we would perceive it in a very bad way. Whilst if t_d_first has a low enough threshold (< 10ms), that inconsistency wouldn't matter to us (we can't perceive it).
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #13 on: Sun, 21 April 2013, 16:48:52 »
Right... ish. So you can maybe react a bit faster, and more consistently, when using a fixed time from the first change detected. However, you'd still do a (at least) another read to confirm the state - otherwise it's simply a delay, and the key event may as well have been generated from the first change, without delay. Something has to give it hysteresis so that noise doesn't cause multiple key events. If that confirming read fails, it all resets to square one, and the total time increases in steps of the debounce time... that would be worse (less consistent) than extending the total time by the scan interval.


Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #14 on: Sun, 21 April 2013, 17:45:42 »
Why are you mixing my statement with the fixed time method? Everything I stated in my last post has nothing to do with fixed time method.

I know that in fixed time method the delay has to be big enough (otherwise you are not debouncing, as you described). And what I'm thinking/guessing is that the last n reads method can successfully debounce with a delay lesser than in the fixed method, at the cost of being inherently inconsistent.


Are we talking the same language? :p Just for the sake of clarity:
Fixed time algorithm
  • After it has been detected the change of state in a switch (from open to close or vice-versa), we should wait a fixed time to consider the new state

Last n reads algorithm
  • The change of state in a switch is considered if and only if the state of the switch is the same in the last n reads
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #15 on: Sun, 21 April 2013, 18:23:51 »
I really don't know about the language... either you haven't explained it clearly enough for me to understand, or the differences between 'fixed time', t_d_last and t_d_first, are not quite what you think they are!

t_d_first appears to be what I'm calling the total debouncing time, which of course would be variable.
t_d_last appears to be what I'm calling the debounce time - i.e. the setting or constant value in firmware.

But what I don't see is how those translate to two different methods.

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #16 on: Sun, 21 April 2013, 18:31:51 »
No no I didn't say they are two different methods. I just tried to use your terminology to explain what I was trying to say. In your other post I think you meant to use the word former (instead of latter).

I'm just trying to state/conjecture about the pros and cons of fixed time versus last n reads.
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #17 on: Sun, 21 April 2013, 19:21:22 »
I did mean latter... the method that waits for the bouncing to stop before starting its count/time will take longer than one which assumes that the bouncing will stop within a certain count/time.

Perhaps if we take the code I posted earlier as an example implementation of a 'standard', we'll have a solid definition to base discussion on... so the 'standard' debounce waits for no changes within n scans, might take a long time with a noisy switch, but is known to be (mostly) reliable in the face of both noisy switches and EMI.

I think we can only go faster than that either by dropping some robustness (e.g. not handling EMI), or by making some assumptions (e.g. switches will stop bouncing within a certain time). Making the assumption that switches behave as specified is reasonable, but the benefit is fairly small - after all, the 'standard' method won't take much extra time if the switch behaves itself. Not handling EMI is a reasonable choice too (worthy of investigation) since that could save a lot of time - maybe we could then act on any change immediately, knowing that it is the switch.

Back to the code... assuming <5ms of bouncing by the switch and a 1kHz scan rate, we could alter the comparisons that detect changes - remember that the debounce buffer is just a history of the key state. We could mask the buffer value with 0b00110001 so we are only looking at the first change and the state 5 scans later. For key_down, we could match with 0b010000, and for key_up, 0b100001. That would implement a 'fixed time' approach, but as I said earlier, fixed time isn't really fixed if the state fails to confirm itself (in code, the confirmation would be matching of the least significant bit). Anyway, if switches typically only bounce for ms or two, then it doesn't save much. Conversely, if a (defective) switch bounces for a long time, there isn't much checking to detect it, with only one read being used for confirmation. Finally, I think implementation of it (at least as an adpation of the bit-shifting code) has other flaws, since the matches could overlap.


Offline hasu

  • Posts: 3471
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Cherry MX debounce times
« Reply #18 on: Sun, 21 April 2013, 22:24:41 »
Ah, I finally got what you want to say. Your terminology doesn't seem to be common, I couldn't understand it at least.
As you know Fixed time method is too primitive to use for keyboard matrix, I've never seen use of this method in open source keyboard projects. This will work nice enough for prototype or application not using switch intensively, though.

Are we talking the same language? :p Just for the sake of clarity:
Fixed time algorithm
  • After it has been detected the change of state in a switch (from open to close or vice-versa), we should wait a fixed time to consider the new state

Last n reads algorithm
  • The change of state in a switch is considered if and only if the state of the switch is the same in the last n reads

So, I couldn't think you were referring that algorithm with 'fixed time'. I even almost forgot that simple debounce method :) BTW, The link you gave shows us interesting read counting method, I'll look into it.
Quote
As for the software debouncing, I was a bit surprised that no one mentioned the other algorithm which validates the input when the last n reads are the same (instead of using a fixed time window). Here you can see a better explanation.

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #19 on: Mon, 22 April 2013, 06:49:00 »
That Karnaugh map derived routine is nice mathematically, and good for hardware. The result is the same as the bit-shifting method. I think bit-shifting has a few advantages for a software implementation:

  • easier to change how many reads to check
  • easier to extend to many keys
  • detects changes in key-state directly

The downside is a small waste of bits, since the upper bits of the debounce buffer(s) might not be used.

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #20 on: Mon, 22 April 2013, 14:36:18 »
I was trying to understanding your code Soarer and I finally figured out that the buffer is used as a "sliding window" through the sampled input bitstream. Is that it, right?

About the Karnaugh map, it was a coincidence to stumble over it again here since I saw this "Don't care" keycaps a few days ago at reedit ;D

And sorry for my poor terminology hasu. Now imagine if I wasn't putting effort (I was!) to post clear replies, it'd be a total mess.
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #21 on: Mon, 22 April 2013, 15:58:36 »
Yup, I guess it could be called a sliding window :)

It seems to work well in 'turbo' mode too (just did first tests tonight)...

Code: [Select]
uint8_t debounce_buffer = 0xFF;                            // history of last 8 readings

void scan(void)
{
    debounce_buffer = (debounce_buffer << 1) | read_key(); // shift and put latest reading in least significant bit
    uint8_t v = debounce_buffer & 0b00111111;              // mask with one more bit than the debounce 'time'
    if ( v == 0b111110 ) {                                 // check for change from unpressed (1) to pressed (0)
        key_down();                                        // produce down event
    } else if ( v == 0b000001 ) {                          // check for change from pressed (0) to unpressed (1)
        key_up();                                          // produce up event
    }
}

... although I haven't tried it next to any noisy electrical equipment yet!

In my real code I've left the normal tests in as well, as a backup in case a turbo test fires erroneously. That way it will produce a random key-press instead of a random stuck key (if hit by enough EMI). edit: damn, just spotted a flaw in that backup plan, will have to rethink :( edit2: no flaw :cool:

Turbo mode has no latency at all  :eek:
« Last Edit: Mon, 22 April 2013, 16:14:01 by Soarer »

Offline F u r u y á

  • Posts: 427
Re: Cherry MX debounce times
« Reply #22 on: Mon, 22 April 2013, 16:43:07 »
So in your turbo mode if you hold "A" for one second, the keyboard outputs "AAAAA...." 1000 times? That guy from your Filter keys thread will love that :D
But to use this you'd have to bypass OS repeat restrictions, right?

--------

Going up one level in the software, is it designed like this:

Quote
µC Timer is configured to fire a interruption every 1ms;
The interruption handler, among many other things, runs scan()

?
Filco 91 | Filco 87* | Filco 105 | PLU 87 | PLU 87 | Cherry 120 | Cherry 120
*

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #23 on: Mon, 22 April 2013, 17:01:46 »
So in your turbo mode if you hold "A" for one second, the keyboard outputs "AAAAA...." 1000 times? That guy from your Filter keys thread will love that :D
But to use this you'd have to bypass OS repeat restrictions, right?

No, it still debounces and works as normal, it just has no latency. It assumes that you can't physically press and release a key in less than 5ms (which shouldn't get past any debouncing routine anyway). The debounce happens before the key state changes, since it requires a stable state there for a few scans, and then it can react instantly to the first state change as soon as it happens. The big assumption is that that state change is the beginning of a real change in state of the key (which may well include a period of bouncing, but that then delays when the next state change is allowed).

Going up one level in the software, is it designed like this:

Quote
µC Timer is configured to fire a interruption every 1ms;
The interruption handler, among many other things, runs scan()

?

Effectively, yes. I don't actually run it within the interrupt handler, but it does run every 1ms.

Also, in the real code, there's a debounce_buffer for each key, so it loops around them. The index is passed to the key_up() and key_down() functions, so that a HID code can be looked up in a table.
« Last Edit: Mon, 22 April 2013, 17:06:47 by Soarer »

Offline suka

  • Posts: 52
Re: Cherry MX debounce times
« Reply #24 on: Mon, 22 April 2013, 19:53:54 »
When I developed my firmware I discovered this neat little debouncing routine that accomplishes nicely what has been discussed above:
It looks obscure, and I had real issues getting around the code myself when I tried adding some timing information, but it seems very performant and reliable.

Code: [Select]
for (uint8_t row = 0; row < ROWS; ++row) {
        activate(row);
        _delay_us(20); // Insert NOPs for synchronization
        data = read_col();
       
        i = kb_state[row] ^ (~data);                    // key changed ?
        ct0[row] = ~( ct0[row] & i );                   // reset or count ct0
        ct1[row] = ct0[row] ^ (ct1[row] & i);           // reset or count ct1
        i &= ct0[row] & ct1[row];                       // count until roll over ?
        kb_state[row] ^= i;                             // then toggle debounced state

        kb_press  [row] |=  kb_state[row] & i;          // 0->1: key press detect
        kb_release[row] |= ~kb_state[row] & i;          // 1->0: key press detect

        if( (kb_state[row] & REPEAT_MASK) == 0 ) {      // check repeat function
            rpt[row] = idle_count + REPEAT_START;       // start delay
        }
        if(  rpt[row] <= idle_count ) {
            rpt[row] = idle_count + REPEAT_NEXT;        // repeat delay
            kb_rpt[row] |= kb_state[row] & REPEAT_MASK;
        }

        // Now evaluate results
        uint8_t p,r,h;
        p=get_kb_press  (ALL_COLS_MASK, row);
        h=get_kb_rpt    (ALL_COLS_MASK, row);
        r=get_kb_release(ALL_COLS_MASK, row);
        rowData[row] = ((rowData[row]|(p|h)) & ~r);
    }


You can find it in context in scan_matrix() in keyboard_class.c - the timing stuff is still missing there, but I noticed key presses were registered after  2-3 bounces - the loop was accessed about once per 1ms. And the debug descriptor for the lazy output I chose will have slowed down the scanning rate, too.

All credits go to Peter Danneger, who presented the code  in German at bottom of page http://www.mikrocontroller.net/articles/Entprellung
and discussed it at http://www.mikrocontroller.net/topic/48465

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #25 on: Mon, 22 April 2013, 21:58:27 »
That is quite an obscure bit of code! So it's a counter approach, with the count split across the two bytes ct0 and ct1, and requires 3 scans of a changed state before it accepts the change. In other words, its debounce time is fixed at 3 scans. That part is quite neat and efficient. The press, release and repeat arrays don't seem so neat, and presumably also need a fair bit of code to decipher them later on. I guess that would depend on whether you really use all of the flexibility they provide!

I suspect that with those 8 arrays the poor AVR is reloading it's pointer register pairs quite a lot! :( A struct per row might help to optimise that.

The delay could be reduced (or possibly eliminated) by moving the activate() call to after the read...
Code: [Select]
    // In initialization code somewhere...
    // Needs to run only once since the scan loop does activate(0) on its last pass, setting it up for the next pass.
    activate(0);
   
    // In scan_matrix()...
    for (uint8_t row = 0; row < ROWS; ++row) {
        _delay_us(??); // can be much less than 20us, since the rest of the code in this loop takes some time
        data = read_col();
        activate(row + 1 == ROWS ? 0 : row + 1);
        // ...
« Last Edit: Mon, 22 April 2013, 22:00:38 by Soarer »

Offline hasu

  • Posts: 3471
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Cherry MX debounce times
« Reply #26 on: Tue, 23 April 2013, 09:33:05 »
I saw some web page calls this count method 'vertical count'. It is a very memory saving. With 2bits per key it can count 4 and maybe it can be extend to 3bit for 8 count?
Fortunately ATmega32u4 have plenty of SRAM and we will be able to even use 1byte per key to debounce.

Datasheet says port read synchronization needs 2 system clock, so two NOP should be enough before read. But I've never try that and I also use some us of delay just because I didn't know about port synchronizer when I came across wrong reading problem. I'll try two NOP instead of delay when I have time.

Offline hasu

  • Posts: 3471
  • Location: Tokyo, Japan
  • @tmk
    • tmk keyboard firmware project
Re: Cherry MX debounce times
« Reply #27 on: Tue, 23 April 2013, 09:53:39 »
Interesting method to detect edge. I know this is just a example and not your real code.
but this code does false detect? Short noise or glitch(0) is ignored correctly  but it appears to cause pseudo key_up event.
read_key() sequence: 111011111

This pseudo event may not harm but redundant.


Simplified version for a single key, looking for 5 readings to be the same...

Code: [Select]
uint8_t debounce_buffer = 0xFF;                            // history of last 8 readings

void scan(void)
{
    debounce_buffer = (debounce_buffer << 1) | read_key(); // shift and put latest reading in least significant bit
    uint8_t v = debounce_buffer & 0b00111111;              // mask with one more bit than the debounce 'time'
    if ( v == 0b100000 ) {                                 // check for change from unpressed (1) to pressed (0)
        key_down();                                        // produce down event
    } else if ( v == 0b011111 ) {                          // check for change from pressed (0) to unpressed (1)
        key_up();                                          // produce up event
    }
}


Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #28 on: Tue, 23 April 2013, 11:21:04 »
It is very close to my real code, which just adds a loop around it for each key, and uses match values that are calculated rather than hard-coded - the hard-coded 0b011111 is much easier to understand! Also, I split the scan and the debounce so that I scan all the keys, and then debounce all the keys (this helps the compiler optimise register usage, since less is going on in each loop).

Code: [Select]
uint8_t debounce_mask = 0xFF >> (7 - matrix_debounce_scans);
uint8_t debounce_up = debounce_mask >> 1;
uint8_t debounce_down = debounce_up ^ debounce_mask;
uint8_t num_keys = matrix_num_strobes * matrix_num_senses;
for ( uint8_t i = 0; i < num_keys; ++i ) {
uint8_t v = *pdebounce++;
v &= debounce_mask;
if ( v == debounce_down ) {
// ...

Yes, it might in certain circumstance call key_down() (or key_up()) when the key is already down (or up). I already had some code later in my processing that discards such events, so I did not worry about it. Also, I think they would be very rare in actual use, so not a performance issue.
« Last Edit: Tue, 23 April 2013, 11:24:18 by Soarer »

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #29 on: Wed, 24 April 2013, 19:56:12 »
Datasheet says port read synchronization needs 2 system clock, so two NOP should be enough before read. But I've never try that and I also use some us of delay just because I didn't know about port synchronizer when I came across wrong reading problem. I'll try two NOP instead of delay when I have time.

Actually, you might still need some delay for the signal to stabilize across the matrix. The relatively weak pull-up could mean that the input level takes some time to go high.

For example, say the strobe 1 / sense 1 key is pressed, and the strobe 2 / sense 1 key is unpressed. As you scan, first strobe 1 is low and sense 1 is low because of the pressed key, then you put strobe 1 high, set strobe 2 low, and read sense 1 again. It should read high, but if the input level takes time to go high, you could read it too soon and read low instead.

I haven't checked how slow the rise is on my keyboard... quite likely it only requires a small delay. My scan is using indirect port access, so it's relatively slow anyway and I haven't needed to add any delays - it has about a us or two between setting a strobe and reading a sense.

Offline nano

  • Posts: 8
  • Location: Germany
Re: Cherry MX debounce times
« Reply #30 on: Wed, 12 June 2013, 13:19:16 »
I just stumbled over this thread and I really like that advanced discussion.

Maybe a bit late but I'd like to mention another debounce method based on an integrator.
http://www.kennethkuhn.com/electronics/debounce.c describes those method precisely.
For me this looked like a good method and I implemented it my own (pre pre alpha) firmware.
So far it works well but I'm not sure if it is good, now that I have seen all those other smart methods.

What do you guys think?

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #31 on: Wed, 12 June 2013, 16:39:58 »
Hmm, not sure. It seems a bit suspect that it would take less time to react to a change after some noise, than it would after a clean transition. After some noise, it would randomly be sitting at 1 or 2, so maybe only one scan away from registering a transition! More scans might help, i.e. setting the maximum value higher, but I think I'm happier with a method that fully restarts looking for a transition after any noise.

Offline TD22057

  • Posts: 177
  • Location: Southern California
Re: Cherry MX debounce times
« Reply #32 on: Fri, 12 July 2013, 19:19:16 »
I did - at least I mentioned that debounce time should be extended by noise. ;)

In effect last n reads is a time interval, since the reads occur at regular intervals.

I'm using something like DebounceSwitch2() from this article, except using 8-bit values instead of 16-bit, which still allows up to 7 readings required to be the same before a key event is produced.

Simplified version for a single key, looking for 5 readings to be the same...

Code: [Select]
uint8_t debounce_buffer = 0xFF;                            // history of last 8 readings

void scan(void)
{
    debounce_buffer = (debounce_buffer << 1) | read_key(); // shift and put latest reading in least significant bit
    uint8_t v = debounce_buffer & 0b00111111;              // mask with one more bit than the debounce 'time'
    if ( v == 0b100000 ) {                                 // check for change from unpressed (1) to pressed (0)
        key_down();                                        // produce down event
    } else if ( v == 0b011111 ) {                          // check for change from pressed (0) to unpressed (1)
        key_up();                                          // produce up event
    }
}

Soarer - I'm learning about debouncing algorithms and really like this code.  Have you been using this in a real keyboard?  How is the instantaneous response working?

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #33 on: Fri, 12 July 2013, 20:06:12 »
Soarer - I'm learning about debouncing algorithms and really like this code.  Have you been using this in a real keyboard?  How is the instantaneous response working?

Yes, I've been using it on two keyboards - my custom mini using MX browns, and an IBM which has vintage Alps. Mostly in 'normal' mode with occasional tests of 'turbo' mode - both have been working well :D

It should also be easy to switch modes on the fly, by just changing the comparison values. That's something I intend to add to my firmware (as a macro command, probably).

Offline TD22057

  • Posts: 177
  • Location: Southern California
Re: Cherry MX debounce times
« Reply #34 on: Fri, 12 July 2013, 21:34:11 »
Yes, I've been using it on two keyboards - my custom mini using MX browns, and an IBM which has vintage Alps. Mostly in 'normal' mode with occasional tests of 'turbo' mode - both have been working well :D

It should also be easy to switch modes on the fly, by just changing the comparison values. That's something I intend to add to my firmware (as a macro command, probably).

I like it.  The macro could increment the number of comparison bits and then roll over at 7 (and maybe flash an LED to correspond to the current number of bits).

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #35 on: Sat, 13 July 2013, 07:08:31 »
I dunno - getting feedback is an issue. I was thinking of just setting it directly, so you'd set up as many macros as the number of different debounce modes you wanted to use. Apart from testing them out, most people would probably only use two in the end - 'safe' and 'fast'.

(In my firmware a macro is made up of 'macro commands', which can take parameters. So the command might end up being e.g. 'debounce 3 turbo', inserted into a macro that triggers on some combination of modifiers plus a final key).

Offline TD22057

  • Posts: 177
  • Location: Southern California
Re: Cherry MX debounce times
« Reply #36 on: Thu, 25 July 2013, 23:20:17 »
Hey Soarer - just wanted to say thanks for your help with this.  I was able to get that bit masking debounce algorithm working in my modified version of hasu's firmware.  I was a little worried since this algorithm is a count based algorithm and not a time based one.  The matrix scan code in my firmware is running at about 2400Hz (0.4 msec/scan) and the cherry spec says a 5ms debounce time.  Since that debounce code is using a 5 count to trigger a state change (only 2msec) I thought that might be too fast but when I wired up a single switch to my prototype breadboard, it seems to work just fine.   

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #37 on: Fri, 26 July 2013, 06:15:49 »
Cool! :cool:

You could up the count to 7 for a 2.8ms debounce with that scan rate. Most Cherry switches will bounce for much less than 5ms though, and seem to age well, so if you're prepared to swap out any that don't work with a shorter debounce then I don't see a problem with it.

Reducing the scan rate is another option. To my mind if you need a longer debounce time there isn't much benefit to scannning so fast!

Actually, I think the next step after 1000Hz is to synchronize the scan with the USB (still 1000Hz), and have it occur as close to the end of the USB frame as possible. That could reduce maximum lag by close to a ms, which I think is better than an unsynchronized 2000Hz scan would manage. It would have a less variable lag as well - just as, if not more, important than maximum lag IMO.

Let's see... time waiting for a scan (tw) is between 0 and 1/scanrate, time for scan routine itself (ts) is fixed at say 0.25ms, and time waiting for USB send (tu) for the unsynchronized case is between 0 and 1ms. So about 2.25ms at 1000Hz, or 1.75ms at 2000Hz, for the worst cases. Sync'ing with USB gives the tu part of the lag a constant value: scanning half way through the USB frame would make it 0.5ms. But since the scan happens within that time, ts is effectively reduced to 0 as well. At 1000Hz scanrate, the worst case is then 1.5ms. At 2000Hz scanrate, the worst case is still 1.5ms (since tu might again be 1ms)... :))

Overall though, I'm of the opinion that going beyond (unsynchronized) 1000Hz has distinctly diminishing returns!
« Last Edit: Fri, 26 July 2013, 06:19:50 by Soarer »

Offline TD22057

  • Posts: 177
  • Location: Southern California
Re: Cherry MX debounce times
« Reply #38 on: Fri, 26 July 2013, 10:31:07 »
Agreed.  From what I can tell, most commercial keyboards scan at ~125 Hz so 1000 Hz is still really fast.  Is there any actual way to sync w/ the usb frame?

I might build in a dynamic wait period.  Grab the timer at the first call and after the 25'th or 100'th and compute a wait time to get the scan rate around 1000 Hz.  That would make each bit in the debounce algorithm around 1ms which is kinda nice.  Still probably unnecessary since it seems to work fine as it is but for some reason I like fiddling with this kind of stuff...

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #39 on: Fri, 26 July 2013, 12:14:59 »
Yep, there's a Start-of-Frame interrupt (SOFI). Of course, it doesn't fire if the USB is suspended, so some other way to trigger scanning would be needed if you wanted to support wake-up.

I run a timer to fire an interrupt every ms. Some things can be done inside that interrupt, but e.g. scanning takes too long. I also maintain a 32-bit counter from that timer (msecs since boot, efffectively), which I use for coarser timings. So I scan from the main loop, and just wait for that counter to change to sync it up.

Offline Findecanor

  • Posts: 5035
  • Location: Koriko
Re: Cherry MX debounce times
« Reply #40 on: Sun, 20 October 2013, 10:20:44 »
Interesting discussion! I want to make my own keyboard firmware, so I looked at how some existing community firmwares do debouncing and then I searched and found this thread.

If I got it right...
* Hasu's tmk_firmware does debouncing on all keys as a block. Polling the matrix in an idle loop until it finds a possible key-press. Then it enters "debouncing mode", where it waits 1 ms between sampling. 5 ms debounce interval on both press and release.
* bpiphany's AVR-Keyboard does no debouncing on press, but 3.9 ms on release, per key. 0.48 ms between advancing the key's debouncing counter, but sampling continuously.
* The ErgoDox firmware does no debouncing at all, waiting 5 ms between samples.

I wonder if anyone has experienced spurious keys or chattering on the Phantom or ErgoDox...
I have a chattering switch on my Phantom/AVR-Keyboard, but that switch also feels different so it must have been damaged when I lubed it.
If I understand it right, you would need debouncing on at least key press or key release to avoid chattering, but you would need it on both if you also need to avoid EMI problems.

I think I'll do the sliding window method, with each generation a bitfield (array of bitfields, really).
Bitfields because using bitfields are the direct result datatypes from scanning and the deghosting routine (if I would need to use it) is simpler with bitfields that it would be otherwise. The complexity is instead laid on the debouncing routine. Vertical counters would use less memory - but they are more complex, especially if you want to allow different intervals for press and release, but then they wouldn't save that much memory anyway.
I am thinking of putting the scanning into a 1 KHz timer interrupt, but the debouncing (and deghosting) in the main loop. That way, if the main loop takes longer than 1 ms (because of various keyboard features :) ), then it would still debounce recent samples.
« Last Edit: Sun, 20 October 2013, 10:44:06 by Findecanor »

Offline Oobly

  • * Esteemed Elder
  • Posts: 3929
  • Location: Finland
Re: Cherry MX debounce times
« Reply #41 on: Sun, 20 October 2013, 12:01:40 »
I was thinking about what we really need when detecting keypress events and the way I see it is this: If we detect a state change, send the changed state and ignore that key for 5ms. Why wait for the state to stabilise before sending the event?
Buying more keycaps,
it really hacks my wallet,
but I must have them.

Offline TD22057

  • Posts: 177
  • Location: Southern California
Re: Cherry MX debounce times
« Reply #42 on: Sun, 20 October 2013, 17:25:45 »
I was thinking about what we really need when detecting keypress events and the way I see it is this: If we detect a state change, send the changed state and ignore that key for 5ms. Why wait for the state to stabilise before sending the event?

That's what the algorithm in this post: http://geekhack.org/index.php?topic=42385.msg863452#msg863452 allows.  Changing the key press and release bit comparisons allows you to send immediate press and then wait, or wait and then send.  Doesn't seem to matter much in my quick experiments but I haven't done any long term tests either.

Offline Findecanor

  • Posts: 5035
  • Location: Koriko
Re: Cherry MX debounce times
« Reply #43 on: Sun, 20 October 2013, 23:45:16 »
Why wait for the state to stabilise before sending the event?
To be able to ignore phantom key events caused by discharges of static electricity and electromagnetic interference.
I don't know if we really have that problem with Cherry MX switches on a PCB and/or in a plate, with the keyboard in an office or at home.

That's what the algorithm in this post: http://geekhack.org/index.php?topic=42385.msg863452#msg863452 allows.
No. In practice yes, but as I understand it, the code in the example is different from the Soarer's intent and production code.

Offline Oobly

  • * Esteemed Elder
  • Posts: 3929
  • Location: Finland
Re: Cherry MX debounce times
« Reply #44 on: Mon, 21 October 2013, 03:04:40 »
Why wait for the state to stabilise before sending the event?
To be able to ignore phantom key events caused by discharges of static electricity and electromagnetic interference.
I don't know if we really have that problem with Cherry MX switches on a PCB and/or in a plate, with the keyboard in an office or at home.

That's what the algorithm in this post: http://geekhack.org/index.php?topic=42385.msg863452#msg863452 allows.
No. In practice yes, but as I understand it, the code in the example is different from the Soarer's intent and production code.

Ah, okay. Thanks for the explanation.

I have done some testing with MX browns and a very simple firmware on my 48 key board. I set up an array of bytes for the debounce, which I shift right every scan, so I can have multiple keys with debounce state and can use up to 7 scans to debounce.

If the key I am scanning has an empty debounce byte, I check for state change. If the state has changed I change the key buffer and reset the debounce byte for that key. I have yet to time the scan loop, so I don't know how many ms it takes to loop, but putting a bit in the 7th place of the debounce byte it seems to work quite well. I have yet to test in a really noisy electrical environment, but I don't suspect there would be enough EMI around to get a signal wire up to the required value for detection by the AVR....

I did notice a double keypress occurs sometimes with the space key if pressed at a certain speed, so I may need to adjust the timing. I would like to try syncing the scan and sendkey to the USB polling interrupt. I think it's possible to have a worst case scenario of less than 1ms response time (assuming the scan takes less than 1ms and syncing is possible). Should be nice for marketing to gamers  ;)

Adding soarer's combined BIOS compatible packets and full NKRO packets we could possibly have the best of all worlds: BIOS compatibility, full NKRO, true 1000Hz polling and sub ms response.
Buying more keycaps,
it really hacks my wallet,
but I must have them.

Offline Soarer

  • * Moderator
  • Posts: 1918
  • Location: UK
Re: Cherry MX debounce times
« Reply #45 on: Mon, 21 October 2013, 22:03:52 »
That's what the algorithm in this post: http://geekhack.org/index.php?topic=42385.msg863452#msg863452 allows.
No. In practice yes, but as I understand it, the code in the example is different from the Soarer's intent and production code.

That is the essence of my code, for 'turbo' mode. Of course, it has the loop for each key removed, uses constants instead of variables for the mask and comparison values, and has a fake read_key() to show storing a new key state into the buffer (my scan functions read all keys into the buffers first)... but only to make it a clearer example :D

I like to debounce both press and release. I guess just doing one of those could work, but it seems right to have debounce delay the events symmetrically, so the duration the key is held down for is accurate.

Anyway, here's the real code - complicated by all the options, but the same at its core...

Code: [Select]
static uint8_t matrix_debounce(void)
{
    uint8_t num_events = 0;
    uint8_t turbo = matrix_debounce_turbo;
    uint8_t debounce_mask = 0xFF >> (7 - matrix_debounce_scans);
    uint8_t debounce_up = debounce_mask >> 1;
    uint8_t turbo_up = 1;
    if ( matrix_sense_polarity ) {
        debounce_up ^= debounce_mask;
        turbo_up ^= debounce_mask;
    }
    uint8_t debounce_down = debounce_up ^ debounce_mask;
    uint8_t turbo_down = turbo_up ^ debounce_mask;
    uint8_t* pdebounce = matrix_debounce_buffers;
    uint8_t num_keys = (matrix_num_muxstrobes + matrix_num_strobes) * matrix_num_senses + matrix_num_unstrobed;
    for ( uint8_t i = 0; i < num_keys; ++i ) {
        uint8_t v = *pdebounce++;
        if ( v != 0 && v != 0xFF ) {
            v &= debounce_mask;
            if ( v == debounce_down ) {
                matrix_key_down(i);
                ++num_events;
            } else if ( v == debounce_up ) {
                matrix_key_up(i);
                ++num_events;
            } else if ( turbo ) {
                if ( v == turbo_down ) {
                    matrix_key_down(i);
                    ++num_events;
                } else if ( v == turbo_up ) {
                    matrix_key_up(i);
                    ++num_events;
                }
            }
        }
    }
    return num_events;
}

Code: [Select]
uint8_t matrix_task(void)
{
    static uint8_t prev_tick = 0;
    uint8_t this_tick = read_ticks_lo();
    if ( (uint8_t)(this_tick - prev_tick) > matrix_scan_interval ) {
        prev_tick = this_tick;
        matrix_scan_muxstrobes();
        matrix_scan(matrix_debounce_buffers + matrix_num_muxstrobes * matrix_num_senses, matrix_strobe_mode, matrix_num_strobes, matrix_num_senses, matrix_strobes, matrix_senses, matrix_sense_delay);
        matrix_scan_unstrobed();
        return matrix_debounce();
    }
    return 0;
}
« Last Edit: Mon, 21 October 2013, 22:22:20 by Soarer »

Offline Matt3o

  • -[°_°]-
  • ** Robot Emeritus
  • Posts: 3547
  • Location: Italy
Re: Cherry MX debounce times
« Reply #46 on: Thu, 30 January 2014, 08:34:58 »
I believe gems like this post should be pinned forever on the forum :)

a big thank you to Soarer and Hasu!