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.

TMS320F28P650DK: USB Peripheral Enters Device Mode by Itself

Part Number: TMS320F28P650DK
Other Parts Discussed in Thread: SYSCONFIG, C2000WARE, TMS320F28069

Tool/software:

I am attempting to get a USB MSC host application working on prototype hardware.
I successfully ran the "usb_msc_host_ex7" example on our hardware, with minor modifications (clock settings, pin assignment, etc). I can attach a flash drive, and the program enumerates it and mounts the FAT filesystem.
The problems start when I try to interact with USB from CPU2, rather than CPU1. The USB peripheral appears to spontaneously leave host mode and enter device mode, and then stay there forever.

I tried to make minimal changes to the example code. The USB peripheral+library is set up for CPU2 in sysconfig.

Pseudocode for CPU1:
- Run device_init(), board_init(), etc. to set up clocks and other hardware.
- Run "SysCtl_selectCPUForPeripheralInstance(SYSCTL_CPUSEL_USBA, SYSCTL_CPUSEL_CPU2);", to transfer USB peripheral control to CPU2.
- Boot CPU 2 and ask it to start initialization (via IPC flag).
- Wait for CPU 2 to finish initialization (via IPC flag).
- Enable interrupts and start main loop.

Pseudocode for CPU2:
- Wait for CPU 1 to tell us to start initialization.
- Run device_init(), board_init(), etc.
- Run C2000ware_libraries_init(). Notably, this also calls "USBStackModeSet(0, eUSBModeForceHost, ModeCallback);", since sysctl is aware that we want to run in host mode.
- Tell CPU1 that we are done with initialization (via IPC flag).
- Enable interrupts and start main loop. The main loop calls USBHCDMain() (from usbhost.h) and monitors status.

Here is what happens:
1. The system starts up successfully. At this point, the USBDEVCTL register contains DEV=0, HOST=0, and FSDEV=0.
2. As soon as interrupts are enabled, a USB_INTCTRL_MODE_DETECT interrupt goes off. Nothing changes, except that USBIDVISC=1. The main loop starts running.
3. I plug in a flash drive. A USB_INTCTRL_CONNECT interrupt goes off. At this point, the USBDEVCTL register contains DEV=0, HOST=1, FSDEV=1.
4. The main loop calls USBHCDMain(), which sees that a device has connected. USBHCDMAIN() calls USBHCDReset() to reset the device.
5. A USB_INTCTRL_RESET interrupt goes off. At this point, the USBDEVCTL register contains DEV=1, HOST=0, FSDEV. The USB controller has decided that it is no longer a host, and is now a device.
      -- Note that the USBDEVCTL change has already occurred at the start of the ISR, so the ISR cannot be causing the problem.
      -- Note that this happens before the main context has even returned from USBHCDReset().
6. Meanwhile, USBHCDMain() continues to run through its state machine. It starts a control transfer to retrieve the device descriptor, but the control transfer hangs forever because the USB controller thinks it's in device mode. Disconnecting and connecting the flash drive no longer triggers interrupts.

Reading through the documentation, I am struggling to understand how the USB controller decides which mode to be in. It seems to be a dynamic process, which depends on the state of the D+ and D- pins, and the (nonexistent) ID and Vbus-detect pins. Does anyone know why the peripheral might be setting itself to device mode after a bus reset? Another poster seems to have had the same issue in the past, with a TMS320F28069.

Thank you!

  • Hi,

    As you mentioned, the device or host mode detection depends on the USB signals. 

    Do you know what is causing the reset interrupt to occur?  Also, are you seeing the same behavior if you run this only on CPU1 core?

    Best Regards

    Siddharth 

  • "The device or host mode detection depends on the USB signals"
    Is there any more specific documentation on this? I have called "USBStackModeSet(0, eUSBModeForceHost, ModeCallback);" -- so I would expect the peripheral to stay in host mode regardless of what happens with the USB signals. The C2000ware USB library is clearly not expecting this behavior either, since it gets stuck in an infinite loop waiting for a control transaction to complete and does not re-check the USBDEVCTL register.
    The peripheral was originally designed to use hardware ID and VBUS signals, but these don't exist on this chip -- so how does it decide which mode to be in? Which conditions trigger a switch to device mode?

    "Do you know what is causing the reset interrupt to occur?"
    The interrupt occurs immediately after:
    USBHCDMain() -> ProcessUSBDeviceStateMachine() -> USBHCDReset() -> USBHostReset(USB_BASE, 1)
    This bus reset is part of the normal enumeration process. However, in a working example, it does not trigger the reset interrupt.

    "Also, are you seeing the same behavior if you run this only on CPU1 core?"
    No. USB seems to work fine if I use just CPU1, on the same hardware. When I plug in a flash drive, the program successfully enumerates it and mounts a filesystem.

    Thank you for taking the time to look into this!

  • Hi, 

    Sorry for delayed response.

     A USB host detects the USB slave device by sensing the status of the two USB data lines – D+/D-.  USB hosts pull the D+ and D- data lines low whereas a full speed USB device pull the D+ line high and a low speed USB device pull the D- line high.  As soon as one of the data lines is pulled up, it assumes that a new device has been connected. This is as per the USB protocol specification.

    The USB Library assumes that the USB peripheral remains in host mode once set. However certain conditions like specified above can cause it to switch mode from host to device. 

    I am still not sure why the behavior is different between the two CPUs.  Is it possible for you to log the USB Interrupt flags and compare the sequences between both CPUs.  Also, if you can share the code snippets of the USB configuration , I will take a look at it and see if anything is missing.

    Best Regards

    Siddharth

  • If I understand you correctly, the architecture of the USB peripheral and library leaves me a little worried. Let's say that:
    1. Everything is working correctly (ie, the current issue gets resolved), and a flash drive mounts successfully under normal circumstances.
    2. The D+ and D- lines both get pulled down for some reason (moisture ingress, customer plugs in a noncompliant device, EMI, etc).
    In this situation, it seems like the USB peripheral will automatically switch to device mode, the USB library will get stuck, and our firmware will crash. Is this correct? If so, it's not necessarily a big problem, but it would be good for us to take into account.

    "Is it possible for you to log the USB Interrupt flags and compare the sequences between both CPUs"
    Yes. I ran a test where I modified USB0HostIntHandler (usbhostenum.c) to log the output of USBIntStatus().
    CPU1 (working):
    USB_INTCTRL_MODE_DETECT (This happens as soon as interrupts are enabled)
    USB_INTCTRL_CONNECT (When a flash drive is connected)
    USB_INTCTRL_SOF (Response to device descriptor request control transfer)
    ... And then many more USB_INTCTRL_SOF interrupts as enumeration continues.

    CPU2 (broken):
    USB_INTCTRL_MODE_DETECT (This happens as soon as interrupts are enabled)
    USB_INTCTRL_CONNECT (When a flash drive is connected)
    USB_INTCTRL_RESET (Happens while the program is inside a call to USBHCDReset() (usbhostenum.c))

    Code snippets:
    main_cpu1.c

    void main(void)
    {
        // Initialize device clock and peripherals
        Device_init();
    
        // Initialize GPIO and configure the GPIO pin as a push-pull output
        Device_initGPIO();
        
        // Transfer USB peripheral to CPU2
        SysCtl_selectCPUForPeripheralInstance(SYSCTL_CPUSEL_USBA, SYSCTL_CPUSEL_CPU2);
        GPIO_setControllerCore(42, GPIO_CORE_CPU2);
        GPIO_setControllerCore(43, GPIO_CORE_CPU2);
     
        // Initialize PIE and clear PIE registers. Disables CPU interrupts.
        Interrupt_initModule();
    
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        Interrupt_initVectorTable();
    
        // Initialize settings from SysConfig
        Board_init();
        C2000Ware_libraries_init();
        
        // Boot CPU2 core
        // Sync CPUs before starting the main loop.
        IPC_clearFlagLtoR(IPC_CPU1_L_CPU2_R, IPC_FLAG_ALL);
        Device_bootCPU2(BOOT_MODE_CPU2);
        // Start booting CPU 2
        IPC_sync(IPC_CPU1_L_CPU2_R, IPC_SYNC);
        // Wait for CPU 2 to finish booting
        IPC_sync(IPC_CPU1_L_CPU2_R, IPC_SYNC);
    
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        EINT;
        ERTM;
    
        //
        // Loop Forever
        //
        for(;;)
        {       
        }
    }

    main_cpu2.c
    void main(void)
    {
    
        // Sync CPUs before starting the main loop
        IPC_clearFlagLtoR(IPC_CPU2_L_CPU1_R, IPC_FLAG_ALL);
        IPC_sync(IPC_CPU2_L_CPU1_R, IPC_SYNC);
    
        // Initialize device clock and peripherals
        Device_init();
    
        // Initialize PIE and clear PIE registers. Disables CPU interrupts.
        Interrupt_initModule();
    
        // Initialize the PIE vector table with pointers to the shell Interrupt
        // Service Routines (ISR).
        Interrupt_initVectorTable();
    
        // Initialize settings from SysConfig
        Board_init(); 
    
        C2000Ware_libraries_init();
        USBPostInit();
        
        IPC_sync(IPC_CPU2_L_CPU1_R, IPC_SYNC);
    
        // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
        EINT;
        ERTM;
    
        // Loop Forever
        for(;;)
        {
            USBMain();
        }
    }

    usb_hal.c is largely an amalgamation of parts of main.c and usb_hal.c from the usb_ex7_host_msc example.
    Some relevant snippets:
    USBMain(void)
    {
        tState eStateCopy;
    
        //
        // See if a mass storage device has been enumerated.
        //
        if(g_eState == STATE_DEVICE_ENUM)
        {
            //
            // Take it easy on the Mass storage device if it is slow to
            // start up after connecting.
            //
            if(USBHMSCDriveReady(g_psMSCInstance) != 0)
            {
                //
                // Wait about 100ms before attempting to check if the
                // device is ready again.
                //
                SysCtl_delay(SysCtl_getClock(DEVICE_OSCSRC_FREQ)/30);
    
                return;
            }
    
            //
            // Reset the working directory to the root.
            //
            g_cCwdBuf[0] = '/';
            g_cCwdBuf[1] = '\0';
    
            //
            // Attempt to open the directory.  Some drives take longer to
            // start up than others, and this may fail (even though the USB
            // device has enumerated) if it is still initializing.
            //
            f_mount(0, &g_sFatFs);
            if(f_opendir(&g_sDirObject, g_cCwdBuf) == FR_OK)
            {
                //
                // The drive is fully ready, so move to that state.
                //
                g_eState = STATE_DEVICE_READY;
            }
        }
    
        //
        // Run the main routine of the Host controller driver.
        //
        USBHCDMain();
    }
    
    void USBPostInit(void)
    {
        // Register the host class drivers.
        USBHCDRegisterDrivers(0, g_ppHostClassDrivers, NUM_CLASS_DRIVERS);
    
        // Open an instance of the mass storage class driver.
        g_psMSCInstance = USBHMSCDriveOpen(0, (tUSBHMSCCallback)MSCCallback);
    
        // Initialize the file system.
        f_mount(0, &g_sFatFs);
    }
    
    __interrupt void
    INT_myUSB0_ISR(void)
    {
        USB0HostIntHandler();
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
    }

    The auto-generated c2000ware_libraries.c for CPU2:
    void C2000Ware_libraries_init()
    {
        USBLib_init();
    }
    
    
    uint8_t g_pui8HCDPool[myUSB0_LIB_HCD_MEMORY_SIZE];
    void USBLib_init(){
        //
        // Initialize the USB stack mode and pass in a mode callback.
        //
        USBStackModeSet(0, eUSBModeForceHost, ModeCallback);
        //
        // Initialize the power configuration.
        //
        USBHCDPowerConfigInit(0,USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
        //
        // Initialize the USB controller for OTG operation with a 2ms polling
        // rate.
        //
        USBHCDInit(0,g_pui8HCDPool, myUSB0_LIB_HCD_MEMORY_SIZE);
    }

    I can also send you the entire project in a private message, if you would like. Let me know if you need any other information. Thank you!

  • Hi, 

    This detection mechanism (pull up on D+/D- for high/low devices) is how the USB is expected to behave.  There may have to be some error handling added in case you are not expecting any device to be connected. 

    I took a look at the code that you shared. It looks fine. I am still not sure why there is a different sequence between the CPUs since the USB library/events are not CPU specific. 

    Best Regards

    Siddharth 

  • Ok--
    After much experimentation, I have found the following:
    - This issue (USB peripheral switching to device mode) actually does happen on CPU1, just a lot less often. Conversely, the issue occasionally does not happen on CPU2.
    - The problem happens more frequently (but not exclusively) with a low-quality USB flash drive. The same flash drive sometimes fails to enumerate even on the dev kit running the unmodified USB example, but I was unable to reproduce the switching-to-device-mode issue on the dev kit.
    - The problem is much, much rarer when I attach the flash drive to our board through a Beagle USB analyzer -- I have only reproduced it this way once, after 100+ connect/disconnects. Even though our design has the D+ and D- lines correctly going straight from the MCU to the port, this makes me wonder whether the difference between our board and the dev kit may just be some stray capacitance or something along those lines.

    I thought I could perhaps mitigate the issue by using the FORCEH bit in the USBTEST register -- however, I don't seem to be able to write it. I can write and read back the lower four (reserved) bits, but the upper four bits seem to read back as 0 no matter what I do to them, whether I write them before or after the USB peripheral is initialized. I realize that the register was probably not meant for production use, but are there any usage examples (or, indeed, confirmation that it even exists in the final release hardware)?

    Also --
    The USB library hangs forever inside of USBHCDMain() when the hardware is in device mode and the library expects it to be in host mode. Given that the hardware is capable of autonomously switching modes without an explicit command from firmware, I think this could be considered a bug. Apart from a watchdog timer or modifying the library myself, there is no way for me to guarantee that this won't happen and bring down the system.