Currently, I have a few buttons connected via an I2C shift register, hooked up to an MCU. Super simple, so not sure if it needs a schematic, but I can share one (and any other details) if needed. I'm flexible with what MCU I can use, suggestions welcome.

The MCU checks the button state every n ms. The problem is that if the user presses the push button too quickly, between the MCU loop function, the button press is not detected. One temporary solution is to increase the loop speed, but this isn't super energy efficient, and only diminishes the problem. Even with a faster loop, it's still possible to miss the pulse from the button press if pressed quickly enough, since the MCU has it's speed limitations, especially if it's doing some intense operation then the loop delay is increased unpredictably.

Thinking out loud, possibly rambling: I figured maybe I'd use a latch. I suspect there is a way of getting a good balance between hardware and firmware without over-complicating either. It seems to me that if I want to use a push button for a simple toggle switch, it's simpler to use a latch rather than a flipflop. If a flipflop is the best option, does it matter if D or JK? Or positive/native edge? Or, is there a way to avoid using a flipflop altogether and let the MCU do the work?

Edit: Seems the toggle can also be done with a timer IC.

Edit 2: Can't believe I forgot to mention this, but I'm using the PCF8574.

  • 3
    \$\begingroup\$ "Even with a faster loop, it's still possible to miss the pulse from the button press if pressed quickly enough, since the MCU has it's speed limitations, especially if it's doing some intense operation then the loop delay is increased unpredictably." - You can't press and release a button faster than about 10 times per second, so a loop frequency of 50 Hz should be plenty fast enough. Do you have 'intense' operations taking much longer than 20ms that cannot be interrupted? \$\endgroup\$ Jun 24, 2022 at 11:36
  • 4
    \$\begingroup\$ Generate an MCU interrupt. \$\endgroup\$
    – Andy aka
    Jun 24, 2022 at 12:10
  • 2
    \$\begingroup\$ Don't get the switches to generate the interrupt. Instead, run a timer interrupt to poll the switches. Simplifies the wiring. The interrupt rate would be between, say, 5 and 15 ms depending on what else you've got going on. Debounce the switches by only passing on a switch level when it's been the detected at the same level for three interrupts in succession. The well-written Interrupt Service Routine (ISR) will be very short so add tiny execution overhead and power consumption to the MCU unless it's already loaded to the absolute max. \$\endgroup\$
    – TonyM
    Jun 24, 2022 at 13:11
  • 1
    \$\begingroup\$ @TonyM Good point, that'll save adding extra circuitry and components. \$\endgroup\$ Jun 24, 2022 at 13:16
  • 1
    \$\begingroup\$ Well, the answer would depend on what other things the MCU does and how, and whether you can even use interrupts, as if there is a long I2C transaction going on to some other devive, you must wait it complete before reading the buttons and then it will be too late already. \$\endgroup\$
    – Justme
    Nov 9, 2022 at 14:56

1 Answer 1


Solution 1: Interrupt pin

The PCF8574 IO expander has an interrupt pin (INT), which is open-drain, and should be connected to a pull-up resistor. Connect this to a free pin on the MCU, and check it frequently using a high resolution timer. When the pin is pulled low by the IO expander, query the IO expander's state from the MCU.

If using a less sophisticated shift register (e.g. 74HC595), connect all buttons to the same interrupt pin on the MCU via a diode. Same as above, when this pin state is changed, poll the shift register.

Credit: Andy aka

Edit: But, this is only useful if power is very tight (thanks to @TonyM for pointing that out).

Solution 2: Interrupt timer

Another solution is to use an interrupt timer to poll the shift register more regularly, so that other running code in the main loop won't delay the switch detect.

Credit: TonyM

  • \$\begingroup\$ Those may be workarounds to a problem, but it is unclear what the problem even is so how to solve the actual problem. Generally speaking, UI buttons should not require interrupts, but in this case it might be used to start the read of shift register. But if you already are transferring data on I2C bus then you can't start the read but do it later. Third solution, make the main loop more efficient. How? If you post the code, you may get answers how to not have the problem to begin with. \$\endgroup\$
    – Justme
    Jul 7, 2022 at 16:35
  • \$\begingroup\$ Yes, I agree. The problem is not well understood, so finding a solution is going to be hard. I think the question can be improved by having a better understanding of the problem. I’ll do some more thinking about the problem. \$\endgroup\$ Jul 9, 2022 at 18:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.