Author Topic: Button press not registering inside interrupt handler but works in main loop  (Read 1292 times)

0 Members and 1 Guest are viewing this topic.

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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: [Select]
#include <xc.h>

#define ENC_A PORTBbits.RB0
#define ENC_B PORTBbits.RB1
#define ENC_C PORTBbits.RB2


#define NUM_OPTIONS 4
const char* OPTIONS[NUM_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_index]);
           
            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 trigger
            switch (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;
    }
}
« Last Edit: February 20, 2023, 07:59:03 pm by newtekuser »
 

Offline Andy Watson

  • Super Contributor
  • ***
  • Posts: 2124
I think you need to declare menu_change (and any other variables which can be changed "outside" the present code) to be volatile.
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4418
  • Country: nl
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.

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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.

It 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: [Select]
INTCON2bits.INTEDG0 = 0;
Per the PIC16F887 datasheet:

Quote
bit 6 INTEDG: Interrupt Edge Select bit
1 = Interrupt on rising edge of INT pin
0 = Interrupt on falling edge of INT pin

Maybe 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...



 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4418
  • Country: nl
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.

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
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: [Select]
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?
 
The following users thanked this post: newtekuser

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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.

I 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?
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4418
  • Country: nl
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.

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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: [Select]
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?

I 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: [Select]
INTCONbits.INTE = 1;
    OPTION_REGbits.INTEDG = 0;
    INTCONbits.GIE = 1; 

I have now modified it in my main post to eliminate confusion.
« Last Edit: February 20, 2023, 07:59:28 pm by newtekuser »
 

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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.

I see... so the appropriate way to handle such a scenario is polling for the button press in the main loop?
 

Offline pcprogrammer

  • Super Contributor
  • ***
  • Posts: 4418
  • Country: nl
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.

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
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).
 

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
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).

So, 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?
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 855
No, keep the button where it is, use the interrupt-on-change using the IOCB register.
 

Offline newtekuserTopic starter

  • Frequent Contributor
  • **
  • Posts: 433
  • Country: us
No, keep the button where it is, use the interrupt-on-change using the IOCB register.

Thank you very much!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf