Debouncing FSM

The foundation of the debouncing FSM relies on discerning between stable states and fluctuating states. We know that the button can either be pressed or released. These are the stable states. Additionally, we know that the button input can bounce when transitioning from one state to another. Our goal is to monitor these transition states and see if the inputs are rapidly fluctuating or not. If they aren’t fluctuating, then we can safely change to the desired state. If the inputs are fluctuating, then we should go back to the original stable state to avoid an error. Since the transition from pressed to released can have a different outcome from the transition from released to pressed, we should make these transition states distinct from each other. As such, the four states of the FSM are stable pressed (Stable_P), transition from pressed to released (Tran_PtoR), stable released (Stable_R), and transition from released to pressed (Tran_PtoR). 

Next, we need to determine the inputs and outputs of the FSM. Let’s define an input for the FSM and call it the “raw button.” This is the input that the microcontroller sees, like in the scope capture. The raw button input can be affected by bouncing, and therefore this input can fluctuate. Let’s also define an FSM output and call it the “debounced button.” This signal is the what we want the microcontroller to use as the button reading; it is the clean, filtered, debounced button. We don’t want the debounced button value to change during a transition until the bouncing subsides. Here is how it works: if the button is currently released, the debounced button should also indicate “released.” Suppose we then push the button and bouncing occurs. While the raw button input goes back and forth between “pushed” and “released,” the debounced button status should stay at “released” until the raw button input stabilizes at “pushed,” at which point the debounced button signal becomes “pushed.” 

The question now is how to determine when bouncing is no longer occurring. For this, we need to observe the raw button for a short period of time. If the input does not change within, say, 100 milliseconds, then it is safe to transition to the desired stable state. If it does change within that time, then we should return to the previous stable state. Knowing this, we can make usage of a one-shot timer that expires after 100 milliseconds have passed. Since the FSM transitions depend on the status of this timer, we need to define a new input for the FSM. We call this new input, “timer status,” which is true if the timer has expired and false if the timer has not yet expired. We also need to know when to start the timer. We start the timer whenever the FSM moves from a stable state (Stable_R or Stable_P) to a transitional state (Tran_RtoP or Tran_PtoR). So, the signal that controls the starting of the timer is the output of the FSM. We call this new output, “start timer,” which is true if we need to start the timer and false if we do not need to start the timer. In summary, the black-box diagram of the FSM looks as follows:

With our inputs and outputs defined, now, we need to draw up the transitions between the states. There are various ways to achieve the same debouncing outcome with small differences. In this example, we implement an FSM that accomplishes the following: A reading opposite of the stable state (e.g., pressed raw button input in the stable released state) moves the FSM to a transient state. If we observe that opposing input until the timer expires, the FSM moves to the new opposite stable state. Otherwise, it moves back to the original stable state. 

Here is the detailed explanation: If the button is currently in the stabilized pressed state (Stable_P), it should remain in this state if the raw button input does not change. In other words, if the raw button input remains as “pressed” while in this state, then the debounced button output should also stay as “pressed” and the state should still be Stable_P. Since we do not need to check for any bouncing since this input is stable, we have no use for the timers. Thus, we do not need to start the timer, nor do we care about its expiration.

When the raw button input becomes “released,” however, we need to check for any bouncing. We are now transitioning from a stabilized pressed state to a possible released state. Thus we should be moving to the Tran_PtoR state. We don’t want to change our debounced button output yet, so it should remain as “pressed.” Since we are now checking for bouncing, we need to start the timer. At this time, we do not care about the timer being expired. 

If the raw button input remains as "released" and the timer does not expire, we should remain in the Tran_PtoR state. We should not change the debounced button output and should not restart the timer. However, if the raw button input ever changes to "pressed," before the timer has expired, then we don't have a 100-ms streak of clean input yet. So, we transition back to the Stable_P state, keeping the debounced button output as "pressed." We should not start the timer, because, in the stable state, we do not care about the timer.

But if we are in the Tran_PtoR state, the raw button input is still "released," and the timer has expired, we can safely say that the new raw button is stable. So we can now transition to the Stable_R state. We have the option of making the debounced button input "released" now without consequence. But there is no harm if we keep it as "pressed" either, as the debounced button input will be changed to the appropriate value upon reaching this half of the FSM anyway.

Note that "D" in the below FSM means "don't care."

This particular FSM is symmetric by nature. The same process we used to go from Stable_P to Stable_R can be applied to going from Stable_R to Stable_P, but this time using the Tran_RtoP state as a middleman and with the opposite button inputs. 

The below figure shows the status of the raw button and debounced button with respect to time for a button that starts at stable released, it is pressed once and then goes back to released again. The FSM has also been color-coded to match