Although the data might be skewed one way or the other, I wouldn't trust either direction to be completely bounce-free.
When I first started with embedded software, I would use timers for debouncing - ignore further transitions within x time of an accepted one - but as my I/O count went up, it quickly became unwieldy. Now
Now I scan the entire user interface at a low enough rate that the switch-bounce no longer matters but still fast enough to appear responsive.** This includes both buttons and LED's. In
In other words, all of my user interface code is in one place and runs periodically at that rate. Your low-level drivers (PWM, shift registers, etc.) can run faster than that if necessary or convenient, but there's no point in refreshing their values any faster.
** The reason this works is if I read it mid-bounce:
- I can read the same as the previous sample, meaning no change and I'll catch it next time.
- I can read the same as the next sample, meaning that I caught it this time.
Either way, I get a clean transition in software simply because I sample it slower than the bounce time. But it's still faster than the user's perception of "instantaneous", so one sample period worth of jitter is okay.