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.

EK-TM4C123GXL: USB Speed

Part Number: EK-TM4C123GXL


Hi all,

I am trying to use the TM4C123G LaunchPad as an oscilloscope which communicates with an application on host-side. To be able to measure different voltage ranges we developed a BoosterPack which allows us this.  Before sending the measured data via USB I need to prepare them, as there are some communication regulations on the host-side. 

My goal is to reach a samplerate of at least 500kHz of one channel using ADC0. I configured a DMA-Channel in Ping-Pong-Mode to sample continously ADC-data. The time needed to prepare the data is about one 1/4 of the time elapsed between two ADC interrupts. The time left should (theoretically) be big enough to get rid of the prepared data (based on a transmission rate of 12bit*500000kHz).

I created a custom USB-bulk-device based on some parts of the TivaWare usb bulk driver (usbdbulk.c/.h). The USB_EP_AUTO_SET bit is not enabled for the endpoint which I am sending the prepared data over. So I am putting the data with USBEndpointDataPut to the specified FIFO and then sending it with USBEndpointDataSend to the Host. My problem is that I am not able to reach a samplerate which is enough for my needs. I am getting pleasing results if I set the ADC-samplerate down to 31,25kHz by enabling 32 times hardware oversampling. With samplerates above this the errorcounter in the send_packet function is increasing rapidly. So my question is if there is a better way to send data than the way I am doing it right now (see code example below)?

Thanks a lot in advance for your help! If you need more information please let me know.

Regards,

Felix 

void
ADC0IntHandler(void)
{
	//
	// Clear the ADC interrupt
	//
	MAP_ADCIntClear(ADC0_BASE, 0);

	if(g_OsziRun)
	{
		

		if(!MAP_uDMAChannelSizeGet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT))
		{
			// g_ulAdcBuf_ping is ready for processing
			// rewrite channel configuration for next transfer
			// toggle Pin PA4
			
			g_ulBadPeriphIsr1++;
			ui8Oszi_state = PING_DONE;

			MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_4, GPIO_PIN_4);			// debug purposes

			MAP_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_PRI_SELECT,
					UDMA_MODE_PINGPONG,
					(void *)(ADC0_BASE + ADC_O_SSFIFO0 + (0x20 * 0)),
					g_ulAdc0Buf_ping, ADC0_DSTBUF_SIZE);
			MAP_uDMAChannelEnable(UDMA_CHANNEL_ADC0);
			MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_PIN_7);			// debug purposes
			create_oszi_packet(g_ui8ADCModules, PING_DONE);
			MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, 0);				// debug purposes
			MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, GPIO_PIN_0);
			send_packet(PING_DONE);
			MAP_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 0);
		}
		else if(!MAP_uDMAChannelSizeGet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT))
		{
			// g_ulAdcBuf_pong is ready for processing
			// rewrite channel configuration for next transfer
			// toggle Pin PA4
			
			g_ulBadPeriphIsr2++;
			ui8Oszi_state = PONG_DONE;
			
			MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_4, 0);					// debug purposes

			MAP_uDMAChannelTransferSet(UDMA_CHANNEL_ADC0 | UDMA_ALT_SELECT,
					UDMA_MODE_PINGPONG,
					(void *)(ADC0_BASE + ADC_O_SSFIFO0 + (0x20 * 0)),
					g_ulAdc0Buf_pong, ADC0_DSTBUF_SIZE);
			MAP_uDMAChannelEnable(UDMA_CHANNEL_ADC0);
			//MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, GPIO_PIN_7);			// debug purposes
			create_oszi_packet(g_ui8ADCModules, PONG_DONE);
			send_packet(PONG_DONE);
			//MAP_GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_7, 0);
		}
		else
		{
			// normal ADC interrupt, not uDMA interrupt / what???
			g_ulIntOsziCount++;
		}
	}
}

void send_packet(uint8_t ui8PingPong)
{
	static uint32_t routine_count, packet_sent, errorcounter;
	uint8_t buffer_mode = ui8PingPong;
	uint8_t *pData;
	int32_t i32Retcode;
	uint32_t ui32EPStatus;


	routine_count++;

pData = (uint8_t *)&packet_one;
	i32Retcode = MAP_USBEndpointDataPut(USB0_BASE, USB_EP_4, pData, 63);
	ui32EPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_4);
	if(i32Retcode != -1)
	{
		i32Retcode = MAP_USBEndpointDataSend(USB0_BASE, USB_EP_4, USB_TRANS_IN);
		packet_sent++;
		
	}
	else
	{
		
		errorcounter++;
		
	}


}

  • Hello Refe

    I believe you should be using the usblib as is and not make functional changes like that for EP Auto Set. By making such changes you are causing a conflict between the application and the usblib driver files.
  • Hi Amit,
    thanks for the fast reply. So far I just changed the tDeviceInfo struct and therefore also created custom event handlers. For my purpose I need a USB-device with 3 interfaces and 1-2 EPs per interface. Is there a better way to create such a device based on the provided TivaWare drivers?
  • Hello Refe

    You would need to use a composite device for such an implementation if the device types that are going to be used are different. Did you try the basic bulk example for first getting the throughput?
  • Hi Amit,
    thanks again for your help. Yes, the basic bulk example is reaching the needed throughput.
    I did create a composite device now. The last thing I am stuck on now is how to use different endpoints than the ones that are used by default. I do have a CDC device and would like to use EP3 as Interrupt EP IN in the Control Interface. And EP1 IN / EP2 OUT for the data interface of the CDC device. Also I do have a Bulk device for which I would like to use EP4 IN. Changing the specific defines in usbdbulk.c/usbdcdc.c is not working. What could I miss?
    Regards,
    Felix
  • Hello Refe

    How have you created the different descriptor structures and how is the callback being assigned?
  • Hi Amit,

    I created a tUSBDBulkDevice and tUSBDCDCDevice for each of the devices. For the Composite Device I created a tUSBDCompositeDevice. Then I initialized the devices via the USBDCDCCompositeInit / USBDBulkCompositeInit functions. I am not using the instance data again.

    tUSBDBulkDevice g_sBulkDeviceA =
    {
    
        USB_VID_TI_1CBE,
        USB_PID_BULK,
        0,
        USB_CONF_ATTR_SELF_PWR,
        USBBufferEventCallback,
        (void *)&g_sRxBuffer,
        USBBufferEventCallback,
        (void *)&g_sTxBuffer,
        g_ppui8StringDescriptors,
        NUM_STRING_DESCRIPTORS
    
    };
    
    
    tUSBDCDCDevice g_psCDCDevice =
    {
    
            USB_VID_TI_1CBE,
            USB_PID_SERIAL,
            0,
            USB_CONF_ATTR_SELF_PWR,
            ControlHandler,
            (void *)&g_psCDCDevice,
            USBBufferEventCallback,
            (void *)&g_sRxBufferCDC,
            USBBufferEventCallback,
            (void *)&g_sTxBufferCDC,
            g_ppui8StringDescriptors,
            NUM_STRING_DESCRIPTORS
    };
    
    //*****************************************************************************
    //
    // Receive buffer bulk (from the USB perspective).
    //
    //*****************************************************************************
    uint8_t g_pui8USBRxBufferBulk[BULK_BUFFER_SIZE];
    tUSBBuffer g_sRxBuffer =
    {
        false,                          // This is a receive buffer.
        RxHandler,                      // pfnCallback
        (void *)&g_sBulkDeviceA,         // Callback data is our device pointer.
        USBDBulkPacketRead,             // pfnTransfer
        USBDBulkRxPacketAvailable,      // pfnAvailable
        (void *)&g_sBulkDeviceA,         // pvHandle
        g_pui8USBRxBufferBulk,              // pi8Buffer
        BULK_BUFFER_SIZE,               // ui32BufferSize
    };
    
    //*****************************************************************************
    //
    // Transmit buffer (from the USB perspective).
    //
    //*****************************************************************************
    uint8_t g_pui8USBTxBufferBulk[BULK_BUFFER_SIZE];
    tUSBBuffer g_sTxBuffer =
    {
        true,                           // This is a transmit buffer.
        TxHandler,                      // pfnCallback
        (void *)&g_sBulkDeviceA,         // Callback data is our device pointer.
        USBDBulkPacketWrite,            // pfnTransfer
        USBDBulkTxPacketAvailable,      // pfnAvailable
        (void *)&g_sBulkDeviceA,         // pvHandle
        g_pui8USBTxBufferBulk,              // pi8Buffer
        BULK_BUFFER_SIZE,               // ui32BufferSize
    };
    
    
    //*****************************************************************************
    //
    // Receive buffer CDC (from the USB perspective).
    //
    //*****************************************************************************
    uint8_t g_pui8USBRxBufferCDC[256];
    tUSBBuffer g_sRxBufferCDC =
    {
        false,                          // This is a receive buffer.
        RxHandler,                      // pfnCallback
        (void *)&g_psCDCDevice,          // Callback data is our device pointer.
        USBDCDCPacketRead,              // pfnTransfer
        USBDCDCRxPacketAvailable,       // pfnAvailable
        (void *)&g_psCDCDevice,          // pvHandle
        g_pui8USBRxBufferCDC,              // pui8Buffer
        256,               // ui32BufferSize
    };
    
    //*****************************************************************************
    //
    // Transmit buffer (from the USB perspective).
    //
    //*****************************************************************************
    uint8_t g_pui8USBTxBufferCDC[256];
    tUSBBuffer g_sTxBufferCDC =
    {
        true,                           // This is a transmit buffer.
        TxHandler,                      // pfnCallback
        (void *)&g_psCDCDevice,          // Callback data is our device pointer.
        USBDCDCPacketWrite,             // pfnTransfer
        USBDCDCTxPacketAvailable,       // pfnAvailable
        (void *)&g_psCDCDevice,          // pvHandle
        g_pui8USBTxBufferCDC,              // pui8Buffer
        256,               // ui32BufferSize
    };
    tCompositeEntry g_psCompEntries[2];
    
    tUSBDCompositeDevice g_sCompDevice =
    {
    		//
    		// Texas Intruments C-Series VID.
    		//
    		USB_VID_TI_1CBE,
    		//
    		// Texas Intruments C-Series PID for composite serial device.
    		//
    		PID_ASSIGNED_BY_TI,
    		//
    		// This is in 2mA increments so 500mA.
    		//
    		250,
    		//
    		// Bus powered device.
    		//
    		USB_CONF_ATTR_SELF_PWR,
    		//
    		// Composite event handler.
    		//
    		0,
    		//
    		// The string table.
    		//
    		g_ppui8StringDescriptors,
    		NUM_STRING_DESCRIPTORS,
    		//
    		// The Composite device array.
    		//
    		2,
    		g_psCompEntries
    
    };
    
    uint8_t g_pui8DescriptorData[DESCRIPTOR_DATA_SIZE];
    
    
    .
    .
    .
    // Pass device information to the USB library and place the device on the bus
    
        pvCDCDevice = USBDCDCCompositeInit(0, &g_psCDCDevice, &g_psCompEntries[0]);
    
        pvBulkDeviceA = USBDBulkCompositeInit(0, &g_sBulkDeviceA, &g_psCompEntries[1]);
    
    
        USBDCompositeInit(0, &g_sComp.Device, DESCRIPTOR_DATA_SIZE, g_pui8DescriptorData);
    

  • Hello Refer

    When using a composite device with CDC class, as far as I know there is a certain amount reserved for the interrupt status as generated by the interrupt end point. I am not sure what this fixed value is (should be there in the specification). Can you please check the same in the USB spec?

    I am not sure as to the performance profile of such a mix especially when there is a real time data being sent. Also when you mention that there is a increase in the error counter send_packet function it could be possible that the USB pipe is busy that it does not release the same to the end point. Since you are able to get the performance in a bulk implementation can you instead rely on the same for data transfer?
  • Hi Amit,
    sorry for the late response. The two different classes come from an earlier version of the Host-Application. With the newest hardware (older versions of Atmel-MCU and now TI LaunchPad) data is being sent over the Bulk EP, whereas the commands towards the µC are being sent over the CDC class device. To guarantee compability with older and newer hardware I am trying to use the given USB-Configuration of the older hardware also within the LaunchPad. Within these devices the same device mix was used and working. So I think assigning different EPs should work here too.
    So far the Host-Application is able to connect and communicate with the LaunchPad. I am now reaching samplerates of about 250kHz. I think with more efficient use of the USB-buffers I will be able to go even higher. This is what I am working on right now. If I'll face some more problems I'd come back to you with more questions :-)
    For now the only open topic here is the use of different EPs for each of the devices than the ones assigned by default by the composite device driver.
    Thanks again for your input. The device is already working much better than before!
    Regards,
    Felix
  • Hello Refe

    The different EP's are required when handling composite devices. So it is the correct model that you have. I always use a single set of EP's for bulk IN and OUT with packet structure containing control and data information.
  • Hello Amit,

    I think I wasn't clear enough. The thing what I mean is that I want to use e.g. EP4 as the EP used for data information. Right now the EP number is assigned by default (counting upwards starting from EP1; see picture below). To guarantee compability with older hardware boards I'd like to use EP3 IN as the Interrupt EP instead of EP1 IN. Also I'd like to change the other EP numbers relating to the different devices and interfaces.
    When creating a single bulk device I was able to change the EPs assigned to an interface by setting it in the usbdbulk.c in lines 73-74 (defines named DATA_IN_ENDPOINT and DATA_OUT_ENDPOINT). Changing these values has no effect on a composite device.

    Regards,

    Felix

  • Hello Felix,

    The use of different EP's is OK. I don't think we place a hard and fast rule on the implementation model. Rather we have used the EP's in the incremental order of their occurrence in the USB core.
  • Hi Amit,

    your tips were very helpful and the software could get released as planned. 

    The next upgrade should deliver a better samplerate of the osciloscope, which means to increase the speed of the USB-transmission. Right now we are reaching a transmission rate of 0,5 MByte/s. The transmission rate reached with the USB-Bulk-Example is at about 0,8 MByte/s. 

    Both is far away from the USB 2.0 rate (60MByte/s) and also the USB 1.0 rate (1.5 MByte/s). Is the TM4C able to use USB2.0? 

    In this thread I read about double-packet buffering (USB-Speed Thread) and followed Tsuneo Chinzeis advice/code snippet. Unfortunately it did not help to increase the transmission rate.

    So far I've tested the different usblib functions USBBufferWrite and USBBufferDataWritten. As expected the USBBufferDataWritten function was way faster, than the USBBufferWrite function. The next step would be to implement a USB-DMA-channel and test how this is increasing the USB-speed. But as we are so far lower than the possible max. transmission rate I am afraid, that this would not help as much as expected.

    Do you have some other advice on how to speed up the transmission?

    We'd like to achieve a transmission rate as high as possible, but at least at about 2 MByte/s.

    Many thanks in advance,

    Felix