Bird
Raised Fist0
Embedded Cprogramming~30 mins

Debouncing as a state machine in Embedded C - Mini Project: Build & Apply

Choose your learning style10 modes available
Debouncing as a State Machine
📖 Scenario: You are working on a small embedded system with a button input. When the button is pressed, the signal can be noisy and cause multiple unwanted triggers. To fix this, you will create a debouncing mechanism using a state machine in C.
🎯 Goal: Build a simple state machine in C to debounce a button input. The program will read a noisy button signal and output a clean, stable button press state.
📋 What You'll Learn
Create an enum for the debounce states
Create a variable to hold the current state
Write a function to update the state machine based on the button input
Print the debounced button state
💡 Why This Matters
🌍 Real World
Debouncing is used in embedded systems to ensure that noisy mechanical button presses do not cause multiple unwanted signals.
💼 Career
Understanding state machines and debouncing is important for embedded software engineers working on hardware interfaces and real-time systems.
Progress0 / 4 steps
1
Create debounce states enum
Create an enum called DebounceState with these exact states: BUTTON_UP, BUTTON_FALLING, BUTTON_DOWN, BUTTON_RISING.
Embedded C
Hint

Use typedef enum { ... } DebounceState; to define the states.

2
Create current state variable
Create a variable called currentState of type DebounceState and initialize it to BUTTON_UP.
Embedded C
Hint

Declare currentState as DebounceState and set it to BUTTON_UP.

3
Write debounce state machine function
Write a function called debounceUpdate that takes an int buttonInput (0 or 1) and updates currentState using a switch statement on currentState. Implement these transitions:

- BUTTON_UP: if buttonInput == 0, stay BUTTON_UP, else go to BUTTON_FALLING
- BUTTON_FALLING: if buttonInput == 1, go to BUTTON_DOWN, else go to BUTTON_UP
- BUTTON_DOWN: if buttonInput == 1, stay BUTTON_DOWN, else go to BUTTON_RISING
- BUTTON_RISING: if buttonInput == 0, go to BUTTON_UP, else go to BUTTON_DOWN
Embedded C
Hint

Use a switch on currentState and update it based on buttonInput as described.

4
Print debounced button state
Write a main function that calls debounceUpdate with these button inputs in order: 0, 1, 1, 0, 0. After each call, print the current debounced state as an integer using printf. Use printf("%d\n", currentState);.
Embedded C
Hint

Call debounceUpdate with each input and print currentState after each call.

Practice

(1/5)
1.

What is the main purpose of using debouncing in embedded C button handling?

easy
A. To avoid false triggers caused by mechanical noise
B. To speed up button press detection
C. To reduce power consumption
D. To increase button press sensitivity

Solution

  1. Step 1: Understand mechanical noise in buttons

    Mechanical buttons can create multiple rapid on/off signals when pressed or released, called noise.
  2. Step 2: Purpose of debouncing

    Debouncing filters out these rapid changes to detect a single clean press or release.
  3. Final Answer:

    To avoid false triggers caused by mechanical noise -> Option A
  4. Quick Check:

    Debouncing = Avoid false triggers [OK]
Hint: Debouncing stops noisy signals from causing multiple presses [OK]
Common Mistakes:
  • Thinking debouncing speeds up detection
  • Confusing debouncing with power saving
  • Assuming it increases sensitivity
2.

Which of the following is the correct way to declare a state variable for a debounce state machine in embedded C?

?
easy
A. char state = 0;
B. int state = "IDLE";
C. enum ButtonState {IDLE, DEBOUNCE, PRESSED}; ButtonState state = IDLE;
D. bool state = false;

Solution

  1. Step 1: Identify proper state variable type

    Using an enum clearly names states and improves code readability.
  2. Step 2: Check syntax correctness

    enum ButtonState {IDLE, DEBOUNCE, PRESSED}; ButtonState state = IDLE; correctly declares an enum and initializes the state variable.
  3. Final Answer:

    enum ButtonState {IDLE, DEBOUNCE, PRESSED}; ButtonState state = IDLE; -> Option C
  4. Quick Check:

    Enum for states = enum ButtonState {IDLE, DEBOUNCE, PRESSED}; ButtonState state = IDLE; [OK]
Hint: Use enum for clear, readable state variables [OK]
Common Mistakes:
  • Assigning string to int variable
  • Using bool for multiple states
  • Using char without clear meaning
3.

Given this snippet of a debounce state machine, what will be the value of state after 3 calls if the button is pressed and held?

enum State {IDLE, DEBOUNCE, PRESSED};
State state = IDLE;
int timer = 0;

void update(bool buttonPressed) {
  switch(state) {
    case IDLE:
      if(buttonPressed) {
        state = DEBOUNCE;
        timer = 2;
      }
      break;
    case DEBOUNCE:
      if(timer > 0) timer--;
      else state = PRESSED;
      break;
    case PRESSED:
      if(!buttonPressed) state = IDLE;
      break;
  }
}

// Calls:
update(true);
update(true);
update(true);
medium
A. PRESSED
B. Undefined
C. IDLE
D. DEBOUNCE

Solution

  1. Step 1: Trace state changes through calls

    First call: state IDLE -> DEBOUNCE, timer=2. Second call: DEBOUNCE, timer=2>0, timer-- to 1, still DEBOUNCE. Third call: DEBOUNCE, timer=1>0, timer-- to 0, still DEBOUNCE (else not executed).
  2. Step 2: Confirm final state after 3 calls

    Button is still pressed, state=DEBOUNCE (timer=0 now, changes to PRESSED on next call).
  3. Final Answer:

    DEBOUNCE -> Option D
  4. Quick Check:

    Timer counts down, state changes only if timer==0 at start of DEBOUNCE [OK]
Hint: Count timer ticks before state changes to PRESSED [OK]
Common Mistakes:
  • Thinking PRESSED after exactly 3 calls
  • Not decrementing timer
  • Resetting state incorrectly
4.

Identify the bug in this debounce state machine code snippet:

enum State {IDLE, DEBOUNCE, PRESSED};
State state = IDLE;
int timer = 0;

void update(bool buttonPressed) {
  switch(state) {
    case IDLE:
      if(buttonPressed) {
        state = DEBOUNCE;
        timer = 3;
      }
      break;
    case DEBOUNCE:
      if(timer == 0) state = PRESSED;
      else timer--;
      break;
    case PRESSED:
      if(!buttonPressed) state = IDLE;
      break;
  }
}

What is the problem?

medium
A. State never changes from DEBOUNCE
B. Timer decrements after checking zero, causing one extra cycle
C. Timer is never initialized
D. Button press is ignored in PRESSED state

Solution

  1. Step 1: Analyze timer decrement logic

    The code checks if timer == 0 before decrementing, so timer reaches 0 but state changes only next call.
  2. Step 2: Identify effect on debounce timing

    This causes the debounce to last one extra cycle longer than intended.
  3. Final Answer:

    Timer decrements after checking zero, causing one extra cycle -> Option B
  4. Quick Check:

    Check timer before decrement causes delay [OK]
Hint: Decrement timer before zero check to fix timing [OK]
Common Mistakes:
  • Thinking state never changes
  • Ignoring timer initialization
  • Assuming button ignored in PRESSED
5.

You want to implement a debounce state machine that only confirms a button press if the button is held for at least 5 cycles. Which approach below correctly implements this behavior?

enum State {IDLE, DEBOUNCE, PRESSED};
State state = IDLE;
int timer = 0;

void update(bool buttonPressed) {
  switch(state) {
    case IDLE:
      if(buttonPressed) {
        state = DEBOUNCE;
        timer = 5;
      }
      break;
    case DEBOUNCE:
      if(buttonPressed) {
        if(timer > 0) timer--;
        else state = PRESSED;
      } else {
        state = IDLE;
      }
      break;
    case PRESSED:
      if(!buttonPressed) state = IDLE;
      break;
  }
}

What happens if the button is pressed for 3 cycles then released?

hard
A. State returns to IDLE without reaching PRESSED
B. State changes to PRESSED after 3 cycles
C. State stays in DEBOUNCE indefinitely
D. State immediately changes to PRESSED on press

Solution

  1. Step 1: Trace timer decrement with button held 3 cycles

    Timer starts at 5, decrements each cycle while buttonPressed is true, so after 3 cycles timer = 2.
  2. Step 2: Button released before timer reaches zero

    On release, state immediately returns to IDLE without reaching PRESSED.
  3. Final Answer:

    State returns to IDLE without reaching PRESSED -> Option A
  4. Quick Check:

    Hold less than 5 cycles resets state [OK]
Hint: Hold button full timer cycles to reach PRESSED [OK]
Common Mistakes:
  • Assuming PRESSED reached early
  • Thinking DEBOUNCE lasts indefinitely
  • Believing immediate PRESSED on press
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard