Созданное CubeMX USB HID-устройство отправляет неверные данные при изменении как конечной точки, так и адреса PMA

Я отлаживаю проблему с созданным мною составным устройством и воссоздаю проблему в только что созданном CubeMX HID-коде, чтобы упростить ее решение.

Я добавил небольшое количество кода в main() чтобы я мог посылать щелчки мыши USB HID и мигать светодиодом при нажатии синей кнопки.

...
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 байта.)

Затем я изменил HID_EPIN_ADDR в usdb_hid.h из 0x81 к 0x83, чтобы устройство использовало конечную точку 3 для сообщений HID вместо конечной точки 1.

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

С этим изменением все продолжало работать, с ожидаемым изменением, что пакеты отправляются с xx3. Пакеты по-прежнему содержат:

01 00 00 00
00
00 00 00 00
00

Насколько я понимаю, это не должно работать, поскольку я еще не выделил адрес для конечной точки 3 (0x83) в PMA (область памяти пакетов).

Я делаю это, редактируя 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 вплоть до USB_WritePMA в 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
    {

Почему данные на проводе не те данные, которые я даю 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() в stm32f3xx_ll_usb.c.

Удивительно, но это всегда вызывается только для конечной точки 0.

Следовательно ep->pmaadress (sic) никогда не "записывается в аппаратное обеспечение", а используется только в коде более высокого уровня.

Это должно означать, что значения 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 это txaddr конечной точки 3, хотя USB_ActivateEndpoint() только что позвонили в первый раз.

После долгих поисков я обнаружил, что PCD_SET_EP_TX_ADDRESSstm32f3xx_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.

Я могу сделать вывод, по наличию 0x00 в rxaddr[3] (конечная точка 3), что они происходят парами (так как нет вызова HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x3 , PCD_SNG_BUF, 0x0);).


Обновление 4:

После изменения устройства, чтобы снова использовать конечную точку 1, значение 0x100в txaddrs[3] остались. Он просто был там с последнего запуска, что устраняет небольшую путаницу.


Обновление 5:

Это проблема BTABLE. Регистр BTABLE имеет значение 0x00, помещая btable в начало PMA.

PMA выглядит так: а начало PMA - это таблица b.

Я нашел:

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

Это показывает, что конечные точки 0x81 а также 0x82 работать, потому что оба 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...

2 ответа

Решение

Адрес TX EP3 перезаписывается входящим пакетом USB, потому что он расположен по тому же смещению в PMA, что и буфер RX для управляющего EP0. Исходный код работает нормально, потому что он использует только EP1.

Как именно эти смещения устанавливаются, зависит от того, что находится в слоях STMCube, и моя копия кажется другой, но это появляется там, где смещения буферов RX и TX в EP0 установлены в коде 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, но как она используется, неясно.

Все остальное кажется просто отвлекающим фактором.

Чтобы решить эту проблему, добавьте две строки, следующие за // correct PMA BTABLE к HAL_PCD_EP_Transmit() в 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;
  ...

Это вызывает исправление местоположения буфера TX конечной точки 3 перед каждой записью. Это расточительно, но недостаточно установить его один раз, так как значение вpma[12] был перезаписан.

Я использовал этот обходной путь для успешного создания составного CDC (последовательного) и HID-устройства.

Чтобы решить эту проблему правильно, мне нужен ответ на вопрос: что инициализирует содержимое USB BTABLE STM32, когда макрос __HAL_RCC_USB_CLK_ENABLE() выполняется в HAL_PCD_MspInit()?

Другие вопросы по тегам