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.

USB bulk code doesn't finish initializing

Other Parts Discussed in Thread: LMFLASHPROGRAMMER

Hello. I'm starting out with the Stellaris Launchpad, and after making the LEDs blink I wanted to play with USB a bit.


- I was able to install and run CCS,

- I loaded the usb_dev_bulk code and was able to compile everything without errors or warnings.

- The code loaded into the Launchpad, started executing ('Initializing USB' appeared on the UART) but then hung.

- Step-by-step seems to indicate the hanging occurs in USBDBulkInit(0, (tUSBDBulkDevice *)&g_sBulkDevice);

Stepping into the function, the parameter pfnCallback seems to be 0.So, there shouldn't happen anything really. Still, the code seems to hang every time I run it.

Update: The fact that the program stopped at the mentioned line, was just because there was a breakpoint there. In fact, each time an error was produced: '_c_int00 does not contain frame...'


From many pages  consulted, it seems this vector has to be initialized. Checking the .map file, I see:

ENTRY POINT SYMBOL: "_c_int00"  address: 0000045d

and

.intvecs   0    00000000    0000026c     
                00000000    0000026c     lm4f120h5qr_startup_ccs.obj (.intvecs)

How can this error occur? Do I have to execute some command to assure _c_int00 gets initialized at run-time?

Thanks on beforehand.

  • Hello John,

    The first thing you may want to check is to download the bin file using the LMFlashProgrammer to the launchpad, connect the device USB cable and see if the example works. Please report if the same works?

    Then we can check what is happening with the compiled use case!!

    Regards,
    Amit
  • Thanks for replying, Amit.

    First, I now noticed something strange: On the one hand, the compilation went through and 'Build finished', but in the 'problems' window, I have now 14 error messages. How could the build terminate with 14 unresolved symbols? They are classified as 'Semantic errors'. There seem to be some reports on this problem:

    e2e.ti.com/.../920344
    processors.wiki.ti.com/.../Build_Errors_in_CCS
    e2e.ti.com/.../200857

    Those message are probably about CCSv4 or v5, but I'm running v6 here... Also, I am actually quite sure I had eliminated those errors (at least as linking errors).

    Anyway, those messages do seem to indicate that the resulting binary is actually viable, so I went ahead and tried to follow your suggestion.

    I recompiled the original usb_dev_bulk (the binary in the package was dated 2012), and got a new .bin, a couple of tens of bytes larger. The downloaded binary ran fine and enumerated correctly.

    By now, I detected that the .out file had to be converted to .bin (even though lm4flash happily loaded the .out file into the board). So I added the command below to the Post commands in my project. The resulting .bin was shorter than the demo (8kB instead of 12kB), and did not run correctly.

    ${CCE_INSTALL_ROOT}/utils/tiobj2bin/tiobj2bin "${BuildArtifactFileName}" "${BuildArtifactFileBaseName}.bin"
  • Hello John,

    A few 10's of bytes may be OK, but almost a 33% drop in size means something is not correctly compiled. Can you please share your source code that is generating the 8KB bin file?

    Regards
    Amit
  • Here's the main.c function - basically the same as the usb_dev_bulk example. I suspect however, that the real lies in the linking process.

    //*****************************************************************************
    //
    // usb_dev_bulk.c - Main routines for the generic bulk device example.
    //
    // Copyright (c) 2012 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 9453 of the EK-LM4F120XL Firmware Package.
    //
    //*****************************************************************************
    
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/debug.h"
    #include "driverlib/fpu.h"
    #include "driverlib/gpio.h"
    #include "driverlib/interrupt.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/systick.h"
    #include "driverlib/timer.h"
    #include "driverlib/uart.h"
    #include "driverlib/rom.h"
    #include "usblib/usblib.h"
    #include "usblib/usb-ids.h"
    #include "usblib/device/usbdevice.h"
    #include "usblib/device/usbdbulk.h"
    #include "utils/uartstdio.h"
    #include "utils/ustdlib.h"
    #include "usb_bulk_structs.h"
    
    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>USB Generic Bulk Device (usb_dev_bulk)</h1>
    //!
    //! This example provides a generic USB device offering simple bulk data
    //! transfer to and from the host.  The device uses a vendor-specific class ID
    //! and supports a single bulk IN endpoint and a single bulk OUT endpoint.
    //! Data received from the host is assumed to be ASCII text and it is
    //! echoed back with the case of all alphabetic characters swapped.
    //!
    //! A Windows INF file for the device is provided on the installation CD and
    //! in the C:/StellarisWare/windows_drivers directory of StellarisWare
    //! releases.  This INF contains information required to install the WinUSB
    //! subsystem on WindowsXP and Vista PCs.  WinUSB is a Windows subsystem
    //! allowing user mode applications to access the USB device without the need
    //! for a vendor-specific kernel mode driver.
    //!
    //! A sample Windows command-line application, usb_bulk_example, illustrating
    //! how to connect to and communicate with the bulk device is also provided.
    //! The application binary is installed as part of the ``Windows-side examples
    //! for USB kits'' package (SW-USB-win) on the installation CD or via download
    //! from www.ti.com/stellarisware .  Project files are included to allow
    //! the examples to be built using Microsoft VisualStudio 2008.  Source code
    //! for this application can be found in directory
    //! StellarisWare/tools/usb_bulk_example.
    //
    //*****************************************************************************
    
    
    //*****************************************************************************
    //
    // The system tick rate expressed both as ticks per second and a millisecond
    // period.
    //
    //*****************************************************************************
    #define SYSTICKS_PER_SECOND     100
    #define SYSTICK_PERIOD_MS       (1000 / SYSTICKS_PER_SECOND)
    
    //*****************************************************************************
    //
    // The global system tick counter.
    //
    //*****************************************************************************
    volatile unsigned long g_ulSysTickCount = 0;
    
    //*****************************************************************************
    //
    // Variables tracking transmit and receive counts.
    //
    //*****************************************************************************
    volatile unsigned long g_ulTxCount = 0;
    volatile unsigned long g_ulRxCount = 0;
    #ifdef DEBUG
    unsigned long g_ulUARTRxErrors = 0;
    #endif
    
    //*****************************************************************************
    //
    // Debug-related definitions and declarations.
    //
    // Debug output is available via UART0 if DEBUG is defined during build.
    //
    //*****************************************************************************
    #ifdef DEBUG
    //*****************************************************************************
    //
    // Map all debug print calls to UARTprintf in debug builds.
    //
    //*****************************************************************************
    #define DEBUG_PRINT UARTprintf
    
    #else
    
    //*****************************************************************************
    //
    // Compile out all debug print calls in release builds.
    //
    //*****************************************************************************
    #define DEBUG_PRINT while(0) ((int (*)(char *, ...))0)
    #endif
    
    //*****************************************************************************
    //
    // Flags used to pass commands from interrupt context to the main loop.
    //
    //*****************************************************************************
    #define COMMAND_PACKET_RECEIVED 0x00000001
    #define COMMAND_STATUS_UPDATE   0x00000002
    
    volatile unsigned long g_ulFlags = 0;
    char *g_pcStatus;
    
    //*****************************************************************************
    //
    // Global flag indicating that a USB configuration has been set.
    //
    //*****************************************************************************
    static volatile tBoolean g_bUSBConfigured = false;
    
    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, unsigned long ulLine)
    {
        UARTprintf("Error at line %d of %s\n", ulLine, pcFilename);
        while(1)
        {
        }
    }
    #endif
    
    //*****************************************************************************
    //
    // Interrupt handler for the system tick counter.
    //
    //*****************************************************************************
    void
    SysTickIntHandler(void)
    {
        //
        // Update our system tick counter.
        //
        g_ulSysTickCount++;
    }
    
    //*****************************************************************************
    //
    // Receive new data and echo it back to the host.
    //
    // \param psDevice points to the instance data for the device whose data is to
    // be processed.
    // \param pcData points to the newly received data in the USB receive buffer.
    // \param ulNumBytes is the number of bytes of data available to be processed.
    //
    // This function is called whenever we receive a notification that data is
    // available from the host. We read the data, byte-by-byte and swap the case
    // of any alphabetical characters found then write it back out to be
    // transmitted back to the host.
    //
    // \return Returns the number of bytes of data processed.
    //
    //*****************************************************************************
    static unsigned long
    EchoNewDataToHost(tUSBDBulkDevice *psDevice, unsigned char *pcData,
                      unsigned long ulNumBytes)
    {
        unsigned long ulLoop, ulSpace, ulCount;
        unsigned long ulReadIndex;
        unsigned long ulWriteIndex;
        tUSBRingBufObject sTxRing;
    
        //
        // Get the current buffer information to allow us to write directly to
        // the transmit buffer (we already have enough information from the
        // parameters to access the receive buffer directly).
        //
        USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
    
        //
        // How much space is there in the transmit buffer?
        //
        ulSpace = USBBufferSpaceAvailable(&g_sTxBuffer);
    
        //
        // How many characters can we process this time round?
        //
        ulLoop = (ulSpace < ulNumBytes) ? ulSpace : ulNumBytes;
        ulCount = ulLoop;
    
        //
        // Update our receive counter.
        //
        g_ulRxCount += ulNumBytes;
    
        //
        // Dump a debug message.
        //
        DEBUG_PRINT("Received %d bytes\n", ulNumBytes);
    
        //
        // Set up to process the characters by directly accessing the USB buffers.
        //
        ulReadIndex = (unsigned long)(pcData - g_pucUSBRxBuffer);
        ulWriteIndex = sTxRing.ulWriteIndex;
    
        while(ulLoop)
        {
            //
            // Copy from the receive buffer to the transmit buffer converting
            // character case on the way.
            //
    
            //
            // Is this a lower case character?
            //
            if((g_pucUSBRxBuffer[ulReadIndex] >= 'a') &&
               (g_pucUSBRxBuffer[ulReadIndex] <= 'z'))
            {
                //
                // Convert to upper case and write to the transmit buffer.
                //
                g_pucUSBTxBuffer[ulWriteIndex] =
                    (g_pucUSBRxBuffer[ulReadIndex] - 'a') + 'A';
            }
            else
            {
                //
                // Is this an upper case character?
                //
                if((g_pucUSBRxBuffer[ulReadIndex] >= 'A') &&
                   (g_pucUSBRxBuffer[ulReadIndex] <= 'Z'))
                {
                    //
                    // Convert to lower case and write to the transmit buffer.
                    //
                    g_pucUSBTxBuffer[ulWriteIndex] =
                        (g_pucUSBRxBuffer[ulReadIndex] - 'Z') + 'z';
                }
                else
                {
                    //
                    // Copy the received character to the transmit buffer.
                    //
                    g_pucUSBTxBuffer[ulWriteIndex] = g_pucUSBRxBuffer[ulReadIndex];
                }
            }
    
            //
            // Move to the next character taking care to adjust the pointer for
            // the buffer wrap if necessary.
            //
            ulWriteIndex++;
            ulWriteIndex = (ulWriteIndex == BULK_BUFFER_SIZE) ? 0 : ulWriteIndex;
    
            ulReadIndex++;
            ulReadIndex = (ulReadIndex == BULK_BUFFER_SIZE) ? 0 : ulReadIndex;
    
            ulLoop--;
        }
    
        //
        // We've processed the data in place so now send the processed data
        // back to the host.
        //
        USBBufferDataWritten(&g_sTxBuffer, ulCount);
    
        DEBUG_PRINT("Wrote %d bytes\n", ulCount);
    
        //
        // We processed as much data as we can directly from the receive buffer so
        // we need to return the number of bytes to allow the lower layer to
        // update its read pointer appropriately.
        //
        return(ulCount);
    }
    
    //*****************************************************************************
    //
    // Handles bulk driver notifications related to the transmit channel (data to
    // the USB host).
    //
    // \param pvCBData is the client-supplied callback pointer for this channel.
    // \param ulEvent identifies the event we are being notified about.
    // \param ulMsgValue is an event-specific value.
    // \param pvMsgData is an event-specific pointer.
    //
    // This function is called by the bulk driver to notify us of any events
    // related to operation of the transmit data channel (the IN channel carrying
    // data to the USB host).
    //
    // \return The return value is event-specific.
    //
    //*****************************************************************************
    unsigned long
    TxHandler(void *pvCBData, unsigned long ulEvent, unsigned long ulMsgValue,
              void *pvMsgData)
    {
        //
        // We are not required to do anything in response to any transmit event
        // in this example. All we do is update our transmit counter.
        //
        if(ulEvent == USB_EVENT_TX_COMPLETE)
        {
            g_ulTxCount += ulMsgValue;
        }
    
        //
        // Dump a debug message.
        //
        DEBUG_PRINT("TX complete %d\n", ulMsgValue);
    
        return(0);
    }
    
    //*****************************************************************************
    //
    // Handles bulk driver notifications related to the receive channel (data from
    // the USB host).
    //
    // \param pvCBData is the client-supplied callback pointer for this channel.
    // \param ulEvent identifies the event we are being notified about.
    // \param ulMsgValue is an event-specific value.
    // \param pvMsgData is an event-specific pointer.
    //
    // This function is called by the bulk driver to notify us of any events
    // related to operation of the receive data channel (the OUT channel carrying
    // data from the USB host).
    //
    // \return The return value is event-specific.
    //
    //*****************************************************************************
    unsigned long
    RxHandler(void *pvCBData, unsigned long ulEvent,
                   unsigned long ulMsgValue, void *pvMsgData)
    {
        //
        // Which event are we being sent?
        //
        switch(ulEvent)
        {
            //
            // We are connected to a host and communication is now possible.
            //
            case USB_EVENT_CONNECTED:
            {
                g_bUSBConfigured = true;
                UARTprintf("Host connected.\n");
    
                //
                // Flush our buffers.
                //
                USBBufferFlush(&g_sTxBuffer);
                USBBufferFlush(&g_sRxBuffer);
    
                break;
            }
    
            //
            // The host has disconnected.
            //
            case USB_EVENT_DISCONNECTED:
            {
                g_bUSBConfigured = false;
                UARTprintf("Host disconnected.\n");
                break;
            }
    
            //
            // A new packet has been received.
            //
            case USB_EVENT_RX_AVAILABLE:
            {
                tUSBDBulkDevice *psDevice;
    
                //
                // Get a pointer to our instance data from the callback data
                // parameter.
                //
                psDevice = (tUSBDBulkDevice *)pvCBData;
    
                //
                // Read the new packet and echo it back to the host.
                //
                return(EchoNewDataToHost(psDevice, pvMsgData, ulMsgValue));
            }
    
            //
            // Ignore SUSPEND and RESUME for now.
            //
            case USB_EVENT_SUSPEND:
            case USB_EVENT_RESUME:
            {
                break;
            }
    
            //
            // Ignore all other events and return 0.
            //
            default:
            {
                break;
            }
        }
    
        return(0);
    }
    
    //*****************************************************************************
    //
    // This is the main application entry function.
    //
    //*****************************************************************************
    int
    main(void)
    {
        volatile unsigned long ulLoop;
        unsigned long ulTxCount;
        unsigned long ulRxCount;
    
        //
        // Enable lazy stacking for interrupt handlers.  This allows floating-point
        // instructions to be used within interrupt handlers, but at the expense of
        // extra stack usage.
        //
        ROM_FPULazyStackingEnable();
    
        //
        // Set the clocking to run from the PLL at 50MHz
        //
        ROM_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN |
                           SYSCTL_XTAL_16MHZ);
    
        //
        // Configure the relevant pins such that UART0 owns them.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        GPIOPinConfigure(GPIO_PA0_U0RX);
        GPIOPinConfigure(GPIO_PA1_U0TX);
        ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        //
        // Enable the GPIO port that is used for the on-board LED.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    
        //
        // Enable the GPIO pins for the LED (PF2 & PF3).  
        //
        ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3|GPIO_PIN_2);
    
        
        //
        // Open UART0 and show the application name on the UART.
        //
        UARTStdioInit(0);
        UARTprintf("\033[2JStellaris USB bulk device example\n");
        UARTprintf("---------------------------------\n\n");
    
        //
        // Not configured initially.
        //
        g_bUSBConfigured = false;
    
        //
        // Enable the GPIO peripheral used for USB, and configure the USB
        // pins.
        //
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        ROM_GPIOPinTypeUSBAnalog(GPIO_PORTD_BASE, GPIO_PIN_4 | GPIO_PIN_5);
    
        //
        // Enable the system tick.
        //
        ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / SYSTICKS_PER_SECOND);
        ROM_SysTickIntEnable();
        ROM_SysTickEnable();
    
        //
        // Tell the user what we are up to.
        //
        UARTprintf("Configuring USB\n");
    
        //
        // Initialize the transmit and receive buffers.
        //
        USBBufferInit((tUSBBuffer *)&g_sTxBuffer);
        USBBufferInit((tUSBBuffer *)&g_sRxBuffer);
    
        //
        // Set the USB stack mode to Device mode with VBUS monitoring.
        //
        USBStackModeSet(0, USB_MODE_FORCE_DEVICE, 0);
    
        //
        // Pass our device information to the USB library and place the device
        // on the bus.
        //
        USBDBulkInit(0, (tUSBDBulkDevice *)&g_sBulkDevice);
    
        //
        // Wait for initial configuration to complete.
        //
        UARTprintf("Waiting for host...\n");
    
        //
        // Clear our local byte counters.
        //
        ulRxCount = 0;
        ulTxCount = 0;
    
        //
        // Main application loop.
        //
        while(1)
        {
            //
            // See if any data has been transferred.
            //
            if((ulTxCount != g_ulTxCount) || (ulRxCount != g_ulRxCount))
            {
                //
                // Has there been any transmit traffic since we last checked?
                //
                if(ulTxCount != g_ulTxCount)
                {
                    //
                    // Turn on the Green LED.
                    //
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);
    
                    //
                    // Delay for a bit.
                    //
                    for(ulLoop = 0; ulLoop < 150000; ulLoop++)
                    {
                    }
                
                    //
                    // Turn off the Green LED.
                    //
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0);
                    
                    //
                    // Take a snapshot of the latest transmit count.
                    //
                    ulTxCount = g_ulTxCount;
                }
    
                //
                // Has there been any receive traffic since we last checked?
                //
                if(ulRxCount != g_ulRxCount)
                {
                    //
                    // Turn on the Blue LED.
                    //
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
    
                    //
                    // Delay for a bit.
                    //
                    for(ulLoop = 0; ulLoop < 150000; ulLoop++)
                    {
                    }
                
                    //
                    // Turn off the Blue LED.
                    //
                    GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
    
                    //
                    // Take a snapshot of the latest receive count.
                    //
                    ulRxCount = g_ulRxCount;
                }
    
                //
                // Update the display of bytes transferred.
                //
                UARTprintf("\rTx: %d  Rx: %d", ulTxCount, ulRxCount);
            }
        }
    }
    

  • Hello John,

    I meant the source project that you are using?

    Regards
    Amit
  • My apologies. Part of the original mail should have explained this. It seems to have slipped my mind, after doing several versions of the question before actually posting.

    The original intent of the experiment was not so much testing the USB driver itself, but trying to understand which libraries, include files and external functions had to be linked to the project to get everything compiled and working. So, I created a new project, with just the code from the demo.

    So, I investigated all the error messages from the compiler (missing references etc), and tried to solve all the links. I did solve all the dependency issues, but with the above result...

    I felt that this understanding would be necessary, if I wanted to combine several peripherals, and simple project copying wouldn't work anymore. Also, I thought this experience would help in making a Makefile for gcc.

    To be more specific, I'd like to sample two A/D channels (one on each A/D) at 100kB/s (external clock), optionally oversampling at 4 or 8 times that speed, and sending the samples in N 4-byte blocks over USB.

  • Hello John,

    It would be much simpler to port the original example and make changes to the code base to add the A/D conversion in the main code.

    Regards
    Amit
  • Thanks for the suggestion, Amit.