Button press not registering inside interrupt handler but works in main loop - Page 1
Forum section: electronics / general

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 06:51:26 pm
content: Hi All,I am using a PIC16F887 with a rotary encoder to display the selected menu option on the LCD as well as to print what option was selected by the user when they press on the encoder button. The interrupt is triggering when I rotate the encoder and the menu options show up properly, but the button does not do anything. It is not the wiring at fault, because if I move the switch case into the main loop, the button works. It is only when the switch case for the button is inside the interrupt handler it doesn't do anything.I hooked up the oscilloscope to the RB2 pin and I see a good signal when the button is pressed. To note that I have the signal from the button coming from an output of a Schmitt trigger for de-bouncing. Is that a problem somehow when using interrupts? (I'd think not since it's working in the main loop)What am I missing here?Code:#include <xc.h>#define ENC_A PORTBbits.RB0#define ENC_B PORTBbits.RB1#define ENC_C PORTBbits.RB2#define NUM_OPTIONS 4const char* OPTIONS = {"Option 1","Option 2","Option 3","Option 4"};int menu_index = 0;int menu_changed = 0;void main() {TRISB = 0b00000111;ANSEL = 0x0;ANSELH = 0x0;INTCONbits.INTE = 1;OPTION_REGbits.INTEDG = 0;INTCONbits.GIE = 1;while (1) {if (menu_changed) {LCD_Cmd(LCD_CLEAR);LCD_Goto(1, 1);LCD_Print(OPTIONS);menu_changed = 0;}}}void __interrupt() isr() {if (INTCONbits.INT0IF) {int dir = 0;if (ENC_A == ENC_B) {dir = 1;} else {dir = -1;}menu_index += dir;if (menu_index < 0) {menu_index = NUM_OPTIONS - 1;} else if (menu_index >= NUM_OPTIONS) {menu_index = 0;}if (ENC_C) { // button connected to a Schmitt triggerswitch (menu_index) {case 0:LCD_Goto(1, 2);LCD_Print("Option 1 selected");break;case 1:LCD_Goto(1, 2);LCD_Print("Option 2 selected");break;case 2:LCD_Goto(1, 2);LCD_Print("Option 3 selected");break;case 3:LCD_Goto(1, 2);LCD_Print("Option 4 selected");break;}}menu_changed = 1;INTCONbits.INT0IF = 0;}}

author: Andy Watson
author_rank: Super Contributor
timestamp: February 20, 2023, 07:05:08 pm
content: I think you need to declare menu_change (and any other variables which can be changed "outside" the present code) to be volatile.

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 20, 2023, 07:17:42 pm
content: The "volatile" is a point, but it seems like the "menu_index" is getting updated as intended in the interrupt loop.Have you checked what triggers the interrupt. A low to high or a high to low change, or is it level triggered. (I'm not familiar with the PIC chips)If it is from high to low it won't see the button pushed any more in the interrupt.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 07:38:26 pm
content: Quote from: pcprogrammer on February 20, 2023, 07:17:42 pmIt is triggered on the falling edge of the INT pin if that's what you mean. I'm not very familiar with interrupts TBH.Code:INTCON2bits.INTEDG0 = 0;Per the PIC16F887 datasheet:QuoteMaybe of importance is what pin of the encoder is connected to the INT pin? I did not wire the push button of the encoder to the INT pin, but the CLK pin of the encoder.I figured I'd trigger the interrupt when rotating the knob, maybe I should have wired the button instead...

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 20, 2023, 07:44:20 pm
content: Well that explains it. If the interrupt is only triggered on the "clk" signal of the rotary dial, just pushing the button of it will not trigger the interrupt.This means that it is better to handle it in the main loop.Also the falling edge of the interrupt means that the signal on the interrupt pin needs to be high and then go low for the interrupt to be activated. This would mean that it would be on the release of the button if it were to be used as the interrupt input. The checking in the interrupt if the signal is high would not work in that case.Edit: wiring the interrupt to just the button line would kill the rotary dial functionality.

author: cv007
author_rank: Frequent Contributor
timestamp: February 20, 2023, 07:45:16 pm
content: You are only checking the button when in the isr (polling via isr), so unless you turn and press at the same time, you will not see a press. Most likely when the encoder is pressed, it will not be turning so no reading of the button. When you moved it to the main loop, you were polling continually, so reads were working.Code:INTCON2bits.INTEDG0 = 0;I don't think INTCON2 is in any pic16 (nor INTEDG0), and INT0IE not in a pic16f887. Seems like a pic18- are you compiling for the correct mcu?

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 07:46:23 pm
content: Quote from: pcprogrammer on February 20, 2023, 07:44:20 pmI understand that pushing the button will not trigger the interrupt, but once the interrupt is triggered by rotating the knob I'm inside the interrupt routine, why isn't the button working at that point?

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 20, 2023, 07:55:20 pm
content: Because you will only be in the interrupt routine for a very short time.A way to see this is to configure an output pin and set it high on entering the interrupt routine. Then set it low again at the end of the routine. Connect your scope to this pin and rotate your rotary dial.Only if you keep the button pressed and then turn the rotary dial it will detect the pressed button in the interrupt.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 07:56:40 pm
content: Quote from: cv007 on February 20, 2023, 07:45:16 pmI was thinking the same thing about the button only working at the same time of rotating the encoder...You're right about the registers, I pasted them from the wrong version of my code that I found online initially. In the last version where the interrupt riggers for me it's actually:Code:INTCONbits.INTE = 1;OPTION_REGbits.INTEDG = 0;INTCONbits.GIE = 1;I have now modified it in my main post to eliminate confusion.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 07:58:08 pm
content: Quote from: pcprogrammer on February 20, 2023, 07:55:20 pmI see... so the appropriate way to handle such a scenario is polling for the button press in the main loop?

author: pcprogrammer
author_rank: Super Contributor
timestamp: February 20, 2023, 08:00:21 pm
content: Yes. Or if the PIC has more interrupt inputs you can connect it to another one and use that interrupt to handle the button push.

author: cv007
author_rank: Frequent Contributor
timestamp: February 20, 2023, 08:06:44 pm
content: Your mcu have interrupt-on-change for PORTB pins. You can enable the button pin irq, and in the isr read the pin (you will be there when it presses and when it releases).

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 10:03:11 pm
content: Quote from: cv007 on February 20, 2023, 08:06:44 pmSo, wire the push button to the INT pin, then I can still use the encoder in the isr? How do I implement interrupt on change?

author: cv007
author_rank: Frequent Contributor
timestamp: February 20, 2023, 11:12:04 pm
content: No, keep the button where it is, use the interrupt-on-change using the IOCB register.

author: newtekuser
author_rank: Frequent Contributor
timestamp: February 20, 2023, 11:23:39 pm
content: Quote from: cv007 on February 20, 2023, 11:12:04 pmThank you very much!