Созданное 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_report
s фрагментированы на 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_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.
Я могу сделать вывод, по наличию 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
Это показывает, что конечные точки 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()?