Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Switch debouncing inside an ISR (Hi-Tech C)

Status
Not open for further replies.

Pete_UK

New Member
Hello all,

I've been experimenting with the Hi-Tech C compiler with a 16F876A microcontroller, and this time it's with interrupts. My code is working up to a point where I can toggle the PWM module in the PIC on and off with a single push button wired into RB5 (using interrupt on change). Additionally, the main program repeatedly flashes an LED.

The program works intermittently in so far as if the button is pressed repeatedly it will turn the PWM on and off correctly, leading me to believe that switch bounce is the reason why it isn't working smoothly. I've written previous programs to debounce switches and they have worked really well, but when I try to insert the code into the ISR the program does not function at all. Is it not possible to insert timing loops using the __delay_ms macro within ISRs? Is there an alternative method apart from using an RC circuit across the push button, and what impact would this have on the PIC if I used it?

Thanks in advance for any suggestions and advice,

Pete
 

Attachments

  • main.C
    1.4 KB · Views: 328
Hi Pete it's not a good idea to have delays inside ISR's because it stalls the rest of your program while waiting out the delay. It's also not a good idea to have non-debounced (RC filter) switches functioning as a Port change interrupt because of the debouncing issue.

A better way would be to setup a timer interrupt which reads the switch (or switches) every say 20ms and acts on the switch press if one is detected. Then you have a 20ms cushion between presses for simple debouncing. There is still a little room for error if the switch contacts are bouncing when the ISR is called.

You could also use a smaller timer interrupt period and wait for a couple successive same reads of the switch before acting on it.
 
Last edited:
You can use a timer to provide the debounce without adding a delay in the ISR. The pin change interrupt sets a timer to fire an interrupt in, say, 10ms. So, if the pin changes again within this 10ms, the timer interrupt is rescheduled to be in another 10ms time. When the timer interrupt finally fires, it means there's been no pin change activity for 10ms i.e. properly debounced. Inside the timer interrupt you can toggle your enable bit or whatever you want to do when a button is pushed.

Something like the following should work.
Code:
void interrupt pwm_on (void)
{
	RBIF = 0;			//Clear RBIF (RB change flag)
	
	if (RB5 == 0)		//Is RB5 depressed? (active low)
    {
        set timer interrupt for 10ms from now & enable it
    }
}

void interrupt timer_interrupt()
{
    disable timer interrupt
    
    sT2CON = sT2CON ^ xorvar;	//Perform XOR operation on sT2CON to mask Timer2 enable bit
    T2CON = sT2CON;				//Equate sT2CON to T2CON register
}

Also, you've used 'ie();' inside the ISR itself - not only do you not need to do that (returning from the ISR reenabled interrupts automatically), it can cause some problems if the ISR nests & reenters the ISR before it's returned.

Hope this helps :)
 
You can use a timer to provide the debounce without adding a delay in the ISR. The pin change interrupt sets a timer to fire an interrupt in, say, 10ms. So, if the pin changes again within this 10ms, the timer interrupt is rescheduled to be in another 10ms time. When the timer interrupt finally fires, it means there's been no pin change activity for 10ms i.e. properly debounced. Inside the timer interrupt you can toggle your enable bit or whatever you want to do when a button is pushed.

Nice solution. I never thought of doing it that way.
 
Hi all,

Thanks very much for your replies and feedback.

Gobbledok said:
Hi Pete it's not a good idea to have delays inside ISR's because it stalls the rest of your program while waiting out the delay.

Yeh, I suspected this would be the case.

Gobbledok said:
A better way would be to setup a timer interrupt which reads the switch (or switches) every say 20ms and acts on the switch press if one is detected. Then you have a 20ms cushion between presses for simple debouncing. There is still a little room for error if the switch contacts are bouncing when the ISR is called.

Thanks for the suggestion. I have implemented this method (see attached code) and it works really well. I am only occasionally getting intermittent response when the button is pressed but I'm yet to implement the code to sample the switch state a number of times.

dougy83 said:
You can use a timer to provide the debounce without adding a delay in the ISR. The pin change interrupt sets a timer to fire an interrupt in, say, 10ms. So, if the pin changes again within this 10ms, the timer interrupt is rescheduled to be in another 10ms time. When the timer interrupt finally fires, it means there's been no pin change activity for 10ms i.e. properly debounced. Inside the timer interrupt you can toggle your enable bit or whatever you want to do when a button is pushed.

I attempted this method but had a vague memory that only one interrupt can be used with Hi-Tech C (or the 16F76A) because there is only a single interrupt vector. I think this is an ideal solution if I was using a PIC that could handle multiple interrupts with C. I appreciate your help.

dougy83 said:
Also, you've used 'ie();' inside the ISR itself - not only do you not need to do that (returning from the ISR reenabled interrupts automatically), it can cause some problems if the ISR nests & reenters the ISR before it's returned.

I've been finding quite a few of these caveats whilst programming, but I suppose it's impossible for the PIC's datasheet to take into account the various compilers and how they work (it's probably in the manual somewhere but it was late at night when I wrote the program!). Cheers for the heads up!

Pete
 

Attachments

  • main.C
    1.7 KB · Views: 239
Last edited:
I attempted this method but had a vague memory that only one interrupt can be used with Hi-Tech C (or the 16F76A) because there is only a single interrupt vector. I think this is an ideal solution if I was using a PIC that could handle multiple interrupts with C. I appreciate your help.

Hi Pete you can use more than one interrupt. You just need to test the interrupt flags within the ISR to see which interrupt occurred before acting on it.


I have implemented this method (see attached code) and it works really well. I am only occasionally getting intermittent response when the button is pressed but I'm yet to implement the code to sample the switch state a number of times.

Also, you shouldn't act on the next button press until the last one has been released. Otherwise the code will still toggle the PWM every 10ms.

Code:
void interrupt pwm_on (void)
{
	static char counter = 0;	//Declare a static variable of type char and initialise to ZERO.
	static char Button_temp = 1;

	TMR0IF = 0;

	counter++;					//Increment counter by a value of 1.

	if (counter == 10)			//Test whether interrupt has occured 5 times (5 x 2mS = 10mS)
	{
		if (Button_temp == 0)		//This will effectively do nothing until the button has been released
		{
			if (RB1 == 1)
			{
				Button_temp = 1;	//Sets Button_temp to 1 so the ISR can act on the next button press
			}
		}
		else
		{		
			if (RB1 == 0)			//Test whether button has be pressed
			{
				sT2CON ^= xorvar;	//Perform XOR operation on sT2CON to mask Timer2 enable bit	
				T2CON = sT2CON;		//Update Timer2 configuration register
				Button_temp = 0;	//	
			}
		}

		counter = 0;			//Reset counter
	}
}

That should work though I haven't tried it and should stop any false button presses however dougy's code below is a lot simpler.
 
Last edited:
I attempted this method but had a vague memory that only one interrupt can be used with Hi-Tech C (or the 16F76A) because there is only a single interrupt vector. I think this is an ideal solution if I was using a PIC that could handle multiple interrupts with C. I appreciate your help.
Yeah, sorry it's not compilable code. You can still only have a single c function marked with 'interrupt' which handles all interrupts. So the code would be closer to:
Code:
void interrupt isr (void)
{
   if(RBIF)
   {
	RBIF = 0;			//Clear RBIF (RB change flag)
	
	if (RB5 == 0)		//Is RB5 depressed? (active low)
        {
            set timer interrupt for 10ms from now & enable it
        }
   }

   if(timer interrupt flagged)
   {
      disable timer interrupt
    
      sT2CON = sT2CON ^ xorvar;	//Perform XOR operation on sT2CON to mask Timer2 enable bit
      T2CON = sT2CON;				//Equate sT2CON to T2CON register
    }
}
I've been finding quite a few of these caveats whilst programming, but I suppose it's impossible for the PIC's datasheet to take into account the various compilers and how they work (it's probably in the manual somewhere but it was late at night when I wrote the program!). Cheers for the heads up!
I would think the RETI is called universally as a return from isr by pic c compilers.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top