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.

Hi,Is there a way to tell which pin from the regular GPIO trigger the interrupt?

I am using TM4C129EXL launchpad, I am trying to using the regular GPIO as a edge detector, I am trying to find a way to identify which pin detected the edge and raised the interrupt, I checked the NVIC, the port P and Port Q has the specific ISR for specific pin, is there any other way?

  • Shan Cao said:
    I am trying to using the regular GPIO as a edge detector ... trying to find a way to identify which pin detected the edge and raised the interrupt ...

    This is easy - simply "read" that GPIO Port - early w/in your interrupt handler.   If only a few pins are interrupt capable (i.e. set-up as inputs) they may be masked to ease/limit the source detection.

    This approach was long used w/past/lesser MCUs - which rarely (never) enjoyed the ability to (directly) identify the specific, interrupt-triggering, port bit.

    (Joseph Yiu's book (still) awaits you...)

  • Isn't that exactly what the code below is supposed to do?

    void GPIOJIntHandler(void)
    {
    uint32_t ui32Status;
    ui32Status = MAP_GPIOIntStatus(GPIO_PORTJ_BASE, true);

    ...

    }

    This is an excerpt than spans across lots of Tivaware examples. Your ui32Status variable contains the bits related to the ports which triggered the interrupt.

  • Poster noted that Ports P & Q succeeded in that "triggering" identification.

    My belief continues that (only) few ports contain this "bit-level, trigger ID, detail" yet I'd not bet the (entire) farm...

    Logically speaking - should what you note prove true - why would this vendor make "special efforts" for those "trigger-identification eased" Ports? I believe that's a critical point - missed by your suggestion...   (where's Luis when we need him?)

  • Agreed CB1, some applications might need a response to a pin RIGHT AWAY, before there's time to check for a bitmask variable...

    But then again... in most configurations (on mine at least), the interrupts work in a way that code does not reply to a new interrupt immediately when it is already serving another ISR - it will service the later call only after finishing the first one... meaning that "critical real time" is dead here.

    So, should we really complain of another 8 cycles (~60ns?) to verify the right pin? If so, well, then the typical TM4 microcontroller is not the answer for that application...

    Further, experience is gold: simply having read and noted that some ports have pin-specific interrupts is a major step forward, and the developer might as well use those interrupts for all his relevant needs (instead of choosing any port, and 3 months later when working the code, discover that such stupid port does not have pin-level irq!!!)

    If the application involve "precise timing", then a DMA approach is a nice solution. We've got one such example, when the border crossing would trigger two dma's: one for the port values and one for a continuous timer. We would only service those values some microseconds later, but at least we had a more reliable information of "precisely when" the border got crossed.
  • Hi Bruno,

    You make good points - yet I can find no response to the (success) of your (proposed) "MAP_GPIOIntStatus()" function when aimed at a "standard" (not "bit-level, trigger detecting") Port.

    If that call succeeds across ALL Ports - your post adds much value. Yet - vendor's "special efforts" aimed at only, (some ports) makes me suspicious of the "universality" of your approach... (i.e. vendor's (bit detect) effort nearly pointless - if such a function exists & resolves!)   I don't believe (vendor) missed that!

  • Hi, CB1, thanks for your reply, I tried with the function MAP_GPIOIntStatus(), I find that, the function partially works, but no matter which pin trigger the interrupt, the TM4C129EXL board only treat the interrupt was triggered by the very first pin, here is the code for the ISR I have been put in, there is two question I need your help,

    • when the interrupt was triggered, the ISR was loaded, but when I put a break point in side the code  LEDWrite(CLP_D1, 1); I found that the ISR was triggered again before the interrupt was acknowledged and the LED was lighted, is that because the switch bounce? or because the edge detection is very fast, the ISR was trigger again
    • MAP_GPIOIntStatus(GPIO_PORTJ_AHB_BASE,true)&GPIO_INT_PIN_1, this is a code line I used to detecte the interrupt raw status register, I was confused and I am trying to catch the result from status_int=GPIOIntStatus(GPIO_PORTJ_BASE,true); but it did not works, every time it show me a runtime error, cannot load from non-primitive location, is that because the edge level is very fast so in that way the status will never be catched?
    void GPIOjIntHandler(void)
    {
    	uint32_t status_int;
    	status_int=GPIOIntStatus(GPIO_PORTJ_BASE,true);
    	//UARTprintf((char *)GPIO_PORTJ_AHB_RIS_R);
    	//uint32_t statusRIS = GPIO_PORTJ_AHB_RIS_R;
    	if (MAP_GPIOIntStatus(GPIO_PORTJ_AHB_BASE,true)&GPIO_INT_PIN_0)
    	{
    		MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_0);
    		LEDWrite(CLP_D1, 1);
    	}
    
    	if (MAP_GPIOIntStatus(GPIO_PORTJ_AHB_BASE,true)&GPIO_INT_PIN_1)
    	{
    		MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_1);
    		LEDWrite(CLP_D1,0);
    	}
    	//LEDWrite(CLP_D1, 0);
    }

     

  • Here is the entire code I used.
    
    #include <stdint.h>
    #include <stdbool.h>
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_nvic.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/systick.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "drivers/pinout.h"
    #include "utils/uartstdio.h"
    
    uint32_t g_ui32SysClock;
    
    
    void  InitializeSensor(void)
        {
    
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ); //PORT J
        ROM_SysCtlDelay(3);
        GPIOPinTypeGPIOInput(GPIO_PORTJ_BASE,GPIO_PIN_0);  //PJ0
        GPIOPinTypeGPIOInput(GPIO_PORTJ_BASE,GPIO_PIN_1);  //PJ1
    
        GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_0, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD);
        GPIOPadConfigSet(GPIO_PORTJ_BASE, GPIO_PIN_1, GPIO_STRENGTH_12MA, GPIO_PIN_TYPE_STD);
    
        ROM_GPIOIntTypeSet(GPIO_PORTJ_BASE, GPIO_PIN_0, GPIO_RISING_EDGE);
        ROM_GPIOIntTypeSet(GPIO_PORTJ_BASE, GPIO_PIN_1, GPIO_FALLING_EDGE);
        ROM_SysCtlDelay(3);
    
        //ROM_IntMasterEnable();
    
        GPIOIntEnable(GPIO_PORTJ_BASE, GPIO_PIN_0);
        GPIOIntEnable(GPIO_PORTJ_BASE, GPIO_PIN_1);
    
        ROM_IntEnable(INT_GPIOJ);
        }
    
    
    void GPIOjIntHandler(void)
    {
    	uint32_t status_int;
    	status_int=GPIOIntStatus(GPIO_PORTJ_BASE,true);
    	if (MAP_GPIOIntStatus(GPIO_PORTJ_AHB_BASE,true)&GPIO_INT_PIN_0)
    	{
    		MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_0);
    		LEDWrite(CLP_D1, 1);
    	}
    
    	if (MAP_GPIOIntStatus(GPIO_PORTJ_AHB_BASE,true)&GPIO_INT_PIN_1)
    	{
    		MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_1);
    		LEDWrite(CLP_D1,0);
    	}
    	//LEDWrite(CLP_D1, 0);
    }
    
    int main(void) {
    
        //
        // Run from the PLL at 120 MHz.
        //
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
                    SYSCTL_OSC_MAIN | SYSCTL_USE_PLL |
                    SYSCTL_CFG_VCO_480), 120000000);
    
    
        //
        // Configure the device pins.
        //
        PinoutSet(false, false);
    
        ConfigureUART();
        InitializeSensor();
    
        ROM_SysCtlDelay(1);
        GPIO_PORTJ_AHB_IS_R &= ~0x10;
        GPIO_PORTJ_AHB_IBE_R &= ~0x10;
        GPIO_PORTJ_AHB_IEV_R &= ~0x10;
        GPIO_PORTJ_AHB_ICR_R = 0x10;
        GPIO_PORTJ_AHB_IM_R |= 0x10;
    
    
    
        ROM_IntEnable(INT_GPIOJ);
        ROM_IntMasterEnable();
        ROM_IntPrioritySet(INT_GPIOJ, 0x00);
    
    
        ROM_IntMasterDisable();
    	*/
        while (1)
        {
    
        }
    	return 0;
    }
    

  • Ni hao,

    I'm not double checking that port J has individual interrupt, but for ports with "one interrupt for the whole port", your sequence is a bit wrong. It should be:

    1) Read the interrupt status into the uint32 variable. Note that some implementations consider "true" as 0b00000001, which I believe is not the proper mask (again, writing this from memory with no documentations available) - maybe you should use 0xFF as the mask there.

    2) Immediatelly erase the GPIO interrupts (the initial state is on your variable already!)

    3) Now, compare your variable status_int to the desired port, one "bit" per port, such as

    IF (status_int & 0x01) { // interrupt was on pin 0;}

    If (status_int & 0x02) { // interrupt was on pin 1;}

    ...all the way to pin7, of course, checking only the pins that are connected/configured.

    Do not clear bit by bit of the interrupt.

  • I believe you're in most able hands here w/Bruno. My review of vendor's "GPIOPinIntStatus()" confirms Bruno's belief - and I can find NO limitation based upon Port's "Individual Pin Interrupt" capability. (such was my earlier concern - appears unfounded...)
  • Hi, Bruno, so if the Port J does not have individual interrupt, so the status_int will always return the interrupt is generated by the very first pin by the port J, that is what I got if I use the code below.

    void GPIOjIntHandler(void)
    {
        uint32_t status_int;
        status_int=GPIOIntStatus(GPIO_PORTJ_BASE,true);
        MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_0);
        MAP_GPIOIntClear(GPIO_PORTJ_AHB_BASE,GPIO_INT_PIN_1);
        if(status_int&0x01)
        {
        		LEDWrite(CLP_D1, 1);
        }
        else if (status_int&0x02)
        {
        		LEDWrite(CLP_D1, 0);
        }
        else
        {
    
        }
    
    }

  • I checked the documentation of the GPIOIntStatus(), and for the return variables, it shows that

    Returns the current interrupt status for the specified GPIO module. The value returned is the logical OR of the GPIO_INT_* values that are currently active.

    what the logical or of the GPIO_INT_*, is there a way we can check the source code of the ROM_program of the tiva c?

    Shan
  • Probably going over the threshold with so many details on this post, but let me try to help you and hopefully other readers...

    Q: Can you check the source on ROM?
    A: Don't bother: the source on ROM for TivaWare does the same as the Tivaware library functions. That means that, when you need to "find out what a Tivaware function actually does", simply open the relevant .c file.

    In this particular case, look inside driverlib/gpio.c.

    uint32_t
    GPIOIntStatus(uint32_t ui32Port, bool bMasked)
    {
        ASSERT(_GPIOBaseValid(ui32Port)); 
        if(bMasked)
        {
            return(HWREG(ui32Port + GPIO_O_MIS));     // Return the interrupt status.
        }
        else
        {
            return(HWREG(ui32Port + GPIO_O_RIS));
        }
    }

    Now, look at what are those registers, one is the Raw Interrupt Status (GPIORIS) and the other one is the Masked Status (GPIOMIS). Just go on and do the test, you will see that such register gives you exactly what you are looking for. Interrupts are "second lesson" for microcontrollers, just play with them a bit and you should not have further doubts.

    Again, don't bother dealing with the register along your IRS - just read the value at the very beginning and treat the variable later on. Also, remember to clear the interrupt flags as "early as possible" inside your IRS, which means right after you've ready the status!

    Hao bu hao?

  • Hao, Thanks, I found that if I put a breakpoint in the ISR, I will see the ISR will be read twice, the first time did nothing, but the second time, the ISR will invoke. is that because the switch bounce? just a pure guess.

    Shan
  • Debouncing is a whole science in itself, switches can bounce a lot! It is not impossible that a single press into a switch causes your interrupt routine to be calles as much as 10 times! You will figure out, eventually, that every push button software implementation has to consider debouncing. And some hardware solutions won't hurt either... Good luck there!

    By the way, I suggest you search for Jack Ganssle "A Guide to Debouncing".