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.

SYSBIOS: AM335x GPIO interrupt configuration

Part Number: SYSBIOS

Hello,

For two days I've been wandering lost through this documentation, trying to find out how one can connect a GPIO event (for example GPIO_INT_TYPE_FALL_EDGE) to the ISR that is declared in the CFG file, or created dynamically.

(Not that this should affect things, but I'm using CCS 7, am335x PDK 1.0.7, SYS/BIOS 6.46.5.55)

This page says (obviously) how to activate the GPIO pins.  But doesn't say HOW  the input change event it tied to the ISR. 

https://processors.wiki.ti.com/index.php/StarterWare_GPIO_V2

I've searched dozens of posts, and found no information.  This document, also, doesn't detail HOW they are connected.

http://www.ti.com/lit/ug/spruex3t/spruex3t.pdf

I can "register" HWIs all day long.  And I can enable the GPIO lines.

But where is the code that actually says :"When the GPIO Input changes, here is the ISR function" ?

Can someone point me to the most basic, fundamental information?  Because it's all a breeze in a TIVAC device without using SYSBIOS.  But I'm trying to do this in a BBB.  And I would like to avoid doing an end-run and going straight to the metal to register things (as I have done before).  After all, SYSBIOS is supposed to support all this stuff.

Here is the basic code block I am using  "Task_create()" and "BIOS_start()" left off, because they are not relevant to what I need to do.

Hwi_Handle myHwi;
Error_Block eb;
Void myHwiFxn(UArg arg);

void ConfigureHardwareInterrupt() {
    Error_init(&eb);

    GPIOModuleEnable(SOC_GPIO_1_REGS,1);
    GPIOModuleReset(SOC_GPIO_1_REGS);

    GPIOSetDirMode(SOC_GPIO_1_REGS,28,GPIO_DIRECTION_INPUT );

    GPIOSetIntrType(SOC_GPIO_1_REGS,28,GPIO_INTR_MASK_FALL_EDGE );
    GPIOIntrClear (SOC_GPIO_1_REGS,0,28);

    GPIOIntrEnable (SOC_GPIO_1_REGS,0,28);

    /*  This is a SYSBIOS HWI...   WHAT TRIGGERS IT??  */
    myHwi = Hwi_create(5, myHwiFxn, NULL, &eb);
    if (myHwi == NULL) {
        System_abort("Hwi create failed");
    }
}


Void myHwiFxn(UArg arg) {
    /*  This SHOULD clear the interrupt flag so it doesn't re-enter.  */
    GPIOIntrClear (SOC_GPIO_1_REGS,0,28);
    /*  Here we CLEAR the triggered SYSBIOS HWI, which we have no idea what trigger it yet */
    Hwi_clearInterrupt(5);
}

Of course, app.cfg has "var Hwi = xdc.useModule('ti.sysbios.hal.Hwi');" in it, but nothing is using it.

Thanks for any assistance.

  • Additional...

    Apparently "GPIOModuleEnable" and "GPIOModuleReset"  fucntions are in library "C:\ti\pdk_am335x_1_0_7\packages\ti\csl\lib\am335x\a8\release\ti.csl.aa8fg"

    However, "GPIOSetDirMode", "GPIOSetIntrType", "GPIOIntrClear", "GPIOIntrEnable"  and NOWHERE to be found.  Despite being documented in

    C:\ti\pdk_am335x_1_0_7\packages\ti\starterware\docs\API_Reference_02_01_01_03.chm

    Can anyone please tell me which libraries those functions are hidden in?

  • Hi Christopher,

    I suggest using the GPIO LLD:

    An LED blink test is provided which configures and uses GPIO interrupts with SYSBIOS. Documentation for creating PDK projects is here: http://software-dl.ti.com/processor-sdk-rtos/esd/docs/06_01_00_08/rtos/index_overview.html#pdk-example-and-test-project-creation. Issue this on the command line from <PDK>\packages to create the GPIO LED blink example:

    pdkProjectCreate AM335x bbbAM335x little gpio test arm

    The GPIO interrupt is configured in main_led_blink.c, function gpio_test():

    /* Set the callback function */
    GPIO_setCallback(USER_LED0, AppGpioCallbackFxn);

    /* Enable GPIO interrupt on the specific gpio pin */
    GPIO_enableInt(USER_LED0);

    Here the application callback function is AppGpioCallbackFxn(). The HWI is configured inside the driver GPIO_init() function, so it shouldn't be necessary to handle this in the application. The code for the HWI configuration is contained in GPIO_v1.c, function GPIO_setConfig_v1():

    /* Initialize with defaults */
    Osal_RegisterInterrupt_initParams(&interruptRegParams);

    interruptRegParams.corepacConfig.isrRoutine = (&GPIO_v1_hwiFxn);
    interruptRegParams.corepacConfig.arg = (uintptr_t)(portNum);

    interruptRegParams.corepacConfig.priority = GPIO_v1_config.intPriority;
    interruptRegParams.corepacConfig.name=NULL;
    interruptRegParams.corepacConfig.corepacEventNum=hwAttrs->line1EventId; /* Event going in to CPU */
    interruptRegParams.corepacConfig.intVecNum=hwAttrs->line1IntNum; /* Host Interrupt vector */

    /* Register interrupts */
    GPIO_osalRegisterInterrupt(&interruptRegParams,&(hwiHandle));

    The GPIO LLD uses the Chip Support Library (CSL). If for some reason you need to configure the HWI at the application level, I suggest you use the GPIO LLD code as a guide.

    Regards,
    Frank

  • Hi Christopher,

    The "GPIOSetDirMode", "GPIOSetIntrType", "GPIOIntrClear", "GPIOIntrEnable" functions are defined in the Starterware library libdal.a. Please see <PDK>\pdk_am335x_1_0_16\packages\ti\starterware\binary\dal\lib\am335x-evm\a8\release\gcc.

    Similar functions are provided for the CSL, please see:

    • <PDK>\packages\ti\csl\csl_gpio.h
    • <PDK>\packages\ti\csl\src\ip\gpio\V1\gpio_v2.h
    • <PDK>\packages\ti\csl\lib\am335x\a8\release\ti.csl.aa8fg.

    For example:

    /**
    * \brief This API configures the direction of a specified GPIO pin as being
    * either input or output.
    *
    * \param baseAdd GPIO base address
    * \param pinNumber The number of the pin in the GPIO instance
    * \param pinDirection The required direction for the GPIO pin
    *
    * 'pinNumber' can take one of the following values:
    * (0 <= pinNumber <= 31)\n
    *
    * 'pinDirection' can take one of the following values:
    * - GPIO_DIR_INPUT - to configure the pin as an input pin\n
    * - GPIO_DIR_OUTPUT - to configure the pin as an output pin\n
    *
    * \return None
    */
    void GPIODirModeSet(uint32_t baseAdd,
                        uint32_t pinNumber,
                        uint32_t pinDirection);

    Regards,
    Frank

  • Frank,
    Thanks for the reply, but this is getting worse...

    Staring with something from the example BLINK project 

    GPIO_setCallback(USER_LED0, AppGpioCallbackFxn);

    USER_LED0 is an enumerated value of either 0 or 1... How does that connect to GPIO1_21 (Register 1, bit 21)??

    I added the DAL library, and now have multiple definitions for GPIOPinRead, GPIOPinWrite, GPIOModuleEnable, etc...
    Which, as you state, are also in (and conflicts with) ti.csl.aa8fg... a lib that I am NOT specifying, but is likely coming from the CFG file, which yet ANOTHER undocumented component,  and supposed to be managed by that KONG tool.
    (Where is "ti.drv.spi" defined?  There is NOTHING I can find that describes how to use it...  But I can find lots on "ti.sysbios....") 

    The BLINK project is the most convoluted example of simply accessing a GPIO pin I've ever seen. Trying to connect the dots between that project and the online CSL doc is futile...

    And, of course, it's not using GPIOPinRead, GPIOPinWrite, GPIOModuleEnable anyway.

    Here are some examples of useless statements from the CSL online doc...

    "In addition it initializes UART instance for Console/STDIO."
    I've run into this before... How does someone disable it?
    It is FORCING me to link to libs I don't want to link to, provide hundreds of lines of code to supply structures I don't want.
    Even if you DON'T specify "BOARD_INIT_UART_STDIO" in Board_init() it still doesn't link for missing identifiers. 
    What if I don't WANT it to? What if I need that UART for some other purpose?


    "GPIO_soc.c binds driver with hardware attributes on the board. "
    That file has a structure called: GPIO_v1_hwAttrs_list... They NAME the file, but do not provide any documentation about the structure it contains.
    "Hardware attributes includes base address, interrupt number etc."
    "Etc." is always fun... It means there a boat load of other stuff that isn't adequately defined anywhere.

    The Blink project uses something called "GPIO_v1_Config", which does not appear anywhere in the documentation.

    I've gone into the DOC folders...  They are just auto-generated stuff from header files.

    And any documentation created by "Doxygen" is just a demonstration of the abdication of responsible doc writing.
    You are aware, aren't you" that the stuff it spits out is NOT search-able? You have to know WHAT and WHERE anything is
    in order to drill down and find it? And most of the information in it is just circular definitions?
    Then there is no place where it answers one of the most fundemental questions: What LIB contains this function which I need?
    Plus it is bereft of any examples...

    Sorry to rant...  but this is actually going BACKWARD in terms of understanding the architecture of this code.  The TIVAC is lightyears ahead in their doc.

  • Frank,

    I appreciate your answers, even if it's just muddying all this for me...  And really frustrating me.

    So I am trying a new tact on this.  And have only one question which you can probably answer.  But after some "level-set" understanding.

    Please set aside some time to read this, so as to not rush through it.

    I am scrapping the CSL, the UART,  the GPIO, and board stuff from the CFG, as I have no doc, and no control over what it does.  I barely understood how to remove it.

    I am gonna go with just plain starterware, compiled into the basic RTOS tasks.

    So lets level set what I understand...  Please correct me if I'm in error.

    Years ago, when the world was young, CPU's had one interrupt line.  Trigger it, and the ISR vector gets called.  Priority was controlled by the ISR deciding what it should do first.

    Next, someone came up with TWO interrupt vectors, of different priority.  Input lines or internal peripherals could decide which interrupt level they wanted to use.

    Now, this Sitara chip has a big table of interrupts.  It would appear something like 128.

    So... How to attach (using starterware) the GPIO line to one of those vectors?

    I see in "gpio.h" all the calls to set the GPIO to be an interrupt.  And I see in "interrupt.h" all the calls to register an ISR to a specific interrupt number (which I assume is the vector from the vector table).

    Also, I see in the RTOS, the "Hwi_create(...)" that you can specify the ISR vector entry you want to have trigger the HWI.

    Simply put:  In starterware, what function would be used to tell the GPIO line which of the interrupt vectors it should trigger on?

    I can see all this easily in the TIVAC packages. In fact, I even wrote assembly to set some of those vectors as I needed.  But that one step is missing from the PDK's starterware libs.  I'm tempted to do it myself, but there really has to be a way...  somehow.

    If you know the answer, it would be greatly appreciated.  Thanks.

  • I figured it out...  So I am answering, and posting this so anyone in the future can see how to simply set up GPIOs as inputs, outputs, and trigger an external interrupt...  While still using TI-TROS, tasks, and an HWI

    The use of the Interrupt Vector table entries are not definable.  They have predefined intentions (I don't know why I forgot that).  The table is defined in the reference document "spruh73" , table 6-1.

    The GPIOs are tied to interrupts [96, 97], [98, 99], [32, 33], [62, 63] for GPIO 0, 1, 2, and 3 respectively.

    Two interrupts per register, apparently because there are two interrupt "buses", allowing the two cores to react to interrupts without conflicting with each other.

    I gutted the CFG file of the ti.csl, ti.drv.gpio, ti.drv.uart, and  ti.drv.board  (while there is some doc on ti.sysbios, and ti.csl,  I can find nothing on ti.drv).  Doing that remove the bloat of the UART stuff, required GPIO_Config structure, and bunches of other overhead which is unexplained and (seems intentionally) obfuscated.  I even took out the ti.osal, since I can't find docs about what it does.

    Then I had to manually link to the DAL and SOC libs in the starterware directories "starterware\binary\dal\lib\am335x-evm\a8\release\gcc" and "starterware\binary\soc\lib\am335x-evm\a8\release\gcc"

    For posterity sake, I am posting my solution.  It has some comments. Also there is big block on the bottom that is #defined out. Originally  I could not find the equivalent functions in the PDK starterware libs, so I had to import them.  Then I found the actual SOC library call by just having to reverse engineer the PDK Starterware code (isn't the purpose of a library so you DON'T have to reverse engineer code to use it??  But again, no useful doc anywhere explaining any of these).  I left the code there in case someone else wants to examine it.

    Anyone trying this code will have to insure they have the correct search paths for the includes.

    The program is very simply.  Create a TI-RTOS task that flashes the built in LED on the BeagleBone board.  If a global is set, also turn on another LED, then turn if off on the next loop.  Because the task runs with 1/4 second delays, you can see when the flag is set and the LED lights, and I didn't have to worry about debounce.

    It ties GPIO 1 bit 28 to an interrupt.  Then defines a hardware interrupt (HWI) to run when the interrupt occurs. That HWI will SET the global flag.

    I didn't use any of the switches, instead the pin chosen is on the header P9, pin 12, so it could be jumpered out to something else in the real world.

    (I edited some code in this post, as there are other functions which I was writing, but not related to solving this problem - if it doesn't compile, it should be easy to figure out)

    /*
     *  ======== main.c ========
     */
    
    #include <xdc/std.h>
    
    #include <xdc/runtime/Error.h>
    #include <xdc/runtime/System.h>
    
    #include <ti/sysbios/BIOS.h>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/hal/Hwi.h>
    
    /*   Starterware headers...  */
    #include "gpio.h"        /* GPIO directions,  GPIOxxxxx(..)  functions */
    #include "hw/soc_AM335x.h"      /*  Hardware addresses for this Sitara */
    #include "hw/hw_types.h"       /* HWREG MACROS plus simple types (T/F, boolean, ..) */
    
    #include <ti/csl/csl_utils.h>       /*  That CONST  */
    #include <ti/starterware/include/prcm.h>       /*  Register Addresses of the AM335x  */
    #include <ti/starterware/include/hw/soc_am335x.h>       /*  Register Addresses of the AM335x  */
    #include <ti/starterware/include/am335x/hw_cm_per.h >
    
    
    void ConfigureHardwareInterrupt();
    //void GPIO1ModuleClkConfig(void);
    //void GPIO2ModuleClkConfig(void);
    /*
     *  ======== taskFxn ========
    */
    int intFlag;
    Void taskFxn(UArg a0, UArg a1) {
    
    
        do {
            GPIOPinWrite(SOC_GPIO_1_REGS,23,0);
            GPIOPinWrite(SOC_GPIO_1_REGS,24,1);
            Task_sleep(250);
            GPIOPinWrite(SOC_GPIO_1_REGS,24,0);
    
            if (intFlag) {
                GPIOPinWrite(SOC_GPIO_1_REGS,23,1);
                intFlag = 0;
            }
            Task_sleep(250);
    
        } while (1);
    
    }
    
    /*
     *  ======== main ========
     */
    Int main() {
        Task_Handle task;
        Error_Block eb;
        ConfigureHardwareInterrupt();
        Error_init(&eb);
        task = Task_create(taskFxn, NULL, &eb);
        if (task == NULL) {
    
            BIOS_exit(0);
        }
        BIOS_start();    /* does not return */
        return(0);
    }
    
    
    Hwi_Handle myHwi;
    Error_Block eb;
    Void myHwiFxn(UArg arg);
    
    void ConfigureHardwareInterrupt() {
        Error_init(&eb);
    
    //    GPIO1ModuleClkConfig();  // Use when we couldn't find the way to init the module...
    //    GPIOSetDirMode(SOC_GPIO_1_REGS,24,GPIO_DIRECTION_OUTPUT );
    
        PRCMModuleEnable(CHIPDB_MOD_ID_GPIO,1,0);   /*  from SOC lib */
    
        GPIOModuleEnable(SOC_GPIO_1_REGS,1);
    //    GPIOModuleReset(SOC_GPIO_1_REGS);      // Whatever this is, it blocks and doesn't return
    
        GPIOSetDirMode(SOC_GPIO_1_REGS,24,GPIO_DIRECTION_OUTPUT );   //  The BBB LEDs
        GPIOSetDirMode(SOC_GPIO_1_REGS,23,GPIO_DIRECTION_OUTPUT );
    
    
       //    Define the Interrupt to be on GPIO1, BIT 28
        GPIOSetDirMode(SOC_GPIO_1_REGS,28,GPIO_DIRECTION_INPUT );
        GPIOSetIntrType(SOC_GPIO_1_REGS,28,GPIO_INTR_MASK_FALL_EDGE );
        GPIOIntrClear (SOC_GPIO_1_REGS,0,28);
        GPIOIntrEnable (SOC_GPIO_1_REGS,0,28);
    
        /*  This is a SYSBIOS HWI... We want to trip on the interrupt 96 from the vector table */
        myHwi = Hwi_create(98, myHwiFxn, NULL, &eb);
        if (myHwi == NULL) {
            System_abort("Hwi create failed");
        }
    }
    
    
    Void myHwiFxn(UArg arg) {
        /*  This SHOULD clear the interrupt flag so it doesn't re-enter.  */
        GPIOIntrClear (SOC_GPIO_1_REGS,0,28);
        /*  Here we CLEAR the triggered SYSBIOS HWI */
        Hwi_clearInterrupt(98);
    
        intFlag = 1;
    }
    
    #ifdef USE_MINE_NOT_THE_PDK
    /**********************************************
     *  LIFTED FROM STARTERWARE - Because the ways to actually activate
     *  IO lines is so screwy, it was almost better to just grab the code
     *  and implement it myself
     ****************************************/
    
    /*
    ** This function enables the L3 and L4_PER interface clocks.
    ** This also enables functional clocks of GPIO1 instance.
    */
    void GPIO1ModuleClkConfig(void) {
    
        /* Writing to MODULEMODE field of CM_PER_GPIO1_CLKCTRL register. */
        HWREG(SOC_CM_PER_REGS + CM_PER_GPIO1_CLKCTRL) |=
              CM_PER_GPIO1_CLKCTRL_MODULEMODE_ENABLE;
    
        /* Waiting for MODULEMODE field to reflect the written value. */
        while(CM_PER_GPIO1_CLKCTRL_MODULEMODE_ENABLE !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO1_CLKCTRL) &
               CM_PER_GPIO1_CLKCTRL_MODULEMODE));
        /*
        ** Writing to OPTFCLKEN_GPIO_1_GDBCLK bit in CM_PER_GPIO1_CLKCTRL
        ** register.
        */
        HWREG(SOC_CM_PER_REGS + CM_PER_GPIO1_CLKCTRL) |=
              CM_PER_GPIO1_CLKCTRL_OPTFCLKEN_GPIO_1_GDBCLK;
    
        /*
        ** Waiting for OPTFCLKEN_GPIO_1_GDBCLK bit to reflect the desired
        ** value.
        */
        while(CM_PER_GPIO1_CLKCTRL_OPTFCLKEN_GPIO_1_GDBCLK !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO1_CLKCTRL) &
               CM_PER_GPIO1_CLKCTRL_OPTFCLKEN_GPIO_1_GDBCLK));
    
        /*
        ** Waiting for IDLEST field in CM_PER_GPIO1_CLKCTRL register to attain the
        ** desired value.
        */
        while((CM_PER_GPIO1_CLKCTRL_IDLEST_FUNC <<
               CM_PER_GPIO1_CLKCTRL_IDLEST_SHIFT) !=
               (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO1_CLKCTRL) &
                CM_PER_GPIO1_CLKCTRL_IDLEST));
    
        /*
        ** Waiting for CLKACTIVITY_GPIO_1_GDBCLK bit in CM_PER_L4LS_CLKSTCTRL
        ** register to attain desired value.
        */
        while(CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_1_GDBCLK !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKSTCTRL) &
               CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_1_GDBCLK));
    }
    
    
    void GPIO2ModuleClkConfig(void)
    {
        /*  All changes are to register 44E0 + the control offset of AC:  */
    
        /* Writing to MODULEMODE field of CM_PER_GPIO1_CLKCTRL register.
         * Bit 1 is enable the module  */
        HWREG(SOC_CM_PER_REGS + CM_PER_GPIO2_CLKCTRL) |=
              CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE;
    
        /* Waiting for MODULEMODE field to reflect the written value. */
        while(CM_PER_GPIO2_CLKCTRL_MODULEMODE_ENABLE !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO2_CLKCTRL) &
               CM_PER_GPIO2_CLKCTRL_MODULEMODE));
        /*
        ** Writing to OPTFCLKEN_GPIO_1_GDBCLK bit in CM_PER_GPIO1_CLKCTRL
        ** register.
        ** Set Optional Functional Block Control (bit 18)
        */
        HWREG(SOC_CM_PER_REGS + CM_PER_GPIO2_CLKCTRL) |=
              CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK;
    
        /*
        ** Waiting for OPTFCLKEN_GPIO_1_GDBCLK bit to reflect the desired
        ** value.
        */
        while(CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO2_CLKCTRL) &
               CM_PER_GPIO2_CLKCTRL_OPTFCLKEN_GPIO_2_GDBCLK));
    
        /*
        ** Waiting for IDLEST field in CM_PER_GPIO1_CLKCTRL register to attain the
        ** desired value.
        ** Testing the mask of bits 16, 17 waiting for them to turn OFF
        */
        while((CM_PER_GPIO2_CLKCTRL_IDLEST_FUNC <<
               CM_PER_GPIO2_CLKCTRL_IDLEST_SHIFT) !=
               (HWREG(SOC_CM_PER_REGS + CM_PER_GPIO2_CLKCTRL) &
                CM_PER_GPIO2_CLKCTRL_IDLEST));
    
        /*
        ** Waiting for CLKACTIVITY_GPIO_1_GDBCLK bit in CM_PER_L4LS_CLKSTCTRL
        ** register to attain desired value.
        ** Testing the base plus offset 0 (Clock Status Ctrl)  bit 19
        ** is "GPIO 1 Clock is active"
        */
        while(CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK !=
              (HWREG(SOC_CM_PER_REGS + CM_PER_L4LS_CLKSTCTRL) &
               CM_PER_L4LS_CLKSTCTRL_CLKACTIVITY_GPIO_2_GDBCLK));
    }
    
    
    #endif
    
    

    Hopefully this will help the next person in the future trying to make sense of these things.