Open Call: Send Us Your Debounce Code

If you’ve ever designed an embedded system with at least one button you’ve had to deal with button debouncing. This is also know as contact bounce, a phenomenon where a button press can be registered as multiple button presses if not handled correctly. One way to take care of this is with a hardware filter built from a resistor-capacitor setup, or by using a couple of NAND gates. We find that [Jack Ganssle] put together the most comprehensive and approachable look at contact bounce which you should read through if you want to learn more.

We’re interested in software solutions for debouncing buttons. This seems to be one of the most common forum questions but it can be hard to find answers in the form of reliable code examples. Do you have debounce code that you depend on in every application? Are you willing to share it with the world? We’d like to gather as many examples as possible and publish them in one-post-to-rule-them-all.

Send your debounce code to: debounce@hackaday.com

Here’s some guidelines to follow:

  • Please only include debounce code. Get rid of other unrelated functions/etc.
  • You should send C code. If you want to also send an assembly code version that’s fine, but it must be supplementary to the C code.
  • Please comment your code. This will help others understand and use it. You may be tempted to explain the code in your email but this info is best placed in the code comments
  • Cite your sources. If you adapted this code from someone else’s please include a note about that in the code comments.

As an example we’ve included one of our favorite sets of debounce code after the break. Please note how it follows the guidelines listed above.

/*--------------------------------------------------------------------------
10/13/2010: Button debounce code by Mike Szczys

based on "danni debounce" code by Peter Dannegger:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=189356#189356

This code detects and debounces button presses. It is tailored for use with
AVR microcontrollers but I've adapted it for other architectures easily and
successfully. It can be modified to use all eight bits on the same port
for up to eight buttons.

The interrupt service routine (ISR) at the bottom uses binary counter
variables (ct0 and ct1) to check the buttons once every 10ms until 40ms has
passed. If the button registeres the first and last times it reads it as
a keypress. There is no functionality in this code for detecting a held
button.
--------------------------------------------------------------------------*/

// F_CPU used by debounce to calculate 10ms interrupts
#define F_CPU 1200000

#include <avr/io.h>
#include <avr/interrupt.h>

//define pins used by buttons
#define KEY_DDR		DDRB
#define KEY_PORT	PORTB
#define KEY_PIN		PINB
#define KEY0		1	//Button on PB1
#define KEY1		2	//Button on PB2

//Debounce variables
unsigned char debounce_cnt = 0;
volatile unsigned char key_press;
unsigned char key_state;

/*--------------------------------------------------------------------------
  Prototypes
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask );
void init_timers(void);
void init_io(void);

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Used to read debounced button presses
  PARAMS: A keymask corresponding to the pin for the button you with to poll
  RETURNS: A keymask where any high bits represent a button press
--------------------------------------------------------------------------*/
unsigned char get_key_press( unsigned char key_mask )
{
  cli();			// read and clear atomic !
  key_mask &= key_press;	// read key(s)
  key_press ^= key_mask;	// clear key(s)
  sei();			// enable interrupts
  return key_mask;
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Sets and starts a system timer
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_timers(void)
{
  cli();			// read and clear atomic !
  //Timer0 for buttons
  TCCR0B |= 1<<CS02 | 1<<CS00;	//Divide by 1024
  TIMSK0 |= 1<<TOIE0;		//enable timer overflow interrupt
  sei();			// enable interrupts
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Initialize input and output registers
  PARAMS: NONE
  RETURNS: NONE
--------------------------------------------------------------------------*/
void init_io(void)
{
  //Setup Buttons
  KEY_DDR &= ~((1<<KEY0) | (1<<KEY1));	//Set pins as input
  KEY_PORT |= (1<<KEY0) | (1<<KEY1);	//enable pull-up resistors
}

/*--------------------------------------------------------------------------
  FUNC: 10/13/10 - Main
--------------------------------------------------------------------------*/
int main(void)
{
  init_timers();	//start the timer
  init_io();		//setup the buttons

  for (;;) //loop forever
  {
    if( get_key_press( 1<<KEY0 ))
    {
      //KEY0 press detected. Do something here
    }

    if (get_key_press( 1<<KEY1 ))
    {
      //KEY1 press detected. Do something here
    }
  }
}

//--------------------------------------------------------------------------
ISR(TIM0_OVF_vect)           // interrupt every 10ms
{
  static unsigned char ct0, ct1;
  unsigned char i;

  //TCNT0 is where TIMER0 starts counting. This calculates a value based on
  //the system clock speed that will cause the timer to reach an overflow
  //after exactly 10ms
  TCNT0 = (unsigned char)(signed short)-(((F_CPU / 1024) * .01) + 0.5);   // preload for 10ms interrupts

  i = key_state ^ ~KEY_PIN;    // key changed ?
  ct0 = ~( ct0 & i );          // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);       // reset or count ct1
  i &= ct0 & ct1;              // count until roll over ?
  key_state ^= i;              // then toggle debounced state
  key_press |= key_state & i;  // 0->1: key press detect
}

[Photo credit: Jack Ganssle]

75 thoughts on “Open Call: Send Us Your Debounce Code

  1. There’s other ways (besides the per-switch RC networks, etc.) to do this in hardware, esp with large numbers of switches to debounce.

    Consider that PLDs (or CPLDs) coupled with a single RC delay circuit allow a multi-channel debouncer to essentially “sample” the state of all the switches at some number of milliseconds (determined by said delay) after the first rising (or falling) edge on any of the switch lines.

    Sadly that PLD code won’t qualify for the contest :P

    I needed to do this in a system that would not tolerate the synchronous noise generated by an MCU, btw.

  2. If switch bounce is a major issue for your application, you should do it in hardware. It’s not hard to do in code, you just shouldn’t.

    The general idea is to poll it at some frequency, then only switch if all / a majority of the samples in a given interval match. Run one variable for lastValue and one for matchCount. For each new sample, compare to lastValue, then increment or reset matchCount. If matchCount passes your threshold, toggle whatever the switch is switching. Sample at whatever frequency makes sense for your switch and application, and set the matchCount threshold similarly.

  3. “you’ve had to deal with button debouncing. This is also know as contact bounce”

    button debouncing ≠ contact bounce. Contact bounce is the repetitive make/break problem and debouncing is how we mitigate this problem. Kinda the opposite things.

    I remember doing this on an FPGA in VHDL. Just started a 100mSec timer when a value changed. For a multi-button system wherein you may expect rapid pressing, the delay would need adjustment.

    that’s my time.

  4. The method I use is to sample the switches once every 50ms. Switch bounce normally lasts no more than 20ms so you will get 1 solid state every time with no bounce. You can’t get much simpler than that, and it works!

    Then just add code to look for edges, held buttons, double-clicks etc. to suit the application.

  5. There aren’t really that many correct ways to do software debouncing. The example above is basically what most coders will implement eventually (we’re going to ignore people who hardcode wait loops in their main routine). However, the above example is trading off flexibility and accuracy for the ability to debounce larger numbers of inputs without too much memory use. Since there’s a global 10ms timer and only two possible counts, the debounce timeout can actually range from 10ms to almost 20ms. You have to design for the shortest timeout and accept random longer delays. If you have switches that need longer debouncing, say 50ms max, then since there’s only two counts per switch you must change the interrupt timer to 50ms and accept possible timeouts of 50ms to 100ms. The only way around this is to use a much more fine grained interrupt, say 1ms or less, then implement a separate multi-bit counter for every pin. Obviously using a byte counter is easiest, but if your button count rises 32, 64, or more inputs then the memory impact starts to get significant. Processing all the counters also starts requiring a lot of CPU time, especially if you’re trying to do it once every millisecond. So I would say software debouncing is viable for contacts with short bounce times, and low pin counts…but for larger numbers of inputs, some other type of debouncing method should be explored.

  6. Debounching is one of the most basic fundamental concepts one should learn in the world of embedded electronics. I don’t disagree with explaining the subject thoroughly, but the N00bs that might actually use this code need to learn how to write it themselves. Otherwise they won’t fulfill their potential as engineers. This rings back to something about teaching and fishing…

    MAKE THE N00Bs PROGRAM FOR REALZ, ITS GOOD FOR THEM!

  7. Dave McDave: your method is not at all guaranteed to work. Your 50ms sample might take place during a low cycle of a contact bounce, and you might miss the button press entirely. Your method would only work if the input sample was capturing any event within the 50ms window, which is viable only if you are using external RC debouncing.

  8. Why write off hardware debouncing? One of the most reliable ways (and it’s really not terribly expensive…) is to use an SPDT instead of a single-contact switch with cross-linked NAND gates (aka: SR Flip-Flop). You can do something similar in software if you’re willing to use up 2 inputs (or do something creative) for your switch, but it will end up synchronized to the clock. See http://www.elexp.com/t_bounc.htm for a decent explanation.

  9. With intelligent interrupts, why bother?

    Interrupt on High to Low edge transition on Pin X. Button was pressed, run code, reset Interrupt, move on with your life.

    With an interrupt, no need to read the state of the button. As long as a transition from one state to the other has taken place, your golden.

  10. First off – I love hardware. But debouncing should NOT be done in hardware, no matter what you guys say. It increases cost and complexity when complexity for software is virtually free.

    As mentioned, the easiest way to do it is just call a ‘delay’ function using either timers or a dummy count loop.

    I don’t have specific code atm, but this is the best way I know of to do it on a PIC (not sure if you can do it for AVRs):

    Assumptions: Idle waiting is bad. Buttons are on an interrupt.

    When you get a port interrupt, you know the button was pressed. Disable interrupts on that port and set an interrupt ‘debounce’ timer. Go about with your business handling the interrupt and executing code. Eventually your debounce timer will break you out of your code (note that you can – and should! – make this a very low priority interrupt if you want real-time performance) and then re-enable port interrupts. If you want continuous button presses detected, you’ll have to force another interrupt if the button is still down.

  11. I just used a transition interrupt to reset a counter. Anyway, max freq < sample freq / 2. Above that, it should not respond. Test with wave generator if you will or have too much time to spend on trivial things. geez.

  12. @cde
    uhh, because the button might bounce and transition from high to low to high to low and run your ISR extra times? An ISR doesn’t necessarily fix anything. I don’t think you understood the problem…

  13. just read the input
    if the reading != from last reading then check what is the input state
    HIGH => it bounced
    LOW => it debounced

    set a flag as you want it, this way bounce or debounce is no biggie. (my way ;) )

  14. toto: that is not a reliable method. If you’re sampling the pins at regular intervals some period > than the expected bounce time you need to compare more than two of them to know whether you caught a bounce or a release.

  15. To quote Will Ferrell, “I feel like I’m taking crazy pills here!” Seriously, HAD is making a mountain out of a molehill with this. I generally use tactile NO or NC switches with a pullup resistor and check for state change (usually 1->0) on the input pin, either by polling or interrupt. Set a 10ms timer or hardcode a 10ms delay if you can afford it, and go about your business. tact switches will debounce in <1ms so 10ms is even overkill.

    Or, if you need to be able to hold the button down and not trigger anything until the button is released, simply do a 10ms debounce after a state transition is detected, and then use a while loop or interrupt to react on the opposite state change (release button) and do another debounce. Easy as pie.

  16. @fluidic:
    Yeah, I usually do it that way, too.
    With larger required values of “matchCount”, you also have some amount of RF-interference-robustness.

    However, for multiple buttons, the vertical counter implementation posted by “glagnar” and “nes” seems to be the way to go.

  17. This is one reason I like using bounceless switches, like Hall Effect, Capacitive or similar.

    Cherry/Microswitch had a GREAT keyboard a while back. 5VDC, two OC outputs, Hall Effect and a 50uS output (I think it was 50).

    But, ya gotta do what ya gotta do.

  18. “We’re interested in software solutions for debouncing buttons.”

    Umm why? I’d rather throw a resistor & cap at it while keeping precious processor power dedicated to the actual task. If one is to write a closed loop program, having it sit around timing button states seems like a waist and will severely hinder processing capabilities. I can’t tell you how many times I’ve come across situations where a microcontroller seems to have exhausted it’s processing abilities (this is usually when your average person opts for a more powerful processor) where the entire issue could be remedied with efficient code and/or an additional component or two. One could argue that adding components drives up the cost. To that, I say buying microcontrollers with more horsepower than you actually need to get the job done adds to the price as well. Last I checked, microcontrollers cost a tad more than a small cluster of SMD resistors & caps.

    I think the issue stems from the decay of modern code due to a lack of programmers who still approaching programming as an art form. These days, it seems that too many programmers/coders are only focused on getting the job done and don’t want to slow down to go low level and really think things through anymore. In fact, many don’t even want to write their own code. They take bits n’ pieces from other sources which, all too often, are scraps found on the net. It has gotten to the point where this approach is not only accepted, but also becoming the norm and the line between programming and script writing is blurred. The big issue comes into play when programmers fail to acknowledge the reality that these scraps of code are sometimes flawed. By the time this is realized, the code has already found its way to many other programs or have even become libraries which accelerate the spread enabling the faulty code to infect new programs like a rampant virus. Just look at the stock market flash crash. Scary stuff. Image when this sort of rubbish makes its way into hardware. In a world facing the steady rise of robotic population whose actions depend on quality programming, this sort of negligence becomes extra scary! When I develop the AI to control the warrior class robots for the robot uprising, I’m writing my code from scratch. :-) All of it!

    If you’re going to program, then really program. If you’re going to apply this to hardware, then don’t be afraid to actually add some hardware. And, if you’re going to write a long comment on hack-a-day, grammar check is optional. I’m sure I’ll be hearing about it. ;)

    Now, I’ll say this: I have no formal educations with electronics/programming and I don’t do either as my day job. So, if someone wants to explain why I seems to be the only guy who thinks this way, let me know the reasons and the errors of my way. On the other hand, if you’re with me, can I get a “hell yeah!” or “amen!” or whatever you’re in the mood to exclaim.

  19. ¿Would it be useful that i posted a simple AVR hardcoded debounce code?

    It was programmed in my early days with AVR, I began with assembler but today I code everything in C.

  20. It is very application specific. I did some LCD / Mp3 work a few years ago. Skip to next song didnt need to be debounced because the code took so long to load the next file off the FS. It depends on the process time of the action.

  21. Specific code isn’t needed, just knowledge of the proper way to debounce a button.

    Mr. Syczys said, “One way to take care of this is with a hardware filter built from a resistor-capacitor setup” (better known as an RC integrator). But that’s only partially correct. It’s also necessary to convert the analog output of that filter to a digital signal through a Schmitt trigger. MCUs typically have these built-in on their digital inputs, so a separate one isn’t needed; but you *must* acknowledge that it exists if you’re going to emulate a proper debounce circuit in software.

    So as an example, to emulate a 10ms hardware debounce, just execute the following pseudocode every 1ms:

    1) If RawInput is pressed, then increment Counter by one, up to a max of ten.
    2) If RawInput is not pressed, then decrement Counter by one, down to a min of zero.
    3) If Counter is ten, then set DebouncedInput to pressed.
    4) If Counter is zero, then set DebouncedInput to not pressed.
    5) If Counter is any other value, then leave DebouncedInput at its previous state.
    6) Repeat for as many additional inputs as you need.

    1-2 implement the integrator, 3-5 implement the Schmitt trigger.

    Any reasonably competent person should be able to understand and correctly convert this pseudocode to actual code, for their target hardware and language of choice, in just a minute or two. Anyone who can’t should work it out on their own, rather than relying on a trivial canned code example, because they need the practice!

  22. Chris.

    I usually use an interrupt to increment the counter, but otherwise your example it what I use.

    usually debounce from most hardware(buttons) lasts about 7-23 ms with 14 being close to average. (this number is based solely on my own observations over the years, no testing on animals (or people other than myself) was performed.

    So an extra step
    0)Interrupt is called by MCU when pin pulled low(example)

  23. My numbers are for a simple contact switch and the time it takes for it to settle to high or low state on a 5 volt high. No hardware other than a pull -up resistor. Better switches will have lower denounce times, but as most people hold the contact way way longer than 10ms the code just works better if you count a little past. Again, this is from real-world practical experience.

  24. 1. I am disappointed in HAD for requesting C code. They should have requested psuedo code instead.

    2. My debouncing method on mcu’s with system clocks is as follows:

    BOUNCE_DURATION = 30 milliseconds

    if button is pushed
    {
    	static prev_time = 0
    	
       if(currentTime() - prev_time &gt;= BOUNCE_DURATION)
       {
    		do stuff
    		prev_time = currentTime()
       }
    
    }
  25. I would normally agree with everyone who says this is being blown out of proportion, but from reading the comments its clear that some people don’t understand debouncing properly.

    cde says to just have an interrupt “and move on with your life”. He is missing though that the whole point of debouncing isn’t just how to read the button, but dealing with the fact that mechanical buttons almost always “bounce” and actuate multiple times. If you have it in a fast interrupt, it will fire more than once with every press!

    toto says to just compare it to what it was and see if it changed, but again, you’ll just end up with it actuating multiple times if it bounces.

    Then others say do it in hardware, and others say do it in software. But really, there isn’t a best answer to that, though “hardware” is the more reliable option. Software is “free” until you run out of overhead, then its very expensive (paying a coder to do weird workarounds, or selecting a faster processor). Putting an RC filter on a button will pretty much never fail, and you don’t have to pay your programmer to deal with it. It may not matter in some applications, but if you’re runnings a single threaded processor and doing something complex, you might not want to sit and wait for 10 or 20 milliseconds to see if the button calms down.

    Hardware, then, seems like a much safer option. It adds two components per button, but does not require any additional thought, and it much less likely to get screwed up. With all the crazy ideas people have here on software debounce, it’s clear that there are plenty of coders who will screw it up and then waste time trying to fix it. Best then to do hardware and not have to worry about it, unless you have a lot of buttons and/or have plenty of processor idle time.
    -Taylor

  26. So … okay, I’ve never debounced a button. But I’m seeing a lot of people talking about solutions, without actually defining the problem, and I think I’m seeing a lot of people making incompatible assumptions about what exactly the problem is.

    Before you make a claim about what the right way to debounce a switch is, you need to know: (1) How long the bounce event lasts, (2) How much delay between button-press and software-response you can tolerate, and (3) What’s the minimum length of button-press-and-release cycle (or release-and-press cycle) you need to capture.

    For instance, if a bounce lasts 10ms, and you only care about a 50ms response time and don’t care if you miss press-and-releases shorter than 50ms, then debouncing is trivial — just sample every 25ms. If you sample in the middle of a bounce, it’ll either match the signal before the press or the signal after, and the worst case is it matches the one before and so you don’t pick up on the keypress until the next sample — which is still within the 50ms window.

  27. This is a very good new (and overdue?) direction for HaD, and this “simple” problem has already opened a really large and interesting can of worms. You name the task and some programmer will think of (n+1)’th way of doing it.

    But the code imperialism!?!?. “Must be in C”, even if already in assembler? First up, it’s good for the soul to follow coding in something you don’t know; secondly, apart from giving assembler for a stated chip, what is more generalised than pseudo-code or a Warnier/Orr diagram? As someone who has written major projects in 27 different languages (not including any dialect of C), I say man up and grow a pair.

    I think you should offer a prize for the first working debounce routine submitted in Lisp. :))

  28. I have a sweet bit of button code i wrote that will read 4 buttons (can be expanded on easily) and will let you know when a button is pressed, held or released. The ‘held’ flag will re-set itself every 100ms (can set that to be anything) and that 100ms will decrease the longer you hold the button.

    It’s pretty cool and works really well. Also recuires nothing more then a tick timer to run in your interrupt to keep time and the button code can be called as often or as fast as you want and even be irregular.

    I wrote it for work so i’m not sure if i can post this code here or not and my boss is away for the week but i’ll ask him and if i can i will post it up for you lot to have a look at it.

    It’s very large code btw, its about 1K of flash (and very little RAM) but i have loads of flash and i needed the functionality of this code for my project.

  29. This is a really good article. I wrote my own debounce code in C from scratch a few years ago based on looking at the bounce behavior on a scope just like the guy in the article did. Since my code was designed for a specific switch a value of around 25ms worked great. I tested the code by incrementing a value on a simple LCD, seemed to work great.

    Hardware is a little more fool-proof if designed correctly, but sometimes you just don’t have the space on a board for low-pass filtering on every button.

  30. my approach to debounce has changed over the years.

    lately I use a simple method in assembly which goes something like this:

    1. Test switches, comparing them to the last known state.
    2. If any switch doesn’t match the last state stored in the ‘last_state’ variable (see step 5), then set ‘test_switches’ flag to test again later.
    3. Do some stuff. (execute some subroutines that won’t take forever to run). A delay loop can be substituted, but why not do something useful while waiting for the debounce.
    4. If the ‘test_switches’ flag is set, then retest the switches. If any of them don’t match ‘last_state’, then execute the function associated with the specific switch state.
    5. Store the new switch values in last_state variable.

    If no switches have changed, then only a few cycles will be used on the switch debounce code. The point is to minimize the code needed as well as not have to resort to using timers or delay loops. A timer is a valuable resource which shouldn’t be wasted on switch debounce, and delay loops are wasteful.

  31. At first I didn’t know what debounce meant, then I realized it was just different terminologies, or at least I think. I have always referred to this as buffered input. I handle it with a button state and previous state, or a byte with a bitmask can hand four button states and previous states with a simple bitcheck.

  32. RC on digital inputs isn’t a great solution to all those that advocate it. As the button is pressed, ignoring any bouncing, the cap charges causing a slowing down of the rise time on the digital input. This can lead to a metastable state, with the digital input state of the pin switching between 0 and 1 unpredictably.

  33. I don’t mind people thinking up clever code, but I do agree with those saying it should be done in hardware, it’s dirtcheap and very simple and avoids a lot of hassle and possible errors.
    Incidentally don’t they sell switches with inbuilt debounce circuitry? If they don’t that really surprises me I have to say.

  34. One that works like a marvel and doesn’t burn processing time with timers or delays. Just poll with something between 10 and 100 Hz or use an interrupt. (written in pseudo code, adapt for your system)

    int button_push_handled;
    int push;

    If ( button_pushed ) {
    if ( button_push_handled == 0 )
    push = 1;
    button_push_handled = 1;
    }
    else {
    button_push_handled = 0;
    push = 0;
    }

  35. //added some extra parentheses for clarity. Space indents doen’t seem to work in the comments.

    int button_push_handled;
    int push;

    If ( button_pushed ) {
    if ( button_push_handled == 0 ) {
    push = 1;
    }
    button_push_handled = 1;
    }
    else {
    button_push_handled = 0;
    push = 0;
    }

  36. @Superfungus and @Taylor Once the interrupt has been triggered, it won’t trigger again until you have cleared the interrupt flag, and enable it again. Atleast with the msp430s I am working with. So interrupt signals button press, do whatever, and at a reasonable time, reenable the interrupt. Sure, it doesn’t work for situations where you want multiple presses in a short period, but for simple cases, its the easiest way.

  37. So simple, I tested this on many projects.

    Basically read the switch state, have a short delay (milliseconds) then check if switch is still pressed.

    The delay period will be different for every type of switch. Simply try different values with your device untill the switch action feels correct.

    I have used this same code with a multiplexed door lock keypad and also a simple joystick interface. The result is a natural feeling switch action with no bounce.

    Good luck.

    // *************************************************//
    int GetSwitchState(void)
    {
    int switchState = 0;

    IF (PinIsHigh())
    {
    DelayMs(100);

    If (PinIsHigh())
    {
    switchState = 1;
    }
    }

    return switchState;
    }
    // *************************************************//

  38. This is an excellent example of HAD shining a bit.

    I don’t even get involved in switch debounce, but it’s something I’ve heard about in the past.

    Reading the article and comments has been enlightening and made me read up a little more on the subject.

    Consider me a little enlightened on the subject.

    Thank you all! :)

  39. @bigmechpilot
    @macegr and
    @anyone else who thinks that every application is precious.

    The reality is for many applications processing power is not at all even remotely precious. If you have a precious timing sensitive application which can’t handle the processor not talking to it for 20ms then yes maybe you should implement a hardware solution, or maybe just run that time sensitive aspect of an interrupt service routine with a higher priority than your debounce option.

    The fact is that at the interrupt simply disabling that interrupt and then locking the processor in a loop for 20ms before re-enabling that interrupt is not only perfectly valid for a very large number of applications, but it is also possibly the shortest (inefficient yes) possible way of writing debouncing code and makes use of no additional fancy timers or polling which may very well be needed for other parts of your program.

    Regardless of the people who insist that it’s “wrong” it works incredibly well in most applications that would be considered by your typical HAD audience and takes 3 lines of code.

    cli();
    delay_ms(30);
    sei();

    – Garbz

Leave a Reply to martinCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.