当端点和 PMA 地址都更改时,CubeMX 生成的 USB HID 设备发送错误数据
CubeMX-generated USB HID device sends wrong data when both endpoint and PMA address are changed
我正在调试我正在创建的复合设备的问题,并在新的 CubeMX 生成的纯 HID 代码中重新创建了该问题,以使其更容易解决。
我在 main()
中添加了少量代码,让我发送 USB HID 鼠标点击,并在按下蓝色按钮时闪烁 LED。
...
uint8_t click_report[CLICK_REPORT_SIZE] = {0};
extern USBD_HandleTypeDef hUsbDeviceFS;
...
int main(void)
{
...
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == GPIO_PIN_SET){
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_SET);
click_report[0] = 1; // send button press
USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);
HAL_Delay(50);
click_report[0] = 0; // send button release
USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);
HAL_Delay(200);
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_RESET);
}
}
我正在使用 Wireshark 和 usbmon(在 Ubuntu 16.04 上)查看我的 STM32F3DISCOVERY 板发送的数据包。
使用这个新生成的代码,我可以看到 URB_INTERRUPT
个数据包从 3.23.1 发送。 (只有该地址的最后一部分,端点,是相关的。)
数据包内容为:
01 00 00 00
00
00 00 00 00
00
符合预期。
(5 字节的 click_report
被分成 4 字节和 1 字节的消息,因为 HID 的最大数据包大小为 4 字节。)
然后我将 usdb_hid.h
中的 HID_EPIN_ADDR
从 0x81
更改为 0x83
,使设备使用端点 3 来接收 HID 消息,而不是端点 1。
//#define HID_EPIN_ADDR 0x81U
#define HID_EPIN_ADDR 0x83U
通过此更改,一切继续正常工作,预期的更改是从 x.x.3 发送数据包。数据包仍然包含:
01 00 00 00
00
00 00 00 00
00
据我所知,这应该不能工作,因为我还没有在 PMA 中为端点 3 (0x83
) 分配地址 (数据包存储区)。
我这样做,编辑 usb_conf.c:
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x180);
/* USER CODE END EndPoint_Configuration_HID */
return USBD_OK;
}
现在,当我发送相同的 01 00 00 00 00
和 00 00 00 00 00
click_reports
时,我看到数据包内容为:
58 00 2c 00
58
58 00 2c 00
58
我已经将非 PMA 缓冲区的内容一直追溯到 stm32f3xx_ll_usb
中的 USB_WritePMA
。
发送码(在stm32f3xx_ll_usb
)是:
/* IN endpoint */
if (ep->is_in == 1U)
{
/*Multi packet transfer*/
if (ep->xfer_len > ep->maxpacket)
{
len = ep->maxpacket;
ep->xfer_len -= len;
}
else
{
len = ep->xfer_len;
ep->xfer_len = 0U;
}
/* configure and validate Tx endpoint */
if (ep->doublebuffer == 0U)
{
USB_WritePMA(USBx, ep->xfer_buff, ep->pmaadress, (uint16_t)len);
PCD_SET_EP_TX_CNT(USBx, ep->num, len);
}
else
{
当我为端点地址 0x83
添加 HAL_PCDEx_PMAConfig(...
后,为什么网络上的数据不是我提供的数据 USB_WritePMA
?
更新:
如果我更改 usb_conf.c
让端点地址 0x83
使用 0x81
通常使用的 PMA 地址:
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
线路上的数据包仍然损坏:
58 00 2c 00
58
58 00 2c 00
58
如果我 return usb_conf.c
到它的初始生成状态(其中 0x83
没有 PMA 地址,并且 0x81
使用 0x100
):
/* 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_HID */
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
输出按预期工作:
01 00 00 00
00
00 00 00 00
00
更新 2:
我在stm32f3xx_ll_usb.c
中的USB_ActivateEndpoint()
中添加了一个断点。
令人惊讶的是,这只在端点 0 上被调用过。
因此,ep->pmaadress
(原文如此)永远不会 "written into hardware",并且只用于更高级别的代码。
这一定意味着端点 pmaadress
的值设置为某个默认值,而我不知道端点 0x83
的默认值,因此无法设置。
当我return星期五上班时,我将添加调试以读取默认值。如果它们不存在,我会很困惑。
更新 3:
我添加了以下调试:
uint16_t *tx_addr_ptr(USB_TypeDef *USBx, uint8_t ep_num) {
register uint16_t *_wRegValPtr;
register uint32_t _wRegBase = (uint32_t)USBx;
_wRegBase += (uint32_t)(USBx)->BTABLE;
_wRegValPtr = (uint16_t *)(_wRegBase + 0x400U + (((uint32_t)(ep_num) * 8U) * 2U));
return _wRegValPtr;
}
uint16_t *rx_addr_ptr(USB_TypeDef *USBx, uint8_t ep_num) {
register uint16_t *_wRegValPtr;
register uint32_t _wRegBase = (uint32_t)USBx;
_wRegBase += (uint32_t)(USBx)->BTABLE;
_wRegValPtr = (uint16_t *)(_wRegBase + 0x400U + ((((uint32_t)(ep_num) * 8U) + 4U) * 2U));
return _wRegValPtr;
}
...
HAL_StatusTypeDef USB_ActivateEndpoint(USB_TypeDef *USBx, USB_EPTypeDef *ep)
{
...
int txaddrs[8] = {0};
int rxaddrs[8] = {0};
for (int i = 0; i < 8; ++i) {
txaddrs[i] = *tx_addr_ptr(USBx, i);
rxaddrs[i] = *rx_addr_ptr(USBx, i);
}
这向我显示了以下值(在调试器中):
txaddrs:
0: 0x58
1: 0xf5c4
2: 0xc1c2
3: 0x100
rxaddrs:
0: 0x18
1: 0xfa9b
2: 0xcb56
3: 0x0
这些,出乎意料,看起来是正确的。
0x100
是端点 3 的 txaddr,即使 USB_ActivateEndpoint()
只是第一次被调用。
经过大量grepping,我发现PCD_SET_EP_TX_ADDRESS
(在stm32f3xx_hal_pcd.h
中)不仅直接在USB_ActivateEndpoint()
中使用,而且在PCD_SET_EP_DBUF0_ADDR
宏中`stm32f3xx_hal_pcd.h.
PCD_SET_EP_DBUF0_ADDR
似乎没有被使用,所以我不知道 usbd_conf.c:
的(更改)值是如何产生的
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
...
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
进入内存映射 USB 寄存器。
根据 rxaddr[3]
(端点 3)中 0x00
的存在,我可以推断它们成对发生(因为没有调用 HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x3 , PCD_SNG_BUF, 0x0);
)。
更新 4:
更改设备再次使用端点 1 后,txaddrs[3] 中 0x100
的值仍然存在。它只是从上一个 运行 开始就在那里,这消除了一点混乱。
更新 5:
这是一个 BTABLE 问题。 BTABLE 寄存器的值为 0x00,将 btable 放在 PMA 的开头。
PMA 看起来像这样:
PMA 的开始是 btable。
我发现:
PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT
这表明端点 0x81
和 0x82
有效,因为 pma[4]
和 pma[8]
都设置为 0x100
。
端点 0x83
不起作用,因为 pma[12]
设置为 0x0
。
这与值为 58 00 2c 00
的损坏数据一致 - USB 硬件正在读取 pma[12]
,因此从 pma[0]
发送 uint16_t,这是 0x0058 0x002c
,由于小端顺序而被反向发送。 (注意:PMA只有16位宽,所以这里每个地址只有两个字节。)
对HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82, PCD_SNG_BUF, 0x100);
的调用没有在pma[12]
设置btable指针,它只是指出要复制到的PMA地址。
现在只需要找到btable的内容写到哪里了...
解决方法是在 stm32f3xx_hal_pcd.c
中的 // correct PMA BTABLE
到 HAL_PCD_EP_Transmit()
之后添加两行:
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
{
PCD_EPTypeDef *ep;
ep = &hpcd->IN_ep[ep_addr & EP_ADDR_MSK];
/*setup and start the Xfer */
ep->xfer_buff = pBuf;
ep->xfer_len = len;
ep->xfer_count = 0U;
ep->is_in = 1U;
ep->num = ep_addr & EP_ADDR_MSK;
// correct PMA BTABLE
uint32_t *btable = (uint32_t *) USB_PMAADDR;
btable[ep->num * 4] = ep->pmaadress;
...
这导致在每次 写入之前更正端点 3 的 TX 缓冲区的位置。这很浪费,但设置一次是不够的,因为 pma[12]
中的值正在被覆盖。
我已使用此解决方法成功创建了复合 CDC(串行)和 HID 设备。
为了正确解决这个问题,我需要一个答案:
EP3 的 TX 地址正在被传入的 USB 数据包覆盖,因为它与控制 EP0 的 RX 缓冲区位于 PMA 中相同的偏移量。原始代码工作正常,因为它只使用 EP1。
这些偏移量的准确设置取决于 STMCube 层中的内容,我的副本似乎有所不同,但这出现在 OP 代码中设置 EP0 中的 RX 和 TX 缓冲区的偏移量的位置:
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
这些常量需要改为0x40和0x80(例如)。
在我的版本中,这些偏移量是在头文件中定义的,还有 EP_NUM 常量,但不清楚如何使用。
其他一切似乎都只是分心。
我正在调试我正在创建的复合设备的问题,并在新的 CubeMX 生成的纯 HID 代码中重新创建了该问题,以使其更容易解决。
我在 main()
中添加了少量代码,让我发送 USB HID 鼠标点击,并在按下蓝色按钮时闪烁 LED。
...
uint8_t click_report[CLICK_REPORT_SIZE] = {0};
extern USBD_HandleTypeDef hUsbDeviceFS;
...
int main(void)
{
...
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == GPIO_PIN_SET){
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_SET);
click_report[0] = 1; // send button press
USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);
HAL_Delay(50);
click_report[0] = 0; // send button release
USBD_HID_SendReport(&hUsbDeviceFS, click_report, CLICK_REPORT_SIZE);
HAL_Delay(200);
HAL_GPIO_WritePin(LD4_GPIO_Port, LD4_Pin, GPIO_PIN_RESET);
}
}
我正在使用 Wireshark 和 usbmon(在 Ubuntu 16.04 上)查看我的 STM32F3DISCOVERY 板发送的数据包。
使用这个新生成的代码,我可以看到 URB_INTERRUPT
个数据包从 3.23.1 发送。 (只有该地址的最后一部分,端点,是相关的。)
数据包内容为:
01 00 00 00
00
00 00 00 00
00
符合预期。
(5 字节的 click_report
被分成 4 字节和 1 字节的消息,因为 HID 的最大数据包大小为 4 字节。)
然后我将 usdb_hid.h
中的 HID_EPIN_ADDR
从 0x81
更改为 0x83
,使设备使用端点 3 来接收 HID 消息,而不是端点 1。
//#define HID_EPIN_ADDR 0x81U
#define HID_EPIN_ADDR 0x83U
通过此更改,一切继续正常工作,预期的更改是从 x.x.3 发送数据包。数据包仍然包含:
01 00 00 00
00
00 00 00 00
00
据我所知,这应该不能工作,因为我还没有在 PMA 中为端点 3 (0x83
) 分配地址 (数据包存储区)。
我这样做,编辑 usb_conf.c:
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x180);
/* USER CODE END EndPoint_Configuration_HID */
return USBD_OK;
}
现在,当我发送相同的 01 00 00 00 00
和 00 00 00 00 00
click_reports
时,我看到数据包内容为:
58 00 2c 00
58
58 00 2c 00
58
我已经将非 PMA 缓冲区的内容一直追溯到 stm32f3xx_ll_usb
中的 USB_WritePMA
。
发送码(在stm32f3xx_ll_usb
)是:
/* IN endpoint */
if (ep->is_in == 1U)
{
/*Multi packet transfer*/
if (ep->xfer_len > ep->maxpacket)
{
len = ep->maxpacket;
ep->xfer_len -= len;
}
else
{
len = ep->xfer_len;
ep->xfer_len = 0U;
}
/* configure and validate Tx endpoint */
if (ep->doublebuffer == 0U)
{
USB_WritePMA(USBx, ep->xfer_buff, ep->pmaadress, (uint16_t)len);
PCD_SET_EP_TX_CNT(USBx, ep->num, len);
}
else
{
当我为端点地址 0x83
添加 HAL_PCDEx_PMAConfig(...
后,为什么网络上的数据不是我提供的数据 USB_WritePMA
?
更新:
如果我更改 usb_conf.c
让端点地址 0x83
使用 0x81
通常使用的 PMA 地址:
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
线路上的数据包仍然损坏:
58 00 2c 00
58
58 00 2c 00
58
如果我 return usb_conf.c
到它的初始生成状态(其中 0x83
没有 PMA 地址,并且 0x81
使用 0x100
):
/* 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_HID */
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
输出按预期工作:
01 00 00 00
00
00 00 00 00
00
更新 2:
我在stm32f3xx_ll_usb.c
中的USB_ActivateEndpoint()
中添加了一个断点。
令人惊讶的是,这只在端点 0 上被调用过。
因此,ep->pmaadress
(原文如此)永远不会 "written into hardware",并且只用于更高级别的代码。
这一定意味着端点 pmaadress
的值设置为某个默认值,而我不知道端点 0x83
的默认值,因此无法设置。
当我return星期五上班时,我将添加调试以读取默认值。如果它们不存在,我会很困惑。
更新 3:
我添加了以下调试:
uint16_t *tx_addr_ptr(USB_TypeDef *USBx, uint8_t ep_num) {
register uint16_t *_wRegValPtr;
register uint32_t _wRegBase = (uint32_t)USBx;
_wRegBase += (uint32_t)(USBx)->BTABLE;
_wRegValPtr = (uint16_t *)(_wRegBase + 0x400U + (((uint32_t)(ep_num) * 8U) * 2U));
return _wRegValPtr;
}
uint16_t *rx_addr_ptr(USB_TypeDef *USBx, uint8_t ep_num) {
register uint16_t *_wRegValPtr;
register uint32_t _wRegBase = (uint32_t)USBx;
_wRegBase += (uint32_t)(USBx)->BTABLE;
_wRegValPtr = (uint16_t *)(_wRegBase + 0x400U + ((((uint32_t)(ep_num) * 8U) + 4U) * 2U));
return _wRegValPtr;
}
...
HAL_StatusTypeDef USB_ActivateEndpoint(USB_TypeDef *USBx, USB_EPTypeDef *ep)
{
...
int txaddrs[8] = {0};
int rxaddrs[8] = {0};
for (int i = 0; i < 8; ++i) {
txaddrs[i] = *tx_addr_ptr(USBx, i);
rxaddrs[i] = *rx_addr_ptr(USBx, i);
}
这向我显示了以下值(在调试器中):
txaddrs:
0: 0x58
1: 0xf5c4
2: 0xc1c2
3: 0x100
rxaddrs:
0: 0x18
1: 0xfa9b
2: 0xcb56
3: 0x0
这些,出乎意料,看起来是正确的。
0x100
是端点 3 的 txaddr,即使 USB_ActivateEndpoint()
只是第一次被调用。
经过大量grepping,我发现PCD_SET_EP_TX_ADDRESS
(在stm32f3xx_hal_pcd.h
中)不仅直接在USB_ActivateEndpoint()
中使用,而且在PCD_SET_EP_DBUF0_ADDR
宏中`stm32f3xx_hal_pcd.h.
PCD_SET_EP_DBUF0_ADDR
似乎没有被使用,所以我不知道 usbd_conf.c:
USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
{
...
/* 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_HID */
//HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0x100);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0x100);
/* USER CODE END EndPoint_Configuration_HID */
进入内存映射 USB 寄存器。
根据 rxaddr[3]
(端点 3)中 0x00
的存在,我可以推断它们成对发生(因为没有调用 HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x3 , PCD_SNG_BUF, 0x0);
)。
更新 4:
更改设备再次使用端点 1 后,txaddrs[3] 中 0x100
的值仍然存在。它只是从上一个 运行 开始就在那里,这消除了一点混乱。
更新 5:
这是一个 BTABLE 问题。 BTABLE 寄存器的值为 0x00,将 btable 放在 PMA 的开头。
PMA 看起来像这样:
我发现:
PMAAddr + BASEADDR_BTABLE + 0x00000000 : EP0_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000002 : EP0_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000004 : EP0_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000006 : EP0_RX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000008 : EP1_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x0000000A : EP1_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x0000000C : EP1_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x0000000E : EP1_RX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000010 : EP2_TX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000012 : EP2_TX_COUNT
PMAAddr + BASEADDR_BTABLE + 0x00000014 : EP2_RX_ADDR
PMAAddr + BASEADDR_BTABLE + 0x00000016 : EP2_RX_COUNT
这表明端点 0x81
和 0x82
有效,因为 pma[4]
和 pma[8]
都设置为 0x100
。
端点 0x83
不起作用,因为 pma[12]
设置为 0x0
。
这与值为 58 00 2c 00
的损坏数据一致 - USB 硬件正在读取 pma[12]
,因此从 pma[0]
发送 uint16_t,这是 0x0058 0x002c
,由于小端顺序而被反向发送。 (注意:PMA只有16位宽,所以这里每个地址只有两个字节。)
对HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82, PCD_SNG_BUF, 0x100);
的调用没有在pma[12]
设置btable指针,它只是指出要复制到的PMA地址。
现在只需要找到btable的内容写到哪里了...
解决方法是在 stm32f3xx_hal_pcd.c
中的 // correct PMA BTABLE
到 HAL_PCD_EP_Transmit()
之后添加两行:
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
{
PCD_EPTypeDef *ep;
ep = &hpcd->IN_ep[ep_addr & EP_ADDR_MSK];
/*setup and start the Xfer */
ep->xfer_buff = pBuf;
ep->xfer_len = len;
ep->xfer_count = 0U;
ep->is_in = 1U;
ep->num = ep_addr & EP_ADDR_MSK;
// correct PMA BTABLE
uint32_t *btable = (uint32_t *) USB_PMAADDR;
btable[ep->num * 4] = ep->pmaadress;
...
这导致在每次 写入之前更正端点 3 的 TX 缓冲区的位置。这很浪费,但设置一次是不够的,因为 pma[12]
中的值正在被覆盖。
我已使用此解决方法成功创建了复合 CDC(串行)和 HID 设备。
为了正确解决这个问题,我需要一个答案:
EP3 的 TX 地址正在被传入的 USB 数据包覆盖,因为它与控制 EP0 的 RX 缓冲区位于 PMA 中相同的偏移量。原始代码工作正常,因为它只使用 EP1。
这些偏移量的准确设置取决于 STMCube 层中的内容,我的副本似乎有所不同,但这出现在 OP 代码中设置 EP0 中的 RX 和 TX 缓冲区的偏移量的位置:
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, 0x18);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
这些常量需要改为0x40和0x80(例如)。
在我的版本中,这些偏移量是在头文件中定义的,还有 EP_NUM 常量,但不清楚如何使用。
其他一切似乎都只是分心。