2

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

然后我从更改为HID_EPIN_ADDR,以使设备将端点 3 用于 HID 消息,而不是端点 1。usdb_hid.h0x810x83

//#define HID_EPIN_ADDR                 0x81U
#define HID_EPIN_ADDR                 0x83U

有了这个变化,一切都继续工作,预期的变化是从 xx3 发送数据包。数据包仍然包含:

01 00 00 00
00
00 00 00 00
00

据我所知,这应该不起作用,因为我还没有为0x83PMA(数据包内存区域)中的端点 3 ( ) 分配地址。

我通过编辑 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_WritePMAin stm32f3xx_ll_usb

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

为什么在线上的数据不是我提供的数据USB_WritePMA,一旦我添加HAL_PCDEx_PMAConfig(...了端点地址0x83


更新:

如果我更改usb_conf.c为让端点地址0x83使用通常由以下人员使用的 PMA 地址0x81

  /* 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()我在in 中添加了一个断点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(in stm32f3xx_hal_pcd.h)不仅直接用于in 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 寄存器。

0x00我可以从in (端点 3)的存在推断rxaddr[3]它们成对发生(因为没有调用HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x3 , PCD_SNG_BUF, 0x0);)。


更新 4:

再次将设备更改为使用端点 1 后,0x100txaddrs[3] 中的值保持不变。上次运行时它就在那里,消除了一些混乱。


更新 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]并因此发送来自 的 uint16_t ,由于小端序pma[0],它们被0x0058 0x002c反向发送。(注意:PMA 只有 16 位宽,所以这里每个地址只有两个字节。)

调用HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82, PCD_SNG_BUF, 0x100);不会处设置 btable 指针pma[12],它只是记录要复制到的 PMA 地址。

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

4

2 回答 2

1

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

其他一切似乎都只是干扰。

于 2020-01-29T04:16:24.327 回答
0

一种解决方法是将以下两行添加// correct PMA BTABLEHAL_PCD_EP_Transmit()in stm32f3xx_hal_pcd.c

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 缓冲区的位置进行更正。这很浪费,但设置一次是不够的,因为 in 的值pma[12]被覆盖了。

我已使用此解决方法成功创建了复合 CDC(串行)和 HID 设备。

为了正确解决这个问题,我需要一个答案:当在 HAL_PCD_MspInit() 中执行 __HAL_RCC_USB_CLK_ENABLE() 宏时,什么会初始化 STM32 的 USB BTABLE 的内容?

于 2020-01-28T14:45:51.243 回答