当端点和 PMA 地址都更改时,CubeMX 生成的 USB HID 设备发送错误数据

Posted

技术标签:

【中文标题】当端点和 PMA 地址都更改时,CubeMX 生成的 USB HID 设备发送错误数据【英文标题】:CubeMX-generated USB HID device sends wrong data when both endpoint and PMA address are changed 【发布时间】:2020-05-08 10:12:47 【问题描述】:

我正在调试我正在创建的复合设备的问题,并在新生成的仅 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_reports 被分成 4 字节和 1 字节的消息,因为 HID 的最大数据包大小为 4 字节。)

然后,我将 usdb_hid.h 中的 HID_EPIN_ADDR0x81 更改为 0x83,以使设备使用端点 3 而不是端点 1 来接收 HID 消息。

//#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) 分配地址。 p>

我通过编辑 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 0000 00 00 00 00 click_reports 时,我看到了以下数据包内容:

58 00 2c 00
58
58 00 2c 00
58

我已将非 PMA 缓冲区的内容一直追踪到 USB_WritePMA in stm32f3xx_ll_usb

发送代码(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

如果我将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:

我在USB_ActivateEndpoint() 中的stm32f3xx_ll_usb.c 中添加了一个断点。

令人惊讶的是,这只会为端点 0 调用。

因此,ep->pmaadress(原文如此)永远不会“写入硬件”,而只会用于更高级别的代码。

这一定意味着端点的pmaadress的值被设置为某个默认值,而我不知道端点0x83的默认值,所以无法设置。

周五返工时,我会添加调试以读出默认值。如果它们不存在,我会很困惑。


更新 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() 只是第一次被调用。

经过大量的grep,我发现PCD_SET_EP_TX_ADDRESS(在stm32f3xx_hal_pcd.h中)不仅在USB_ActivateEndpoint()中直接使用,而且在`stm32f3xx_hal_pcd.h的PCD_SET_EP_DBUF0_ADDR宏中也有。

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-&gt;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

在https://community.st.com/s/question/0D50X00009XkaUASAZ/stm32-usb-endpoint-configuration-clarification-questions

这表明端点0x810x82 工作,因为pma[4]pma[8] 都设置为0x100

端点0x83 不起作用,因为pma[12] 设置为0x0

这与具有值 58 00 2c 00 的损坏数据一致 - USB 硬件正在读取 pma[12] 并因此从 pma[0] 发送 uint16_t,它们是 0x0058 0x002c,由于 little-endianness 而发送反向。 (注意:PMA只有16位宽,所以这里每个地址只有两个字节。)

HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev-&gt;pData , 0x82, PCD_SNG_BUF, 0x100); 的调用没有pma[12] 处设置btable 指针,它只是指出要复制到的PMA 地址。

现在我只需要找到 btable 的内容正在写入的位置...

【问题讨论】:

这看起来很相关:community.st.com/s/question/0D50X00009XkdeRSAR/… 【参考方案1】:

一种解决方法是在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 设备。

为了正确解决这个问题,我需要回答:What initialises the contents of the STM32's USB BTABLE when the __HAL_RCC_USB_CLK_ENABLE() macro is executed in HAL_PCD_MspInit()?

【讨论】:

为什么 rxaddrs[0] 是 0x18?这似乎是错误的。假设 BTABLE 在 PMA 的开头,那么有 EP0 RX 缓冲区,然后是 EP0 TX 缓冲区(在 0x58 = 0x18 + 64 - ok),这意味着 Cube 只为 3 个端点分配缓冲区空间。值 0 可以作为 USB 接收的一部分写入,因为它与 EP 0 的 RX 缓冲区相同。 EP_NUM 的值是多少?如果您使用 EP 3,它应该至少为 4。 您的 pma[2] 为 0x18。这是 PMA 中的字节偏移量,如硬件所见,指定 EP0 的 RX 缓冲区位置。是的,这很可能会发生。 我不确定它在生成的代码中设置的位置,但在我的ST USB示例中,EP_NUM在usb_conf.h中定义,以及BTABLE值和端点编号等。跨度> 实际上,我现在看到缓冲区中的 EP0 偏移量也是硬编码的。我认为代码是动态的。我会将我的设置添加到答案中作为示例。【参考方案2】:

EP3 的 TX 地址被传入的 USB 数据包覆盖,因为它在 PMA 中与控制 EP0 的 RX 缓冲区位于相同的偏移量处。原始代码可以正常工作,因为它只使用 EP1。

这些偏移量的具体设置方式取决于 STMCube 的层中的内容,我的副本似乎有所不同,但这出现在 EP0 中的 RX 和 TX 缓冲区的偏移量是在 OP 的代码中设置的位置:

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 常量,但它的使用方式尚不清楚。

其他一切似乎都只是分心。

【讨论】:

我想你可能是在正确的轨道上,因为我还没有遇到 EP_NUM。我在项目目录中用grep -R "EP_NUM" * 找不到它。 (我已经看到端点上的各种循环使用for (i = 0U; i &lt; hpcd-&gt;Init.dev_endpoints; i++),但dev_endpointsusbd_conf.c 中被初始化为8。) 感谢您的所有帮助。这是完全正确的。 (为了保持一致,我还将 EP3 的缓冲区从 0x100 推到了 0x128。)

以上是关于当端点和 PMA 地址都更改时,CubeMX 生成的 USB HID 设备发送错误数据的主要内容,如果未能解决你的问题,请参考以下文章

JAXWS — 如何更改端点地址 [重复]

更改 config.inc.php 后锁定“pma__column_info”时出错

STM32CubeMx:如何添加自己的“USER CODE BEGIN / END”部分?

在运行时更改服务引用端点,有时会使用错误的端点

使用 php sdk 动态更改 Paypal Express 端点

生成谷歌云端点客户端库时出错