2020-09-15 02:08 PM
I'm planning to use a cheap STM32 board (either with a F103 or F401/11) as a joystick/rotary encoder controller for arcade games. I already have a working proof of concept using the old STM32duino cores (not HAL based), but I prefer to use the HAL environment to avoid more abstraction levels.
I used an existing example as a starting point (https://github.com/miniwinwm/BluePillDemo/tree/master/BluePillDemo_USB_HID), and I run into a problem trying to send data at high rates.
I'm using a Windows PC with mouserate.exe (http://tscherwitschke.de/old/mouseratechecker.html) to check effective rates. Windows can handle 1000Hz rates for mice.
First, I modified usbd_conf.h and changed
#define HID_FS_BINTERVAL 0x1 // was 0x0A
to increase the reported polling rate to 1000Hz
Then I put the following code in my main.c
int main(void)
{
/* USER CODE BEGIN 1 */
// in the mouse report byte 0 contains 3 button state bits
// byte 1,2,3 is x, y, thumbwheel movement
uint8_t mouse_report[5] = {0};
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USB_DEVICE_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
mouse_report[1] = 4;
mouse_report[2] = 0;
USBD_HID_SendReport(&hUsbDeviceFS, mouse_report, 5);
HAL_Delay(2);
mouse_report[1] = -4;
mouse_report[2] = 0;
USBD_HID_SendReport(&hUsbDeviceFS, mouse_report, 5);
HAL_Delay(2);
}
/* USER CODE END 3 */
}
to create a "mouse jiggler" that moves the mouse back and forth by 4 units. It works well, and achieves an average of 333Hz refresh rate
If I reduce the delay to HAL_delay(1), though, the rate increases, but periodically the computer only receives a 10-15 moves left (or right), as if the Windows PC and the STM32 were out of sync and the PC misses a few USB_HID_SendReport(). Which, considering how USB polling works, it's probably what happens. If I remove the delays, then the mouse moves right for a while, then left for a while, and so forth, basically missing even more events
Is there a way to wait until the USB device is ready to be polled, instead of using a HAL_delay(), which by its nature risks always being problematic? Or another way to write code to be interrupt driven instead of using delays?
Solved! Go to Solution.
2020-09-15 04:10 PM
Check for "hhid->state == HID_IDLE" prior to sending. This is what is done inside USBD_HID_SendReport, before it returns USBD_OK regardless. The USB implementation in general seems way worse than the rest of HAL. Probably just doesn't get many eyes on it.
uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len)
{
USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef *)pdev->pClassData;
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (hhid->state == HID_IDLE)
{
hhid->state = HID_BUSY;
(void)USBD_LL_Transmit(pdev, HID_EPIN_ADDR, report, len);
}
}
return (uint8_t)USBD_OK;
}
2020-09-15 04:10 PM
Check for "hhid->state == HID_IDLE" prior to sending. This is what is done inside USBD_HID_SendReport, before it returns USBD_OK regardless. The USB implementation in general seems way worse than the rest of HAL. Probably just doesn't get many eyes on it.
uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len)
{
USBD_HID_HandleTypeDef *hhid = (USBD_HID_HandleTypeDef *)pdev->pClassData;
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
if (hhid->state == HID_IDLE)
{
hhid->state = HID_BUSY;
(void)USBD_LL_Transmit(pdev, HID_EPIN_ADDR, report, len);
}
}
return (uint8_t)USBD_OK;
}
2020-09-15 07:20 PM
This was it, thanks! After this simple change, I can reliably hit ~500Hz on average with the puny STM32F103, quite impressive