2024-02-20 11:13 AM
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:
Does anyone know what is the problem? Thanks.
Mark
2024-02-20 10:18 PM
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.
2024-02-21 01:32 AM
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.
2024-02-21 07:34 PM
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));
}
}
}
2024-02-21 10:05 PM
My "five cents":
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.
2024-02-22 01:38 AM
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.
2024-02-22 09:46 AM
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
2024-02-23 05:29 AM
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.