2025-09-21 3:02 AM
Hi all,
After reading through both the HAL, low-layer and USB Device library code flow several times I am still confused about a peculiar point.
When I ask STM32CubeIDE to generate a Custom HID base implementation, I get a "usbd_conf.c" file that has a function like this:
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
/* Init USB Ip. */
/* Link the driver to the stack. */
hpcd_USB_FS.pData = pdev;
pdev->pData = &hpcd_USB_FS;
... hpcd init ...
... callback registration ...
/* USER CODE BEGIN EndPoint_Configuration */
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
/* USER CODE END EndPoint_Configuration */
/* USER CODE BEGIN EndPoint_Configuration_CUSTOM_HID */
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPIN_ADDR , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPOUT_ADDR , PCD_SNG_BUF, 0xD8);
/* USER CODE END EndPoint_Configuration_CUSTOM_HID */
return USBD_OK;
}
This code seems clear and straightforward: there's EP0 IN and OUT, and EP1 IN and OUT, all configured to a particular PMA area. The 0x18 and 0x58 etc. are calculated based on the wMaxPacketSize of the endpoint. All is well.
However, when I ask it to generate a USB Audio sample, I get something like this instead:
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
/* Init USB Ip. */
/* Link the driver to the stack. */
hpcd_USB_FS.pData = pdev;
pdev->pData = &hpcd_USB_FS;
... hpcd init ...
... callback registration ...
/* USER CODE BEGIN EndPoint_Configuration */
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
/* USER CODE END EndPoint_Configuration */
return USBD_OK;
}
In this variant, the PMA address of EP1 OUT is not configured at all. I assume that its value remains zero, since the BTABLE section in the reference manual is said to be initialized with zeroes after hardware or USB reset.
Now, in the audio example's "usbd_audio.c" there is this code, then:
static uint8_t USBD_AUDIO_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
USBD_AUDIO_HandleTypeDef *haudio;
/* Open EP OUT */
USBD_LL_OpenEP(pdev, AUDIO_OUT_EP, USBD_EP_TYPE_ISOC, AUDIO_OUT_PACKET);
pdev->ep_out[AUDIO_OUT_EP & 0xFU].is_used = 1U;
/* Allocate Audio structure */
pdev->pClassData = USBD_malloc(sizeof(USBD_AUDIO_HandleTypeDef));
if (pdev->pClassData == NULL)
{
return USBD_FAIL;
}
else
{
haudio = (USBD_AUDIO_HandleTypeDef *) pdev->pClassData;
haudio->alt_setting = 0U;
haudio->offset = AUDIO_OFFSET_UNKNOWN;
haudio->wr_ptr = 0U;
haudio->rd_ptr = 0U;
haudio->rd_enable = 0U;
/* Initialize the Audio output Hardware layer */
if (((USBD_AUDIO_ItfTypeDef *)pdev->pUserData)->Init(USBD_AUDIO_FREQ,
AUDIO_DEFAULT_VOLUME,
0U) != 0)
{
return USBD_FAIL;
}
/* Prepare Out endpoint to receive 1st packet */
USBD_LL_PrepareReceive(pdev, AUDIO_OUT_EP, haudio->buffer, AUDIO_OUT_PACKET);
}
return USBD_OK;
}
If I follow the path from "USBD_LL_PrepareReceive" I find a section which prepares the EP1 to receive AUDIO_OUT_PACKET amount of data during the next OUT token. When the CTR_RX bit is set, the interrupt handler invokes USB_ReadPMA to read data from the PMA area designated for the endpoint into the "transfer buffer" that's part of the endpoint data structure. For this call, the PMA address is used.
Doesn't this mean it is actually trying to read data from the PMA area that's devoted to the BTABLE, and for EP0 IN and OUT? When the hardware was receiving data from the USB did it actually overwrite the BTABLE area with this data?
Am I looking at a bug in the code generation, or is there some special flag, setting or a mystery bit somewhere that I just cannot see?
Solved! Go to Solution.
2025-09-30 3:17 AM
Hi @ankes
Thank you for the provided details. An internal ticket is submitted to dedicated team for a fix (218624)
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.
2025-09-22 4:14 AM - edited 2025-09-22 4:18 AM
2025-09-22 9:30 AM
Hi,
Here are the exact reproduction steps I did:
The entire workspace is attached here as an archive.
My STM32CubeIDE version is "1.18.1", "build 24813_20250409_2138 (UTC)".
2025-09-26 9:35 AM
Hi @FBL!
Any news on your side? Are we looking at a code generation bug? Can I somehow further assist in solving this by providing logs or anything?
2025-09-29 3:40 AM - edited 2025-09-29 3:40 AM
Hi @ankes
Unfortunately, CubeMX does not configure PMA in USB controller at all. This must be added by developer. The example firmware you provided should be there before regenerating the code since configured between tags /* USER CODE BEGIN EndPoint_Configuration */ and /* USER CODE END EndPoint_Configuration */
The forthcoming article will provide an in-depth explanation of the packet memory area, which should help simplify the interpretation of the reference manual and ease development.
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.
2025-09-29 10:58 AM
Thank you for the reply.
In that case this looks like a bug in the template files that CubeMX uses to generate the files in the project folder.
A short amount of detective work later, and voilá. In the folder "C:\Program Files\STM32CubeIDE_1.18.1\STM32CubeIDE\plugins\com.st.stm32cube.common.mx_6.14.1.202504091515\db\templates" there's a file "usbdconf_f0_c.ftl" with content like this:
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
... other code ...
#t/* USER CODE BEGIN EndPoint_Configuration */
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
#t/* USER CODE END EndPoint_Configuration */
[#if className == "MSC"]
#t/* USER CODE BEGIN EndPoint_Configuration_MSC */
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x98);
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0xD8);
#t/* USER CODE END EndPoint_Configuration_MSC */
[/#if]
[#if className == "HID"]
#t/* USER CODE BEGIN EndPoint_Configuration_HID */
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
#t/* USER CODE END EndPoint_Configuration_HID */
[/#if]
[#if className == "CDC"]
#t/* USER CODE BEGIN EndPoint_Configuration_CDC */
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xC0);
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0x110);
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82 , PCD_SNG_BUF, 0x100);
#t/* USER CODE END EndPoint_Configuration_CDC */
[/#if]
[#if className == "CUSTOMHID"]
#t/* USER CODE BEGIN EndPoint_Configuration_CUSTOM_HID */
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPIN_ADDR , PCD_SNG_BUF, 0x98);
#tHAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CUSTOM_HID_EPOUT_ADDR , PCD_SNG_BUF, 0xD8);
#t/* USER CODE END EndPoint_Configuration_CUSTOM_HID */
[/#if]
[/#if]
#treturn USBD_OK;
}
There is no clause for "[#if className == "AUDIO"]".
So, this is clearly a bug. The next question is, how should I report it, and to where, so that it eventually might get fixed?
2025-09-30 3:17 AM
Hi @ankes
Thank you for the provided details. An internal ticket is submitted to dedicated team for a fix (218624)
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.