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?