cancel
Showing results for 
Search instead for 
Did you mean: 

Urgent Help Needed: USB CDC Transmit Stuck, hcdc->TxState Never Resets!

AChap.1
Associate III

Hello ST Community,

I'm developing a USB CDC communication project on an STM32L476RET6 microcontroller, using STM32CubeIDE. While I can successfully receive data from a PC using PuTTY, I'm facing a challenge with transmitting data back to the PC using the CDC_Transmit_FS function.

Environment:

  • MCU: STM32L476RET6
  • IDE: STM32CubeIDE
  • USB Configuration: Configured as a CDC Virtual COM Port
  • OS: Windows 10

Issue Description: Data reception from the PC works flawlessly. However, when attempting to transmit data back using CDC_Transmit_FS, the function seems to never complete. The code gets indefinitely stuck waiting for hcdc->TxState to reset to 0, which, based on my understanding, should happen via an interrupt once the transmission completes.

Troubleshooting Done:

  • Confirmed correct USB descriptor configurations.
  • Debugged to pinpoint that the halt occurs during data transmission, specifically waiting for hcdc->TxState to reset.

Questions:

  • Are there any known issues with USB CDC on STM32L476RET6 that might cause this behaviour?
  • Could there be any CubeMX configuration or USER CODE that I might have overlooked that could lead to this issue?

I've attached relevant code snippets for context. The problem arises when sending the "FLASHING_START" command, which is successfully received by the STM32, but any subsequent attempt to send data back to the PC doesn't complete.

Code Snippets:

Message Handler:

 

void messageHandler(uint8_t* Buf)
{
	if(string_compare((char*)Buf, ERASE_FLASH_MEMORY, strlen(ERASE_FLASH_MEMORY))
			&& flashStatus != Unlocked)
	{
		eraseMemory();
		CDC_Transmit_FS((uint8_t*)"Flash: Erased!\n", strlen("Flash: Erased!\n"));
	}else if(string_compare((char*)Buf, FLASHING_START, strlen(FLASHING_START)))
	{
		CDC_Transmit_FS((uint8_t*)"Can we print some text?\n", strlen("Can we print some text?\n")); // THIS IS WHERE ALL MY PROBLEMS BEGIN
		unlockFlashAndEraseMemory();
		CDC_Transmit_FS((uint8_t*)"Flash: Unlocked!\n", strlen("Flash: Unlocked!\n"));
	}else if(string_compare((char*)Buf, FLASHING_FINISH, strlen(FLASHING_FINISH))
			  && flashStatus == Unlocked)
	{
		lockFlash();
		CDC_Transmit_FS((uint8_t*)"Flash: Success!\n", strlen("Flash: Success!\n"));
	}else if(string_compare((char*)Buf, FLASHING_ABORT, strlen(FLASHING_ABORT))
			  && flashStatus == Unlocked)
	{
		lockFlash();
		eraseMemory();
		CDC_Transmit_FS((uint8_t*)"Flash: Aborted!\n", strlen("Flash: Aborted!\n"));
	}else
	{
		CDC_Transmit_FS((uint8_t*)"Error: Incorrect step or unknown command!\n",
			  strlen("Error: Incorrect step or unknown command!\n"));
	}
}

 

 

CDC Transmit Function:

 

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 7 */
      uint32_t startTick = HAL_GetTick();

      USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData;
      if (hcdc == NULL) {
          return USBD_FAIL;
      }

      // Wait for previous transmission to complete or for timeout
      while (hcdc->TxState != 0) {
          // Check for timeout
          if ((HAL_GetTick() - startTick) > CDC_TRANSMIT_TIMEOUT_MS) {
              return USBD_FAIL;  // Transmission timed out
          }
      }

      // Set the transmission buffer and length
      USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);

      // Start the transmission
      result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);

      if (result == USBD_OK) {
          // Wait for transmission to complete or for timeout
          startTick = HAL_GetTick();
          while (hcdc->TxState != 0) {    // THIS NEVER RESETS TO ZERO!
              // Check for timeout
              if ((HAL_GetTick() - startTick) > CDC_TRANSMIT_TIMEOUT_MS) {
                  return USBD_FAIL;  // Transmission timed out
              }
          }
      }
  /* USER CODE END 7 */
  return result;
}

 

 

Any insights or recommendations from the community would be immensely appreciated.

Thank you for your support!

Adrian.

1 ACCEPTED SOLUTION

Accepted Solutions
FBL
ST Employee

Hi @AChap.1 

 

Which Cube firmware revision are you using? 1.18.0? If I understand correctly, you are using Putty to print from STM32 using UART?

I think if all necessary endpoints are properly initialized in your code, you would not face this issue.

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.

View solution in original post

9 REPLIES 9
FBL
ST Employee

Hello @AChap.1 

I was wondering if you have already tried to use the example project provided. 

Could you watch in live debug your data struct hUsbDeviceFS while setting transmission buffer and its length then while starting the transmission. I guess Len should be uint32_t.

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.

AChap.1
Associate III

 

Hi @FBL,

I managed to find the ST Open Bootloader after you mentioned examples.

https://community.st.com/t5/stm32-mcus/how-to-use-the-st-open-bootloader-for-stm32-microcontrollers/ta-p/49896 

However, this is for the STM32G0. Are there similar examples for STM32L4?

Thanks,

Adrian.

AChap.1
Associate III

Hi @FBL,

 

Regarding observing the data struct hUsbDeviceFS:

At the point of this line of code: USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len);

This is the structure:

 

Name : hUsbDeviceFS

Details:{id = 0 '\0', dev_config = 1, dev_default_config = 0, dev_config_status = 0, dev_speed = USBD_SPEED_FULL, ep_in = {{status = 0, total_length = 7, rem_length = 7, maxpacket = 64, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 16}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 0, bInterval = 0} <repeats 13 times>}, ep_out = {{status = 0, total_length = 7, rem_length = 7, maxpacket = 64, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 0, bInterval = 0} <repeats 14 times>}, ep0_state = 5, ep0_data_len = 7, dev_state = 3 '\003', dev_old_state = 1 '\001', dev_address = 12 '\f', dev_connection_status = 0 '\0', dev_test_mode = 0 '\0', dev_remote_wakeup = 0, ConfIdx = 0 '\0', request = {bmRequest = 161 '¡', bRequest = 33 '!', wValue = 0, wIndex = 0, wLength = 7}, pDesc = 0x20000150 <FS_Desc>, pClass = 0x20000038 <USBD_CDC>, pClassData = 0x20001c0c <mem>, pUserData = 0x2000013c <USBD_Interface_fops_FS>, pData = 0x20001700 <hpcd_USB_OTG_FS>, pBosDesc = 0x0, pConfDesc = 0x200000b4 <USBD_CDC_CfgFSDesc>}

 

At the point of this line of code: result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);

This is the structure:

 

Name : hUsbDeviceFS

Details:{id = 0 '\0', dev_config = 1, dev_default_config = 0, dev_config_status = 0, dev_speed = USBD_SPEED_FULL, ep_in = {{status = 0, total_length = 7, rem_length = 7, maxpacket = 64, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 16}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 0, bInterval = 0} <repeats 13 times>}, ep_out = {{status = 0, total_length = 7, rem_length = 7, maxpacket = 64, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 1, bInterval = 0}, {status = 0, total_length = 0, rem_length = 0, maxpacket = 0, is_used = 0, bInterval = 0} <repeats 14 times>}, ep0_state = 5, ep0_data_len = 7, dev_state = 3 '\003', dev_old_state = 1 '\001', dev_address = 12 '\f', dev_connection_status = 0 '\0', dev_test_mode = 0 '\0', dev_remote_wakeup = 0, ConfIdx = 0 '\0', request = {bmRequest = 161 '¡', bRequest = 33 '!', wValue = 0, wIndex = 0, wLength = 7}, pDesc = 0x20000150 <FS_Desc>, pClass = 0x20000038 <USBD_CDC>, pClassData = 0x20001c0c <mem>, pUserData = 0x2000013c <USBD_Interface_fops_FS>, pData = 0x20001700 <hpcd_USB_OTG_FS>, pBosDesc = 0x0, pConfDesc = 0x200000b4 <USBD_CDC_CfgFSDesc>}

 

I have to admit, I’m not sure what I’m looking for, as I’m not familiar with the low level workings of the USB peripheral.

I hope it sheds some light on the problem.

Thanks,

Adrian.

 

FBL
ST Employee

Hello @AChap.1 

Applications shared for now, apart from FAQ article, based on the Open Bootloader library are : STM32L5 STM325WB and STM32WL See here

About hUsbDeviceFS, it is the same structure shared. I suggest you checking section 6.5.6 How to use UM1734_STM32Cube.book

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.

gbm
Lead III

Is the messageHandler() routine called from CDC reception callback? If so, then 1. it cannot wait for the timer. 2: It cannot wait for any USB context change since the change may only occur as a result of USB interrupt.

On the other hand, if it is called from main, then calling CDC_Transmit() from it will sooner or later cause the USB stack to hang.

;)

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
AChap.1
Associate III

 

Thanks @gbm for the suggestion on handling USB communications in a non-blocking manner. Based on what you mentioned, I'm thinking about integrating an event-driven approach where data transmission is attempted only when the USB isn't busy. Here's a quick outline of what I'm considering:

if (usbDataReady && (hcdc->TxState == 0)) {
    CDC_Transmit_FS(dataBuffer, dataLength);
    usbDataReady = false; // Reset flag after attempting transmission
}

This seems like a practical way to keep the bootloader lean while managing USB tasks efficiently. Appreciate the input.

AChap.1
Associate III

 

Hello @FBL 

 

Thank you for detailing the specific applications of the Open Bootloader library. I observed that the provided examples primarily cater to the STM32L5, STM32WB, and STM32WL series. Despite the absence of an STM32L4 series example, I'm keen to know if the Open Bootloader could still be adapted for the STM32L476RET6.

 

Regarding the USB communication issues, based on the documentation you shared, I've thoroughly reviewed the setup. This prompted me to consider the possibility of an underlying issue within the HAL code. Upon discovering an update for the STM32L4 series, I was hopeful it might rectify the problem. However, post-update, my device no longer connects to PuTTY as it previously did, despite no alterations to settings or code.

 

Considering successful USB CDC communication implementations with similar STM32 models by others, I'm inclined to suspect the issue might originate from my side. I would value any insights or advice you might offer on this matter.

FBL
ST Employee

Hi @AChap.1 

 

Which Cube firmware revision are you using? 1.18.0? If I understand correctly, you are using Putty to print from STM32 using UART?

I think if all necessary endpoints are properly initialized in your code, you would not face this issue.

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.

AChap.1
Associate III

Hi @FBL 

I've started a new project with Cube version 1.18.0, and it works.

Thank you for your help, and let everyone know I really appreciate the work you all do.

Adrian.