-
Notifications
You must be signed in to change notification settings - Fork 46
Description
I'm having issues with getting a USB HID device with an OUT endpoint to work. My goal here is to use the USB HID protocol to provide a generic communication channel to a device with which I can then control the device from a PC. HID was chosen because it doesn't require drivers on any operating system, and many libraries for the PC side are available.
Environment:
Chip used: STM32F407VG (in 3.3V mode with 32.768kHz + 25MHz external oscillators)
Programming via SWD programming header with a STLink3 (official, not a clone)
Used current master branch (2021-06-22) of both STM32_XPD and USBDevice repositories
I've observed the following behavior with STM32_XPD + USBDevice:
- device enumerates correctly to the operating system
- send 64 bytes via HID to device: appears to work (send appears to be successful), but interrupt handle on device is not triggered (checked via gdb via STLink)
- receive 64 bytes via HID from device: timeout (because the handler is not executed and no report is ever sent back -- if reports are sent back from the main loop manually, the can be received by the application)
- I can repeat that as often as I like (same behavior)
- however: send less than 64 bytes via HID to device (e.g. 63 bytes): handler is executed, but second "data" argument of SetReport handler is set to NULL (seen in debugger), so garbage is sent back (but something is sent back)
- after the send with less than 64 bytes the USB stack on the device appears to have gone into some undefined state and nothing appears to work correctly anymore and I have to reset the device to perform further USB operations (the device itself is not hung totally, if I add a button and some LEDs and some simple logic, those will still work -- and the debugger just tells me that the USB interrupt isn't called anymore for some reason.
The following code was used for setup:
mcu_hwconfig.h:
#ifndef MCU_HWCONFIG_H_
#define MCU_HWCONFIG_H_
#include <xpd_usb.h>
#include <xpd_gpio.h>
#ifdef __cplusplus
extern "C"
{
#endif
#define USB_DP_PIN PA12
#define USB_DM_PIN PA11
#define USB_VBUS_PIN PA9
extern void SystemClock_Config(void);
extern void HwConfig_USB_Bind(void);
extern void HwConfig_USB_Init();
#ifdef __cplusplus
}
#endif
#endif /* MCU_HWCONFIG_H_ */
mcu_hwconfig.c:
#include <xpd_rcc.h>
#include <xpd_nvic.h>
#include <usbd.h>
#include <usbd_dfu.h>
#include <usbd_hid.h>
#include "mcu_hwconfig.h"
#include <string.h>
static USB_HandleType h_usb_handle;
USB_HandleType *const g_usb_handle = &h_usb_handle;
static const RCC_PLL_InitType pllconf = {
.State = ENABLE,
.Source = HSE,
.M = HSE_VALUE_Hz / 1000000,
.N = 336,
.P = 4, /* 1 * 336 / 4 = PLLP = 84000000 -> SYSCLK */
.Q = 7 /* 1 * 336 / 7 = PLLQ = 48000000 -> USB */
};
static const GPIO_InitType usb_pinconf =
{
.Mode = GPIO_MODE_ALTERNATE,
.Pull = GPIO_PULL_FLOAT,
.Output.Type = GPIO_OUTPUT_PUSHPULL,
.Output.Speed = VERY_HIGH,
.AlternateMap = GPIO_OTG_FS_AF10
};
/* System clocks configuration */
void SystemClock_Config(void)
{
RCC_eHSE_Config(OSC_ON);
RCC_ePLL_Config(&pllconf);
/* System clocks configuration */
RCC_eHCLK_Config(PLL, CLK_DIV1, 3);
RCC_vPCLK1_Config(CLK_DIV2);
RCC_vPCLK2_Config(CLK_DIV1);
}
void HwConfig_USB_Bind(void)
{
GPIO_vInitPin(USB_DM_PIN, &usb_pinconf);
GPIO_vInitPin(USB_DP_PIN, &usb_pinconf);
USB_INST2HANDLE(g_usb_handle, USB_OTG_FS);
}
/** @brief USB device configuration */
const USBD_DescriptionType hdev_cfg = {
.Vendor = {
.Name = "Sample Vendor https://www.example.com/",
.ID = 5824,
},
.Product = {
.Name = "HID Test Device",
.ID = 1503,
.Version.bcd = 0x0100,
},
#if (USBD_SERIAL_BCD_SIZE > 0)
.SerialNumber = (USBD_SerialNumberType*)DEVICE_ID_REG,
#endif
.Config = {
.Name = "HID Test Device",
.MaxCurrent_mA = 500,
.RemoteWakeup = 0,
.SelfPowered = 0,
},
}, *const dev_cfg = &hdev_cfg;
static void HID_GetReport(void* itf, USBD_HID_ReportType type, uint8_t reportId);
static void HID_SetReport(void* itf, USBD_HID_ReportType type, uint8_t* data, uint16_t length);
__alignment(USBD_DATA_ALIGNMENT)
static const uint8_t HID_Report[] __align(USBD_DATA_ALIGNMENT) = {
0x06, 0x00, 0xFF,
0x0A, 0x00, 0xFF,
0xA1, 0x01,
0x15, 0x00,
0x25, 0xFF,
0x75, 0x08,
0x95, 0x40,
0x09, 0x01,
0x81, 0x00,
0x09, 0x02,
0x91, 0x00,
0x09, 0x03,
0xB1, 0x02,
0xC0
};
static const USBD_HID_ReportConfigType HID_ReportConfig = {
.Desc = HID_Report,
.DescLength = sizeof(HID_Report),
.MaxId = 0,
.Input.MaxSize = 64,
.Input.Interval_ms = 5,
.Output.MaxSize = 64,
.Output.Interval_ms = 5,
};
static USBD_HID_AppType hid_app = {
.Name = "HID Test Device",
.SetReport = &HID_SetReport,
.GetReport = &HID_GetReport,
.Report = &HID_ReportConfig,
};
static USBD_HID_IfHandleType h_hid_handle = {
.App = &hid_app,
.Config.InEpNum = 0x81,
.Config.OutEpNum = 0x01,
};
static USBD_HID_IfHandleType *const g_hid_handle = &h_hid_handle;
void HwConfig_USB_Init(void)
{
NVIC_SetPriorityConfig(OTG_FS_IRQn, 1, 1);
NVIC_SetPriorityConfig(OTG_FS_WKUP_IRQn, 1, 2);
USBD_Init(g_usb_handle, dev_cfg);
USBD_HID_MountInterface(g_hid_handle, g_usb_handle);
NVIC_EnableIRQ(OTG_FS_IRQn);
USBD_Connect(g_usb_handle);
}
void OTG_FS_IRQHandler(void)
{
USB_vIRQHandler(g_usb_handle);
}
void HID_GetReport(void* itf, USBD_HID_ReportType type, uint8_t reportId)
{
(void) itf;
(void) type;
(void) reportId;
}
void HID_SetReport(void* itf, USBD_HID_ReportType type, uint8_t* data, uint16_t length)
{
uint8_t buffer[64];
if (type == HID_REPORT_OUTPUT) {
if (length > 64)
length = 64;
// Reverse the data, send it back
for (int i = 0; i < length; ++i)
buffer[length - i - 1] = data[i];
USBD_HID_ReportIn(g_hid_handle, buffer, length);
}
}
main.c:
#include <usbd_dfu.h>
#include <usbd_hid.h>
#include <usbd.h>
#include <xpd_flash.h>
#include <xpd_rcc.h>
#include <xpd_utils.h>
#include <xpd_gpio.h>
#include "mcu_hwconfig.h"
#include <string.h>
int main(void)
{
/* Reset hardware to default state */
XPD_vDeinit();
RCC_vDeinit();
// Basic initialization
XPD_vInit();
FLASH_vPrefetchBuffer(ENABLE);
// Configure pinout
HwConfig_USB_Bind();
// Configure system clock
SystemClock_Config();
// Configure USB
HwConfig_USB_Init();
while (1)
XPD_vDelay_ms(1000);
return 0;
}
On the computer side, the following code via HIDAPI:
#ifdef WIN32
#include <windows.h>
#endif
#include <hidapi.h>
#include <iostream>
#include <cstddef>
#include <cstdlib>
#include <cstring>
int main()
{
hid_device *handle;
// Initialize the hidapi library
(void) hid_init();
// Open the device using the VID, PID,
// and optionally the Serial number.
handle = hid_open(0x16c0, 0x05df, NULL);
// Write some data
uint8_t buf[65];
ssize_t len;
memset(buf, 0, sizeof(buf));
std::cout << "Sending: ";
// hid_write requires the report id in the first byte,
// since we don't use report ids, set it to 0
// (only 64 bytes will be transferred to the HID device)
buf[0] = 0;
for (int i = 0; i < 64; ++i) {
buf[i + 1] = rand();
if (i > 0)
std::cout << ' ';
std::cout << int(buf[i + 1]);
}
std::cout << std::endl;
len = hid_write(handle, buf, 65);
std::cout << "Write result: " << len << std::endl;
// Read some data
std::cout << "Reading data..." << std::endl;
len = hid_read(handle, buf, 64);
std::cout << "Read result: " << len << std::endl;
std::cout << "Received: ";
for (int i = 0; i < len; ++i) {
if (i > 0)
std::cout << ' ';
std::cout << int(buf[i]);
}
std::cout << std::endl;
// Close device
hid_close(handle);
// Finalize the hidapi library
(void) hid_exit();
return 0;
}
For comparison, I created a very simple project with STM32CubeIDE and used the ST-integrated library instead of XPD and this one, and there I can get the USB HID communication to work. (The ST library has other drawbacks, which is why I attempted to use this one, because I liked the design a lot more.)
I've used the debugger via the STLink device to attempt to get more information, and step through parts of the XPD and/or USBDevice code, but I don't understand the code well enough to easily see what's wrong. (From a plain reading of the code that is triggered by the USB interrupt it seems correct to me at first glance.)
So it appears to me that either I'm using USBDevice wrong, in which case it would be great to know how, or there's a bug there, and I'm not quite sure how to find it.
If necessary I can provide more information, such as a tarball with the complete code, or USB traces on Linux/Windows, or gdb expressions taken at certain places, but the bug report is long enough as-is, so I'll wait for responses first.
Many thanks in advance!