alambe94 / I-CUBE-USBD-Composite

Create STM32 USB Composite devices with ease.
MIT License
142 stars 30 forks source link

HID Keyboard + Audio Microphone #19

Open pitman75 opened 1 year ago

pitman75 commented 1 year ago

Need do working USB composite device USB keyboard and USB microphone. I did it for CubeMX standalone devices, it works well.

The MCU is STM32L476 STM32CubeMX 1.9.0

Software Pack Componet Selector marked:

Software Packs Parameter Settings marked:

Also configured FreeRTOS, MX_USB_DEVICE_Init() called at once when default task started.

Build process break on:

`#if(!STM32F1_DEVICE) else if (hpcd->Init.speed == PCD_SPEED_HIGH) { speed = USBD_SPEED_HIGH; }

endif`

PCD_SPEED_HIGH undeclared.

After remove this #if....#endif build process is success. PC Windows/Linux found composite USB device keyboard + microphone. But when I send key like keyboard via function USBD_HID_Keyboard_SendReport keys not typed on console. I used wireshark in Linux and see that MCU send ISO IN packet like in worked standalone solution. But no any key on console.

Also I see error in USB description by Thesicon USB Descriptor Dumper

`AC Interface Header Descriptor:

0x09 bLength 0x24 bDescriptorType 0x01 bDescriptorSubtype 0x0100 bcdADC 0x0026 wTotalLength (38 bytes)

usb_composite_not_wrk.txt

What is wrong?

pitman75 commented 1 year ago

Dump USB data transfer for STM32 HID Keyboard standalone and for composite HID Keyboard + UAC MIC

2022-07-20-213846_1920x1080_scrot

Looks like for composite device host doesn't read some config data and device for host is in wrong state. Data goes but rejected.

RAW dump data usb_composite-1-2022-07-20.zip usb_keyboard-2-2022-07-20.zip

pitman75 commented 1 year ago

After some changes my composite device works well. Thanks a lot of your huge work!

alambe94 commented 1 year ago

That is nice!! Do you mind sharing your changes? If so, please send a pull request.

pitman75 commented 1 year ago

I can explain my changes and test it on STM32L475. Now I have hardware USB protocol analyzer and can debug USB communications on low-level.

General:

if(!STM32F1_DEVICE) else if (hpcd->Init.speed == PCD_SPEED_HIGH) { speed = USBD_SPEED_HIGH; }

I'll resume later. Your plugin is very helpfull I can help you to do it better.

pitman75 commented 1 year ago

Also for HID keyboard I replaced report descriptor

In the file usbd_hid_keyboard.h change size of report descriptor

define HID_KEYBOARD_REPORT_DESC_SIZE 63U

In the file usbd_hid_keyboard.c replace report descriptor

/ HID keyboard report descriptor / ALIGN_BEGIN static uint8_t HID_KEYBOARD_ReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE] ALIGN_END = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x08, // REPORT_SIZE (8) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x01, // REPORT_SIZE (1) 0x05, 0x08, // USAGE_PAGE (LEDs) 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 0x29, 0x05, // USAGE_MAXIMUM (Kana) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x03, // REPORT_SIZE (3) 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) 0x95, 0x06, // REPORT_COUNT (6) 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x65, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0 // END_COLLECTION };

pitman75 commented 1 year ago

After this changes for keyboard it works well.

pitman75 commented 1 year ago

Generic changes for usb microphone:

  1. In the file usbd_audio_mic.c fix incorrect size of section:

    /* USB Microphone Audio Feature Unit Descriptor */
    0x07 + AUDIO_MIC_CHANNELS + 1,   /* bLength */
  2. In the file usbd_audio_mic.h change value for MIN, MAX, RES of audio control

define AUDIO_MIC_VOL_MIN 0x0000

define AUDIO_MIC_VOL_RES 0x0001

define AUDIO_MIC_VOL_MAX 0x0064

After this changes Windows 10 read its without any problem.

Dirty solution for send audio data

  1. Disable any code in the function USBD_AUDIO_MIC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
  2. Add callback to start/stop fill data to

case USB_REQ_SET_INTERFACE: if (pdev->dev_state == USBD_STATE_CONFIGURED) { if ((uint8_t)(req->wValue) <= USBD_MAX_NUM_INTERFACES) { haudio->alt_setting = (uint8_t)(req->wValue); //printf("Set interface USB command: %lu for EP 0x%02X\r\n", haudio->alt_setting, AUDIO_MIC_EP); if (haudio->alt_setting) { printf("%lu start\r\n", HAL_GetTick()); audio_play_start(); } else { printf("%lu stop\r\n", HAL_GetTick()); audio_play_stop(); }

      USBD_LL_FlushEP(pdev, AUDIO_MIC_EP);
    }
    else
    {
        printf("wVal wrong 0x%02X\r\n", req->wValue);
      /* Call the error management function (command will be NAKed */
      USBD_CtlError(pdev, req);
      ret = USBD_FAIL;
    }
  }
  else
  {
      printf("USB_REQ_GET_INTERFACE wrong 0x%02X\r\n", pdev->dev_state);
    USBD_CtlError(pdev, req);
    ret = USBD_FAIL;
  }
  break;
  1. Add flush MIC IN to static uint8_t USBD_AUDIO_MIC_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) { UNUSED(pdev); UNUSED(epnum); USBD_LL_FlushEP (pdev, AUDIO_MIC_EP); return (uint8_t)USBD_OK; }

  2. When capture audio started -> enable 1ms timer with interrupt to fill audio data to TX buffer.

Very important that when capture started to fill any data to output buffer. Without data after few rejected IN requests in Windows and 5000 IN requests in Linux -> the MIC IN pipe will be stopped without notification on application side. I added play silent white noise before and after played prerecorded audio all time while capture exist.