This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CCS/MSP432P401R: GPIO Interrupt on Multiple Pins of the Same Port

Part Number: MSP432P401R


Tool/software: Code Composer Studio

I am trying to enable GPIO interrupts on P3.0, P3.2, P3.3, P3.5, and P3.6 where I have external buttons connected. When I enable an interrupt for just P3.0 I can get my code to execute, but I'm unsure what I need to enable to get interrupts on the other pins.

Additionally, I know that, generally, when you have interrupts on multiple pins of the same port you need to determine which IFG was set in the ISR. However, I'm not sure if I need to do this as I want all of my interrupts to accomplish the same task, so I don't care which pin triggers the interrupt.

I have attached code that enables an interrupt on P3.0 and is attempting, unsuccessfully, to enable an interrupt for P3.2 below, if anybody could provide any insight into how to initialize the interrupts on the other pins, and how to handle the IFG bits in the ISR, I would greatly appreciate it!

/* Test ADC ON by external button and OFF by Timer A */

#include "msp.h"
#include "mytiming.h"

volatile unsigned int UART_flag = 0;
volatile unsigned int timer_flag = 0;
volatile unsigned int  button_flag = 0;
volatile unsigned int var = 0;
int millivolt = 0;

void UART0_init(void);
void setFreq(int freq);

int main(void) {

    setFreq(FREQ_3_MHZ);

    int mv_char_0 = 0;
    int mv_char_1 = 0;
    int mv_char_2 = 0;
    int mv_char_3 = 0;
    char uart_0;
    char uart_1;
    char uart_2;
    char uart_3;

    WDT_A->CTL = WDT_A_CTL_PW |             // Stop WDT
                 WDT_A_CTL_HOLD;

    // GPIO Setup
    P5->SEL1 |= BIT4;                       // Configure P5.4 for ADC
    P5->SEL0 |= BIT4;

    // Initialize Port 3 (button connections)
    P3->SEL0 = 0;  //clear register selection
    P3->SEL1 = 0;  //clear register selection
    P3->SEL0 |= BIT2;
    P3->SEL1 |= BIT2;
    P3->DIR = 0;
    P3->DIR = BIT2;
    P3->OUT = BIT0;
    P3->OUT = BIT2;
    P3->REN = BIT0;   //enable pull up resistor
    P3->REN = BIT2;

    P3->IES = BIT0;  //interrupt on high-low transition
    P3->IES = BIT2;
    P3->IFG = 0;     //clear any pending flags
    P3->IE = BIT0;   //enable port 3 interrupts
    P3->IE = BIT2;

    UART0_init();

    // Enable global interrupt
    __enable_irq();

    // Enable ADC and Timer A0 interrupts in NVIC module
    NVIC_EnableIRQ(TA0_0_IRQn);
    NVIC_EnableIRQ(ADC14_IRQn);
    NVIC_EnableIRQ(PORT3_IRQn);

    // Sampling time, S&H=16, ADC14 on
    ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON;
    ADC14->CTL1 = ADC14_CTL1_RES_2;
    ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1;   /* A1 ADC input select;
                                               Vref=AVCC */
    ADC14->IER0 |= ADC14_IER0_IE0;          /* Enable ADC conv
                                               complete interrupt */

    SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;  /* Wake up on exit from
                                              ISR */

    while (1){
        if (button_flag){                      //button pushed, turn ADC on
            ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC; //start sampling/conversion
            button_flag = 0;                  //Reset flag
            P3->IFG = 0;                      //clear any pending flags
        }
        if (timer_flag){                      //time up, turn ADC off
            ADC14->CTL0 &= ~ADC14_CTL0_ENC;   //turn off ADC
            timer_flag = 0;
            P3->IE |= BIT0;                   //re-enable P3.0 interrupt
            P3->IE |= BIT2;                   //re-enable P3.2 interrupt
        }
        if (UART_flag) {
            UART_flag = 0;
            millivolt = (int)((var + 1.46) / 1.2361);
            mv_char_3 = millivolt / 1000;
            mv_char_2 = millivolt / 100 - (mv_char_3 * 10);
            mv_char_1 = millivolt / 10 - (mv_char_3 * 100 + mv_char_2 * 10);
            mv_char_0 = millivolt - (mv_char_1 * 10 + mv_char_3 * 1000 + mv_char_2 * 100);
            uart_0 = (char) mv_char_0 + '0';
            uart_1 = (char) mv_char_1 + '0';
            uart_2 = (char) mv_char_2 + '0';
            uart_3 = (char) mv_char_3 + '0';
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_3;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_2;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_1;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = uart_0;
            while(!(EUSCI_A0->IFG & 0x02)) { }
            EUSCI_A0->TXBUF = 0x0D;
        }
    }
}

// ADC14 interrupt service routine
void ADC14_IRQHandler(void) {
    UART_flag = 1;
    var = ADC14->MEM[0];
}

//Timer A0 interrupt service routine
void TA0_0_IRQHandler(void){
    timer_flag = 1;         //flag when timer reaches value
    TA0CCTL0 &= ~CCIFG;     //clear pending interrupt flag
    TA0CTL = 0;             //turn off the timer
}

void PORT3_IRQHandler(void)       //Interrupt handler for Port 3
{
       button_flag = 1;              //Set flag to signal button press detected
       //configure Timer A0
       TA0CCR0 = 900000;             //Timer length = 300ms
       TA0CCTL0 |= CCIE;
       TA0CTL |= TASSEL_2 | MC_1;
       P3->IFG = 0;                  //Clear pending interrupt flag
       P3->IE  &= ~BIT0;            //Disable interrupt for P3.0 for debouncing
       P3->IE  &= ~BIT2;            //Disable interrupt for P3.2 for debouncing

}

void UART0_init(void)
{
    EUSCI_A0->CTLW0 |= 1;       // put in reset mode for config
    EUSCI_A0->MCTLW = 0;        // disable oversampling
    EUSCI_A0->CTLW0 = 0x0081;   /* 1 stop bit, no parity,SMCLK,byte
                                   data */
    EUSCI_A0->BRW = 26;         // 3,000,000 / 115200 = 26
    P1->SEL0 |= 0x0C;           // P1.3, P1.2 for UART
    P1->SEL1 &= ~0x0C;
    EUSCI_A0->CTLW0 &= ~1;      // take UART out of reset mode
}

  • There are several places where the code uses = but should have used |=.
    Using |= sets some bit(s); using = instead also clears all other bits.

    The interrupt handler's flag handling looks correct.
  • I changed all of the instances of = that I thought should be |= instead and I have the proper interrupt functionality when the button is connected to P3.0, but not P3.2. When I connect an external button to P3.2 nothing happens, and I'm not sure why.

    /* Test ADC ON by external button and OFF by Timer A */
    
    #include "msp.h"
    #include "mytiming.h"
    
    volatile unsigned int UART_flag = 0;
    volatile unsigned int timer_flag = 0;
    volatile unsigned int  button_flag = 0;
    volatile unsigned int var = 0;
    int millivolt = 0;
    
    void UART0_init(void);
    void setFreq(int freq);
    
    int main(void) {
    
        setFreq(FREQ_3_MHZ);
    
        int mv_char_0 = 0;
        int mv_char_1 = 0;
        int mv_char_2 = 0;
        int mv_char_3 = 0;
        char uart_0;
        char uart_1;
        char uart_2;
        char uart_3;
    
        WDT_A->CTL = WDT_A_CTL_PW |             // Stop WDT
                     WDT_A_CTL_HOLD;
    
        // GPIO Setup
        P5->SEL1 |= BIT4;                       // Configure P5.4 for ADC
        P5->SEL0 |= BIT4;
    
        // Initialize Port 3 (button connections)
        P3->SEL0 = 0;  //clear register selection
        P3->SEL1 = 0;  //clear register selection
        P3->SEL0 |= BIT2;
        P3->SEL1 |= BIT2;
        P3->DIR |= BIT0;
        P3->DIR |= BIT2;
        P3->OUT |= BIT0;
        P3->OUT |= BIT2;
        P3->REN |= BIT0;   //enable pull up resistor
        P3->REN |= BIT2;
    
        P3->IES |= BIT0;  //interrupt on high-low transition
        P3->IES |= BIT2;
        P3->IFG = 0;     //clear any pending flags
        P3->IE |= BIT0;   //enable port 3 interrupts
        P3->IE |= BIT2;
    
        UART0_init();
    
        // Enable global interrupt
        __enable_irq();
    
        // Enable ADC and Timer A0 interrupts in NVIC module
        NVIC_EnableIRQ(TA0_0_IRQn);
        NVIC_EnableIRQ(ADC14_IRQn);
        NVIC_EnableIRQ(PORT3_IRQn);
    
        // Sampling time, S&H=16, ADC14 on
        ADC14->CTL0 = ADC14_CTL0_SHT0_2 | ADC14_CTL0_SHP | ADC14_CTL0_ON;
        ADC14->CTL1 = ADC14_CTL1_RES_2;
        ADC14->MCTL[0] |= ADC14_MCTLN_INCH_1;   /* A1 ADC input select;
                                                   Vref=AVCC */
        ADC14->IER0 |= ADC14_IER0_IE0;          /* Enable ADC conv
                                                   complete interrupt */
    
        SCB->SCR &= ~SCB_SCR_SLEEPONEXIT_Msk;  /* Wake up on exit from
                                                  ISR */
    
        while (1){
            if (button_flag){                      //button pushed, turn ADC on
                ADC14->CTL0 |= ADC14_CTL0_ENC | ADC14_CTL0_SC; //start sampling/conversion
                button_flag = 0;                  //Reset flag
                P3->IFG = 0;                      //clear any pending flags
            }
            if (timer_flag){                      //time up, turn ADC off
                ADC14->CTL0 &= ~ADC14_CTL0_ENC;   //turn off ADC
                timer_flag = 0;
                P3->IE |= BIT0;                   //re-enable P3.0 interrupt
                P3->IE |= BIT2;
            }
            if (UART_flag) {
                UART_flag = 0;
                millivolt = (int)((var + 1.46) / 1.2361);
                mv_char_3 = millivolt / 1000;
                mv_char_2 = millivolt / 100 - (mv_char_3 * 10);
                mv_char_1 = millivolt / 10 - (mv_char_3 * 100 + mv_char_2 * 10);
                mv_char_0 = millivolt - (mv_char_1 * 10 + mv_char_3 * 1000 + mv_char_2 * 100);
                uart_0 = (char) mv_char_0 + '0';
                uart_1 = (char) mv_char_1 + '0';
                uart_2 = (char) mv_char_2 + '0';
                uart_3 = (char) mv_char_3 + '0';
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_3;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_2;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_1;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = uart_0;
                while(!(EUSCI_A0->IFG & 0x02)) { }
                EUSCI_A0->TXBUF = 0x0D;
            }
        }
    }
    
    // ADC14 interrupt service routine
    void ADC14_IRQHandler(void) {
        UART_flag = 1;
        var = ADC14->MEM[0];
    }
    
    //Timer A0 interrupt service routine
    void TA0_0_IRQHandler(void){
        timer_flag = 1;         //flag when timer reaches value
        TA0CCTL0 &= ~CCIFG;     //clear pending interrupt flag
        TA0CTL = 0;             //turn off the timer
    }
    
    void PORT3_IRQHandler(void)       //Interrupt handler for Port 3
    {
           button_flag = 1;              //Set flag to signal button press detected
           //configure Timer A0
           TA0CCR0 = 900000;             //Timer length = 300ms
           TA0CCTL0 |= CCIE;
           TA0CTL |= TASSEL_2 | MC_1;
           P3->IFG = 0;                  //Clear P3.0 pending interrupt flag
           P3->IE  &= ~BIT0;            //Disable interrupt for P3.0 for debouncing
           P3->IE  &= ~BIT2;             //Disable interrupt for P3.0 for debouncing
    
    }
    
    void UART0_init(void)
    {
        EUSCI_A0->CTLW0 |= 1;       // put in reset mode for config
        EUSCI_A0->MCTLW = 0;        // disable oversampling
        EUSCI_A0->CTLW0 = 0x0081;   /* 1 stop bit, no parity,SMCLK,byte
                                       data */
        EUSCI_A0->BRW = 26;         // 3,000,000 / 115200 = 26
        P1->SEL0 |= 0x0C;           // P1.3, P1.2 for UART
        P1->SEL1 &= ~0x0C;
        EUSCI_A0->CTLW0 &= ~1;      // take UART out of reset mode
    }
    

  • Since you're planning on doing more of these, I suggest a helper function to help you keep track:

    void pi_setup(unsigned char pinmask)
    {
        P3->SEL0 &= ~pinmask;    // GPIO, not
        P3->SEL1 &= ~pinmask;    //   alternate function
        P3->DIR  &= ~pinmask;    // input
        P3->OUT  |=  pinmask;    // pull up, not down
        P3->REN  |=  pinmask;    // enable pullup
        P3->IES  |=  pinmask;    // high->low transition
        P3->IFG  &= ~pinmask;    // clear possible stale IFG
        P3->IE   |=  pinmask;    // enable IFG
        return;
    }
    

    Call it with e.g. "pi_setup(BIT2);". It's not the quickest method, but you only do this once at startup. 

  • I implemented a similar function and was able to get everything working as desired. Thank you very much!

**Attention** This is a public forum