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.

DK-TM4C123G - Need Advice on Adding Device Drivers to my .cfg file

Other Parts Discussed in Thread: MSP430WARE, TMP006, SYSBIOS

I have the DK-TM4C123G board and I'm using CCS with TI-RTOS 1.21.00.09

I have designed a piggy-back board that adds the following:

- SPI device connected to SPI3 on PK3..0

- UART device on UART1 PC5..4 PF1..0 (latter for CTS-RTS)

- UART device on UART4 PJ1..0

- SPI device on SSI3 PK3..0

I'm trying to work from the example project grlibdemo_TivaTM4C123GH6PGE

I've been reading through the TI-RTOS 1.21 UG (spruhd4e.pdf) document and it talks about modifying the board file - DK_TM4C123G.c

I've written non-RTOS code that talks to my piggy back board but I am seeking advice on how to proceed as far as working in RTOS from my chosen example.

Questions:

1. Once I've clicked on Add UART/SPI to my configuration, what do I need to add in terms of code?

2. Will the XDC tools write the code into the files for me?

3. Is there a checklist of what I need to do to add the specific parts I want to add?

  • Hi Ted,

    Were you planning on using the TI-RTOS drivers for your "piggy-back" board?  If you are, you should look at the UART and SPI examples that come with TI-RTOS.
    See the TI-RTOS support wiki page:  http://processors.wiki.ti.com/index.php/TI-RTOS_Support

    #1.  Again, see the UART and SPI example in TI-RTOS.  You might want to also want to walk through the TI-RTOS training lab tutorial.  http://processors.wiki.ti.com/index.php/File:MCU_Deep_Dive_TI-RTOS_Training_Lab.pdf
    The application should:
    a.  #include <ti/drivers/Driver.h>
    b.  Initialize the hardware via the board files
    c.  Call Driver_open(), Driver_close(), and Driver_* APIs.

    #2.  No xdc.useModule only allows your application to find the correct TI-RTOS driver libraries. (TivaWare, MSP430Ware, MWare, etc...)

    #3.  See the "Porting to a custom development board" wiki page.  It describes a few portions of the "board" files that need to be modifiled.
    http://processors.wiki.ti.com/index.php/TI-RTOS_Porting_Guide
    http://processors.wiki.ti.com/index.php/Migrating_a_TI-RTOS_project_to_a_custom_development_board

    Judah

  • Thanks for the input Judah.

    I do want to eventually use TI-RTOS drivers for my  "piggy-back" board so I realize that I'll have to eventually add to the hardware definitions in DK_TM4C123G.c and .h

    I thought I'd start off simple by leaving my  "piggy-back" board disconnected and trying to add I2C functionality to the grlibdemo example so that I could read the data from the MPU9150 motion sensor IC that's standard on the DK-TM4C123G board and connected to I2C3 on PD[1..0].  I have been walking through the code in DK_TM4C123G.c and that leaves me confused because the code seems to use only two I2C instances, I2C0 and I2C2 but the MPU9150 is connected to I2C3 so I think I'm going to have to add that to the enumerated set and include the hardware setup.  Can you confirm that?  Why would Ti leave the instance out for the actual hardware that connected on the dev board? Am I missing something?

    here's the code in DK_TM4C123G.c starting around line 209...

    #if TI_DRIVERS_I2C_INCLUDED
    #include <ti/drivers/I2C.h>
    #include <ti/drivers/i2c/I2CTiva.h>
    
    /* I2C objects */
    I2CTiva_Object i2cTivaObjects[DK_TM4C123G_I2CCOUNT];
    
    /* I2C configuration structure, describing which pins are to be used */
    const I2CTiva_HWAttrs i2cTivaHWAttrs[DK_TM4C123G_I2CCOUNT] = {
        {I2C0_BASE, INT_I2C0},
        {I2C2_BASE, INT_I2C2}
    };
    
    const I2C_Config I2C_config[] = {
        {&I2CTiva_fxnTable, &i2cTivaObjects[0], &i2cTivaHWAttrs[0]},
        {&I2CTiva_fxnTable, &i2cTivaObjects[1], &i2cTivaHWAttrs[1]},
        {NULL, NULL, NULL}
    };
    
    /*
     *  ======== DK_TM4C123G_initI2C ========
     */
    Void DK_TM4C123G_initI2C(Void)
    {
        /* I2C0 Init */
        /* Enable the peripheral */
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    
        /* Configure the appropriate pins to be I2C instead of GPIO. */
        GPIOPinConfigure(GPIO_PB2_I2C0SCL);
        GPIOPinConfigure(GPIO_PB3_I2C0SDA);
        GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
        GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    
        /* I2C2 Init */
        /* Enable the peripheral */
        SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);
    
        /* Configure the appropriate pins to be I2C instead of GPIO. */
        GPIOPinConfigure(GPIO_PF6_I2C2SCL);
        GPIOPinConfigure(GPIO_PF7_I2C2SDA);
        GPIOPinTypeI2CSCL(GPIO_PORTF_BASE, GPIO_PIN_6);
        GPIOPinTypeI2C(GPIO_PORTF_BASE, GPIO_PIN_7);
    
        I2C_init();
    }
    #endif /* TI_DRIVERS_I2C_INCLUDED */

     

  • more confusion because, in the file called 'Board.h, is the following...

    #define Board_I2C0                  DK_TM4C123G_I2C0
    #define Board_I2C1                  DK_TM4C123G_I2C2
    

    Note that it seems to imply that Board_I2C1 translates to DK_TM4C123G_I2C2

    Can you explain this or is it a typo?

  • Ted,

    Ted Mawson said:
    I have been walking through the code in DK_TM4C123G.c and that leaves me confused because the code seems to use only two I2C instances, I2C0 and I2C2 but the MPU9150 is connected to I2C3 so I think I'm going to have to add that to the enumerated set and include the hardware setup.  Can you confirm that?  Why would Ti leave the instance out for the actual hardware that connected on the dev board? Am I missing something?

    Sorry about that, that's my mistake. The DK_TM4C123G.[ch] was originally derived from a EK_LM4F232 development board, for which I only had I2C0 and I2C2 pinned out in the board files. I thought that the DM_TM4C123G was the same...but that's apparently not the case.

    But you are correct by adding the additional enumeration in DK_TM4C123G.c. Also, don't forget to add the additional enumeration in DK_TM4C123G_I2C2.h (DK_TM4C123G_I2CName).

    Regarding your 2nd post, these #define statements are used to make the TI-RTOS example more generic across other TI devices. It's basic mapping from the example application to the DK_4MC123.h. It isn't a typo as the DK_TM4C123G_I2C2 is defined in DK_TM4C123G.h.

    #define Board_I2C0                  DK_TM4C123G_I2C0
    #define Board_I2C1                  DK_TM4C123G_I2C2
    I've added a tracking number (SDOCM00106529) to get the DK_TM4C123G board files updated.
  • Tom,

    Thanks for the info.

    When you wrote about adding the enumeration in DKTM4C123G_I2C2.h I went looking for a file that doesn't exist.  I'm pretty sure you meant DKTM4C123G.h where there is an enum that now looks like this...

    /*!
     *  @def    DK_TM4C123G_I2CName
     *  @brief  Enum of I2C names on the DK_TM4C123G dev board
     */
    typedef enum DK_TM4C123G_I2CName {
        DK_TM4C123G_I2C0 = 0,
        DK_TM4C123G_I2C2,
        DK_TM4C123G_I2C3,						// ##TM##
    
        DK_TM4C123G_I2CCOUNT
    } DK_TM4C123G_I2CName;

    Please confirm that it's OK to miss out I2C1 which isn't used on the DK-TM4C123G board (or my piggy-back board).

  • Sorry to be so demaning but I'd like a recommendation on the steps I need to take to port the MPU9150 code from the non-TI-RTOS code that's in the qs-logger example for the MPU9150 into my modified grlibdemo TI-RTOS code.

    So to get data from the MPU9150 via I2C3, I'd like to leverage the structure that's in the qs-logger example which sets out a whole bunch of registers for all 3 axes of all 3 sensors and grabs them all at once.  The structure looks like this...

    //*****************************************************************************
    //
    // The structure that defines the internal state of the MPU9150 driver.
    //
    //*****************************************************************************
    typedef struct
    {
        //
        // The pointer to the I2C master interface instance used to communicate
        // with the MPU9150.
        //
        tI2CMInstance *psI2CInst;
    
        //
        // The AK8975 inst that used to access the on-chip AK8975 magnetometer
        //
        tAK8975 sAK8975Inst;
    
        //
        // The I2C address of the MPU9150.
        //
        uint8_t ui8Addr;
    
        //
        // The state of the state machine used while accessing the MPU9150.
        //
        uint8_t ui8State;
    
        //
        // The current accelerometer afs_sel setting
        //
        uint8_t ui8AccelAfsSel;
    
        //
        // The new accelerometer afs_sel setting, which is used when a register
        // write succeeds.
        //
        uint8_t ui8NewAccelAfsSel;
    
        //
        // The current gyroscope fs_sel setting
        //
        uint8_t ui8GyroFsSel;
    
        //
        // The new gyroscope fs_sel setting, which is used when a register write
        // succeeds.
        //
        uint8_t ui8NewGyroFsSel;
    
        //
        // The data buffer used for sending/receiving data to/from the MPU9150.
        //
        uint8_t pui8Data[24];
    
        //
        // The function that is called when the current request has completed
        // processing.
        //
        tSensorCallback *pfnCallback;
    
        //
        // The callback data provided to the callback function.
        //
        void *pvCallbackData;
    
        //
        // A union of structures that are used for read, write and
        // read-modify-write operations.  Since only one operation can be active at
        // a time, it is safe to re-use the memory in this manner.
        //
        union
        {
            //
            // A buffer used to store the write portion of a register read.
            //
            uint8_t pui8Buffer[6];
    
            //
            // The write state used to write register values.
            //
            tI2CMWrite8 sWriteState;
    
            //
            // The read-modify-write state used to modify register values.
            //
            tI2CMReadModifyWrite8 sReadModifyWriteState;
        }
        uCommand;
    }
    tMPU9150;
    

    I obviously need to put that structure somewhere, probably into DK_TM4C123G.h or would you create a new file?

    Then I need to refer to the data type in my line that I added in DK_TM4C123G.c

    const I2C_Config I2C_config[] = {
        {&I2CTiva_fxnTable, &i2cTivaObjects[0], &i2cTivaHWAttrs[0]},
        {&I2CTiva_fxnTable, &i2cTivaObjects[1], &i2cTivaHWAttrs[1]},
        {&I2CTiva_fxnTable, &i2cTivaObjects[2], &i2cTivaHWAttrs[2]},    // ##TM##
        {NULL, NULL, NULL}
    };
    

    can you help me with the correct syntax for this?

    The way that the qs-logger example works is that there's an interrupt pin that's connected to PB2 - goes low I think when there's data ready to be collected.  In a port to TI-RTOS, I presume that I'll have to set up an HWI to read the data when it's ready.

    I've got it figured in regular C code but I'm struggling to get how it will work in RTOS, for instance I'm not clear how to use the recommended callback function.

    Thanks in advance.

  • Ted,

    the DK_TM4C123G_I2CName enum you have is fine and so is I2C_config[].

    If you have your enum similar to this:

    typedef enum DK_TM4C123G_I2CName {
        DK_TM4C123G_I2C0 = 0,
        DK_TM4C123G_I2C2,
        DK_TM4C123G_I2C3,                       // ##TM##
        DK_TM4C123G_I2CCOUNT
    } DK_TM4C123G_I2CName;

    then make sure that you also add the i2cTivaHWAttrs struct as such:

    /* I2C configuration structure, describing which pins are to be used */
    const I2CTiva_HWAttrs i2cTivaHWAttrs[DK_TM4C123G_I2CCOUNT] = {
        {I2C0_BASE, INT_I2C0},  //I2C0
        {I2C2_BASE, INT_I2C2},  //I2C2
        {I2C3_BASE, INT_I2C3}   //I2C3
    };
    Add the extra pinmuxing code in DK_TM4C123G_initI2C() needed for your pins. You probably can just copy and paste and update some pins for I2C3.
    See the TI-RTOS User manual for a full explanation (section 5.2.2).
    After you have this all set up, you can write your application to use I2C_open(DK_TM4C123G_I2C3, &i2cparams); From there a I2C_transfer() will handle the I2C transactions. You can see the TMP006 or any of the other I2C examples. The TI-RTOS user guide (section 5.5) also explains on how to use the I2C driver.
    Ted Mawson said:

    The way that the qs-logger example works is that there's an interrupt pin that's connected to PB2 - goes low I think when there's data ready to be collected.  In a port to TI-RTOS, I presume that I'll have to set up an HWI to read the data when it's ready.

    I've got it figured in regular C code but I'm struggling to get how it will work in RTOS, for instance I'm not clear how to use the recommended callback function.

    Yes, the GPIO interrupt needs to be a Hwi. See section 5.5.6.4 for callback mode. Here's some pesudocode that you could use for your implementation. 
    Void gpioInterruptHwiFxn(UArg arg)
    {
        Semaphore_post(sem);
    }
    
    Void myFxn(I2C_Handle handle, I2C_Transaction *transaction, Bool success)
    {
        if (success) {
            // Finished I2C successfully
        }
        Semaphore_post(waitForFinish);
    }
    
    
    Void qsLoggerTask(UArg arg0, UArg arg1)
    {
        I2C_Params_init(&i2cParams);
        i2cparams.transferMode = I2C_MODE_CALLBACK;
        i2cparams.transferCallbackFxn = myFxn;
        i2c = I2C_open(DK_TM4C123G_I2C3, &i2cParams);
    
        while (1) {
           Semaphore_pend(sem, BIOS_WAIT_FOREVER);
            
           i2cTransaction.address = 0x99;
           i2cTransaction.writeBuf = x;
           i2cTranaction.writeCount = xx;
           i2cTransaction.readBuf = y;
           i2cTransaction.readCount = yy;
           I2C_transfer(i2c, &i2cTransaction);
    
           Semaphore_pend(waitForFinish, BIOS_WAIT_FOREVER); //Or use I2C blocking mode
        }
    }


    If you use the I2C driver in blocking mode then you won't need the waitForFinish semaphore.

  • In the TI-RTOS grlibdemo_TivaTM4C123GH6PGE example code, in file DK_TM4C123G.c there's a code segment associated with the I2C drivers, I've given the beginning part of it below...

    #if TI_DRIVERS_I2C_INCLUDED
    #include <ti/drivers/I2C.h>
    #include <ti/drivers/i2c/I2CTiva.h>
    
    /* I2C objects */
    I2CTiva_Object i2cTivaObjects[DK_TM4C123G_I2CCOUNT];
    
    /* I2C configuration structure, describing which pins are to be used */
    const I2CTiva_HWAttrs i2cTivaHWAttrs[DK_TM4C123G_I2CCOUNT] = {
        {I2C0_BASE, INT_I2C0},
        {I2C2_BASE, INT_I2C2},
        {I2C3_BASE, INT_I2C3}											// ##TM##
    };
    
    const I2C_Config I2C_config[] = {
        {&I2CTiva_fxnTable, &i2cTivaObjects[0], &i2cTivaHWAttrs[0]},
        {&I2CTiva_fxnTable, &i2cTivaObjects[1], &i2cTivaHWAttrs[1]},
        {&I2CTiva_fxnTable, &i2cTivaObjects[2], &i2cTivaHWAttrs[2]},    // ##TM##
        {NULL, NULL, NULL}
    };
    
    /*
     *  ======== DK_TM4C123G_initI2C ========
     */
    Void DK_TM4C123G_initI2C(Void)
    {
        /* I2C0 Init */

    In CCS, the code segment has a gray background which makes me think it's disabled, should I be defining the TI_DRIVERS_I2C_INCLUDED parameter?  I assumed that would get set in code when I used XDCtools to tell the project that I wanted to Add I2C to my configuration which I have done.

    Also in that code are function declarations that use the word 'Void' with a capitalized 'V' for example

    Void DK_TM4C123G_initDMA(Void)

    if I change it all to lower case, the word 'void' changes to a purple color which indicates that it's recognized.  Is there something special about using the word with a capital 'V'?

  • Ted,

    Ted Mawson said:
    In CCS, the code segment has a gray background which makes me think it's disabled, should I be defining the TI_DRIVERS_I2C_INCLUDED parameter?  I assumed that would get set in code when I used XDCtools to tell the project that I wanted to Add I2C to my configuration which I have done.

    Yeah, the grey'ed out code blocks are a known issue, but only a minor one. When you add xdc.useModule('ti.drivers.I2C'), it will defined TI_DRIVERS_I2C_INCLUDED for you. I'm not sure why the code block greyed out, my guess is because CCS doesn't know about the generated #define statement. You can find the #define statements in the CCS project's /Debug/configPkg/package/cfg/grlibdemo_*.h

    Ted Mawson said:

    Also in that code are function declarations that use the word 'Void' with a capitalized 'V' for example

    Void DK_TM4C123G_initDMA(Void)

    if I change it all to lower case, the word 'void' changes to a purple color which indicates that it's recognized.  Is there something special about using the word with a capital 'V'?

    "Void" isn't registered in CCS as a keyword, so that's why you don't see the syntax color change.

    All TI-RTOS examples use XDCtools data types. http://rtsc.eclipse.org/docs-tip/RTSC_Coding_Conventions#Data_Types

  • Tom,

    I'm trying to get to a continuous level of understanding such that I can attempt to write the I2C code for my application but I haven't got there yet.  Can I do a check of understanding on your pseudocode please?

    Obviously assuming that the initialization is all done....

    1. The HWI that triggers from the MPU9150 will cause your first gpioInterruptHwiFxn to run and all we want it to do is post the semaphore.  That will unblock the task qsloggerTask that sets up the i2Ctransaction data elements and then initiates a transfer

    2. The myFxn is the function that we point to for the callback I think?  All that it does is allow the task to loop back around and repeat what we've just been through - right?

    3. Clearly this code will go and get some data from the slave device once the slave has indicated - via GPIO HWI - that is is ready to be read but what about the initial setup writes to the device, are these done before we start running SYSBios?

     

    I appreciate the help. I understand that you're telling me that 'Void' isn't registered in CCS as a keyword (although 'void' clearly is) but the link you provided does include examples with the keyword Void in them so I'm assuming that 'Void' IS recognized by XDCtools - I guess the question might be, is 'void' recognized the same as 'Void' by XDCtools?.

    The original reference I posted was taken from the stock code in the TI-RTOS example in file DK-TM4C123G.c - it was not code written by me.

  • Ted,

    Ted Mawson said:
    1. The HWI that triggers from the MPU9150 will cause your first gpioInterruptHwiFxn to run and all we want it to do is post the semaphore.  That will unblock the task qsloggerTask that sets up the i2Ctransaction data elements and then initiates a transfer

    Correct. qsloggerTask initiates the I2C_transfer(). Since it's using the driver in callback mode, the task will not block and immediately drop out of I2C_transfer() to allow your task to continue execution. Meanwhile, the I2C driver will perform the I2C transaction in an event-based Hwi (interrupt) context. When the Hwi has determined it has finished the entire transaction (w/ or w/o errors), it will call the callback function "myFxn".

    Ted Mawson said:
    2. The myFxn is the function that we point to for the callback I think?  All that it does is allow the task to loop back around and repeat what we've just been through - right?

    Yes. The callback is used as means to let your application know that your I2C_transfer() has finished. Callback mode is useful if you want to process some code within the same task while you're waiting for the transaction to finish. If you use the driver in blocking mode, then you won't need a callback function because I2C_transfer will stop your task's execution until the transaction has finished. It saves you from needing the "waitForFinish" semaphore.

    Ted Mawson said:
    ..but what about the initial setup writes to the device, are these done before we start running SYSBios?

    No. Your application needs to configure the peripheral via I2C or whatever means it specifies in the datasheet. I'd recommend configuring the device in qsLoggerTask() before you get into the while(1) loop.

    Ted Mawson said:
    'Void' IS recognized by XDCtools - I guess the question might be, is 'void' recognized the same as 'Void' by XDCtools?.

    Yes.

  • In order to set up my interrupt for the MPU9150 (as wired on the DK-TM4C123G board), I need to set a GPIO interrupt on PB2.

    In file grlibdemo.c the GPIO interrupts seem to be set up using the following calls

    /* Init and enable interrupts */
        GPIO_setupCallbacks(&DK_TM4C123G_gpioPortMCallbacks);
        GPIO_enableInt(DK_TM4C123G_SW3, GPIO_INT_RISING);
        GPIO_enableInt(DK_TM4C123G_SW4, GPIO_INT_RISING);

    In a non-RTOS environment, I'd just enable the peripheral and then setup the port for input, add interrupt logic, and enable.  Does the above represent some specific way that interrupts need to be set up when using TI-RTOS vs normal C code?

    Thanks

  • Ted,

    The code snippet you posted comes from a GPIO driver that grlibdemo.c uses as part of the example. We provide it as an example but you don't have to use the GPIO driver in your application.

    If you wish, you can follow the same non-RTOS environment mechanism, but you'll have to create the Hwi on the interrupt vector for the GPIO port. Most TivaWare APIS are OK to use, just don't use the TivaWare APIs that attempt to manipulate the NVIC registers! You need the use HWI APIS for that.

  • Tom,

    Thanks for all your help.  I think I've got as far as a basic setup for the ports and I2C parameters that compiles without errors.  I've dug in so deep now that I have realized that the grlibdemo_TivaTM4C123GH6PGE code defines and sets up the I2C0 peripheral which defines PB2 for use as I2C0_SCL but, according to the schematic, PB2 is connected to pin 12 of the MPU9150 device (U2) and is labeled 'PB2_GYRO-INT and there is no I2C device connetced to I2C0. I am pretty sure this is an error as you can't have PB2 used as an interrupt input to the MCU and have it used as SCL output for the MCU.

    As far as I can see, the only I2C device on the DK-TM4C123G is the MPU9150 and it's connected to I2C3; it appears to be connected to be used in the way that the non-TI-RTOS example called qslogger demo (the program that's loaded to the DK-TM4C123G when it ships) uses it.

    I'm going to continue working on this but it would be really awesome if someone in TI could create a TI-RTOS demo example for the DK-TM4C123G that reads the 3 axes of accelerometer, gyro, and magnetometer from the MPU9150.  This would be a port of the qslogger code structures into TI-RTOS; I'd be happy to work with them to define and develop the code.

    It seems wrong that there are no I2C demo programs in the TI-RTOS examples for this $150 demo board which features the MPU9150 motion-sensing as the only I2C device on the board.

    I know you guys could do it faster than me and it would provide a great starting platform for all those people like me who want to use a TI processor to run the MPU9150 in an RTOS environment.

    What say you?

  • Hi Ted, 

    I am working on activating multiple UARTs. You know, the UART0 is usable via USB.

    But I am quite interested on your statemets:

    - UART device on UART1 PC5..4 PF1..0 (latter for CTS-RTS)

    - UART device on UART4 PJ1..0

    Do you make UART1and4 to be functional?

    How you do that? Could you help me out?

    Below link is my post...

    http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/385946/1362976.aspx#1362976

    Thanks in advance!

    Cheers,

    mh

  • Minghua,

    Are you using TI-RTOS? If so, what version?

    Todd

  • Hi Todd, I am using the "TivaWare_C_Series-2.1.0.12573" but not "tirtos_tivac_2_10_01_38"...

    Cheers,

    mh

  • If Ted does not answer, you probably should ask this question on the TivaC forum (http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908) since you are not using TI-RTOS.

    Todd