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.

TM4C1294NCPDT: Getting a USB device class not supported directly by TivaWare USBLIB to work

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: SYSCONFIG

Back in February I asked about "Implementing other USB device classes," and I went down a rabbit hole and sorta got lost in it all and put it aside as other pressing work demanded attention.

I just got back onto this. I did a survey of other processors that could do what I wanted -- USB High Speed support, 100 Mbit Ethernet and an external bus interface. Tiva M4 ticks all of the boxes, so I started digging back in.

I chose the USB MIDI class for two reasons. One, it's simple. Two, I need it for a design. So it makes sense to use it as a demonstration of how to implement USB device classes not supported by TivaWare. That USB MIDI is supported by all of the major operating systems means no custom host drivers, unlike the TivaWave USB Bulk demo.

I started basically from scratch. I updated to CCS 10 and TivaWave 2.2. I used the PinMux tool (now SysConfig) to generate the pinout file. I added the device driver library and the USB library to my project. I put the class device descriptors into the format that the TivaWare USB Library wants. (I developed the descriptors for a Silicon Labs EFM8UB2, so I know they are correct as that design is working.)

I used the usbdbulk device driver as a guide, but got rid of the stuff that is meant to support a composite device (that is, two instances of the same device class). For starters my goal was to just get the device to enumerate, with data transfer on the bulk endpoints to follow, so the handlers set up in tCustomHandlers are just stubs. The USB0DeviceIntHandler is put into the startup source in the correct place. All of the structures for the device information get set up as in the examples, and I call USBDCDInit().

And then nothing happened. There is no attempt at enumeration -- no USB traffic at all according to my analyzer.

Stepping through the code doesn't help as you can't step through the compiled library. So I deleted the library reference from the linker options and pulled all of the sources into the project. That compiles, now I can step through. And I see that it's doing what I expect, and even gets to "attach the device using the soft connect" and "enable the USB interrupt." Nothing happens after that connect.

But! "enable the USB interrupt." Is the interrupt even being triggered? And the answer is ... NO. Why the heck not? It's enabled. Global interrupts are enabled. The bulk example works, but mine does not. What is different? I start stepping through the bulk example, I get into the PinoutSet() function. Since bool bUSB is true, it initializes the USB-related pins. I checked my code to see what SysConfig created in pinout.c.

In SysConfig I enabled the USB peripheral, and checked the DM, DP, VBUS and EPEN signals. This results in a pinout.c that calls GPIOPinType for each of those pins.

Back in the USB bulk example's pinout, I see the same calls for the pins, except there's an addition:

       

        HWREG(GPIO_PORTD_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
        HWREG(GPIO_PORTD_BASE + GPIO_O_CR) = 0xff;
        MAP_GPIOPinConfigure(GPIO_PD6_USB0EPEN);

OK, what the heck is GPIO_LOCK_KEY? And what does this have to do with pin PD6?

Whatever -- I add that snippet to my code and voila -- enumeration as a USB MIDI device. I seem to be up and running.


The data sheet tells us that USB0EPEN can be assigned to pin PD6 and is "optionally used in Host mode to control an external power source to supply power to the USB bus." I'm a device, so who cares. But what is the lock all about?

  1. 10.3.4  Commit Control

    The GPIO commit control registers provide a layer of protection against accidental programming of critical hardware peripherals. Protection is provided for the GPIO pins that can be used as the four JTAG/SWD pins and the NMI pin (see “Signal Tables” on page 1772 for pin numbers). Writes to protected bits of the GPIO Alternate Function Select (GPIOAFSEL) register (see page 770), GPIO Pull Up Select (GPIOPUR) register (see page 776), GPIO Pull-Down Select (GPIOPDR) register (see page 778), and GPIO Digital Enable (GPIODEN) register (see page 781) are not committed to storage unless the GPIO Lock (GPIOLOCK) register (see page 783) has been unlocked and the appropriate bits of the GPIO Commit (GPIOCR) register (see page 784) have been set.

None of this has to do with JTAG/SWD or NMI -- so is this lock requirement something particular to the eval kit board or is it generally required?

  • Hello Andy,

    Did you add only the GPIO_LOCK_KEY sequence, or also the PD6 line?

    I am not seeing anything that the LOCK_KEY is needed for that pin, though some TM4C's also require PD7 for USB. So that code is probably due to that and not PD6.

    Can you remove the GPIO_LOCK_KEY piece and test to see if the code still works?

  • Ralph Jacobi said:

    Did you add only the GPIO_LOCK_KEY sequence, or also the PD6 line?

    I already had PD6 set in the SysConfig as USB0EPEN, so I added the GPIO_LOCK_KEY sequence as it was in the bulk driver (and it's in others, too).

    I am not seeing anything that the LOCK_KEY is needed for that pin, though some TM4C's also require PD7 for USB. So that code is probably due to that and not PD6.

    Some searching in the data sheet turned up PD7 as a possible pin for USB0PFLT, described as "Optionally used in Host mode by an external power
    source to indicate an error state by that power source." But I'm not in host mode so that is not relevant.

    Can you remove the GPIO_LOCK_KEY piece and test to see if the code still works?

    I did, and ... it does. My guess is that the unlock feature is there because PD7 can be configured as the NMI input. So if the design uses PD7 for any purpose, it has to be unlocked. And since I'm in Device mode, not Host or OTG, I don't need this input at all.


    Some further investigating. In the bulk driver example (drivers\pinout.c) in the if(bUSB) we find:

    MAP_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    PB1 is the USB0VBUS I/O, and PB0 is USB0ID input.

    In my code from scratch using SysConfig, I had not set up USB0ID. And if you don't set that up, the USB never connects. The code I copied from the bulk example has that pin set as USB Analog so that's why it worked.

    Some further reading says "If the USB controller is used as either a dedicated Host or Device, the DEVMOD field in the USB General-Purpose Control and Status (USBGPCS) register can be used to connect the USB0VBUS and/or USB0ID inputs to fixed levels internally, freeing the PB0 and PB1 pins for GPIO use." 

    So it's all there ... and it's helpful to have the full version of the data sheet where the Mentor Graphics USB core is actually documented.

  • Hi Andy,

    Sorry that it took a bit extra to figure out all those pieces. I am not sure if it will help you moving forward, but you don't have to configure USB0ID or USB0VBUS if you won't use them anyways by using this API:

    //
    // Set the USB stack mode to force device mode without VBUS / ID monitoring.
    //
    USBStackModeSet(0, eUSBModeForceDevice, 0);

  • Thanks, Ralph.

    BTW, that call to USBStackModeSet() with eUSBModeForceDevice is in the usb_dev_cdcserial example, but in usb_dev_bulk  the call uses simply eUSBModeDevice. The CDC code example does have a good comment about the use of that function.

        //
        // Forcing device mode so that the VBUS and ID pins are not used or
        // monitored by the USB controller. For USB OTG, this function should
        // not be called.  If the USB Host will supply power, and the LaunchPad
        // power jumper is set to "OTG", this function should not be called.
        //
        USBStackModeSet(0, eUSBModeForceDevice, 0);
    

    Anyways ... enumeration is working, now to sort out the data transfer. The CDC example is a good model for this, I think.