Старт ARM. Реализуем USB HID на STM32F4. Часть 2. HAL

Всем привет!) продолжаем работать с кастомным USB HID – ом, теперь на STM32F4 (STM32F4DISCOVERY). Будем юзать HAL, для начала необходимо скачать STM32CubeMX. После установки запускаем его (SMT32F4CubeMX – и обновляем его с помощью STM32CubeUpdater – необходимо скачать данную софтину), и создаем новый проект New Project, переходим во вкладку Board Selector и из приведенного списка выбираем Discovery STM32F4DISCOVERY STM32F407VG и нажимаем ОК. Алгоритм наших действий:
1. Во вкладке Pinout -> IPs, находим USB_OTG_FS, выбираем из выпадающего списка Mode -> Device Only (можно поставить галки на Activate_SOF и Activate_VBUS).
2. Во вкладке Pinout -> MiddleWares, находим USB_DEVICE, ПО нам подсказывать, что нужно выбрать Class For FS IP, затем выбираем из выпадающего списка Human Interface Device Class (HID).
3. В верхнем меню выбираем Project -> Generate Code (Ctrl + Shift +G).
4. В появившемся окне, вводим названием проекта, путь сохранения проекта и IDE-шку в которую мы с вами будем портировать проект, у меня это Keil (MDK – ARM).
5. Открылся проект в Keil, щелкаем 2 раза по Drivers/STM32F4xxHAL_Driver, в папке проекта переходим Drivers-> STM32F4xxHAL_Driver->Src-> stm32f4xx_hal_pcd_ex.c добавляем этот файл.
6. В верхнем меню Project->Options for Target ‘Ваше название проекта Configuration’, переходим во вкладку Utilities, снимаем галку Use Debag Driver, нажимаем кнопку Settings во вкладке Flash Download, делаем настройки: вместо Erase Sectors -> Erase Full Chip и ставим галку на Reset and Run
7. В том же окне в Debug меняем Port: JTAG на SW.
8. Нажимаем F7 Build проекта и Download проекта.

В системе определился USB HID Mouse, да – да мышь, думаю в 3 части про USB HID я выложу проект зловредной флешки, с виду флешка, а на самом деле USB HID Keyboard, которая устроит полный апокалипсис вашему компу, со всеми схемами и pcb- ишку.

Но нам необходимо кастомный USB HID, «дело было вечером, делать было нечего», начинаем изменять проект:

USBD_ClassTypeDef  USBD_HID = 
{
  USBD_HID_Init,
  USBD_HID_DeInit,
  USBD_HID_Setup,
  NULL, /*EP0_TxSent*/  
  NULL, /*EP0_RxReady*/
  USBD_HID_DataIn, /*DataIn*/
  NULL, /*DataOut*/
  NULL, /*SOF */
  NULL,
  NULL,      
  USBD_HID_GetCfgDesc,
  USBD_HID_GetCfgDesc, 
  USBD_HID_GetCfgDesc,
  USBD_HID_GetDeviceQualifierDesc,
};	USBD_ClassTypeDef  USBD_HID = 
...
{
  USBD_HID_Init,
  USBD_HID_DeInit,
  USBD_HID_Setup,
  NULL, /*EP0_TxSent*/  
  NULL, /*EP0_RxReady*/
  USBD_HID_DataIn, /*DataIn*/
  USBD_HID_DataOut, /*DataOut*/
  NULL, /*SOF */
  NULL,
  NULL,      
  USBD_HID_GetCfgDesc,
  USBD_HID_GetCfgDesc, 
  USBD_HID_GetCfgDesc,
  USBD_HID_GetDeviceQualifierDesc,
};
/* USB HID device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ]  __ALIGN_END =
{
  0x09, /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
  USB_HID_CONFIG_DESC_SIZ,
  /* wTotalLength: Bytes returned */
  0x00,
  0x01,         /*bNumInterfaces: 1 interface*/
  0x01,         /*bConfigurationValue: Configuration value*/
  0x00,         /*iConfiguration: Index of string descriptor describing
  the configuration*/
  0xE0,         /*bmAttributes: bus powered and Support Remote Wake-up */
  0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/
  
  /************** Descriptor of Joystick Mouse interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
  0x00,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x01,         /*bNumEndpoints*/
  0x03,         /*bInterfaceClass: HID*/
  0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0,            /*iInterface: Index of string descriptor*/
  /******************** Descriptor of Joystick Mouse HID ********************/
  /* 18 */
  0x09,         /*bLength: HID Descriptor size*/
  HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
  0x11,         /*bcdHID: HID Class Spec release number*/
  0x01,
  0x00,         /*bCountryCode: Hardware target country*/
  0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
  0x22,         /*bDescriptorType*/
  HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
  0x00,
  /******************** Descriptor of Mouse endpoint ********************/
  /* 27 */
  0x07,          /*bLength: Endpoint Descriptor size*/
  USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
  
  HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
  0x03,          /*bmAttributes: Interrupt endpoint*/
  HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
  0x00,
  HID_POLLING_INTERVAL,          /*bInterval: Polling Interval (10 ms)*/
  /* 34 */
} ;	__ALIGN_BEGIN static uint8_t 
...
USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ]  __ALIGN_END =
{
  0x09, /* bLength: Configuration Descriptor size */
  USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
  USB_HID_CONFIG_DESC_SIZ,
  /* wTotalLength: Bytes returned */
  0x00,
  0x01,         /*bNumInterfaces: 1 interface*/
  0x01,         /*bConfigurationValue: Configuration value*/
  0x00,         /*iConfiguration: Index of string descriptor describing
  the configuration*/
  0xE0,         /*bmAttributes: bus powered and Support Remote Wake-up */
  0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/
  
  /************** Descriptor of Joystick Mouse interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
  0x00,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x02,         /*bNumEndpoints*/
  0x03,         /*bInterfaceClass: HID*/
  0x00,//0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x00,//0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0,            /*iInterface: Index of string descriptor*/
  /******************** Descriptor of Joystick Mouse HID ********************/
  /* 18 */
  0x09,         /*bLength: HID Descriptor size*/
  HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
  0x11,         /*bcdHID: HID Class Spec release number*/
  0x01,
  0x00,         /*bCountryCode: Hardware target country*/
  0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
  0x22,         /*bDescriptorType*/
  HID_MOUSE_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
  0x00,
  /******************** Descriptor of Mouse endpoint ********************/
  /* 27 */
  0x07,          /*bLength: Endpoint Descriptor size*/
  USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
  
  HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
  0x03,          /*bmAttributes: Interrupt endpoint*/
  HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
  0x00,
  HID_POLLING_INTERVAL,          /*bInterval: Polling Interval (10 ms)*/
  /* 34 */
	0x07,          /*bLength: Endpoint Descriptor size*/
  USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
  
  HID_EPOUT_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
  0x03,          /*bmAttributes: Interrupt endpoint*/
  HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
  0x00,
  HID_POLLING_INTERVAL,          /*bInterval: Polling Interval (10 ms)*/
} ;
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]  __ALIGN_END =
{
  0x05,   0x01,
  0x09,   0x02,
  0xA1,   0x01,
  0x09,   0x01,
  
  0xA1,   0x00,
  0x05,   0x09,
  0x19,   0x01,
  0x29,   0x03,
  
  0x15,   0x00,
  0x25,   0x01,
  0x95,   0x03,
  0x75,   0x01,
  
  0x81,   0x02,
  0x95,   0x01,
  0x75,   0x05,
  0x81,   0x01,
  
  0x05,   0x01,
  0x09,   0x30,
  0x09,   0x31,
  0x09,   0x38,
  
  0x15,   0x81,
  0x25,   0x7F,
  0x75,   0x08,
  0x95,   0x03,
  
  0x81,   0x06,
  0xC0,   0x09,
  0x3c,   0x05,
  0xff,   0x09,
  
  0x01,   0x15,
  0x00,   0x25,
  0x01,   0x75,
  0x01,   0x95,
  
  0x02,   0xb1,
  0x22,   0x75,
  0x06,   0x95,
  0x01,   0xb1,
  
  0x01,   0xc0
};	__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE]  __ALIGN_END =
{
//.................................
0x06, 0xFF, 0xFF,                                                        
// 04|2   , Usage Page (vendor defined?)
0x09, 0x01,                                                                    
// 08|1   , Usage      (vendor defined
0xA1, 0x01,                                                                    
// A0|1   , Collection (Application)
//..................................
// IN report
0x09, 0x02,                                                                    
// 08|1   , Usage      (vendor defined)
0x09, 0x03,                                                                    
// 08|1   , Usage      (vendor defined)
0x15, 0x00,                                                                    
// 14|1   , Logical Minimum(0 for signed byte?)
0x26, 0xFF, 0x00,                                                        
// 24|1   , Logical Maximum(255 for signed byte?)
0x75, 0x08,                                                                    
// 74|1   , Report Size(8) = field size in bits = 1 byte
// 94|1   , ReportCount(size) = repeat count of previous item
0x95, 0x40,
0x81, 0x02,                                                                    
// 80|1   , IN report (Data,Variable, Absolute)
//....................................
// OUT report
0x09, 0x04,                                                                    
// 08|1   , Usage      (vendor defined)
0x09, 0x05,                                                                    
// 08|1   , Usage      (vendor defined)
0x15, 0x00,                                                                    
// 14|1   , Logical Minimum(0 for signed byte?)
0x26, 0xFF, 0x00,                                                        
// 24|1   , Logical Maximum(255 for signed byte?)
0x75, 0x08,                                                                    
// 74|1   , Report Size(8) = field size in bits = 1 byte
// 94|1   , ReportCount(size) = repeat count of previous item
0x95, 0x40,
0x91, 0x02,                                
// 90|1   , OUT report (Data,Variable, Absolute)
// Feature report
0x09, 0x06,                                    
// 08|1   , Usage      (vendor defined)
0x09, 0x07,                                    
// 08|1   , Usage      (vendor defined)
0x15, 0x00,                                    
// 14|1   , LogicalMinimum(0 for signed byte)
0x26, 0xFF, 0x00,                                    
// 24|1   , Logical Maximum(255 for signed byte)
0x75, 0x08,                                    
// 74|1   , Report Size(8) =field size in bits = 1 byte
0x95, 0x40,                    
// 94|x   , ReportCount in byte
0xB1, 0x02,                                    
// B0|1   , Feature report
0xC0                                
// C0|0   , End Collection
};
static uint8_t  USBD_HID_Init (USBD_HandleTypeDef *pdev, 
                               uint8_t cfgidx)
{
  uint8_t ret = 0;
  
  /* Open EP IN */
  USBD_LL_OpenEP(pdev,
                 HID_EPIN_ADDR,
                 USBD_EP_TYPE_INTR,
                 HID_EPIN_SIZE);  
  
  pdev->pClassData = USBD_malloc(sizeof (USBD_HID_HandleTypeDef));
  
  if(pdev->pClassData == NULL)
  {
    ret = 1; 
  }
  else
  {
    ((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE;
  }
  return ret;
}	static uint8_t  USBD_HID_Init (USBD_HandleTypeDef *pdev, 
                               uint8_t cfgidx)
{
  uint8_t ret = 0;
  
  /* Open EP IN */
  USBD_LL_OpenEP(pdev,
                 HID_EPIN_ADDR,
                 USBD_EP_TYPE_INTR,
                 HID_EPIN_SIZE);  
	/* Open EP OUT */
	USBD_LL_OpenEP(pdev,
                 HID_EPOUT_ADDR,
                 USBD_EP_TYPE_INTR,
                 HID_EPOUT_SIZE);
	
	USBD_LL_PrepareReceive(pdev, 
                 HID_EPOUT_ADDR,                                      
                 usb_rx_buffer,
                 64);	
  
  pdev->pClassData = USBD_malloc(sizeof (USBD_HID_HandleTypeDef));
  
  if(pdev->pClassData == NULL)
  {
    ret = 1; 
  }
  else
  {
    ((USBD_HID_HandleTypeDef *)pdev->pClassData)->state = HID_IDLE;
  }
  return ret;
}

Добавляем в начала сишника прототип на функцию и затем где-нибудь в конце вставляем уже описание функции

static uint8_t  USBD_HID_DataOut (USBD_HandleTypeDef *pdev, 
                              uint8_t epnum)
{
	if (epnum != (HID_EPOUT_ADDR & 0x0F))
		return USBD_FAIL;

  int8_t i = 0;
	while (i < 64)
	{
		OutBuffer[i] = usb_rx_buffer[i];
		i++;
	}
		USBD_LL_PrepareReceive(pdev, 
                 HID_EPOUT_ADDR,                                      
                 usb_rx_buffer,
                 64);	
	
	return USBD_OK;

Так же из usb_device.c необходимо вырезать USBD_HandleTypeDef hUsbDeviceFS и вставить в usbd_hid.c и вверху файла usbd_hid.c добавляем

static uint8_t  USBD_HID_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t usb_rx_buffer[64];
extern uint8_t OutBuffer[64];

Изменяем ашник usbd_hid.h

#define HID_EPIN_ADDR                 0x81
#define HID_EPIN_SIZE                 0x40

#define HID_EPOUT_ADDR                0x02
#define HID_EPOUT_SIZE                0x40

#define USB_HID_CONFIG_DESC_SIZ       (34 + 7)
#define USB_HID_DESC_SIZ              9
//#define HID_MOUSE_REPORT_DESC_SIZE    74
#define HID_MOUSE_REPORT_DESC_SIZE    53

#define HID_DESCRIPTOR_TYPE           0x21
#define HID_REPORT_DESC               0x22
//#define HID_POLLING_INTERVAL          0x0A
#define HID_POLLING_INTERVAL          0x01

Изменяем сишник usbd_desc.c

#define USBD_VID     1155
#define USBD_LANGID_STRING     1033
#define USBD_MANUFACTURER_STRING     "mcu.by"
#define USBD_PID_FS     22315
#define USBD_PRODUCT_STRING_FS     "USB Device mcu.by"
#define USBD_SERIALNUMBER_STRING_FS     "00000000001A"
#define USBD_CONFIGURATION_STRING_FS     "HID Config"
#define USBD_INTERFACE_STRING_FS     "HID Interface"
//…

__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =
  {
    0x12,                       /*bLength */
    USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/
    0x00,                       /*bcdUSB */
    0x02,
    0x00,                       /*bDeviceClass*/
    0x00,                       /*bDeviceSubClass*/
    0x00,                       /*bDeviceProtocol*/
    USB_MAX_EP0_SIZE,          /*bMaxPacketSize*/
    0x51,//LOBYTE(USBD_VID),           /*idVendor*/
    0xC2,//HIBYTE(USBD_VID),           /*idVendor*/
    0x01,//LOBYTE(USBD_PID_FS),           /*idVendor*/
    0x23,//HIBYTE(USBD_PID_FS),           /*idVendor*/
    0x00,                       /*bcdDevice rel. 2.00*/
    0x02,
    USBD_IDX_MFC_STR,           /*Index of manufacturer  string*/
    USBD_IDX_PRODUCT_STR,       /*Index of product string*/
    USBD_IDX_SERIAL_STR,        /*Index of serial number string*/
    USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/
  } ; /* USB_DeviceDescriptor */


Затем меняем main.c добавив несколько строк кода

uint8_t OutBuffer[64];

int main(void)
{
…
   While(1) {
…
    USBD_HID_SendReport(&hUsbDeviceFS, OutBuffer, 64);
   }
}

Проект для stm32f401c-disco (добавлен 05.02.2015)

И наслаждаем работай устройства. Примечание: в прошлой статье я писал, что для верхнего уровня делаем тройную перезапись буфера, зачем?, а за тем что когда идет запись из ПК в устройство, первые данные приходят хорошо, вторые данные могут не прейти потому что, буфер эндпоинта еще не успел их обработать они теряются и девайсена отсылает несколько раз тоже самое, что бы этого не происходило советую написать свой интерфейсик для обработки этой ситуации или ставить таймауты на запись в устройство, они же слипы (функция Sleep), хотя это значительно может снизить эффективность вашего устройства. На сегодня ВСЁ!, всем ПОКА!)

14 thoughts on “Старт ARM. Реализуем USB HID на STM32F4. Часть 2. HAL

    1. пока нельзя, со временем появиться возможность для публикации

  1. Классная статья. У меня все заработало, супер, респек автору. Только у Вас рабочий пример с хидом, а то везде какая то лажа, да же могучий сайт di halt-а не помог, а там все очень на хорошем уровне)

  2. Простите есть же КастомХид в SMT32FCubeMX почему бы не использовать его?

    1. Да, вы правы. К сожалению не была кастомногоХида к моменту написания статьи, сейчас уже есть. Даже проверял работоспособность. Его можно скачать с экзамплами для куба, лежит пример в STM32Cube_FW_F4_V1.5.0\Projects\STM324x9I_EVAL\Applications\USB_Device\CustomHID_Standalone.

      1. У меня камень stm32f103c8 уже неделю не могу найти рабочий пример CustomHID, а скомпилированый с SMT32FCubeMX CustomHID говорит в win8.1 64 “Запуск этого устройства невозможен. (Код 10) Найдена лишняя конечная коллекция, или конечные коллекции не обнаружены.”
        В какую сторону рыть подскажите, очень очень надо передавать и принимать информацию с этого камня. 🙁

        1. По мне лучше использовать вот это “STM32F10x, STM32L1xx and STM32F3xx USB full speed device library (UM0424)” для stm32f1 и там есть кастомный хид

          1. Спасибо за наводку. Интересная информация.
            С примером разобрался, нужно было в ручную прописать дескриптор протокола в файле usbd_custom_hid_if.c . Все заработало.
            Уверен с скоростью будут проблемы но в будущем, этот пример в копилку!

  3. Добрый день! Подскажите, пожалуйста, новичку, что именно прописать в файле usbd_custom_hid_if.c (не нашел в описании USB параметра “дескриптор протокола”). При сборке примера из SMT32FCubeMX Custom HID для STM32F4Discovery (stm32f407) такая же ошибка “Запуск этого устройства невозможен. (Код 10) Найдена лишняя конечная коллекция, или конечные коллекции не обнаружены.” (win8.1 64)

    1. Неправильно написан дескриптор либо что-то лишнее, либо чего-то не хватает.

Comments are closed.