cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5A5 USB CDC issue

Mark5918
Associate II

My project use STM32U595 chip. It will use HS USB to send large amount data to computer.

I use NUCLEO-U5A5ZJ-Q for testing.

The project use Ux_Device_CDC_ACM in NUCLEO-U5A5ZJ-Q of STM32Cube_FW_U5_V1.4.0.

I reference this post https://community.st.com/t5/stm32-mcus-products/nucleo-u5a5zj-q-usb-cdc-acm-issue-with-ux-device-class-cdc-acm/td-p/632086

I modify a little bit of  Kannan1's code as:

 

 

 

#define MAX_PKT_SIZE  2048
//512 /* 200 */ /* 512 */ /* 1024 */
#define TOTALBYTE APP_TX_DATA_SIZE*1000
#define USEHAL

VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
	ULONG actual_length;
	uint32_t i = 0;
	int32_t total_bytes_to_send = TOTALBYTE;
	uint32_t bytes_to_send = 0;
	uint32_t buf_indx = 0;


	UX_PARAMETER_NOT_USED(thread_input);

	for (i = 0; i < APP_TX_DATA_SIZE; i++)
	{
		UserTxBufferFS[i] = i;
	}
	IOinit();
//	tx_thread_sleep(MS_TO_TICK(10000));

	while (1)
	{
		if(1==buttonState()){
			total_bytes_to_send = TOTALBYTE;
			while(1==buttonState());
			LEDrOn();

			while(total_bytes_to_send>0)
			{
				LEDbToggle();
				if (total_bytes_to_send > MAX_PKT_SIZE)
				{
					bytes_to_send = MAX_PKT_SIZE;
				}
				else
				{
					bytes_to_send = total_bytes_to_send;
				}

				/* Send data over the class cdc_acm_write */
				if (ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)(&UserTxBufferFS[buf_indx]),
						bytes_to_send, &actual_length) == UX_SUCCESS)
				{
					total_bytes_to_send -= actual_length;
					buf_indx += actual_length;
					if (buf_indx >= APP_TX_DATA_SIZE)
					{
						buf_indx = 0;
					}
				}else{
					tx_thread_sleep(MS_TO_TICK(10));
				}
			}

			LEDrOff();
		}else{

	    /* Sleep thread for 10ms */
	    tx_thread_sleep(MS_TO_TICK(10));
		}
	}
}

 

 

 

 

the LED and button code:

 

 

 

#define GPIO_Button_Pin GPIO_PIN_13
#define GPIO_Button_GPIO_Port GPIOC
#define GPIO_LEDr_Pin GPIO_PIN_2
#define GPIO_LEDr_GPIO_Port GPIOG
#define GPIO_LEDb_Pin GPIO_PIN_7
#define GPIO_LEDb_GPIO_Port GPIOC
void IOinit()
{
	  GPIO_InitTypeDef GPIO_InitStruct = {0};

	 __HAL_RCC_GPIOC_CLK_ENABLE();

	  __HAL_RCC_GPIOG_CLK_ENABLE();


	  /*Configure GPIO pin Output Level */
	  HAL_GPIO_WritePin(GPIO_LEDr_GPIO_Port, GPIO_LEDr_Pin, GPIO_PIN_RESET);

	  /*Configure GPIO pin Output Level */
	  HAL_GPIO_WritePin(GPIO_LEDb_GPIO_Port, GPIO_LEDb_Pin, GPIO_PIN_RESET);

	  /*Configure GPIO pin : GPIO_Button_Pin */
	  GPIO_InitStruct.Pin = GPIO_Button_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  HAL_GPIO_Init(GPIO_Button_GPIO_Port, &GPIO_InitStruct);

	  /*Configure GPIO pin : GPIO_LEDr_Pin */
	  GPIO_InitStruct.Pin = GPIO_LEDr_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	  HAL_GPIO_Init(GPIO_LEDr_GPIO_Port, &GPIO_InitStruct);

	  /*Configure GPIO pin : GPIO_LEDb_Pin */
	  GPIO_InitStruct.Pin = GPIO_LEDb_Pin;
	  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	  GPIO_InitStruct.Pull = GPIO_NOPULL;
	  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	  HAL_GPIO_Init(GPIO_LEDb_GPIO_Port, &GPIO_InitStruct);
}
void LEDrOn()
{
	HAL_GPIO_WritePin(GPIO_LEDr_GPIO_Port, GPIO_LEDr_Pin, GPIO_PIN_SET);
}
void LEDrOff()
{
	HAL_GPIO_WritePin(GPIO_LEDr_GPIO_Port, GPIO_LEDr_Pin, GPIO_PIN_RESET);
}

void LEDbToggle()
{
	HAL_GPIO_TogglePin(GPIO_LEDb_GPIO_Port, GPIO_LEDb_Pin);
}
//push down 1;release 0
uint32_t buttonState()
{
	uint32_t ret;
	ret = (HAL_GPIO_ReadPin(GPIO_Button_GPIO_Port,GPIO_Button_Pin) == GPIO_PIN_RESET) ? 0:1;
	return ret;
}

 

 

 

 Also in function VOID USBX_APP_Device_Init(VOID): 

 

 

 

//  HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x100);
//  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x10);
//  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x20);
//  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, 0x10);
  HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, USBD_MAX_EP0_SIZE/4);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, USBD_CDCACM_EPIN_HS_MPS/4);
  HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, USBD_CDCACM_EPINCMD_HS_MPS/4);

 

 

 

 So I only modify the function VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input) and VOID USBX_APP_Device_Init(VOID). didn't touch other in original project.

when I set:

#define TOTALBYTE APP_TX_DATA_SIZE*100

it can successfully send the amount data to computer couple of times when I pushed the button. Then the code is locked. 

when #define TOTALBYTE APP_TX_DATA_SIZE*1000

it locked at first time.

When locked, the debug show as:

1.png

 

Does anyone know what is the problem? Thanks.

Mark

2.png

7 REPLIES 7
Mark5918
Associate II

I find the problem:

I use Realterm on windows10 as serial port terminal. This software has issue for receiving no ASCII character.

When I write C++ code to handle the virtual serial port. The STM32U5A5 can complete all data at 4MBPS rate.

FBL
ST Employee

Hello @Mark5918 

Could you specify how did you measure 4MBPS rate? The issue is likely to be related to the buffer size applied in your application. Note that USBD_MAX_EP0_SIZE is only 0x64.

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.

I use SysTick or HAL tick depend on how quick of one cycle:

if #define TOTALBYTE APP_TX_DATA_SIZE*10, I use SysTick. more than 10, I use HAL 1ms ticks.

See the function:

#define MAX_PKT_SIZE  2048
//512 /* 200 */ /* 512 */ /* 1024 */
#define TOTALBYTE APP_TX_DATA_SIZE*1000
#define USEHAL

VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)
{
	ULONG actual_length;
	uint32_t i = 0;
	int32_t total_bytes_to_send = TOTALBYTE;
	uint32_t bytes_to_send = 0;
	uint32_t buf_indx = 0;
	uint32_t NowTick,Tick;

	UX_PARAMETER_NOT_USED(thread_input);

	for (i = 0; i < APP_TX_DATA_SIZE; i++)
	{
		UserTxBufferFS[i] = i;
	}

//	tx_thread_sleep(MS_TO_TICK(10000));

	while (1)
	{
		if(1==buttonState()){
			total_bytes_to_send = TOTALBYTE;
			while(1==buttonState());
			LEDrOn();
#ifdef USEHAL
			Tick = HAL_GetTick();
#else
			Tick = SysTick->VAL;
#endif
			while(total_bytes_to_send>0)
			{
				LEDbToggle();
				if (total_bytes_to_send > MAX_PKT_SIZE)
				{
					bytes_to_send = MAX_PKT_SIZE;
				}
				else
				{
					bytes_to_send = total_bytes_to_send;
				}

				/* Send data over the class cdc_acm_write */
				if (ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)(&UserTxBufferFS[buf_indx]),
						bytes_to_send, &actual_length) == UX_SUCCESS)
				{
					total_bytes_to_send -= actual_length;
					buf_indx += actual_length;
					if (buf_indx >= APP_TX_DATA_SIZE)
					{
						buf_indx = 0;
					}
				}else{
					tx_thread_sleep(MS_TO_TICK(10));
				}
			}
#ifdef USEHAL
			NowTick = HAL_GetTick();
			Tick = NowTick - Tick;
			printf("Tick(ms)=%ld rate(KBps)=%ld\n\r",Tick,TOTALBYTE/Tick);

#else
			NowTick = SysTick->VAL;

			if(Tick >= NowTick)
				Tick = Tick-NowTick;
			else
				Tick = SysTick->LOAD-NowTick+Tick;

			printf("Tick=%ld clock=%ld load=%ld rate(KBps)=%ld\n\r",Tick,SystemCoreClock,SysTick->LOAD,TOTALBYTE*(SystemCoreClock/1000)/Tick);
#endif
			LEDrOff();
		}else{

	    /* Sleep thread for 10ms */
	    tx_thread_sleep(MS_TO_TICK(10));
		}
	}
}

 

My "five cents":

  • USB VCP is just 64bytes max. per packet
  • how many packets per time frame you can send - depends on the Host if "he" allows the Device to send a new packet (maybe your Host pauses to request new packets and you overflow inside MCU)
  • if you send "too much, too fast" - you could overrun internal buffers (it would be a bug in the FW, HAL drivers, if this is possible, but which SW/FW is "rock solid"?)
  • wait a bit between packets (e.g. 1 ms), make the packets not larger as 64 bytes
  • try to find hooks about the success of the previous send (before you keep going to send even more = flow control in MCU)

If it works still with a bit larger data might work, because DMA is used and one transmission in progress and allows to take already the next chunk (to queue it).

It looks more like a "memory corruption" going on: you overwrite a buffer (intended for the VCP transfer). Or you queue so many packets to be sent via DMA (over USB) that the DMA descriptors overflow the maximum possible to setup.

Hard to say, just: there should be a "flow control": "do not send too much and not too fast". Maybe, such a flow control is not implemented in the FW drivers (assuming doing "printfs" sporadically is slow).

When I end up in RTOS scheduler and no thread scheduled/activated anymore - the main reason was a memory corruption.

FBL
ST Employee

Thank you @tjaekel for all these insights.

It's not uncommon for different serial port terminals to have different behavior and issues, so it's always a good idea to test your firmware with different terminals to ensure compatibility and reliability.

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.

Thank you @tjaekel for reply.

I do check the receiving content in computer side. the Byte number and content is good as MCU sending.

For CDC_ACM IN (MCU to computer), The USB will not let MCU to sending if host is busy in blocking write. So It should not have "flow control" and overrun issue.

 

The Ux_Device_CDC_ACM project is using blocking read and write. I would like to try no blocking write. As readme mwntion:

  • CDC ACM non-blocking transmission by default disabled, to enable non-blocking transmission UX_DEVICE_CLASS_CDC_ACM_TRANSMISSION_DISABLE must be disabled and 2048 additional in USBX byte pool and USBX_MEMORY_SIZE should be added

Does "2048 additional in USBX byte pool" means all threads pool need to increase or only some threads? and where is USBX_MEMORY_SIZE definition?

Thanks,

Mark

FBL
ST Employee

Hi @Mark5918 

Thank you for highlighting this. Indeed, it is confusing. USBX_DEVICE_MEMORY_STACK_SIZE and UX_DEVICE_APP_MEM_POOL_SIZE should be precisely defined in order to help user adjust required memory. In some application, we define it as follows:

#define USBX_MEMORY_SIZE             (32U * 1024U)

This issue is reported to dedicated team.

Internal ticket number: 174487 (This is an internal tracking number and is not accessible or usable by customers).

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.