cancel
Showing results for 
Search instead for 
Did you mean: 

Way to be notified on CDC transmit complete?

ryansturmer
Associate II
Posted on March 31, 2016 at 16:14

I have observed in all of the USB-CDC examples I've seen with the STM32Cube libraries, that the transmit buffer is managed with a timer that polls the USB to see if the previous transmission was complete (or rather if the USB is busy) before setting the next chunk of transmit buffer and calling USBD_CDC_TransmitPacket - Is there a way around having to poll this?  Is there an interrupt available that indicates with a transmit is complete so I know as soon as possible when I can send the next packet?  Or do I have to poll?  If I have to poll, what is a good polling interval to get the best possible performance (FS USB, not HS)

#usb #cdc #device #usb-cdc-stm32cube-stm32-polling
1 ACCEPTED SOLUTION

Accepted Solutions
Pavel A.
Evangelist III

@Sebastian​ 

> why can not I send a chain after another, my PC only receive the first

Sending takes time.

 USBD_CDC_TransmitPacket() only queues the data for sending and returns immediately. Until the data has been sent, TxState flag is set, so your second call to  CDC_Transmit_FS() returns USBD_BUSY and the 2nd data is not sent.

The TxState flag is cleared in the USB interrupt callback inside the CDC class module

https://github.com/pavel-a/stm32f0_libs/blob/389cc3c20de72e529038442027d65dd652863305/STM32Cube_FW_F0/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c#L664

There is no direct access to this callback unless you modify the source.

-- pa

View solution in original post

9 REPLIES 9
troy1818
Senior
Posted on April 04, 2016 at 10:19

I wonder this as well...

ryansturmer
Associate II
Posted on April 04, 2016 at 15:21

Right?!  It seems like that's how it should work, but every example I find uses a timer to poll the interface to check if it's busy, which seems like it would introduce needless latency, especially if you didn't want to be taking frequent timer interrupts in your application to check on the USB.  I have been hesitant to dip down into the low level USB code to search for a resolution, but I think I'm getting to that point!

tsuneo
Senior
Posted on April 04, 2016 at 17:51

Hi sturmer,

> Way to be notified on CDC transmit complete?

a) Poll ((USBD_CDC_HandleTypeDef*)(USBD_Device.pClassData))->TxState value in your application code

- While transfer is going on, TxState is kept in 1

- When transfer completes, TxState goes to 0 (see code below)

OR

b) Modify CDC class code, to add your custom callback, which is called at transfer completion interrupt.

\STM32Cube_FW_F4_V1.11.0\Middlewares\ST\STM32_USB_Device_Library\Class\CDC\Src\

usbd_cdc.c

// line 664

static uint8_t  USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)

{

  USBD_CDC_HandleTypeDef   *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;

 

  if(pdev->pClassData != NULL)

  {

    

    hcdc->TxState = 0;

    // <----------------   add your custom callback here.

    return USBD_OK;

  }

  else

  {

    return USBD_FAIL;

  }

}

> so I know as soon as possible when I can send the next packet?

You don't need to split large buffer into chunks.

You may pass ANY size of data buffer to USBD_CDC_SetTxBuffer(), even greater than MPS (Max Packet Size: FS 64 bytes / HS 512 bytes). The device stack automatically splits the buffer into MPS transactions, and it sends them one by one.

When you are starting on an existing ''CDC_Standalone'' project, instead of generated code by CubeMX, this project implements a periodic timer to keep TX (device->host) transfer. This timer is good for USB-UART application to keep transfer speed (size) over slow UART with fast USB. But for other applications, this timer ISR causes unexpected result. And then, delete this timer initialization and its ISR (HAL_TIM_PeriodElapsedCallback())

usbd_cdc_interface.c

static int8_t CDC_Itf_Init(void)

{

  ...

// line 120    Delete these timer setting lines

  /*##-3- Configure the TIM Base generation  #################################*/

  TIM_Config();

 

  /*##-4- Start the TIM Base generation in interrupt mode ####################*/

  /* Start Channel1 */

  if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)

  {

    /* Starting Error */

    Error_Handler();

  }

Tsuneo

> to ST web admin,

''Format Code Block'' button doesn't work with ''Web.config registration missing'' error. Please fix it.

ryansturmer
Associate II
Posted on April 04, 2016 at 18:46

The splitting into chunks comes from wanting to do multiple writes consecutively, and running into the problem of the USB being busy.  If I do two USB writes, and the second one is called before the first one is complete, I either have to sleep and wake up with a timer to poll the interface, or I can do as you describe and use a callback to kick off the next write.  (Or as a third option, i could poll, but this isn't ideal for me)

Do I have this right?  

Amel NASRI
ST Employee
Posted on April 08, 2016 at 11:59

Hi chinzei.tsuneo,

Thanks for all your valuable answers in our STM32 forums.

> to ST web admin,

 

''Format Code Block'' button doesn't work with ''Web.config registration missing'' error. Please fix it.

 

==> We are working to fix this issue. Sorry for such inconvenience.

-Mayla-

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Amel NASRI
ST Employee
Posted on April 13, 2016 at 11:10

Hi,

Please note that the issue related to ''Format Code Block'' or picture insert is already fixed.

-Mayla- 

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

mikael2
Associate II
Posted on April 21, 2016 at 16:44

It seems I may have introduced a bug when I added my queue watermark check algoritm.

In my application I did check TxReady flag from systick:

<code>

SYSTICK_Callback()

{

  /* lock is a struct with uint8_t *p_data and int length */

  static QueueLock lock;

  if (!IsTxBusy() && queue_has_data()) {

  /* unlock area that was owned by USB TX */

  queue_unlock_bytes(&queue, &lock);

  /* lock new bytes that is inserted in queue, ,mainloop can continue insert until queue is full */

  queue_lock_bytes(&queue, &lock);

  USBD_CDC_SetTxBuffer(lock.p_data, lock.length);

  USBD_CDC_TransmitPacket(..)

  }

}

</CODE>

Systick runs every one millisecond and I don't want to change this interval.

However I realized that I also need some kind of watermark of my queue. When queue almost filled I want to enable TX faster than systick. So I changed my code to to also handle this.

This made me remove USBD_CDC_Transfer code to mainloop since I did not want a possible raise condition. BUT this made USB RX sometimes hangs my linux terminal hangs in read() and I get wierd MMIS interrupts on the STM but no Data_OUT events?

My guess now is that systick has higher priority than USB_FS and systick does not get interrupted by USB. But when I move TX enabling to mainloop call to CDC_Transfer may get interrupted by HAL_PCD_IRQHandler in the middle and makes USB stop work?

Could this be the problem or should I do it different or search for errors elsewhere?

Sebastian1
Associate II

Hi

why can not I send a chain after another, my PC only receive the first

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

 /* USER CODE BEGIN Callback 0 */

 /* USER CODE END Callback 0 */

 if (htim->Instance == TIM1) {

   HAL_IncTick();

 }

   if (htim->Instance == TIM2)

   {

      uint8_t text1[] = "Hello World\r\n";

      uint8_t text2[] = "I m here\r\n";

      CDC_Transmit_FS(text1, 13);

      CDC_Transmit_FS(text2, 10);

   }

}

if I m place it on the principal loop I need place an Hal_delay beteween CDC_transmit_S, I m using the CubeMx configuration, the code is:

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)

{

 uint8_t result = USBD_OK;

 /* USER CODE BEGIN 7 */

 USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;

 if (hcdc->TxState != 0){

   return USBD_BUSY;

 }

 USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);

 result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);

 /* USER CODE END 7 */

 return result;

}

someone can help me and say what mistake I'm committing

thanks for your help

Pavel A.
Evangelist III

@Sebastian​ 

> why can not I send a chain after another, my PC only receive the first

Sending takes time.

 USBD_CDC_TransmitPacket() only queues the data for sending and returns immediately. Until the data has been sent, TxState flag is set, so your second call to  CDC_Transmit_FS() returns USBD_BUSY and the 2nd data is not sent.

The TxState flag is cleared in the USB interrupt callback inside the CDC class module

https://github.com/pavel-a/stm32f0_libs/blob/389cc3c20de72e529038442027d65dd652863305/STM32Cube_FW_F0/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c#L664

There is no direct access to this callback unless you modify the source.

-- pa