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.

TM4C1290NCPDT: Problem in switching from USB OTG device mode to host mode

Part Number: TM4C1290NCPDT

Hi All,

We have a custom board which has separate device receptacles and host port for USB0 using TM4C1290NCPDT. Our design has a MUX circuit in order to use the same USB (USB0) data pins on device receptacle and host port.

Software is configured for OTG mode, with mux mapped to the device mode, in the beginning, changing to host if the device is not detected.

Reference from SDK, board dk-tm4c129x and example usb_otg_mouse is modified as per our requirement. Attached the modified main file. 

//*****************************************************************************
//
// usb_otg_mouse.c - USB OTG Mouse (combined host and device mouse).
//
// Copyright (c) 2013-2017 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
// This is part of revision 2.1.4.178 of the DK-TM4C129X Firmware Package.
//
//*****************************************************************************

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_usb.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "grlib/grlib.h"
#include "usblib/usblib.h"
#include "usblib/device/usbdevice.h"
#include "usblib/host/usbhost.h"
#include "utils/uartstdio.h"
#include "drivers/frame.h"
#include "drivers/kentec320x240x16_ssd2119.h"
#include "drivers/pinout.h"
#include "inc/hw_gpio.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"

#include "usb_otg_mouse.h"

//*****************************************************************************
//
//! \addtogroup example_list
//! <h1>USB OTG HID Mouse Example (usb_otg_mouse)</h1>
//!
//! This example application demonstrates the use of USB On-The-Go (OTG) to
//! offer both USB host and device operation.  When the DK board is connected
//! to a USB host, it acts as a BIOS-compatible USB mouse.  The select button
//! on the board (on the bottom right corner) acts as mouse button 1 and
//! the mouse pointer may be moved by dragging your finger or a stylus across
//! the touchscreen in the desired direction.
//!
//! If a USB mouse is connected to the USB OTG port, the board operates as a
//! USB host and draws dots on the display to track the mouse movement.  The
//! states of up to three mouse buttons are shown at the bottom right of the
//! display.
//!
//*****************************************************************************

//*****************************************************************************
//
// The current state of the USB in the system based on the detected mode.
//
//*****************************************************************************
volatile tUSBMode g_iCurrentMode = eUSBModeNone;

//*****************************************************************************
//
// The size of the host controller's memory pool in bytes.
//
//*****************************************************************************
#define HCD_MEMORY_SIZE         254

//*****************************************************************************
//
// The memory pool to provide to the Host controller driver.
//
//*****************************************************************************
uint8_t g_pui8HCDPool[HCD_MEMORY_SIZE];

//*****************************************************************************
//
// This global is used to indicate to the main that a mode change has
// occurred.
//
//*****************************************************************************
uint32_t g_ui32NewState;

//*****************************************************************************
//
// The system clock frequency in Hz.
//
//*****************************************************************************
uint32_t g_ui32SysClock;

//*****************************************************************************
//
// The graphics context for the screen.
//
//*****************************************************************************
tContext g_sContext;

#define WAIT_DELAY_COUNT 100000
//*****************************************************************************
//
// A function which returns the number of milliseconds since it was last
// called.  This can be found in usb_dev_mouse.c.
//
//*****************************************************************************
extern uint32_t GetTickms(void);

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//*****************************************************************************
//
// Callback function for mode changes.
//
//*****************************************************************************
void ModeCallback(uint32_t ui32Index, tUSBMode iMode)
{
    //
    // Save the new mode.
    //
    g_iCurrentMode = iMode;

    switch (iMode)
    {
    case eUSBModeHost:
    {
        break;
    }
    case eUSBModeDevice:
    {
        break;
    }
    case eUSBModeNone:
    {
        break;
    }
    default:
    {
        break;
    }
    }
    g_ui32NewState = 1;
}
//*****************************************************************************
//
//! Configures the device pins for the standard usages on the DK-TM4C129X.
//!
//! This function enables the GPIO modules and configures the device pins for
//! the default, standard usages on the DK-TM4C129X.  Applications that require
//! alternate configurations of the device pins can either not call this
//! function and take full responsibility for configuring all the device pins,
//! or can reconfigure the required device pins after calling this function.
//!
//! \return None.
//
//*****************************************************************************
void PinInit(void)
{
    //
    // Enable all the GPIO peripherals.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOG);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOH);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOJ);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOK);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOP);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOQ);

    //
    // PA0-1 are used for UART0.
    //
    ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    //
    // PB0-1/PD6-7/PL6-7 are used for USB.
    //
    //
    // Configure the required pins for USB operation.
    //
//    HWREG(GPIO_PORTA_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
//    HWREG(GPIO_PORTA_BASE + GPIO_O_CR) = 0xff;
    ROM_GPIOPinConfigure(GPIO_PA6_USB0EPEN);
    ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1); //ID and VBUS
    ROM_GPIOPinTypeUSBDigital(GPIO_PORTA_BASE, GPIO_PIN_6);
    ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 | GPIO_PIN_7); // DP and DM
//        ROM_GPIOPinTypeGPIOInput(GPIO_PORTQ_BASE, GPIO_PIN_4);
    ROM_GPIOPinTypeGPIOInput(GPIO_PORTA_BASE, GPIO_PIN_7);

    //Enable GPIO for USB_SEL and ID_DRIVE
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_5);
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTK_BASE, GPIO_PIN_3);

    //Set values for USB_SEL = 0 and ID_DRIVE = 1 for device configuration
    GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0);//TODO - change it to 0 for while board testing; GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0);
    GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_3, GPIO_PIN_3);

    //VBUS contropl pin for custom board
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTK_BASE, GPIO_PIN_5);
    GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_5, GPIO_PIN_5);
}

//*****************************************************************************
//
// Initialize the USB for OTG mode on the platform.
//
//*****************************************************************************
void USBOTGInit(uint32_t ui32ClockRate, tUSBModeCallback pfnModeCallback)
{
    uint32_t ui32PLLRate;

    //
    // Enable USB controller.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);

    //
    // Set the current processor speed to provide more accurate timing
    // information to the USB library.
    //
    SysCtlVCOGet(SYSCTL_XTAL_25MHZ, &ui32PLLRate);
    USBOTGFeatureSet(0, USBLIB_FEATURE_CPUCLK, &ui32ClockRate);
    USBOTGFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate);

    //
    // Initialize the USB OTG mode and pass in a mode callback.
    //
    USBStackModeSet(0, eUSBModeOTG, pfnModeCallback);

}

//*****************************************************************************
//
// main routine.
//
//*****************************************************************************
int main(void)
{
    tLPMFeature sLPMFeature;
    int count = 0;

    //
    // 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.
    //
    PinInit();

    //
    // Configure the UART.
    //
    UARTStdioConfig(0, 115200, g_ui32SysClock);
    UARTprintf("USB switchover\r\n");

    //
    // Configure USB for OTG operation.
    //
    USBOTGInit(g_ui32SysClock, ModeCallback);

    sLPMFeature.ui32HIRD = 500;
    sLPMFeature.ui32Features = USBLIB_FEATURE_LPM_EN |
    USBLIB_FEATURE_LPM_RMT_WAKE;
    USBHCDFeatureSet(0, USBLIB_FEATURE_LPM, &sLPMFeature);

    //
    // Initialize the host stack.
    //
    HostInit();
    UARTprintf("Host stack initialization done\r\n");

    //
    // Initialize the device stack.
    //
    DeviceInit();
    UARTprintf("Device stack initialization done\r\n");

    //
    // Initialize the USB controller for dual mode operation with a 2ms polling
    // rate.
    //
    USBOTGModeInit(0, 2000, g_pui8HCDPool, HCD_MEMORY_SIZE);

#if 0//Commenting as, device mode configuration is done during int
    //Set values for USB_SEL = 0 and ID_DRIVE = 1 for device configuration
    GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0);//TODO - change it to 0 for while board testing; GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, 0);
    GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_3, GPIO_PIN_3);
#endif
    //
    // Loop forever.
    //
    while (1)
    {
        //
        // Tell the OTG library code how much time has passed in milliseconds
        // since the last call.
        //
        USBOTGMain(GetTickms());

        //
        // Handle deferred state change.
        //
        if (g_ui32NewState)
        {
            UARTprintf("New state detected\r\n");
            g_ui32NewState = 0;
        }

        if (g_iCurrentMode == eUSBModeDevice)
        {
            UARTprintf("connected to host\r\n");
            DeviceMain();
        }

        if (g_iCurrentMode == eUSBModeHost)
        {
            UARTprintf("device connected\r\n");
            HostMain();
        }

        if(count < WAIT_DELAY_COUNT)
        {
            count++;
        }
        else if(count == WAIT_DELAY_COUNT && g_iCurrentMode == eUSBModeNone)
        {

            count++;
            //Set values for USB_SEL = 1 and ID_DRIVE = 0for host configuration
            GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_5, GPIO_PIN_5);
            GPIOPinWrite(GPIO_PORTK_BASE, GPIO_PIN_3, 0);
        }

    }
}

We need to test two cases, anyone among the below cases will be happening in real time.

1. Connect TM4C to host (PC) with USB type-A male connector on the product - this will continue to work in device mode

2. Connect the device with USB type-A male connector to TM4C's USB type-A female port - this will continue to work in host mode

It is not working as expected. Only one mode will work with varying WAIT_DELAY_COUNT value.


Below are the observations,

1. WAIT_DELAY_COUNT > 100000, works TM4C as device mode only but we are missing to capture the TM4C connection to host PC.
2. WAIT_DELAY_COUNT < 10000, works TM4C as host mode only but we are missing to capture the device connection.


Any feedbacks are appreciated.

Thanks in advance.

Regards,

Ambika

  • Hello Ambika,

    I haven't really dealt with a system setup like this before, so let me try and get a better idea of how you have designed the system so far, and what the design specifications you are going after are:

    Can you give more details of the following:

    1) What happens when a cable is plugged into the DEVICE port in terms of initialization and the Mux.

    2) What happens when a cable that has been plugged into the DEVICE port is removed.

    3) What happens when a cable is plugged into the HOST port in terms of initialization and the Mux.

    4) And what happens when a cable that has been plugged into the HOST port is removed.

    5) What is supposed to happen when BOTH ports have a cable plugged

    6) What is supposed to happen when a cable is removed when BOTH ports have had a cable plugged in.

    Understanding how you are initializing the USB interface as well as what happens with the USB interface when a cable removed will give me more context about your setup so I can either explain what needs to be done from a system standpoint, or offer feedback on proper USB setup.

    Also just to cross-check, you have verified Host works on its own and Device works on its own, so you just need to get this combination of them working now, right?
  • Hi Ralph Jacobi,

    Presentation1.pptx

    Please see the above image for the system design. As mentioned there, we have two test cases.

    1.TM4C as a USB Peripheral - USB type-A connector goes to PC, PC acts as USB host as well as provides power.

    2.TM4C as a USB Host - USB device(peripheral) connects to USB type-A port and USB type-A connector goes to the charger. 

    Also just to cross-check, you have verified Host works on its own and Device works on its own, so you just need to get this combination of them working now, right?

    Yes. This understanding is correct.

    I'm repeating the observations again,

    1. WAIT_DELAY_COUNT > 100000, works TM4C as device mode only but we are missing to capture the TM4C connection to host PC.
    2. WAIT_DELAY_COUNT < 10000, works TM4C as host mode only but we are missing to capture the device connection.

    Hope this information gives details required for the understanding. Do let me know if you need further details.

    Thank You.

    Regards,

    Ambika

  • Hello Ambika,

    First thing I am seeing is that the USBID pin is not being used, but you said you are doing OTG? Isn't this causing issues? OTG should have ID pin usage to help determine host vs device mode. Without USB0ID connected to the USB connector you wouldn't be an OTG device.

    Secondly, the information here is only partially what I was seeking, I understand now the general system configuration fully, but all of these questions specific to our TM4C device in terms of it's operation are outstanding:

    1) What happens when a cable is plugged into the DEVICE port in terms of initialization and the Mux. (What code executes?)

    2) What happens when a cable that has been plugged into the DEVICE port is removed. (How is the disconnect handled in software?)

    3) What happens when a cable is plugged into the HOST port in terms of initialization and the Mux. (What code executes?)

    4) And what happens when a cable that has been plugged into the HOST port is removed. (How is the disconnect handled in software?)

    5) What is supposed to happen when BOTH ports have a cable plugged (What is the intended operation for the USB interface here? Device or Host? Or is it based on what was connected first?)

    6) What is supposed to happen when a cable is removed when BOTH ports have had a cable plugged in. (Does there need to be a change over to check for the other port at this point?)

    I need to understand how you are configuring the USB port for device mode, host mode, how the cable plug ins affect this, how the MUX is involved in this, and how you are handling USB disconnect operations. Without ALL of that information, I have no way to give meaningful feedback. This is the first I have seen a system like this, so please help me understand far more detail so I can attempt to guide you on how to do this.

  • Hi  Ralph Jacobi,

    ID and mux are configured in the software using GPIO, initially, it is configured to work in device mode. 

    We have given counter variable to wait in device mode and then switching to host mode by changing the GPIO connected to ID and GPIO connected in mux.

    The initial code is configured in SW(TM4C as device) assuming USB type-A is connected to PC, the counter variable is used considering initial detection and enumeration of TM4C in PC takes some time. If TM4C is not detected as a device, then considering Type-A connector is on the charger, another end will have the device connected to it. So the ID and mux is switched to HOST mode and will be waiting for the device detection forever in SW.

    1) What happens when a cable is plugged into the DEVICE port in terms of initialization and the Mux. (What code executes?)
    In this case, the charger should be connected to the other end. This detection should happen after the delay because the initial assumption is to work TM4C in device mode.
    Since power should always come from USB type a connector of our system, during the initial time(before the delay) of SW life, connected DEVICE will not detect. DP and DM will only change to device port side only after the delay.

    2) What happens when a cable that has been plugged into the DEVICE port is removed. (How is the disconnect handled in software?)
    Power is always from the type-A connector of our system, so only the initial period until the delay will be in device mode. After that SW can continue to work in TM4C in host mode(ID and MUX in SW).

    3) What happens when a cable is plugged into the HOST port in terms of initialization and the Mux. (What code executes?)
    So in this case, other port will not be in use. Within the delay(using the counter variable), detection should happen. Then enumeration and other activity will be continued in the TM4C as device mode.

    4) And what happens when a cable that has been plugged into the HOST port is removed. (How is the disconnect handled in software?)
    This is power off condition.

    5) What is supposed to happen when BOTH ports have a cable plugged (What is the intended operation for the USB interface here? Device or Host? Or is it based on what was connected first?)
    This is Power from USB type-A connector and device to USB type-A port. Here SW should be made to detect the device(TM4C as host). This happens after the delay in SW.

    6) What is supposed to happen when a cable is removed when BOTH ports have had a cable plugged in. (Does there need to be a change over to check for the other port at this point?)
    Power off when USB type-A connector is removed either from charger or PC. In case of the charger, another side even if we remove the device, TM4C should wait for the device to connect again.

    Let me know if need any details on this.

    Thank you.

    Regards,

    Ambika

  • Hello Ambika,

    Okay this is much more along the lines of what I was looking for.

    First off, to be clear, I have not dealt with system requirements like this before, and I'm still pondering whether this is feasible for our device to do a complex host/device relationship with a single USB peripheral.

    That said, the first thought that comes to mind for me - when the device is plugged in and you want the TM4C to be the host, do you provide any power to the device before the TM4C is configured as a host? If so, I would say to first configure the TM4C as a host, and then provide the device power, so it powers on to a waiting Host which would then let the initial USB exchange occur.

    From the host side, you should be able to keep things fairly simple where you don't need the wait delay. If you detect that a USB disconnect event occurs then wouldn't you just be able to go back to being a USB device as long as power is present and check for device presence once again? That would been the delay counter shouldn't even impact anything but the reliable recognition of the USB device. Perhaps I am misunderstanding something slightly though.

    Also I wouldn't consider this an OTG application in the sense that while the TM4C is both a host and device, it doesn't actually act like an OTG device where the USB0ID pin is used to toggle between modes via the HNP, but you are toggling between the modes on the TM4C side with a mux.
  • Hi Ralph,

    First off, to be clear, I have not dealt with system requirements like this before, and I'm still pondering whether this is feasible for our device to do a complex host/device relationship with a single USB peripheral.
    I have tested the same logic on DK-TM4C1237 kit and it worked but our actual board has TM4C129.
    In TM4c129 USB, active ID scanning is not there. Is that causing this problem? If yes, do you recommend to use DEVICE and HOST mode in the software instead of using OTG mode?

    That said, the first thought that comes to mind for me - when the device is plugged in and you want the TM4C to be the host, do you provide any power to the device before the TM4C is configured as a host? If so, I would say to first configure the TM4C as a host, and then provide the device power, so it powers on to a waiting Host which would then let the initial USB exchange occur.
    We are powering the device after configuring TM4C as host. Anyways we checked both scenarios (powering the device before and after configuring the TM4C).

    From the host side, you should be able to keep things fairly simple where you don't need the wait delay. If you detect that a USB disconnect event occurs then wouldn't you just be able to go back to being a USB device as long as power is present and check for device presence once again? That would been the delay counter shouldn't even impact anything but the reliable recognition of the USB device. Perhaps I am misunderstanding something slightly though.
    I'm keeping the system in DEVICE mode to detect the USB event, if system is not connected to the HOST(that means its connected to charger), then I need to change the DP and DM lines to the device port where as before the delay, DP and DM lines are routed to HOST connector.
    I'm using the delay in order to switch the DP and DM lines between HOST and DEVICE.

    Also I wouldn't consider this an OTG application in the sense that while the TM4C is both a host and device, it doesn't actually act like an OTG device where the USB0ID pin is used to toggle between modes via the HNP, but you are toggling between the modes on the TM4C side with a mux.
    In that case do you recommend to use DEVICE and HOST mode in the software instead of using OTG mode?

    Thank You.

    Regards,

    Ambika

  • Hello Ambika,

    Yes I absolutely recommend using DEVICE and HOST mode because OTG mode should be quite dependent on the USB0ID state to properly select Host vs Device functionality.

    Even more so given that you were able to have it working with the Dev Kit that has USB0ID connected and now you are having issues after removing it and staying with USB OTG mode.
  • Hi Ralph,

    Okay. I will implement with DEVICE and HOST mode.

    Even more so given that you were able to have it working with the Dev Kit that has USB0ID connected and now you are having issues after removing it and staying with USB OTG mode.

    This understanding is correct. Before trying in the actual board we tried the same logic of controlling ID and DP DM in Software on DK-TM4C1237 kit and it worked but our actual board has TM4C129 where the same logic is not working.
    Thank You.
    Regards,
    Ambika