• AVR Freaks

Hot!PORTB change interrupt is not triggered

Author
pauline95
New Member
  • Total Posts : 4
  • Reward points : 0
  • Joined: 2019/09/27 00:36:34
  • Location: 0
  • Status: offline
2019/10/21 06:07:04 (permalink)
0

PORTB change interrupt is not triggered

Hi. I'm using the PIC16F887 and I'm trying to toggle the state of an LED (connected to RE0) when any of the four tact switches connected to RB0-RB3 is pressed. I'm using the PORTB change interrupt but the interrupt is not triggered when the any of the tact switches is pressed and so the state of the LED is not toggled.
 
Below is my code:
 
#pragma config CONFIG1 = 0xECD4
#pragma config CONFIG2 = 0xFFFF

#define _XTAL_FREQ 8000000

void PORTB_ISR(){
    RE0 = ~RE0;
    PORTB = PORTB;
    RBIF = 0;
}

void __interrupt() ISR(){
    if (RBIF == 1){
        PORTB_ISR();
    }
}

void main(void) {
    
    OSCCON = 0x70; // Set internal oscillator to 8MHz
    ANSEL = 0xF8; // Set RE0-RE2 as digital pin
    ANSELH = 0x00; // Set RB0-RB5 as digital pin
    TRISB = 0xFF; // Set PORTB as input
    TRISE = 0x00; // Set PORTE as output
    GIE = 1; // Enable global interrupt
    RBIE = 1; // Enable PORTB change interrupt
    nRBPU = 0; // PORTB pull-ups are enabled by individual PORT latch values
    WPUB = 0xFF; // Enable pull-up for RB0-RB7
    
    PORTE = 0x00; // Turn off PORTE at the beginning
    
    while(1){};

    return;
}

 
Does anyone know what's causing the interrupt not being triggered? Any help would be appreciated. 
#1

12 Replies Related Threads

    pcbbc
    Super Member
    • Total Posts : 1373
    • Reward points : 0
    • Joined: 2014/03/27 07:04:41
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/10/21 11:52:47 (permalink)
    0
    Read section 3.4.3
    Control bits IOCB<7:0> enable or disable the interrupt function for each pin.
    #2
    bitdoctor
    Starting Member
    • Total Posts : 24
    • Reward points : 0
    • Joined: 2019/10/11 06:10:09
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/10/21 13:00:14 (permalink)
    +1 (1)
    Unless you have debounced your switches in hardware, you will get unpredictable results because the inputs can toggle many times with only one key press. You haven't accounted for that in your code.
    https://www.allaboutcircu...e-how-to-deal-with-it/
    A likely symptom would be that it might toggle successfully about half the times you push the button.
    post edited by bitdoctor - 2019/10/21 13:02:57
    #3
    pauline95
    New Member
    • Total Posts : 4
    • Reward points : 0
    • Joined: 2019/09/27 00:36:34
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/10/22 03:31:09 (permalink)
    0
    Oh, I've missed out both the IOCB and the debouncing. Thanks for your reply, I will change my code based on that.
    #4
    pauline95
    New Member
    • Total Posts : 4
    • Reward points : 0
    • Joined: 2019/09/27 00:36:34
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 05:13:12 (permalink)
    0
    I've modified my code and below is the code that I have now. I have 3 tact switches connected to RB0, RB1 and RB2. The interrupt on change for these 3 pins is enabled. When the tact switches connected to RB0, RB1, RB2 is pressed, the state of RE0, RE1, RE2 should be toggled. When I tested the code, the interrupt is triggered but it can only turn the output on and does turn it off. Also, when any of the output is being turned on, it will turn off other output, which means that there will only be one output in on state every time the tact switch is pressed. I thought this is caused by the RMW problem, so I tried to write to the whole port instead of toggling a single bit but this doesn't solve the problem. 
     
    #pragma config CONFIG1 = 0xE0D4
    #pragma config CONFIG2 = 0xFFFF

    #define _XTAL_FREQ 8000000

    #include <xc.h>

    void __interrupt() ISR();

    void main(void) {
        
        OSCCON = 0x70; // Set internal oscillator to 8MHz
        
        ANSELH = 0x00; // Configure PORTB as digital pin
        TRISB = 0xFF; // Configure PORTB as input
        nRBPU = 0; // PORTB pull-ups are enabled by individual PORT latch values
        WPUB = 0xFF; // Enable PORTB pull-up
        GIE = 1; // Global interrupt enabled
        RBIE = 1; // Enable interrupt on change
        IOCB = 0x07; // Enable interrupt on change for RB0-RB2
        
        ANSEL = 0xE0; // Configure RE0-RE2 as digital output
        TRISE = 0x00; // Configure PORTE as output
        PORTE = 0x00;
        
        while(1){
            
        }
        
        return;
    }

    void __interrupt() ISR(){
        
        if (RBIF == 1){
            
            if (!RB0){
                if (RE0)
                    PORTE = PORTE & 0b11111110;
                else
                    PORTE = PORTE | 0b00000001;
            }
            else if (!RB1){
                if (RE0)
                    PORTE = PORTE & 0b11111101;
                else
                    PORTE = PORTE | 0b00000010;
            }
            else if (!RB2){
                if (RE0)
                    PORTE = PORTE & 0b11111011;
                else
                    PORTE = PORTE | 0b00000100;
            }
            
            __delay_ms(250);
            char portbRead = PORTB;
            RBIF = 0;
        }
    }

    #5
    pcbbc
    Super Member
    • Total Posts : 1373
    • Reward points : 0
    • Joined: 2014/03/27 07:04:41
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 07:05:29 (permalink)
    +1 (1)
    IOC for push button switches is hardly ever worth the effort.  You need to de-bounce the switch anyway, and the correct way to do that is certainly NOT to put a delay routine in your ISR.
     
    Just set a regular recurring interrupt (100ms = 10Hz will do) and poll your switches.  This will do adequate de-bounce for you, and dispense with all the foilbles of the IOC function.
     
    Using a read of the port itself isn't going to save your from RMW.  The compiler is smarter than you:
    PORTE = PORTE & 0b11111110; => BCF PORTE, 0
    PORTE = PORTE | 0b00000001; => BSF PORTE, 0
     
    Edit:
    Here's the real problem: You are testing RE0 in all cases.  First IF needs to test RE0, second IF RE1, find IF RE2.
     
    Edit again:
    And before someone points it out, much easier to toggle bits with the XOR operation...
           if (!RB0){
                    PORTE = PORTE ^ 0b00000001;
            }
            if (!RB1){
                    PORTE = PORTE ^ 0b00000010;
            }
            if (!RB2){
                    PORTE = PORTE ^ 0b00000100;
            }

    And also your use of IF...ELSE IF... is flawed.  If more than one bit changes at roughly the same time, you may miss interrupts.  TBH you may miss them anyway.  It's one of the flaws of IOC.  But more likely with the original code you posted.
    post edited by pcbbc - 2019/11/01 07:13:03
    #6
    pauline95
    New Member
    • Total Posts : 4
    • Reward points : 0
    • Joined: 2019/09/27 00:36:34
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 07:58:19 (permalink)
    0
    pcbbc
    Using a read of the port itself isn't going to save your from RMW.  

    So the statement if (RE0){} will actually read the port and write it back to the port?
     
    pcbbc
    If more than one bit changes at roughly the same time, you may miss interrupts.  

    But if I poll the pin, I might miss it also if more than one switch is being pressed almost at the same time. So both polling and interrupt have the same problem?
    #7
    1and0
    Access is Denied
    • Total Posts : 9920
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 08:54:50 (permalink)
    0
    pauline95
    So the statement if (RE0){} will actually read the port and write it back to the port?

    No, it just reads the port.
     

    But if I poll the pin, I might miss it also if more than one switch is being pressed almost at the same time. So both polling and interrupt have the same problem?

    Replace your ISR function with this:
    void __interrupt() ISR()
    {
        PORTE ^= ~PORTB & 0x07;
        RBIF = 0;
    }

    Notice this does not handle switch bounces.
     
    <edit> Actually, &0x07 is not needed and can be omitted.
    post edited by 1and0 - 2019/11/01 09:15:00
    #8
    ric
    Super Member
    • Total Posts : 24333
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 14:24:52 (permalink)
    0
    pauline95
    pcbbc
    Using a read of the port itself isn't going to save your from RMW.  

    So the statement if (RE0){} will actually read the port and write it back to the port?

    No.
    The read is happening in this line
    PORTE = PORTE & 0b11111110;

    That is failing because you have incorrectly tried to set RE0 to digital mode with this line:
        ANSEL = 0xE0; // Configure RE0-RE2 as digital output

    In that register, 1 bits set analog, 0 bits set digital, so you have set all the AN bits EXCEPT AN5, AN6 and AN7 to digital. Those bits (which control RE0, RE1 and RE2) are set to 1, so they are analog, so they always read as zero, regardless of the pin level.
     
    pcbbc
    If more than one bit changes at roughly the same time, you may miss interrupts.  

    But if I poll the pin, I might miss it also if more than one switch is being pressed almost at the same time. So both polling and interrupt have the same problem?



    You do the polling from inside a timer interrupt running at 50-100 times a second. You won't miss presses!
    I agree with earlier sentiments. Interrupt on Change is NOT a good way to scan switches, particularly if they are going to need debouncing.
    The only time IOC is useful is if you want to stop the processor running (e.g. SLEEP or IDLE) while waiting for a press. In that case, I'd restart normal polling as soon as an interrupt occurs to detect WHICH button was pressed.
     
        ANSEL =0xE0;// Configure RE0-RE2 as digital output

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #9
    pcbbc
    Super Member
    • Total Posts : 1373
    • Reward points : 0
    • Joined: 2014/03/27 07:04:41
    • Location: 0
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 14:37:54 (permalink)
    0

    But if I poll the pin, I might miss it also if more than one switch is being pressed almost at the same time. So both polling and interrupt have the same problem?

    Not if they are manual key presses - you can’t press keys that fast. If you poll you are guaranteed to be checking again when the polling interval next elapses.  With IOC you’ll not be checking again until there is another change. That could be ages away if the keys are being held down and you miss one.
     
    So the situation with IOC is this...
    1. Key 1 is pressed, IOC interrupt is set...
    2. Interrupt fires and handles the key 1 press...
    3. While it is doing that, or while it is in your (badly engineered) 250ms wait, key 2 is pressed...
    4. Some time later the interrupt ends and clears the IOC interrupt flag.
    5. The press of key 2 goes completely unnoticed.
     
    But with polling...
    1. Key 1 is pressed...
    2. Some time later the polling interrupt fires, notices key 1 has changed state..
    3. While it is doing that key 2 is pressed, but is just missed on this polling cycle...
    4. Next polling cycle fires and detects key 2 has changed state.
     
    The only thing you need to ensure is that your polling interval is quick enough to detect the shortest time a switch can remain pressed, but not so quick as to be impacted by contact bounce.
     
    #10
    ric
    Super Member
    • Total Posts : 24333
    • Reward points : 0
    • Joined: 2003/11/07 12:41:26
    • Location: Australia, Melbourne
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 15:44:57 (permalink)
    0
    Also note, the whole RMW (read-modify-write) problem of changing PORT pins which might be overloaded or in analog mode disappears when you move to current PIC16F device with a LATE register.
    You really make life harder for yourself sticking with obsolete PICs.
     
     

    I also post at: PicForum
    Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
    NEW USERS: Posting images, links and code - workaround for restrictions.
    To get a useful answer, always state which PIC you are using!
    #11
    jtemples
    عُضْوٌ جَدِيد
    • Total Posts : 11415
    • Reward points : 0
    • Joined: 2004/02/13 12:31:19
    • Location: Southern California
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 16:14:05 (permalink)
    0
    I'd restart normal polling as soon as an interrupt occurs to detect WHICH button was pressed.

     
    Yes, this is the only "correct" way to do it.  The interrupt wakes you up, but you just ignore the interrupt (other than clearing its flag) and do the same polling processing you'd do when the PIC is awake.
     
    Just set a regular recurring interrupt (100ms = 10Hz will do)

     
    That's far too slow; it's definitely noticeable to a user.
    #12
    1and0
    Access is Denied
    • Total Posts : 9920
    • Reward points : 0
    • Joined: 2007/05/06 12:03:20
    • Location: Harry's Gray Matter
    • Status: offline
    Re: PORTB change interrupt is not triggered 2019/11/01 16:38:02 (permalink)
    0
    ric
    The only time IOC is useful is if you want to stop the processor running (e.g. SLEEP or IDLE) while waiting for a press. In that case, I'd restart normal polling as soon as an interrupt occurs to detect WHICH button was pressed.
     

    That is the only use (wakeup from Sleep) I have for the IOC ... so far. ;)
     
    #13
    Jump to:
    © 2019 APG vNext Commercial Version 4.5