I'm debugging a problem with a composite device that I'm creating, and have recreated the issue in freshly-CubeMX-generated HID-only code, to make it easier to resolve.
I've added small amount of code to main()
to let me send USB HID mouse-clicks, and flash an LED, when the blue-button is pressed.
...
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);
}
}
I am using Wireshark and usbmon (on Ubuntu 16.04) to look at the packets which my STM32F3DISCOVERY board sends.
With this freshly-generated code, I can see URB_INTERRUPT
packets being sent from 3.23.1. (Only the last part of that address, the endpoint, is relevant.)
The packet contents are:
01 00 00 00
00
00 00 00 00
00
as expected.
(The 5-byte click_report
s are fragmented into 4-byte and 1-byte messages, as there is a 4-byte maximum packet size for HID.)
I then changed HID_EPIN_ADDR
in usdb_hid.h
from 0x81
to 0x83
, to make the device use endpoint 3 for HID messages, instead of endpoint 1.
//#define HID_EPIN_ADDR 0x81U
#define HID_EPIN_ADDR 0x83U
With this change, everything continued to work, with the expected change that packets are being sent from x.x.3. The packets still contain:
01 00 00 00
00
00 00 00 00
00
As far as I can see, this should not work, as I haven't yet allocated an address for endpoint 3 (0x83
) in the PMA (packet memory area).
I do this, by editing 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;
}
Now, when I send the same 01 00 00 00 00
and 00 00 00 00 00
click_reports
I see packet contents of:
58 00 2c 00
58
58 00 2c 00
58
I have traced the contents of the non-PMA buffer right down to USB_WritePMA
in stm32f3xx_ll_usb
.
The sending code (in stm32f3xx_ll_usb
) is:
/* 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
{
Why is the data on the wire not the data that I give USB_WritePMA
, once I've added HAL_PCDEx_PMAConfig(...
for endpoint address 0x83
?
Update:
If I change usb_conf.c
to let endpoint address 0x83
use the PMA address that is normally used by 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 */
the packets on the wire are still corrupted:
58 00 2c 00
58
58 00 2c 00
58
If I return usb_conf.c
to its initial, generated, state (where 0x83
has no PMA address, and 0x81
uses 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 */
the output works as expected:
01 00 00 00
00
00 00 00 00
00
Update 2:
I added a break-point in USB_ActivateEndpoint()
in stm32f3xx_ll_usb.c
.
Surprisingly this is only ever called for endpoint 0.
Therefore, the ep->pmaadress
(sic) is never "written into hardware", and only used in higher-level code.
This must mean that the values of pmaadress
for the endpoints are set to some default value, and I do not know the default value for endpoint 0x83
and so can't set it.
When I return to work on Friday, I will add debugging to read-out the default values. If they do not exist, I will be very confused.
Update 3:
I added the following debugging:
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);
}
This showed me the following values (in the debugger):
txaddrs:
0: 0x58
1: 0xf5c4
2: 0xc1c2
3: 0x100
rxaddrs:
0: 0x18
1: 0xfa9b
2: 0xcb56
3: 0x0
These, unexpectedly, look correct.
0x100
is the txaddr of endpoint 3, even though USB_ActivateEndpoint()
has only just been called for the first time.
With a lot of grepping, I found that PCD_SET_EP_TX_ADDRESS
(in stm32f3xx_hal_pcd.h
) is not only used directly in USB_ActivateEndpoint()
, but also in the PCD_SET_EP_DBUF0_ADDR
macro from `stm32f3xx_hal_pcd.h.
PCD_SET_EP_DBUF0_ADDR
does not appear to be used, so I do not know how the (changed) values from 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 */
get into the memory-mapped USB registers.
I can infer, from the presence of a 0x00
in rxaddr[3]
(endpoint 3) that they happen in pairs (as there is no call to HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x3 , PCD_SNG_BUF, 0x0);
).
Update 4:
After changing the device to use endpoint 1 again, the value of 0x100
in txaddrs[3] remained. It was simply there from the last run, which removes a little confusion.
Update 5:
It's a BTABLE problem. The BTABLE register has a value of 0x00, putting the btable at the start of the PMA.
The PMA looks like this:
and the start of the PMA is the btable.
I found:
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
This shows that endpoints 0x81
and 0x82
work because both pma[4]
and pma[8]
are set to 0x100
.
Endpoint 0x83
does not work because pma[12]
is set to 0x0
.
This is consistent with the corrupted data having the value 58 00 2c 00
- the USB hardware was reading pma[12]
and therefore sending the uint16_t's from pma[0]
, which are 0x0058 0x002c
, sent reversed because of little-endianness. (Note: the PMA is only 16-bits wide, so there are only two bytes at each address here.)
The call to HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82, PCD_SNG_BUF, 0x100);
does not set up the btable pointer at pma[12]
, it just notes that PMA address to copy-to.
Now I just need to find where the content of the btable is being written...