cancel
Showing results for 
Search instead for 
Did you mean: 

USB to transmit real-time data in the least time possible

wfarid
Associate II
Posted on April 16, 2011 at 00:20

USB to transmit real-time data in the least time possible

24 REPLIES 24
tsuneo
Senior
Posted on May 17, 2011 at 14:31

> 1) Is this limitation because of the CDC ??

Maybe, the Microchip author of this presentation still lives in Windows 98. Or, he doesn't realize on the basic endpoint handling of the device side, which I explained in this post,

http://www.cygnal.org/ubb/Forum9/HTML/001627.html

He should retire from USB world soon.

Microchip distributes a USB library in their Application Library

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2680&dDocName=en547784

Using this library as is, I can see transfer of 255 bytes / 1ms USB frame on CDC using PIC24F/32MX. Modifying the library function a little, so that it accepts unsigned short instead of unsigned char, I can see above top speed on these MCUs. PIC18F has core performance penalty. But even on this MCU family, I see more than 200 bytes/frame.

> 2) Does the host need to send the ''transfer'' size before the device sent any data ?

For CDC, host app doesn't need to notify transfer size to the device. Instead, device always has to terminate the bulk IN transfer in short packet (including ZLP - Zero-Length Packet).

PC CDC class driver repeats fixed-size transfer requests on bulk IN pipe. Usually, this size is 4K bytes (the paged memory size of PCI bus). Therefore, each transfer must be terminated by device using short packet.

> 3) When using UART, we can just send data from the uController at any time without the host initiating it, can the bulk transfer mode on the STM32-USB be the same ?

No. Not just on STM32.

USB is designed as host-centric.

Device prepares packet data on the bulk IN endpoint buffer, in advance.

When host initiates an IN transaction, the device USB engine (SIE) returns the data on the buffer in this IN transaction.

If the endpoint buffer is empty, SIE returns NAK (not ready) to the IN transaction. NAK is also a normal response, which consists USB hardware flow control. Receiving NAK, PC host controller repeats IN transaction after short interval. In this way, host controller repeatedly polls bulk IN endpoint, while the endpoint is NAKing. You'll see 30-40 IN-NAK transactions per frame.

> 4) Any idea what is the transfer rate will be using the Isochronous transfer mode ? Shall it reach the maximum 1.0-1.5 MB/sec ?

The top speed of full-speed isoc is 1023 bytes/frame.

> 5) So now, if I have a USB-host with only one port connected to a USB-device which transmits the data using Bulk mode or Isochronous mode.

Bulk is much easier than isoc on both side.

On host side, availability of PC class driver is the problem.

- For bulk, CDC and WinUSB is available as in-box driver.

- For isoc, in-box USB audio. But I believe you don't like your PC app as an audio app :)

On the device side, isoc requires tight timing to keep the transfer in ''isochronous''. Bulk timing is rough.

> -> When using Bulk mode only , is the transfer still considered low priority ??

Yes. As I wrote in other post, these setups ensure full bandwidth for bulk.

1) The device is directly plugged in to a PC USB port (ie. PC host controller), without any other device.

OR

2) The device connects to a PC over a multi-TT hub (*), with other full-/low-speed devices.

> -> When using Isochronous mode only, can we still expect errors to occur compared to having more than one device connected to the USB ??

In usual environment in office and home, you rarely see isoc error.

Tsuneo

wfarid
Associate II
Posted on May 17, 2011 at 14:31

Thank you Tsueno for your clear answers. Kudos !!

wfarid
Associate II
Posted on May 17, 2011 at 14:31

Hi guys,

By modifying the Virtual COM Port example, I'm trying to send a big buffer over USB:

1)

According to what Tsueno mentioned below: I looked at the following functions EP1_IN_Callback() & Handle_USBAsynchXfer(). Tsueno mentioned that the Handle_USBAsynchXfer() is executed every 1mSec, but I don't see this is the case when having a sending a character to USART1 from within this function. It seems to me that this function is only called when I send any characters on USART1, which in return add a character to USART_Rx_Buffer[] and increments the USART_Rx_ptr_in by 1.

So is that right ?

According to what is mentioned below, EP1_IN_Callback() is called by the endpoint interrupt, which happens after the last packet transfer.

2)

What is meant here by the last packet transfer ? Is it after the packet that is transfered by Handle_USBAsynchXfer() ? I think that it is executed if the transfer size is bigger than the data packet size which is 64 Bytes in this case ?

3)

  After commenting the body of the USART_TO_USB_Send_Data(), I used USART_Rx_Buffer[] & USART_Rx_ptr_in to fill my data in this buffer, and to get it on the USB side whenever these functions are called. But, I can't evaluate how long does it take to send the whole buffer ? Is that a valid idea ?

 

 

4)

  I thought about using the function UserToPMABufferCopy

()

''which is called from within EP1_IN_Callback() & Handle_USBAsynchXfer()'' directly to send the whole buffer or to send a Zero Length Packet as below. If I did so, then what is the use of both functions then (EP1_IN_Callback() & Handle_USBAsynchXfer())  ?

UserToPMABufferCopy

(&

USART_Rx_Buffer

[

USB_Tx_ptr

],

ENDP1_TXADDR

,

USB_Tx_length

);

SetEPTxCount

(

ENDP1

,

USB_Tx_length

);

SetEPTxValid

(

ENDP1

);

5)

  I have been trying to find more tutorials for the USB-FS Library, but the UM0424 is the only one. Are there any others ?

Thank you,

- Walid

________________________________________________________________________

As of ST library, I recommend you to start with \Project\Virtual_COM_Port example. This example transfer data from on-chip UART to PC over USB. Also, in the other direction. On the PC side, the chip is exposed as a COM port. Therefore, you don't need to change your PC code so much.

 

You'll modify this example so that it picks up data from GPIO instead of the UART. As this example gives full source code, you may be lost in the forest of the source code. These functions are ones you have to focus on,

 

1) EP1_IN_Callback() in \Project\Virtual_COM_Port\src\usb_endp.c

2) Handle_USBAsynchXfer() in \Project\Virtual_COM_Port\src\hw_config.c

 

Both of these functions moves packetized data from the UART RX buffer to USB endpoint buffer using USB_SIL_Write() (or equivalent). Handle_USBAsynchXfer() is periodically called by USB SOF (Start Of Frame) interrupt, ie. every 1ms. It moves the first packet to the endpoint buffer after interval. EP1_IN_Callback() is called by the endpoint interrupt, which occurs just when the last transaction (a packet transfer) finishes. This routine pushes the second and latter packets into the endpoint, if the RX buffer still holds data more than the packet size.

tsuneo
Senior
Posted on May 17, 2011 at 14:31

Before starting detailed explanation of the Virtual COM Port example, we pick up a simplified case.

Suppose that you have a large data on a buffer to transfer over the bulk IN endpoint (EP) on your firmware. This transfer is split into wMaxPacketSize (64 bytes) transactions.

1) Your code fills the first packet (**1) to the EP buffer using USB_SIL_Write() (and SetEPTxValid() -- **2)

2) The USB engine (SIE: Serial Interface Engine) on the chip waits for an IN transaction from host.

3) When an IN transaction comes on the bulk IN EP, SIE sends the EP buffer to host. At the completion of this transaction, SIE puts interrupt.

4) For this EP interrupt, the USB library calls EPn_IN_Callback(). In this callback,

- your code fills the next packet to the bulk IN EP, using USB_SIL_Write() (and SetEPTxValid()).

OR

- your code fills USB_SIL_Write() (and SetEPTxValid()) with size 0, NULL buffer for ZLP, if required.

OR

- when you have nothing to send, your code just returns from this callback.

In this way, the large data is transferred over the bulk IN EP.

The point of this story is that, your code has to put the first packet outside of EPn_IN_Callback(). Without this first kick, the sequence of EP interrupt doesn't start. This code pattern is same as the case of UART TX, when UART is handled using interrupt. Actually, you can code USB EP like a UART. The major difference is that UART processes a byte, but USB EP does a packet. If UART coding is familiar to you, you'll be soon good at USB EP, too.

**1) In firmware context, we often use the term ''packet'' in this way. This ''packet'' is not an exact USB term. It means the payload of a transaction.

**2) Because of the difference of the SIE on STM32F103 and 105/7 (CL:Connectivity Line), the v3.3.0 library examples apply this #ifdef to send a packet to an IN EP.

#ifdef USE_STM3210C_EVAL

      USB_SIL_Write(EP1_IN, &USART_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);  

#else

      UserToPMABufferCopy(&USART_Rx_Buffer[USB_Tx_ptr], ENDP1_TXADDR, USB_Tx_length);

      SetEPTxCount(ENDP1, USB_Tx_length);

      SetEPTxValid(ENDP1); 

#endif

Actually, this code pattern is (almost) same as this one,

      USB_SIL_Write(EP1_IN, &USART_Rx_Buffer[USB_Tx_ptr], USB_Tx_length);  

#ifndef STM32F10X_CL

      SetEPTxValid((EP1_IN & 0x7F);   // validate (set ready) EP buffer for SIE other than CL

#endif

It's because library codes USB_SIL_Write() as follows,

\Libraries\STM32_USB-FS-Device_Driver\src\usb_sil.c

uint32_t USB_SIL_Write(uint8_t bEpAddr, uint8_t* pBufferPointer, uint32_t wBufferSize)

{

#ifndef STM32F10X_CL

  /* Use the memory interface function to write to the selected endpoint */

  UserToPMABufferCopy(pBufferPointer, GetEPTxAddr(bEpAddr & 0x7F), wBufferSize);

  /* Update the data length in the control register */

  SetEPTxCount((bEpAddr & 0x7F), wBufferSize);

  

#else

  

   /* Use the PCD interface layer function to write to the selected endpoint */

   PCD_EP_Write (bEpAddr, pBufferPointer, wBufferSize); 

   

#endif /* STM32F10X_CL */

  return 0;

}

Tsuneo

tsuneo
Senior
Posted on May 17, 2011 at 14:31

Next, we touch to the transfer speed on the bulk IN transfer of the virtual COM port (VCP).

Keep full-size (64 bytes) packets for faster transfer speed

In above simplified example, we have to keep full-size packets to continue the transfer. It's because a short packet (less than 64 bytes) terminates the transfer. On the PC side, CDC (Communication Device Class: VCP) driver repeatedly issues IN transfer requests. So far so good. However, PC host controller schedules new transfer at every SOF (Start Of Frame) (**1). New transfer doesn't occur until next frame. Once the last transfer terminates, the device has to wait for a new transfer at the next frame, to put a next packet. This delay reduces transfer speed significantly. Therefore, firmware has to keep full-size packets as long as possible to get faster transfer speed.

Greater buffer size prevents data drop

USB is a time-shared bus to which many devices hook on. As bulk transfer is ''best-effort'' type, the activity of other devices affects to bulk transfer speed. Transfer speed varies from USB frame to frame. While the transfer speed drops, incoming data is kept on the RX buffer until the transfer speed recovers. In this reason, greater buffer size decreases the chance of data drop. But in the real application, the buffer size is a limited number. We have to determine explicitly the firmware action when data drop occurs unfortunately. For example,

- Just silently drop the data. Continue the transfer as far as possible.

- Notify data drop to host using notification over the interrupt IN endpoint

etc.

Latency timer

UART RX doesn't always come in 64 bytes chunk. It may come in sporadic interval, and in short chunks. If the device would always wait for 64 bytes on the buffer, no response could pass from the device to PC for long interval. To ensure the deadline response, a latency timer forces a packet transfer, even when the buffer has less than 64 bytes.

These implementation requirements of VCP are so complicated, because it handles UART RX in which we can't predict the timing of incoming data. Though the ST example doesn't satisfy all of these requirements, but it is still complicated, too. In your application, however, the incoming timing is periodical, and the data size is fixed, instead of UART. It makes your implementation much simpler than this example.

(**1) High-Speed host controller schedules transfers at every micro-frame (125 uS). Full-speed device also enjoys this shorter interval when it is connected to a PC over a high-speed hub.

Tsuneo

tsuneo
Senior
Posted on May 17, 2011 at 14:31

Now that we are prepared to answer to your questions :)

1) Tsueno mentioned that the Handle_USBAsynchXfer() is executed every 1mSec, but I don't see this is the case when having a sending a character to USART1 from within this function.

Handle_USBAsynchXfer() is called just from SOF_Callback(), as follows.

SOF_Callback() is called by the library when the SIE receives a SOF (Start Of Frame) packet, which is put by host every 1ms.

/*******************************************************************************

* Function Name  : SOF_Callback / INTR_SOFINTR_Callback

* Description    :

* Input          : None.

* Output         : None.

* Return         : None.

*******************************************************************************/

#ifdef STM32F10X_CL

void INTR_SOFINTR_Callback(void)

#else

void SOF_Callback(void)

#endif /* STM32F10X_CL */

{

  static uint32_t FrameCount = 0;

  

  if(bDeviceState == CONFIGURED)

  {

    if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)

    {

      /* Reset the frame counter */

      FrameCount = 0;

      

      /* Check the data to be sent through IN pipe */

      Handle_USBAsynchXfer();            // <------------- called here

    }

  }  

}

Humm, my bad.. Precisely, the period is not 1ms, but every VCOMPORT_IN_FRAME_INTERVAL ms.

2) What is meant here by the last packet transfer ?

The last transaction completed by the SIE.

3) But, I can't evaluate how long does it take to send the whole buffer ? Is that a valid idea ?

Bulk transfer speed reduces when other devices on the bus disturb the transfer. When you control the connection of devices, as I said on above post, you can tell on expected speed. It's around 400 - 500 Kbytes/sec. For STM32F103, enabling double buffer on the bulk endpoint, the speed will go twice. For STM32F105/7, making USB FIFO work better, you'll see twice speed, too.

Anyway, which MCU line are you working on?

4) If I did so, then what is the use of both functions then (EP1_IN_Callback() & Handle_USBAsynchXfer())  ?

You need a ''first kick'' (Handle_USBAsynchXfer()) to start the sequence of EP1_IN_Callback() calls.

5) but the UM0424 is the only one. Are there any others ?

I don't know.

If you have questions, ask them here. I'll answer.

Tsuneo

hamdi23
Associate II
Posted on October 04, 2012 at 15:15

Hi

could anyone give us a clear explanation about the usb OTG library v2.2 for stm32f4and show us where we should focus because i'am really lost in the forest of codes  
Posted on October 04, 2012 at 18:17

could anyone give us a clear explanation about the usb OTG library v2.2 for stm32f4and show us where we should focus because i'am really lost in the forest of codes 

Please provide some context:

What class of USB device are you interested in?

What experience do you have coding USB devices, or USB implementation in general?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
emalund
Associate III
Posted on October 04, 2012 at 19:09

just a thought not really related.  Maybe you are thinking of the horse when a different cart will do

 

have you thought about ''fata compression''.  I once solved a similar issue by devising a ''compression algorithm'' One thing I have not seen is ''do you need the data to be transferred in absolute real time (1ms)'' i.e. if the data arrives within a second is that OK?

Erik
hamdi23
Associate II
Posted on October 05, 2012 at 02:17

well i'm usb beginner ,the problem is how to use the stm32f4 library correctly what files should i change ,create to configure and send or receive data (for any class )for example  i was reading the ST user manual 1021  and i get confused what function i have to use to initialize the usb (USBD_Init in device library or DCD_Init in the core driver ) how the endpoints and callbacks  works,does the usb work in interrupt mode only or also can in polling mode ?? is there any tutorial could guide me step by step ??