I just thought about a more advanced method of button debouncing on a Microcontroller. The method is made to generate buttonsPressed and finally also buttonsReleased Events, which can then be handled somewhere else (e.g. main loop).
Let's assume the Microcontroller has a port of which all buttons are connected to, the port is interrupt-capable with one interrupt service routine for a whole port with 8 pins, though the pins can be activated independently if they should fire an interrupt or not.
In addition to that, it has a Timer that gets started periodically for different tasks: Start an ADC conversion, blink a LED, etc. It will also be used for debouncing the buttons.
Let's assume Port 1 interrupt enable mask: P1IE. Port 1 interrupt flagged mask: P1IFG. Let's also say P1IFG also gets set if the interrupts for it are not enabled (which is good). Take the Texas Instruments MSP430 as an example for this behavior.
We assume that we want to set (button-)event flags in the interrupt service routine(ISR), so it can be short. Then solve the (button-)events in a big main loop, and reset the event flag there, when it got resolved.
We assume we want to wait a time of "TIME_LIMIT" timer interrupts since the last press of a button, before we can accept new events from it. - But the code could also be easily extended to get button repetition events
When the port interrupt gets fired, in this order:
When the periodic timer interrupt gets fired:
Code could look like this:
// in port interrupt routine: if (button1Time == 0 && P1IFG & 0x01) button1Event = 1; if (button2Time == 0 && P1IFG & 0x02) button2Event = 1; ... more buttons ... if (P1IFG & 0x01) { button1Time = TIME_LIMIT; } ... more buttons ... P1IE &= ~P1IFG; P1IFG = 0; // in periodic timer routine: some_other_stuff(); button1Time--; ... more buttons ... if (button1Time == 0) P1IE |= 0x01; ... more buttons ... // in main loop: if (button1) { dosomething(); button1 = 0; } if (button2) { dosomethingelse(); button2 = 0; } ... more buttons ...
Now we replace the different event variables with only one, in which every bit is an event for that certain button.
Therefore we also want to implement the counters different, in which we use a "shifting array" in which the activated buttons get shifted through
Variables got defined globally like this:
unsigned char buttonsPressed[TIME_LIMIT] = {0...0}; // initialized somewhere else to only zeroes unsigned char buttonsEvent = 0; unsigned char buttonsPressedMask = 0;
When the port interrupt gets fired, in this order:
buttonsEvent |= P1IFG & ~(buttonsPressedMask);
buttonsPressedMask |= P1IFG; P1IE &= ~(buttonsPressedMask);
buttonsPressed[0] |= P1IFG;
P1IFG = 0;
When the periodic timer interrupt gets fired:
/* Reset other bits in Mask, as we have to recalculate it, * and already set it to last element of buttonsPressed due * to the nature of the following for-loop */ buttonsPressedMask = buttonsPressed[0]; for (i=TIME_LIMIT-1;i!=0;i--) { /* shift one further */ buttonsPressed[i] = buttonsPressed[i-1]; /* aggregate all button presses that happened over the last TIME_LIMIT * timer occurences in the buttonsPressedMask */ buttonsPressedMask |= buttonsPressed[i]; } /* Enable all interrupts that are not masked */ P1IE = ~(buttonsPressedMask); /* Reset first element as we have shifted it further */ buttonsPressed[0] = 0;
If we want it even more special we can add a check in the timer interrupt that compares the old with a new bitmask, and posts a buttonReleasedEvent for the differing buttons of the new and old masks, generating an event for a button that got pressed and then released for a certain time.
The new timer interrupt would look like this then:
/* Reset other bits in Mask, as we have to recalculate it, * and already set it to last element of buttonsPressed due * to the nature of the following for-loop */ buttonsMaskOld = buttonsPressedMask; buttonsPressedMask = buttonsPressed[0]; for (i=TIME_LIMIT-1;i!=0;i--) { /* shift one further */ buttonsPressed[i] = buttonsPressed[i-1]; /* aggregate all button presses that happened over the last TIME_LIMIT * timer occurences in the buttonsPressedMask */ buttonsPressedMask |= buttonsPressed[i]; } /* Enable all interrupts that are not masked */ P1IE = ~(buttonsPressedMask); /* Reset first element as we have shifted it further */ buttonsPressed[0] = 0; /* Set buttonsReleasedEvent for buttons which became unpressed */ buttonsReleasedEvent = buttonsMaskOld & ~(buttonsPressedMask);
To just describe it verbally: Add another soft-timer, and if a button is kept press over the timeout, a repetition event gets set