In this switch debouncing tutorial part 2 C code debounce algorithms will be looked at further, and their effectiveness. All the software solutions shown will be demonstrated on the MSP430G Launchpad. However the basic principle of operation shown in the examples, can be applied to all microcontrollers, particularly the last example which is based on some code found at Jack Ganssle tutorial, this can be easily implemented on any system using the C language.
MSP430 Single Switch Debounce WatchDog
The first debounce algorithm example is based on some Arduino code, which uses the millis() function. In this case the millis second count is generated by the watchdog timer on the MSP430. The launchpad switch connected to GPIO P1.3 is used in this test code.
while (1) { int reading; int temp; temp = (P1IN & BIT3); if (temp == BIT3) { reading = 1;} else { reading = 0;} if (reading != lastswitchState) { lastDebounceTime = Mils_Count();} if ((Mils_Count() - lastDebounceTime) > debounceDelay) { switchState = reading;} if (switchState == 1) { P1OUT |= BIT0;} else { P1OUT &= ~BIT0;} lastswitchState = reading; } } #pragma vector=WDT_VECTOR // Watchdog Timer interrupt service routine __interrupt void watchdog_timer(void) { Half_Mil_Sec++; if (Half_Mil_Sec == 2) { Mil_Sec++; Half_Mil_Sec = 0; } } long Mils_Count(void) // Based on the millis() Arduino function { long Count = Mil_Sec; return Count; }
The while loop on line 1 is inside the main function, line 5 AND’s port 1 with BIT3 as this is the only GPIO pin of interested. Lines 6 to 9 will set the variable reading to a 1 if the value on pin P1.3 is a 1 i.e. not pressed, and 0 if the switch is pressed. Lines 11 and 12 check to see if the switch has changed from it’s previous stored state, if this is true then the time when the switch was pressed is saved to the variable lastDebounceTime. Lines 14 and 15 determine if the switches state hasn’t changed for a time equal to the variable debounceDelay, this then means that it is the current stable state of the push switch. The stable state is assigned to the variable switchState, then lines 17 to 20 determine the if the LED connected to GPIO P1.0 is on or off. The debounceDelay was set to 10, and the algorithm performed very well allowing fast presses of the single switch, without any issues.
The watchdog timer was used in this example as it was simple to set-up and generate an interrupt every 0.5mS. Lines 36 to 33 show the interrupt handler, some basic statements inside the interrupt generate a 1mS count, which continuously increments. The function Mils_Count() in lines 35 to 39, is used to obtain the current count value. The watchdog timer is not meant to be used in this way, but it is so often disabled in many examples, yet is a resource that can be exploited. If you were producing a production embedded system this would probably not be the case, but this adds a little extra functionality to some of the low end MSP430G devices. The watchdog set-up shown will be used in some of the other debounce examples in this tutorial, and can easily be substituted with a standard timer.
MSP430 Single Switch Debounce WatchDog Example Code
The link below contains the zip file with the full example C code.
Switch Debouncing Tutorial MSP430 Single Switch Debounce WatchDog
MSP430 Interrupt Button Control
The second example configures the GPIO pins to trigger interrupts when a change in state is detected, which is caused by a switch being pressed. Only the interrupt handler is shown in the code snippet below, but the entire C code can be downloaded further down the page.
// Port 1 interrupt service routine #pragma vector=PORT1_VECTOR __interrupt void Port_1(void){ switch (P1IFG) { case BIT3: P1IES ^= BIT3; P1OUT ^= BIT0; __delay_cycles(40000); P1IFG &= ~BIT3; break; case BIT7: P1IES ^= BIT7; P1OUT ^= BIT6; __delay_cycles(40000); P1IFG &= ~BIT7; break; default: __delay_cycles(10000); P1IFG = 0x00; break; } }
There are two GPIO pins set-up to generate interrupts P1.3 and P1.7. When a switch is pressed on either of these pins, the interrupt handler is called. The switch case statements are used here, the port 1 interrupt flag register (P1IFG) being used as the switch. Once the correct pin interrupt has been identified, the interrupt edge select is toggled (lines 8 and 14). Then the corresponding LED is toggled, as shown in lines 9 and 15. Lines 10 and 16 use a delay function which basically waits for 40mS (1MHz clock). This produced a reasonable outcome at slow to medium rates, pressing the switch at a faster rate produced indeterminate results. The delay function is not the best method to carry out a delay as it wastes CPU time. This technique is also not as robust a the first algorithm, especially if the switch is pressed quickly, but it does allow a whole port of switches to be used.
MSP430 Interrupt Button Control Example Code
The link below contains the zip file with the full example C code.
Switch Debouncing Tutorial MSP430 Interrupt Button Control
MSP430 Multiple Switch Debounce WatchDog
The third example has two examples of switch debounce algorithms, these are split into two individual functions which can be run from the main function, simply by commenting one of them out at a a time. The functions incorporate aspects of the previous two examples, allowing multiple switches to be debounced. The code snippet below shows both of the functions Press_Time() and Debounce_Buttons() residing inside a while loop, which is inside the main function.
while (1) { //Press_Time(); // Simply adds a delay and is dependent on the users press time Debounce_Buttons(); // This is developed from a single switch algorithm, it works well but // is badly implemented due to the amount of if statements being // executed each time it is called }
The Press_Time() function will be looked at first.
void Press_Time(void) { if (0x0088 != (P1IN & 0x88)) { int state = (P1IN & 0x88); Reaction_Count = Mils_Count(); switch (state) { case 0x80: while((Mils_Count() - Reaction_Count) < Button_Reaction_Delay) {} P1OUT ^= BIT0; break; case 0x08: while((Mils_Count() - Reaction_Count) < Button_Reaction_Delay) {} P1OUT ^= BIT6; break; } } }
Starting with line 3, the if statement is used to ensure that the switches connected to BIT3 or BIT7 have been pressed, if not the statement is considered false. Line 5 assigns the variable state with the AND’ed value of port 1 (P1IN) with hex value 0x88 or binary 10001000. Line 7 assigns the current Mils_Count() value to the variable Reaction_Count. The switch case statements are used here with the variable state being used as the switch. If the switch on P1.7 is pressed then the case statement on line 14 will be selected. A while loop is then entered, which waits until the current Mils_Count() minus the variable Reaction_Count is greater than the variable Button_Reaction_Delay. This allows a tunable delay to be entered with ease, for testing a delay of 100-150mS was found to produce satisfactory results. This method produced better results than the interrupt method, but will suffer with increased switching speed.
The second function Debounce_Buttons() is basically a copy of the first example. The variables are just doubled up and surrounded by a if statement so the code is only executed when a switch is pressed.
void Debounce_Buttons(void) { if ((P1IN & 0x88) == (P1IN & 0x08) || (P1IN & 0x80)) { int reading1; int temp1 = (P1IN & BIT3); if (temp1 == 0x08) { reading1 = 1;} else { reading1 = 0;} if (reading1 != lastButtonState1) { lastDebounceTime = Mils_Count();} if ((Mils_Count() - lastDebounceTime) > debounceDelay) { buttonState1 = reading1;} if (buttonState1 == 1) { P1OUT |= BIT0;} else { P1OUT &= ~BIT0;} lastButtonState1 = reading1; int reading2; int temp2 = (P1IN & BIT7); if (temp2 == 0x80) { reading2 = 1;} else { reading2 = 0;} if (reading2 != lastButtonState2) { lastDebounceTime = Mils_Count();} if ((Mils_Count() - lastDebounceTime) > debounceDelay) { buttonState2 = reading2;} if (buttonState2 == 1) { P1OUT |= BIT6;} else { P1OUT &= ~BIT6;} lastButtonState2 = reading2; } }
This works well as per the first example but with two switches, however the code is very inefficient, due to the large number if statements that are executed each time a switch is pressed.
MSP430 Multiple Switch Debounce WatchDog Example Code
The link below contains the zip file with the full example C code.
Switch Debouncing Tutorial MSP430 Multiple Switch Debounce WatchDog
MSP430 Ganssle Switch Debounce Multiple Switches
This fourth and final example is based on sample code provided by Jack Ganssle, he has an excellent tutorial located here. The code is based on his third example, there is also a good description of the code operation with his article. The code snippet below shows the main body of the algorithm, I have made some modifications adding a compound bitwise AND operator, as well as adding some of his considerations regarding OR’ing the final debounced port value.
int debounceSwitch(void) { int i, DebouncedORd_State; prevDebouncedState = debouncedState; state[Index] = rawPortData(); ++Index; debouncedState = 0xFF; for (i = 0; i < MAXCHECKS; i++) { debouncedState &= state[i]; } if (Index >= MAXCHECKS) { Index = 0; } DebouncedORd_State = debouncedState ^ prevDebouncedState; return DebouncedORd_State; }
Line 6 shows a function call for rawPortData(), this function simply returns the current state of port 1 and can be seen below in the next code snippet.
int rawPortData(void) { int portState = P1IN & 0x88; return portState; }
The debounceSwitch() function returns the debouncedORd value, and is called in the following way.
while (1) { checkButtons(debounceSwitch()); }
The checkButtons() function uses switch case statements to interpret which switch or GPIO pin has changed, the nice part about this code is the debouncedORd value makes the code very intuitive.
void checkButtons(int x) { int portState = x; switch (portState) { case BIT3: P1OUT ^= BIT0; break; case BIT7: P1OUT ^= BIT6; break; default: break; } }
This last example is easy to port to other microcontrollers, just by changing the code in functions checkButtons() and rawPortDate(). Needless to say this code works very well and produces excellent results with the PCB tac switches used, under fast or slow switching.
MSP430 Ganssle Switch Debounce Multiple Switches Example Code
The link below contains the zip file with the full example C code.
Switch Debouncing Tutorial MSP430 Ganssle Switch Debounce Multiple Switches
In single switch debouncing,
is debounce delay a variable??
I think it should be a constant for a particular switch.
And usually what range of values would that have?? (I am using MSP430G MC’s switch)
Hi,
Yes a switch bounce will vary from one contact to another and from one switch to another. The small tack switches used on the MSP430 are actually pretty good, but it is still recommended to use some basic hardware debouncing. The values suggested in the tutorial should be enough for basic operation on the MSP430, larger resistor and capacitor values can be used, however be aware that these could affect the speed the buttons can be pressed and released if too large.
Cheers,
Ant
I want to use software debouncing and I want to know a safe numerical value of debounce delay used in the first algorithm above(for MSP430G).
And I have a doubt regarding timers (I have to measure the time for which the switch has been pressed),
Can I use TA1R to measure time?(What is the range of values of TA1R)
If not what is the alternative to measure time in the range of 1sec to 32 sec?
Hi,
The debounce timings are very dependant on the switch used and the intended user interaction, to be sure you need to tune the values to your hardware and software setup, there is no magic number that can be used! You then need then check the settings, by testing your software and hardware so it works as per your requirements.
The MSP430G Timers are 16bit, for example the MSP430G2253 has two 16 bit timers and the MSP430G2231 has one 16bit timer.
Regards,
At
Generally, delay_cycles is something to be avoided when coding for the msp430 for a couple of reasons. First, ISRs should be as brief as possible; putting a delay statement in an ISR is problematic, especially of the system is entirely interrupt-driven as responding to multiple interrupts is more challenging if the system is stuck in a loop inside an ISR. Second, you are using the cpu while in the ISR, which is very power-intensive, and also, clock dependent. So, if you are running at 1MHz, 40000 cycles will have a different delay than if you are at 16MHz.
A different approach is to use the various timers and an interrupt-driven approach.
1) Port ISR to capture the button state
2) Entry to the port ISR disables Button capture and starts a second timer to revisit button port state in some programmed time.
3) Timer ISR determines if the button is still down, and if so, sets the port to detect button up, otherwise it’s up and the port needs to be reset to detect button down.
This avoids hardware solutions, which as you noted in part 1 have their own issues. Minimizes use of the cpu – power hungry. Doesn’t hang the system in an ISR impairing the ability to process other interrupts. Allows a programmable delay for bounce detection. And also allows a simpler algorithm for detecting a stuck switch.
Still, a useful and informative tutorial. Thank you for posting it.
Bob
Hey Bob, thanks for taking the time to read the tutorial and for your feedback.
I totally agree, using delay cycles is a poor way to implement an algorithm but allows for ‘quick and dirty testing’, I never use them apart from prototyping. The tutorial was easier to demonstrate this way and hopefully still showcases different debounce methodologies.
Your timer approach is a nice alternative will look at using this in future, as with anything it’s a trade-off based on the resources available on the microprocessor and the project requirements i.e. how many timers are available and what timer requirements your project has.
Best regards,
Ant
all your tutorials are very clear and use full.can i get c program for PIC and ATMEL Microcontrollers ?
Hi,
Thanks, currently I have only been working with TI microcontrollers, mainly due to the course I completed focussed on these products. I do intend to look at other brands when time allows as it gives you a better overall understanding, but at this time I don’t have any PIC or Atmel development boards.
For the last switch debounce example based on Ganssles code, the actual debounce algorithm is not hardware dependent (apart from line 5), so you can use this on any microcontroller. What you need to identify is the register names for the ports on your particular target microcontroller. There must be a PIC or Atmel help forum, if you post a link to my tutorial and paste the code explaining what you need I am sure someone will be able to help. Another option is to look for a Peripheral driver guide or other API guide for your target processor, then scan the GPIO section which should give you all the information you need.
Sorry I cannot go into further detail at this time!
Regards,
Ant
All your tutorials are brilliant. Thank you very much.
Thanks Terry, glad you found them useful