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.

TM4C123FH6PM: USB host CDC function

Part Number: TM4C123FH6PM
Other Parts Discussed in Thread: TM4C1294NCPDT

Hello!

I need to make a device which connects to board with CP2102 USB-2-UART bridge on it and communicate to it via virtual UART. I do not see CDC Host functions in usblib, how can I implement it?

Thanks.

  • Hello Oleg,

    I have a USB Host CDC example that works for the TM4C1294NCPDT. The same should be able to be ported to the TM4C123x. Please see the following E2E post which has both the project and a detailed description of how to install it and how it works: https://e2e.ti.com/support/tools/code-composer-studio-group/ccs/f/code-composer-studio-forum/1017323/tm4c129x-and-bulk-transfer-and-device/3774058#3774058

    We will look to create the same for the TM4C123x in the near future but do not have that available today.

    Best Regards,

    Ralph Jacobi

  • Thank you!

    It's very useful!

  • I've created the driver for CP210x device for host today. Found some issues in the presented example by the way.

    The problem with CP210x is it does not report device class 2, but class FF, vendor specific, and it does not use interrupt endpoint, so it require polling with TX requests. Also control commands is not compatible with standart CDC devices.

    Later I'll attach my code.

  • Hello Oleg,

    it does not report device class 2, but class FF, vendor specific

    This is an application specific element, yes. Did you need to modify the Host CDC library file to account for this? If so I will see what can be done to improve flexibility so its just in application code.

    it does not use interrupt endpoint, so it require polling with TX requests

    Do you feel that is very standard among CDC devices? The CP210x is a UART to USB translator so I think it may have different requirements than usual?

    Also control commands is not compatible with standart CDC devices.

    These can be easily excluded from application code though, correct?

    I appreciate this feedback because we are considering releasing this example in an application report in the near future so if there are shortcomings that can be resolved I would like to understand them better. The goal is to make it as broadly applicable as possible but I think covering every single use case would be very difficult with how widely used CDC is as an interface.

    Best Regards,

    Ralph Jacobi

  • I just created the driver, which is using most facilities from usbhcdc driver. 

    In fact I see in linux there are two different device classes - one is creating ttyUSBx device entries (which is CP210x and FTDI, for example), and another is creating ttyACMx device entries (as for Tiva usblib CDC devices, and also for MSP430FET and XDS100 JTAG probes). Looks like second one is more standart complaining. As I see, these devices of second class can be drived by universal driver like usbhcdc, but commonly used CP210x, FTDI, Prolific etc. devices will require some customisation. And yes, for older devices polling is usual thing.

    It would be good idea, I thing, to write some framework driver for such devices, allowing easy deriving drivers for it. I can do some work.

  • Hello Oleg,

    Ahh okay that does make sense when you explain it that way. Yes, ACM or Abstract Control Model is the most widely used device class. So for the CP210x to not be ACM makes a lot of sense then.

    I went through some of the USB spec and the codes from 0x80 and onward are Vendor-specific. So I am not sure we would do a lot to adapt that into the example. ACM and maybe Direct Line Control Model are viable options which have the broad use that we would be looking for with this. So I think what may be important is to stress in both the file and the documentation that the code is limited to specific CDC classes and further support would have to be expanded out.

    While supporting CP210x and FTDI would be reasonably broad it puts us in a situation where there may be more and more requests for other broad custom protocols and that would convolute the USB stack (...more than it already is.)

    Best Regards,
    Ralph Jacobi

  • Looking on reality, the most common task is to communicate with some third-party devices and boards from Tiva-based one, because emploing linux boxes is not very good idea in most cases, as the task is usually rather simple - to convert protocols or concentrate several devices via some bus like CAN (like in my case - I have some set of devices, supported by third-party software, and I need to build simple solution for connecting them to PC with one cable, withoput making changes to devices and software).

    Currently I'm writing the framework, it will have a structure like this:

    usbhserial
     |
     +- usbhserialdriver
         |
         +- usbhserialcdc
         +- usbhserialcp210x
         +- ...

    where usbhserial is a HAL layer, defining standart functions:

    extern uint32_t USBHostSerialInit(tUSBCallback pfnCallback);
    
    extern uint32_t USBHostSerialSetupInstance(tSerialInstance *psSerialInstance,
                                               tUSBCallback pfnCallback, void *pvRxBuffer);
    extern void USBHostSerialClose(tSerialInstance *psSerialInstance);
    
    extern uint32_t USBHostSerialReadData(tSerialInstance *psSerialInstance, uint8_t *pui8Data,
                                    uint32_t ui32Size);
    extern uint32_t USBHostSerialWriteData(tSerialInstance *psSerialInstance, uint8_t *pui8Data,
                                     uint32_t ui32Size);
    extern void USBHostSerialScheduleWrite(tSerialInstance *psSerialInstance, uint8_t *pui8Data,
                                     uint32_t ui32Size);
    
    extern uint32_t USBHostSerialInitNewDevice(tSerialInstance *psSerialInstance);
    
    extern uint32_t USBHostSerialSetLineConfig(tSerialInstance *psSerialInstance, uint32_t ui32Baud, uint32_t ui32Coding);
    extern uint32_t USBHostSerialGetBaud(tSerialInstance *psSerialInstance);
    extern uint32_t USBHostSerialGetCoding(tSerialInstance *psSerialInstance);
    extern uint32_t USBHostSerialSetControlLineState(tSerialInstance *psSerialInstance, uint32_t ui32Control);
    extern uint32_t USBHostSerialGetControlLineState(tSerialInstance *psSerialInstance);
    extern uint32_t USBHostSerialBreakSet(tSerialInstance *psSerialInstance);
    extern uint32_t USBHostSerialBreakClear(tSerialInstance *psSerialInstance);
    

    which can be called from application layer without knowing how the underlayer driver works,

    usbhserialdriver defines interface to drivers, declaring driver definition structure:

    //*****************************************************************************
    //
    //! This is the structure that holds all of the data for a given driver of
    //! a serial device.
    //
    //*****************************************************************************
    typedef struct {
    
        //
        //! The interface class that this device class driver supports.
        //
        uint32_t ui32InterfaceClass;
    
        //
        //! Vendor ID of device (0 - do not care)
        //
        uint16_t ui16VID;
    
        //
        //! Product ID of device (0 - do not care)
        //
        uint16_t ui16PID;
    
        //
        //! Polling mode flag
        //
        bool bPolling;
    
        //
        //! Polling interval
        //
        uint32_t ui32Interval;
    
        //
        //! Init function pointer
        //
        uint32_t (* pfnInit)(tSerialInstance *psSerialInstance);
    
        //
        //! Baud set function pointer
        //
        uint32_t (* pfnSetBaud)(tSerialInstance *psSerialInstance, uint32_t ui32Baud);
    
        //
        //! Baud get function pointer
        //
        uint32_t (* pfnGetBaud)(tSerialInstance *psSerialInstance);
    
        //
        //! Line coding set function pointer
        //
        uint32_t (* pfnSetBaud)(tSerialInstance *psSerialInstance, uint32_t ui32Coding);
    
        //
        //! Line coding get function pointer
        //
        uint32_t (* pfnGetCoding)(tSerialInstance *psSerialInstance);
    
        //
        //! Control line set function pointer
        //
        uint32_t (* pfnSetControlLineState)(tSerialInstance *psSerialInstance, uint32_t ui32Control);
    
        //
        //! Control line set function pointer
        //
        uint32_t (* pfnGetControlLineState)(tSerialInstance *psSerialInstance);
    
        //
        //! Break set function pointer
        //
        uint32_t (* pfnBreakSet)(tSerialInstance *psSerialInstance);
    
        //
        //! Break clear function pointer
        //
        uint32_t (* pfnBreakClear)(tSerialInstance *psSerialInstance);
    
    } tUSBSerialDriver;
    

    This approach will allow easy creation of additional drivers. Initially I'll supply two drivers with code: the CDC one and CP210x, as I have them and can implement and test.

    One more note: provided example cannot work with more than one device or multichannel devices and I want to make it fully instance-based.

  • Finished draft framework and 2 drivers. Here is the library:

    usbhserial @ google drive

    And simple example main.c:

    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    
    #include <stdio.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "usblib/usblib.h"
    #include "usblib/usbcdc.h"
    #include "usblib/host/usbhost.h"
    #include "usbhserial.h"
    #include "usbhserialdriver.h"
    #include "usbhserialcdc.h"
    #include "usbhserialcp210x.h"
    
    //*****************************************************************************
    //
    // The number of SysTick ticks per second.
    //
    //*****************************************************************************
    #define TICKS_PER_SECOND 100
    #define MS_PER_SYSTICK (1000 / TICKS_PER_SECOND)
    
    //*****************************************************************************
    //
    // Our running system tick counter and a global used to determine the time
    // elapsed since last call to GetTickms().
    //
    //*****************************************************************************
    uint32_t g_ui32SysTickCount;
    uint32_t g_ui32LastTick;
    uint8_t g_pUSBPipeBuffer[USB_TRANSFER_SIZE];
    
    tUSBSerialDriver g_psDrivers[] =
    {
     DECLARE_USB_SERIAL_CDC_DRIVER,
     DECLARE_USB_SERIAL_CP210X_DRIVER
    };
    uint8_t g_ui8NumDrivers = 2;
    
    //*****************************************************************************
    //
    // This is the handler for this SysTick interrupt.
    //
    //*****************************************************************************
    void
    SysTickIntHandler(void)
    {
        //
        // Update our tick counter.
        //
        g_ui32SysTickCount++;
    }
    
    //*****************************************************************************
    //
    // This function returns the number of ticks since the last time this function
    // was called.
    //
    //*****************************************************************************
    uint32_t
    GetTickms(void)
    {
        uint32_t ui32RetVal;
        uint32_t ui32Saved;
    
        ui32RetVal = g_ui32SysTickCount;
        ui32Saved = ui32RetVal;
    
        if(ui32Saved > g_ui32LastTick)
        {
            ui32RetVal = ui32Saved - g_ui32LastTick;
        }
        else
        {
            ui32RetVal = g_ui32LastTick - ui32Saved;
        }
    
        //
        // This could miss a few milliseconds but the timings here are on a
        // much larger scale.
        //
        g_ui32LastTick = ui32Saved;
    
        //
        // Return the number of milliseconds since the last time this was called.
        //
        return(ui32RetVal * MS_PER_SYSTICK);
    }
    
    uint32_t
    CDCSerialCallback(void *pvCBData, uint32_t ui32Event,
                      uint32_t ui32MsgParam, void *pvMsgData)
    {
        tSerialInstance *psInstance = (tSerialInstance *)pvCBData;
        uint32_t ui32Count;
        uint32_t ui16BytesReceived;
    
        switch(ui32Event)
        {
            //
            // Data was detected.
            //
            case USB_EVENT_RX_AVAILABLE:
            {
                ui16BytesReceived = USBHostSerialReadDataCount(psInstance);
    
                printf("Data received: ");
                for (ui32Count = 0; ui32Count < ui16BytesReceived; ui32Count++)
                {
                    printf("%02X ", g_pUSBPipeBuffer[ui32Count]);
                }
                printf("\n");
                break;
            }
            case USB_EVENT_TX_COMPLETE:
            {
                break;
            }
        }
        return 0;
    }
    
    bool bConnected = false;
    bool bSent = false;
    
    tSerialInstance *psInstance = 0;
    
    uint32_t
    CDCSerialGlobalCallback(void *pvCBData, uint32_t ui32Event,
                      uint32_t ui32MsgParam, void *pvMsgData)
    {
        if(ui32Event == USB_EVENT_CONNECTED)
        {
            psInstance = (tSerialInstance *)pvCBData;
            USBHostSerialSetupInstance(psInstance, CDCSerialCallback, g_pUSBPipeBuffer);
            USBHostSerialInitNewDevice(psInstance);
            USBHostSerialSetLineConfig(psInstance, 19200, USBHS_CONF_STOP_1 | USBHS_CONF_PAR_NONE | USBHS_CONF_DATA_8);
    
            printf("Connected serial device, driver = %d\n", (int)psInstance->ui8Driver);
            bConnected = true;
            return 0;
        }
        if(ui32Event == USB_EVENT_UNKNOWN_CONNECTED)
        {
            printf("Unknown device connected\n");
        }
    }
    
    #define HCD_MEMORY_SIZE         128
    uint8_t g_pHCDPool[HCD_MEMORY_SIZE];
    
    /**
     * main.c
     */
    int main(void)
    {
        SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                       SYSCTL_XTAL_16MHZ);
        uint32_t ui32SysClock = 80000000;
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_5 | GPIO_PIN_4);
    
        //
        // Configure SysTick for a 100Hz interrupt.
        //
        SysTickPeriodSet(ui32SysClock / TICKS_PER_SECOND);
        SysTickEnable();
        SysTickIntEnable();
    
        //
        // Initialize the USB stack in host mode. No callback is needed at this
        // time.
        //
        USBStackModeSet(0, eUSBModeHost, 0);
    
        USBHostSerialInit(CDCSerialGlobalCallback);
    
        //
        // Initialize the power configuration.  This sets the power enable signal
        // to be active high and does not enable the power fault.
        //
        USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
        USBHCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &ui32SysClock);
    
        //
        // Initialize the USB controller for host operation.
        //
        USBHCDInit(0, g_pHCDPool, HCD_MEMORY_SIZE);
    
        printf("USB host initialized\n");
    
        while(1)
        {
            //
            // Tell the OTG library code how much time has passed in
            // milliseconds since the last call.
            //
            USBOTGMain(GetTickms());
            if(bConnected && !bSent)
            {
                uint8_t Out[7] = {0x70, 0x01, 0, 0, 0, 0, 0x71};
    
                USBHostSerialScheduleWrite(psInstance,
                                     Out,
                                     7);
                bSent = true;
            }
        }
    
        return 0;
    }
    

    Example connects to device, determines it's cp210x, configures port, sends sample command and prints response.

  • As you see, adding other USB-2-Serial chips is very easy with this library.

  • Hello Oleg,

    Thanks for sharing this code! I imagine other community members will find it quite helpful in the future. I'll also review it when working on the USB Host CDC projects for improvements that can be made on what I had put together before.

    Best Regards,

    Ralph Jacobi

  • Just shared the library project on github - https://github.com/kiabrin/usbhserial

    I will commit some changes and improivements to library shortly.