STM32 и USB-HID — это просто

На дворе 2014 год, а для связи микроконтроллеров с ПК самым популярным средством является обычный последовательный порт. С ним легко начать работать, он до примитивности прост в понимании — просто поток байт.
Однако все современные стандарты исключили COM порт из состава ПК и приходится использовать USB-UART переходники, чтобы получить доступ к своему проекту на МК. Не всегда он есть под рукой. Не всегда такой переходник работает стабильно из-за проблем с драйверами. Есть и другие недостатки.
Но каждый раз, когда заходит разговор о том, применять USB или последовательный порт, находится множество поклонников логической простоты UART. И у них есть на то основания. Однако, хорошо ведь иметь альтернативу?

Меня давно просили рассказать как организовать пакетный обмен данными между ПК и МК на примере STM32F103. Я дам готовый рабочий проект и расскажу как его адаптировать для своих нужд. А уж вы сами решите — нужно оно вам или нет.

У нас есть плата с современным недорогим микроконтроллером STM32F103C8 со встроенной аппаратной поддержкой USB, я рассказывал о ней ранее


Я сказал, что у последовательного порта есть и другие недостатки:
-часто COM порт отсутствует в ПК или ноутбуке
-питание устройству нужно подавать отдельно
-даже при наличии COM порта в ПК необходимо согласовывать уровни сигналов: ПК использует интерфейс RS232 с дифференциальными уровнями сигналов +15В и -15В, а микроконтроллеры — TTL уровни (+5В, +3.3В, униполярные).
-часто в системе образуются десятки виртуальных COM портов и найти тот порт, что соответствует вашему устройству может оказаться непросто.
В свою очередь USB с нами уже многие годы и имеет свои преимущества:
-Возможность подачи питания от HOST устройства
-Удобная реализация пакетного обмена
-Возможность одновременного подключения к устройству несколькими программами
-Возможность однозначной идентификации подключенного устройства
-Аппаратная поддержка во многих современных МК, что исключает необходимость переходников
Функционал USB чрезвычайно богатый, но это порождает проблему — разобраться не так просто, как с последовательным интерфейсом. Есть отдельный класс устройств — USB-HID, которые не требуют установки драйверов, специально предназначены для взаимодействия с человеком и различными устройствами ввода-вывода. Идеально для организации обмена данными с МК. Лично мне нравится пакетный режим обмена. Это удобная абстракция. К тому же разбирать пакетные сообщения несколько проще и удобнее, чем работать с простым потоком байт.

Выбор профиля HID

USB-HID — довольно обширный класс устройств, поэтому прежде всего придется выбрать какое именно устройство мы будем создавать.
Мы можем создать эмуляцию клавиатуры, мыши, джойстика и других устройств ввода, а можем создать свое устройство, чтобы не зависеть от довольно жестких рамок стандарта и свободно обмениваться данными с ПК.
Я расскажу как cделать Custom HID device. Это дает максимальную свободу. Чтобы не затягивать статью, постарюсь рассказать максимально кратко — описаний стандарта в сети и без меня много, но лично мне они слабо помогли, когда понадобилось решить конкретную задачу.

Структура проекта

Я использую EmBlocks для разработки под STM32. Вы можете использовать любую удобную среду, проект не очень сложно адаптировать.
К базовой структуре проекта добавлены:

  • Папка USB-FS с библиотекой «STM32F10x, STM32L1xx and STM32F3xx USB-FS-Device Driver» версии 4.0.0.
  • В папках Inc и Src файлы:
    platform_config.h — здесь собраны определения, касающиеся конкретной платы и семейства МК
    stm32_it.h, stm32_it.c — здесь определены обработчики прерываний
    usb_conf.h, usb_endp.c — здесь определяются конечные точки (Endpoint), размеры и адреса их буферов, функции-обработчики
    usb_desc.h, usb_desc.c — здесь собрана информаци о самом устройстве — как оно будет определяться при подключении к ПК и определены размеры и формат пакетов данных
    hw_config.c — здесь собрана вся работа с отправкой данных на ПК
    hw_config.h, usb_istr.h, usb_prop.h, usb_pwr.h
    usb_istr.c, usb_prop.c, usb_pwr.c — нужны для работы USB-FS библиотеки, но лезть в них необязательно

Все эти файлы мы добавляем в любой проект, использующий USB.

Инициализация USB

Для корректной работы USB модуля важна частота работы МК. Далеко не все частоты позволяют правильно задать тактирование USB. В нашем случае используется кварцевый генератор на 8МГц и МК работает на частоте 72МГц, а USB модуль на 48МГц.
В main.c достаточно включить всего несколько строк кода

main.c
/* Includes ------------------------------------------------------------------*/
#include "hw_config.h"
#include "usb_lib.h"
#include "usb_pwr.h"

/* Private variables ---------------------------------------------------------*/
__IO uint8_t PrevXferComplete = 1;

int main(void)
{
  Set_System();

  USB_Interrupts_Config();

  Set_USBClock();

  USB_Init();


  while (1)
  {

    if (bDeviceState == CONFIGURED)
    {
      if (PrevXferComplete)
      {
        RHIDCheckState();
      }
    }
  }
}

В функции Set_System() производится настройка пина подтяжки линии D+ к питанию для программного подключения/отключения устройства от ПК (в нашей плате не используется), настраивается прерывание и инициализируются светодиоды и кнопки для демонстрационного проекта.
В USB_Interrupts_Config() настраиваются прерывания в зависимости от семейства МК (поддерживаются F10x, F37x, L1x).
Функция USB_Init() запускает работу USB модуля. Если временно нужно отключить для отладки работу с USB, просто закомментируйте эту строку.
Далее в бесконечном цикле проверяется, удалось ли сконфигурировать USB модуль при подключении к ПК. Если все сработало верно и устройство успешно подключилось, ПК включен и не находится в режиме энергосбережения, то состояние будет CONFIGURED.
Далее проверяется, была ли закончена предыдущая передача данных в ПК и если да, то готовится к отправке новый пакет в функции RHIDCheckState()

Размер пакета и частота передачи

USB-HID девайс не может сам инициировать передачу, т.к. координацией шины занимается host устройство — ПК. Поэтому при подготовке USB дескриптора нашего устройства, мы пишем, как часто нужно опрашивать наше устройство. По спецификации максимальная частота опроса — 1кГц и максимальный размер передаваемого за раз пакета — 64 байта. Если этого недостаточно — придется использовать другие режимы работы — вроде USB bulk, но там уже без драйверов не обойтись.
За настройку взаимодействия с ПК отвечают 3 дескриптора:

Дескриптор устройства
/* USB Standard Device Descriptor */
const uint8_t RHID_DeviceDescriptor[RHID_SIZ_DEVICE_DESC] =
  {
		    RHID_SIZ_DEVICE_DESC,         // общая длина дескриптора устройства в байтах
		    USB_DEVICE_DESCRIPTOR_TYPE, // bDescriptorType - показывает, что это за дескриптор. В данном случае - Device descriptor
		    0x00, 0x02,                 // bcdUSB - какую версию стандарта USB поддерживает устройство. 2.0

			// класс, подкласс устройства и протокол, по стандарту USB. У нас нули, означает каждый интерфейс сам за себя
		    0x00,                       //bDeviceClass
		    0x00,                       //bDeviceSubClass
		    0x00,                       //bDeviceProtocol

		    0x40,                       //bMaxPacketSize - максимальный размер пакетов для Endpoint 0 (при конфигурировании)

			// те самые пресловутые VID и PID,  по которым и определяется, что же это за устройство.
		    0x83, 0x04,                 //idVendor (0x0483)
		    0x11, 0x57,                 //idProduct (0x5711)

		    DEVICE_VER_L, DEVICE_VER_H,                 // bcdDevice rel. DEVICE_VER_H.DEVICE_VER_L  номер релиза устройства

			// дальше идут индексы строк, описывающих производителя, устройство и серийный номер.
			// Отображаются в свойствах устройства в диспетчере устройств
			// А по серийному номеру подключенные устройства с одинаковым VID/PID различаются системой.
		    1,                          //Index of string descriptor describing manufacturer
		    2,                          //Index of string descriptor describing product
		    3,                          //Index of string descriptor describing the device serial number
		    0x01                        // bNumConfigurations - количество возможных конфигураций. У нас одна.
  }
  ; /* CustomHID_DeviceDescriptor */

В комментариях все довольно прозрачно. Обратите внимание на DEVICE_VER_L, DEVICE_VER_H — это константы из usb_desc.h, которые вы можете изменить для идентификации версии своего устройства.

Дескриптор конфигурации (описывает возможности устройства)
/* USB Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
const uint8_t RHID_ConfigDescriptor[RHID_SIZ_CONFIG_DESC] =
  {
		    0x09, 			// bLength: длина дескриптора конфигурации
		    USB_CONFIGURATION_DESCRIPTOR_TYPE, // bDescriptorType: тип дескриптора - конфигурация
		    RHID_SIZ_CONFIG_DESC, 0x00, // wTotalLength: общий размер всего дерева под данной конфигурацией в байтах

		    0x01,         // bNumInterfaces: в конфигурации всего один интерфейс
		    0x01,         // bConfigurationValue: индекс данной конфигурации
		    0x00,         // iConfiguration: индекс строки, которая описывает эту конфигурацию
		    0xE0,         // bmAttributes: признак того, что устройство будет питаться от шины USB
		    0x32,         // MaxPower 100 mA: и ему хватит 100 мА

				/************** Дескриптор интерфейса ****************/
				0x09,         // bLength: размер дескриптора интерфейса
				USB_INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType: тип дескриптора - интерфейс
				0x00,         // bInterfaceNumber: порядковый номер интерфейса - 0
				0x00,         // bAlternateSetting: признак альтернативного интерфейса, у нас не используется
				0x02,         // bNumEndpoints - количество эндпоинтов.

				0x03,         // bInterfaceClass: класс интерфеса - HID
				// если бы мы косили под стандартное устройство, например клавиатуру или мышь, то надо было бы указать правильно класс и подкласс
				// а так у нас общее HID-устройство
				0x00,         // bInterfaceSubClass : подкласс интерфейса.
				0x00,         // nInterfaceProtocol : протокол интерфейса

				0,            // iInterface: индекс строки, описывающей интерфейс

					// теперь отдельный дескриптор для уточнения того, что данный интерфейс - это HID устройство
					/******************** HID дескриптор ********************/
					0x09,         // bLength: длина HID-дескриптора
					HID_DESCRIPTOR_TYPE, // bDescriptorType: тип дескриптора - HID
					0x01, 0x01,   // bcdHID: номер версии HID 1.1
					0x00,         // bCountryCode: код страны (если нужен)
					0x01,         // bNumDescriptors: Сколько дальше будет report дескрипторов
						HID_REPORT_DESCRIPTOR_TYPE,         // bDescriptorType: Тип дескриптора - report
						RHID_SIZ_REPORT_DESC,	0x00, // wItemLength: длина report-дескриптора


					/******************** дескриптор конечных точек (endpoints) ********************/
					0x07,          // bLength: длина дескриптора
					USB_ENDPOINT_DESCRIPTOR_TYPE, // тип дескриптора - endpoints

					0x81,          // bEndpointAddress: адрес конечной точки и направление 1(IN)
					0x03,          // bmAttributes: тип конечной точки - Interrupt endpoint
					wMaxPacketSize, 0x00,    // wMaxPacketSize:  Bytes max
					0x20,          // bInterval: Polling Interval (32 ms)

          0x07,	/* bLength: Endpoint Descriptor size */
          USB_ENDPOINT_DESCRIPTOR_TYPE,	/* bDescriptorType: */
            /*	Endpoint descriptor type */
          0x01,	/* bEndpointAddress: */
            /*	Endpoint Address (OUT) */
          0x03,	/* bmAttributes: Interrupt endpoint */
          wMaxPacketSize,	/* wMaxPacketSize:  Bytes max  */
          0x00,
          0x20,	/* bInterval: Polling Interval (32 ms) */
}
  ; /* RHID_ConfigDescriptor */

Здесь стоит обратить внимание на константу wMaxPacketSize — она определяет максимальный размер пакета, которым мы будем обмениваться с ПК. Проект так настроен, чтобы при ее изменении менялись и размеры буферов. Но не забывайте, что больше 0x40 по стандарту указывать не стоит. С этой константой будьте осторожны — если передаваемый пакет будет отличаться по размеру — будут проблемы!
Следующая за ним константа с комментарием bInterval — это период опроса устройства в миллисекундах. Для нашего устройства задано 32мс.

Дескриптор репорта (описывает протокол)
const uint8_t RHID_ReportDescriptor[RHID_SIZ_REPORT_DESC] =
  {
    0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x01,                    //   USAGE (Vendor Usage 1)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x09, 0x01,                    //   USAGE (Vendor Usage 1)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x02,                    //   REPORT_ID (2)
    0x09, 0x02,                    //   USAGE (Vendor Usage 2)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x09, 0x02,                    //   USAGE (Vendor Usage 2)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x03,                    //   REPORT_ID (3)
    0x09, 0x03,                    //   USAGE (Vendor Usage 3)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, RPT3_COUNT,                    //   REPORT_COUNT (N)
    0xb1, 0x82,                    //   FEATURE (Data,Var,Abs,Vol)
    0x85, 0x03,                    //   REPORT_ID (3)
    0x09, 0x03,                    //   USAGE (Vendor Usage 3)
    0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

    0x85, 0x04,                    //   REPORT_ID (4)
    0x09, 0x04,                    //   USAGE (Vendor Usage 4)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, RPT4_COUNT,                    //   REPORT_COUNT (N)
    0x81, 0x82,                    //   INPUT (Data,Var,Abs,Vol)
    0xc0                           // END_COLLECTION
}

Это самый важный дескриптор — он описывает протокол обмена и функционал устройства. Его формирование — не самая простая задача. Если допустить ошибку при формировании дескриптора — устройство перестанет работать. Формат дескриптора очень жесткий. Есть даже специальная утилита HID Descriptor tool. А в корне проекта лежит файл «RHID.hid» с описанным выше дескриптором для редактирования в этой утилите. Но если вы не понимаете, что делаете, лучше не лезть.
Для простоты я сделал две константы:
RPT3_COUNT — размер OUTPUT буфера в байтах для передачи пакета в МК (в примере — 1 байт)
RPT4_COUNT — размер INPUT буфера в байтах для передачи пакета в ПК (в примере — 4 байта)
Размер любого из этих буферов не должен превышать wMaxPacketSize. Меньше — можно.
Кстати, превратить Custom HID в другой HID девайс, например, клавиатуру или джойстик можно фактически только переписав ReportDescriptor и изменив класс и подкласс устройства в дескрипторе конфигурации.

Что такое Report

Хост (ПК) и девайс (МК) обмениваются пакетами данных заранее оговоренной структуры — report. Пакетов может быть весьма много, их можно предусмотреть на все случаи жизни — например пакет с данными о каких-то событиях в устройстве, пакет с данными, которые запрашивал ПК, пакет с командой для МК. Все, что угодно. Но структура всех пакетов должна быть описана в структуре RHID_ReportDescriptor.
ПК и МК различают репорты по ID, который идет первым байтом в пакете.
В нашем примере 4 типа репортов:

  • REPORT_ID = 1 и 2 — команда МК включить/выключить LED1/LED2. Содержит поле размером 1 бит с желаемым состоянием светодиода и поддерживает отправку как методом SET_REPORT так и методом SET_FEATURE (об этом чуть позже).
  • REPORT_ID = 3 — передает один байт в МК. Просто, чтобы показать, как передать данные МК. Мы будем передавать положение ползунка.
  • REPORT_ID = 4 — это репорт для передачи данных ПК. Возвращает информацию о текущем состоянии светодиодов, кнопок (если они есть) и возвращает переданный в репорте с ID=3 байт, чтобы показать, что данные приняты.

Если вы не до конца разобрались в том, как формировать дескриптор репортов, то просто меняйте константы RPT3_COUNT и RPT4_COUNT, устанавливая нужный размер исходящих и входящих (с точки зрения ПК) пакетов. Остальные репорты можно просто не трогать, они не помешают. Не забывайте, что первым байтом должен быть ID репорта.

Цикл обмена

Итак, мы сконфигурировали наше устройство, установив PID, VID, номер версии, настроили размеры входящих и исходящих пакетов и готовы к работе.
Каждые 32мс, как мы и просили в дескрипторе конфигурации, хост будет нас опрашивать и в функции RHIDCheckState мы проверяем — если у нас есть, что отправить, то формируем пакет данных для хоста.

RHIDCheckState - функция отправки данных
/*******************************************************************************
* Function Name : RHIDCheckState.
* Description   : Decodes the RHID state.
* Input         : None.
* Output        : None.
* Return value  : The state value.
*******************************************************************************/
uint16_t btn1_prev, btn2_prev;
uint8_t Buffer[RPT4_COUNT+1];
uint8_t RHIDCheckState(void)
{
    uint16_t btn1=0, btn2=0;
    btn1 = GPIO_ReadInputDataBit(BTN1_PORT, BTN1_PIN);
    btn2 = GPIO_ReadInputDataBit(BTN2_PORT, BTN2_PIN);
    Buffer[0] = 4;
    Buffer[1] = btn1;
    Buffer[2] = btn2;
    Buffer[3] = (GPIO_ReadInputDataBit(LED_PORT, LED1_PIN) | GPIO_ReadInputDataBit(LED_PORT, LED2_PIN)<<1);

    /* Reset the control token to inform upper layer that a transfer is ongoing */
    PrevXferComplete = 0;

    /* Copy buffer date info in ENDP1 Tx Packet Memory Area*/
    USB_SIL_Write(EP1_IN, Buffer, RPT4_COUNT+1);
    /* Enable endpoint for transmission */
    SetEPTxValid(ENDP1);

    return (btn1 | btn2<<1);
}

Массив uint8_t Buffer[RPT4_COUNT+1] определен как размер полезных данных входящего (рассматривается всегда с точки зрения хоста) пакета + байт ID. Это важно — если размер буфера будет отличаться — будут проблемы. Поэтому для изменения размеров буфера редактируйте значение константы в usb_desc.h.
В функции мы собираем данные в пакет, устанавливаем флаг PrevXferComplete = 0, говорящий о том, что данные отправляются и вызываем функциии библиотеки USB_SIL_Write и SetEPTxValid для отправки данных хосту.
Все, на этом передача данных хосту закончена.

С приемом данных немного сложнее — есть два способа послать данные девайсу — один из них заключается в использовании описанных в дескрипторе репорта возможностей устройства (Features), с соответствующими параметрами посредством функции SET_FEAUTRE. Это некоторая абстракция, для красивого управления устройством с кучей функций, чтобы можно было вызывать осмысленные функции, а не просто слать поток байт.
Второй способ — это работа с устройством как с файлом — просто записываем в него пакет как в файл. Этот метод называется SET_REPORT. На деле работает чуть-чуть медленнее.
Наше устройство поддерживает оба метода, о чем мы и сказали хосту в дескрипторе репортов.

Обработка SET_FEATURE

Данные, отправленные методом SET_FEAUTRE обрабатываются в usb_prop.c

функция HID_Status_In
/*******************************************************************************
* Function Name  : HID_Status_In.
* Description    : HID status IN routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void HID_Status_In(void)
{
  BitAction Led_State;

  if (Report_Buf[1] == 0)
  {
    Led_State = Bit_RESET;
  }
  else
  {
    Led_State = Bit_SET;
  }

  switch (Report_Buf[0])
  {
    case 1: /* Led 1 */
     if (Led_State != Bit_RESET)
     {
       GPIO_SetBits(LED_PORT,LED1_PIN);
     }
     else
     {
       GPIO_ResetBits(LED_PORT,LED1_PIN);
     }
     break;
    case 2: /* Led 2 */
     if (Led_State != Bit_RESET)
     {
       GPIO_SetBits(LED_PORT,LED2_PIN);
     }
     else
     {
       GPIO_ResetBits(LED_PORT,LED2_PIN);
     }
      break;
    case 3: /* Led 1&2 */
       Buffer[4]=Report_Buf[1];
     break;
  }
}

Здесь мы проверяем первый байт в репорте и в соответствии с ним обрабатываем остаток пакета — управляем светодиодами или просто берем байт, отправленный нам хостом и кладем в пакет для последующей отправки обратно в функции RHIDCheckState.
Под Report_Buf зарезервировано wMaxPacketSize байт, чтобы влез любой пакет, который нам отправит хост.

Данные, отправленные методом SET_REPORT обрабатываются в usb_endp.c

функция EP1_OUT_Callback
/*******************************************************************************
* Function Name  : EP1_OUT_Callback.
* Description    : EP1 OUT Callback Routine.
* Input          : None.
* Output         : None.
* Return         : None.
*******************************************************************************/
void EP1_OUT_Callback(void)
{
  BitAction Led_State;

  /* Read received data (2 bytes) */
  USB_SIL_Read(EP1_OUT, Receive_Buffer);

  if (Receive_Buffer[1] == 0)
  {
    Led_State = Bit_RESET;
  }
  else
  {
    Led_State = Bit_SET;
  }


  switch (Receive_Buffer[0])
  {
    case 1: /* Led 1 */
     if (Led_State != Bit_RESET)
     {
       GPIO_SetBits(LED_PORT,LED1_PIN);
     }
     else
     {
       GPIO_ResetBits(LED_PORT,LED1_PIN);
     }
     break;
    case 2: /* Led 2 */
     if (Led_State != Bit_RESET)
     {
       GPIO_SetBits(LED_PORT,LED2_PIN);
     }
     else
     {
       GPIO_ResetBits(LED_PORT,LED2_PIN);
     }
      break;
    case 3: /* Led 1&2 */
        Buffer[4]=Receive_Buffer[1];
     break;
  }

  SetEPRxStatus(ENDP1, EP_RX_VALID);
}

Здесь почти то же самое, только нужно самостоятельно забрать данные вызовом USB_SIL_Read(EP1_OUT, Receive_Buffer) и в конце сообщить, что мы закончили вызовом SetEPRxStatus(ENDP1, EP_RX_VALID);

Настраивать устройство, передавать и принимать данные в пакетах нужного размера с нужной нам периодичностью мы научились.
Собираем проект и прошиваем в устройство.
Работать, это будет примерно так:

Проект поддерживает взаимодействие с утилитой USB HID Demonstrator от ST Microelectronics.
Страница Device capabilities отображает возможности, описанные в Report Descriptor.
Input/Output transfer позволяет вручную поотправлять данные девайсу и посмотреть пакет, который от него приходит.
Graphic view позволяет управлять светодиодами, чекбоксами Led 1, Led 2, настроив соответствующий им Report ID, а также передавать байт ползунком (ReportID=3)

Также я написал маленькую демо-софтинку, которая автоматически определяет подключение к компу и отключение нашего девайса по его VID и PID, отображает статус — подключено/отключено индикатором рядом с чекбоксом Auto Connect

Радиокнока Send using позволяет выбрать метод отправки данных девайсу.
Report: отображает полученный от девайса пакет побайтно, начиная с ReportID.
Щелкая по светодиодам ниже — управляем светодиодами девайса. Их состояние отображает текущее состояние девайса. Считывается из репорта от девайса.
Перемещая ползунок, мы отправляем Report с ID=3 и значением, соответствующим позиции ползунка. Девайс вернет это значение в 4 байте репорта.
В выпадающем комбобоксе отображаются HID девайсы, найденные в системе и если найден наш девайс, то отображается его название.

Скачать все, что необходимо, можно на GitHub. В составе:
DT — HID Descriptor tool
tstHID-STM32F103 — проект для EmBlocks
USB HID Demonstrator — утилита от ST Microelectronics
HIDSTM32.exe — моя демо-софтинка на Delphi аналогичного фукнционала, но не требующая настройки

Если остались вопросы — пишите в комментариях. Постараюсь ответить. Я постарался не утопить суть в куче мелочей, чтобы сложилось общее понимание. Остальное уже можно понять, изучая проект. Но если вам нужно быстро сделать свое устройство, а лезть в дебри некогда — все, что вам нужно, я описал.

P.S. По умолчанию при уходе хоста в режим энергосбережения, девайс засыпает вместе с ним, а если подключить девайс к спящему ПК, то он тоже уйдет в слип. Поэтому если мы просто воткнем в девайс блок питания или запитаем от батареи, то работать он не будет, считая, что подключен к спящему ПК (пакетов конфигурации то от БП не придет точно). Я изменил библиотеку так, чтобы устройство работало и при подключении просто БП. Поэтому девайс будет работать как при подключениии к ПК так и автономно. (У меня ушло немало времени, чтобы разобраться с этим.)

STM32 и USB-HID — это просто: 68 комментариев

  1. Добрый день!

    «Собираем проект и прошиваем в устройство»
    Скажите, можно ли прошивать через USB-интерфейс, т.е. без программаторов и дополнительных приспособлений?

    Спасибо!

  2. Помогите начать, не могу подключить keil к этой плате через jlink (пробывал jtag и swd режимы).

    Купил китайский клон j-link v8, скачала последние драйвера и софт, обучил оригинальный софт работать с китайским клоном j-link.

    Какой порядок подключения этой платы.
    Нужно подключить usb шнурком ? выставить хитрым образом перемычки ? или запаять дополнительные детали ?

    • По работе J-Link да еще китайского клона — вопросы не ко мне. У меня лежит этот девайс без дела. Пару раз помучался с его обновлениями дебаг-сервера, тормозами и ключами и больше не хочу. Лежит на гипотетический случай, если вдруг понадобится работать с продукцией не ST-Micro. Пользуюсь ST-Link V2 он гораздо удобнее. А в качестве IDE удобнее EmBlocks, к тому же EmBlocks бесплатна, а ST-Link дешевле J-Link.
      Не вижу смысла возиться с китайскими клонами и платной IDE.
      Для того, чтобы отлаживать плату нужно подать питание. Можно через USB, можно напрямую на пины 3.3 или 5В.

      • Ну, у меня к сожалению нету ST-Link, даже если я его закажу будет ехать не один день, а руки чешутся. Keil понравилась прежде всего поскольку это коммерческий продукт, и версия для ознакомления не сильно снижает функциональность(я пока игрался с эмулятором не ощутил неудобств).

        Так как это моя первая железка, к корой я пытаюсь подцепится j-tag’ом, у меня нету представления как все должно быть, в идеале.

        Вопрос такой. На плате 3 перемычки

        Boot 0
        Boot 1
        USB

        При каких конфигурациях перемычек, при подключенном шнурке USB, плата однозначно должна работать с J-TAG или swd. Плата чистая, т.е. в неё ничего еще не было прошито.

        • Перемычки должны быть в нормальном рабочем положении. Для отладки их трогать не нужно.
          Они используются только для прошивки через UART ну и других специальных случаях.
          Редактор в KEIL хуже чем EmBlocks, как и в IAR, никаких преимуществ не вижу вообще. Кстати, EmBlocks может компилировать с компилятором KEIL.

          • Спасибо, ответ

            Посмотрю EmBlocks

  3. Разобрался я почему у меня ничего не получалось, правда пока разбирался убил китайский jlink не нужными прошивками.

    Китайцу, будь они не ладны, плохо промыли плату и в итоге процессор (stm32) постоянно, 2 раза в секунду сбрасывался. Поэтому jlink не могу установить с ним связь.

    Промыл плату в ультразвуковой ванне, и jlink смог подключится (подключается и по jtag и по swd протоколу) и начал видеть содержимое памяти, но дебажить не может, ругается на ошибку в jlink

    заказал оригинальный ST-Link/v2

    • у китайского клона J-Link еще есть неприятная особенность — время от времени слетает прошивка и его приходится раскирпичивать. я не сталкивался, но почти все, кто пользуются на постоянной основе, говорят об этом. Так что ST-Link — правильный выбор.

  4. Здравствуйте.

    Проект «STM32 и USB-HID это просто» с STM32F103C8 на EmBlocks 2.00 с Вашим патчем у меня заработал «из коробки». Проблемы настали когда захотел перенести его на плату STM32F103RBT6. Перенести проект в EmBlocks c одной цели на другую у меня не получилось(я сделал вывод что прямой функции «изменить целевой камень на другой» в EmBlocks нет), поэтому я просто взял библиотеки SPL и USB-FS, а также папки src, inc и cmsis и добавил во вновь созданный проект для STM32F103RBT6.
    Проект находится тут:
    http://webfile.ru/3c37c6cb8df03dcac50e1703ce6123be
    При компиляции EmBlocks выбрасывает в логи вот это:

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ||=== RB, Debug ===|
    obj\debug\usb_init.o||In function `USB_Init’:|
    C:\prj\RB\USB-FS\src\usb_init.c|70|undefined reference to `Device_Property’|
    C:\prj\RB\USB-FS\src\usb_init.c|70|undefined reference to `Device_Property’|
    C:\prj\RB\USB-FS\src\usb_init.c|71|undefined reference to `User_Standard_Requests’|
    C:\prj\RB\USB-FS\src\usb_init.c|71|undefined reference to `User_Standard_Requests’|
    obj\debug\main.o||In function `main’:|
    C:\prj\RB\src\main.c|34|undefined reference to `Set_System’|
    C:\prj\RB\src\main.c|36|undefined reference to `USB_Interrupts_Config’|
    C:\prj\RB\src\main.c|38|undefined reference to `Set_USBClock’|
    C:\prj\RB\src\main.c|46|undefined reference to `bDeviceState’|
    C:\prj\RB\src\main.c|46|undefined reference to `bDeviceState’|
    C:\prj\RB\src\main.c|50|undefined reference to `RHIDCheckState’|
    ||=== Build finished: 10 errors, 0 warnings (0 minutes, 6 seconds) ===|
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Что-то не так с библиотекой USB-FS?
    Заменил STM32_USB-FS-Device_Lib_V4.0.0 вместе с SPL(на st.com есть stsw-stm32121.zip). Не помогло.

    Ранее я аналогично переносил проект «STM32 + EmBlocks — мигаем светодиодами»(STM32F103C8 -> STM32F103RBT6). Всё заработало. А здесь нет (.
    Не подскажете почему?

    • А кто будет включать в проект файлы src\usb_*, src\hw_config.c? А определить дефайн для платы например miniSTM32F103C8? А раскомментировать нужные модули в stm32f10x_conf.h?
      Матчасть то нужно изучить хотя бы немного.

      • благодарю за ответ и потраченное время на разбор..
        теперь знаю где лежат дефайны на плату.
        а не добавите на github исходники своей демософтинки HIDSTM32.exe? )

  5. Добрый день!
    Снова к вам за советом. Не могу разобраться.

    По задумке у меня контроллер должен управлять несколькими устройствами (i2c, spi) + иметь usb интерфейс.
    Сделал управление сейчас двумя (радио модуль, и монитор). Работает отлично. Хостится в среде freeRtos.
    Но как только я подключаю USB. Все останавливается, ничего не работает. В добавок еще и GDB сервер Stlink’a перестает работать с девайсом.

    Через StLink Util очищаю чип, и снова процессор начинает видеться через GDB сервер. что дает возможность из EmBlocks заново прошить после прошивки все повторяется по новой. как только прошивается прошивка опять тишина.

    Каждый «девайс» у меня работает в отдельном потоке freeRtos, USB инициализирую тоже в отдельном потоке

    для работы с USB взял пример Custom HID от ST

    У вас увидел комментарий, может у меня в этом же проблема
    >>P.S. По умолчанию при уходе хоста в режим энергосбережения, девайс засыпает вместе с ним, а если подключить девайс к спящему ПК, то он тоже уйдет в слип. Поэтому если мы просто воткнем в девайс блок питания или запитаем от батареи, то работать он не будет, считая, что подключен к спящему ПК (пакетов конфигурации то от БП не придет точно). Я изменил библиотеку так, чтобы устройство работало и при подключении просто БП. Поэтому девайс будет работать как при подключениии к ПК так и автономно. (У меня ушло немало времени, чтобы разобраться с этим.)
    Не знаю куда копать и как найти проблемное место.

    • Сам спросил, сам отвечаю.
      помог разобраться мне китайски клон jLink, c EmBlocks заработал более мене стабильно, и лучше чем официальный StLink.
      JLink смог подключится и показать в каком месте стоит контроллер. Стоял он на суспенде.

      пока выставил переменную
      __IO bool fSuspendEnabled = TRUE; /* true when suspend is possible */
      в FALSE

  6. Спасибо за статью. Разобрался благодаря вашему проекту. У меня остался еще один вопрос: Можно ли как-то передавать через него целые символы (Например ка передать слово «USB»?), ведь из того что я понял пакет формируется в битах (‘ID’ ’01 00 00 00′ ) где каждый бит состояние в вашем случае светодиода.

  7. RHIDCheckState — функция отправки данных.
    uint8_t Buffer[RPT4_COUNT+1]; — массив для данных, которые нужно отправить ПК.
    Пихаем туда все, что нам заблагорассудится, лишь бы не больше чем RPT4_COUNT байт.
    0й байт — Report_ID, его нужно сохранять.

  8. И еще никак не могу заставить передать больше чем 2 байта
    (#define wMaxPacketSize 0x40
    #define EP1TxCount wMaxPacketSize
    #define EP1RxCount wMaxPacketSize
    #define RPT3_COUNT 0x04 //PC->STM32
    #define RPT4_COUNT 0x06//STM32->PC -вроде правильно заполнил)
    хотя в передаваемом массиве их 4.

      • const uint8_t RHID_ReportDescriptor[RHID_SIZ_REPORT_DESC] =
        {
        0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
        0x09, 0x01, // USAGE (Vendor Usage 1)
        0xa1, 0x01, // COLLECTION (Application)
        0x85, 0x01, // REPORT_ID (1)
        0x09, 0x01, // USAGE (Vendor Usage 1)
        0x15, 0x00, // LOGICAL_MINIMUM (0)
        0x25, 0x01, // LOGICAL_MAXIMUM (1)
        0x75, 0x08, // REPORT_SIZE (8)
        0x95, RPT1_COUNT, // REPORT_COUNT (1)
        0xb1, 0x82, // FEATURE (Data,Var,Abs,Vol)
        0x85, 0x01, // REPORT_ID (1)
        0x09, 0x01, // USAGE (Vendor Usage 1)
        0x91, 0x82, // OUTPUT (Data,Var,Abs,Vol)

        0x85, 0x02, // REPORT_ID (2)
        0x09, 0x02, // USAGE (Vendor Usage 2)
        0x15, 0x00, // LOGICAL_MINIMUM (0)
        0x25, 0x01, // LOGICAL_MAXIMUM (1)
        0x75, 0x08, // REPORT_SIZE (8)
        0x95, RPT1_COUNT, // REPORT_COUNT (1)
        0xb1, 0x82, // FEATURE (Data,Var,Abs,Vol)
        0x85, 0x02, // REPORT_ID (2)
        0x09, 0x02, // USAGE (Vendor Usage 2)
        0x91, 0x82, // OUTPUT (Data,Var,Abs,Vol)

        0x85, 0x03, // REPORT_ID (3)
        0x09, 0x03, // USAGE (Vendor Usage 3)
        0x15, 0x00, // LOGICAL_MINIMUM (0)
        0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
        0x75, 0x08, // REPORT_SIZE (8)
        0x95, RPT1_COUNT, // REPORT_COUNT (N)
        0xb1, 0x82, // FEATURE (Data,Var,Abs,Vol)
        0x85, 0x03, // REPORT_ID (3)
        0x09, 0x03, // USAGE (Vendor Usage 3)
        0x91, 0x82, // OUTPUT (Data,Var,Abs,Vol)

        0x85, 0x04, // REPORT_ID (4)
        0x09, 0x04, // USAGE (Vendor Usage 4)
        0x75, 0x08, // REPORT_SIZE (8)
        0x95, RPT2_COUNT, // REPORT_COUNT (N)
        0x81, 0x82, // INPUT (Data,Var,Abs,Vol)
        0xc0 // END_COLLECTION
        }

  9. Большое спасибо автору за статью. У меня все получилось в своем проекте по аналогии. Хотелось бы рассказать о граблях, на которые мне довелось наступить.
    1. Поначалу устройство удачно распознавалось в системе 1 раз из 5-10. Причина оказалась банально в проводе. При этом с данным проводом другие устройства работали, а вот мое — отказалось. Замена провода решила проблему. Не поленитесь поменять несколько проводов и не берите длинные провода для отладки.
    2. Инициализировать USB (вызов USB_Init()) надо делать до разрешения прерываний (вызов USB_Interrupts_Config()), иначе в обработчике прерываний контроллер иногда попадает в бесконечный цикл, сравнивая флаг прерывания с еще незаполненными переменными. Эта ошибка здорово попила мне крови.

  10. Случилась странная история. В начале лета, прочитав эту статью, без проблем реализовал на ее основе обмен данными между PC и MC(делал ради пробы). Теперь же, когда захотел внедрить USB в готовый проект не могу добиться приема данных методом SET_REPORT, точнее работает только с массивом в 2-байта (как в примере), а 3-х и более -массив уже не обрабатывается. Методом же SET_FEATURES без проблем принимается и обрабатывается любой массив. Может кто подскажет в каком месте я ошибаюсь. Заранее спасибо.

    • У меня наоборот было. Через колбэк работало, а через HID_Status_In — нет.
      В результате оставил что работает, не стал разбираться. Если функция EP1_OUT_Callback вообще не вызывается, то проверьте, что при передаче с компьютера правильно указываете Report_ID первым байтом (в примере — 4).

  11. Подскажите в чем может быть проблема: проект собирается без ошибок. А при подключении ничего не происходит. Система его не находит ни в каком виде. Если раньше, когда были ошибки, система ругалась на неопознаное устройство, то сейчас просто тишина. В перечне USB устройств, которые выводит DevCon ничего нет тоже.
    В какую сторону надо смотреть? С чем это может быть связано?

    • Если система не находит, самая вероятная причина — нет подтяжки к питанию USB D+. Без этого девайс не будет обнаружен компом.

  12. Здравствуйте, подскажите где вы определили дефайн USB_USE_EXTERNAL_PULLUP?, я так понимаю его надо определять когда внешний резистор подтягивает к питанию, а у вас на платке так и есть вроде.

    • Можно определить в defines для проекта. В принципе, он не нужен на мелкой плате с STM32F103C8

  13. Здравствуйте. Реализовал Custom HID на DiscoveryF4. При подключении к компьютеру находит «USB устройство ввода» — но драйвер не устанавливает, в диспетчере устройств напротив » USB устройство ввода» горит воскл. знак. Если представляюсь к примеру — мышью, то все ОК. Может в дескрипторе есть какой нюанс?
    Спасибо.

    • Я использовал пример от ST — девайс определялся. Другой вопрос, что были глюки с передачей данных девайсу.

      • Уже второй день бьюсь с передачей данных девайсу, если один байт то все работает замечательно, если изменяю на большее то вешается программа на хосте, размер посылки выставляю правильно, не знаете в чем может быть дело?

  14. Добрый вечер.Переделывал ваш пример под другой камень(stm32f107),собственно потому что под рукой была отладочная плата с ним.Инит практически не отличается за исключением нескольких мелочей,но устройство пк определять уверенно не хочет.В usb я nooby.Есть у вас какие-либо мысли?может нужно изменить что-нибудь в дескрипторе?

    Заранее спасибо.

  15. Подскажите почему может быть так, что к ПК с Win usb подключается, а к планшету Android нет(если только подключить и перезагрузить, и то только один раз, потом вытаскиваю провод и опять). Может быть подтяжка 1k а не 1.5к на плате и что такое usb wkp exti18 что за событие? Кстати могу дать пример под Android для данной темы.

    • Возможно проводок тонковат и падение напряжения высоковато на планшете получается?
      Событие пробуждения девайса по USB вместе с компьютером. Стандартно при отсутствии активности на шине устройство уходит спать до появления указаний от хоста.
      Пример под Android был бы очень полезен.

  16. Добрый день!
    Разбираю пример с http://yadi.sk/d/OXCDaGlsJX5Lz (http://forum.easyelectronics.ru/viewtopic.php?p=321302).
    В HID_ReportDescriptor идёт описание 4 светодиодов, а затем:
    /* key USER Button */
    0x85, 0x05, /* REPORT_ID (5) */
    0x09, 0x05, /* USAGE (USER Button) */
    0x15, 0x00, /* LOGICAL_MINIMUM (0) */
    0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
    0x75, 0x01, /* REPORT_SIZE (1) */
    0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */

    0x09, 0x05, /* USAGE (USER Button) */
    0x75, 0x01, /* REPORT_SIZE (1) */
    0xb1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */

    0x75, 0x07, /* REPORT_SIZE (7) */
    0x81, 0x83, /* INPUT (Cnst,Var,Abs,Vol) */
    0x85, 0x05, /* REPORT_ID (5) */

    0x75, 0x07, /* REPORT_SIZE (7) */
    0xb1, 0x83, /* FEATURE (Cnst,Var,Abs,Vol) */
    /* 114 */

    /* ADC IN */
    0x85, 0x06, /* REPORT_ID (6) */
    0x09, 0x07, /* USAGE (ADC IN) */
    0x15, 0x00, /* LOGICAL_MINIMUM (0) */
    0x26, 0xff, 0x00, /* LOGICAL_MAXIMUM (255) */
    0x75, 0x08, /* REPORT_SIZE (8) */
    0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */
    0x85, 0x06, /* REPORT_ID (6) */
    0x09, 0x06, /* USAGE (ADC in) */
    0xb1, 0x82, /* FEATURE (Data,Var,Abs,Vol) */
    /* 133 */

    Поменял wMaxPacketSize (в примере это HID_IN_PACKET и HID_OUT_PACKET). В функции отправки указал 3 байта: USBD_HID_SendReport (&USB_OTG_dev, Buffer, 3);
    Однако в USB HID Demonstrator’е видно только 2 байта.
    Пробовал Ваш дескриптор, но тогда в USB HID Demonstrator устройство не определяется. Если меняются REPORT_ID, где ещё нужно вносить изменения. Расскажите, пожалуйста: как нужно правильно описать входной и выходной поток для обычной передачи 64 байт. Больше ничего не надо. Плата: Stm32f4Discovery.
    Спасибо.

    • HID демонстратор и не увидит больше — он заточен под демонстрацию 🙂 Нужно писать свое приложение для обработки данных. На F4 вообще могут быть проблемы — там другой модуль USB.
      в USB нет понятия «обычной передачи». Репорты здесь не универсальные, а строго специализированные. Только Custom HID может передавать и принимать произвольные данные. Где в примере нужно менять, я уже не помню, давно его писал, но там все довольно прозрачно.

  17. я пытался настроить USB как com port на ПК и столкнулся с проблемой что при старте если USB шнурок был подключен то он не распознаётся, и приходится перетыкать провод. подскажите где искать проблему

  18. Здравствуйте, спасибо за статью. Реализовал проэкт на её основе. У меня китайская платка на stm32f103c8, каждый раз после прошивки, проц перестает отвечать на команды по usb, приходиться вручную отключать usb-разьем и переподключать заново, что очень неудобно (он еще и определяется с 10го разу). Можно ли это отключение реализовать программно, или хотя бы перемычкой на этом mcu?

  19. Здравствуйте. Отлаживаю HID устройство на макетной плате с STM32F103C8T6 по материалам данной статьи. Обмен данными между устройством и ПК проходит нормально. Но почему-то не работает устройство (подключенное кабелем постоянно) после перезагрузки ПК или при включении ПК? В программе хоста определяется, но обмена данными нет. Спасает только передергивание кабеля. Хотя при выходе из спящего режима ПК устройство определяется и обмен данными продолжается. Пулап резистор подключается транз. ключом, в прошивке предусмотрено. Может что в коде прошивки подправить? Есть ли у кого-то еще такая проблема?

    • Cкорее всего устройство уходит в сон вместе с компом. Посмотрите обработку сигналов от компа об уходе в спящий режим

      • Как раз спящий режим (вход в него (в спящий режим) и выход из него) отрабатывается корректно. Устройство не отваливается, двусторонний обмен данными восстанавливается. Именно после перезагрузки ПК или при включении ПК устройство некорректно инициализируется. В диспетчере устройств — нормально определяется как HID устройство, хостовая программа определяет (VID, PID, StringProduct ). Но передачи данных нет. Только после передергивания кабеля все нормализуется. Может что с дескрипторами, хотя код и exe-шник взяты из статьи. Проект компилирую в CooCox. А как у Вас не было таких проблем?

        • У меня и правда не было такого. Правда я работаю в EmBlocks, а не CooCox. Но не думаю, что дело в нем.
          Я так понимаю, что процесс повторения должен быть таков: подключаем девайс, перезагружаем ПК и он перестает обмениваться данными, так?

          • Взял Custom_HID с сайта st, переназначил периферию согласно своего МК (103С8Т6), скомпилировал в TrueSTUDIO, эффект тот-же — после перезагрузки ПК перестает обмен данными, спасает передергивание кабеля или сброс МК. Это огорчает. Как же это побороть? Не хочется добавлять какие-то таймауты и прочие механизмы контроля. С программным USB HID в AVR такой проблемы не было.

          • Проблема решена с использованием HAL драйвера при компиляции. Теперь устройство работает так как надо. ( с SPL пока некогда разбираться).

  20. Попытался переложить проект под Keil5
    Кое как собралось, но Увы не работает.
    Можно Вас попросить выложить готовый хекс под мини STM32F103C8?
    В исходниках его к сожалению не нашел.
    Я правильно понимаю, что в файле (platform_config.h ) достаточно, чтобы было не закомментировано
    #if defined (STM32F10X_MD) || defined (STM32F10X_HD) || defined (STM32F10X_XL)
    ?
    И вот это ( чуть ниже) не противоречит использованию STM32F103C8?
    #if !defined (USE_STM3210B_EVAL) && !defined (USE_STM3210E_EVAL) && !defined (USE_STM32L152_EVAL) && !defined (USE_STM32L152D_EVAL)&& !defined (USE_STM32373C_EVAL) && !defined (USE_STM32303C_EVAL)
    #define USE_STM3210B_EVAL
    ?????

    • В принципе да. но семейство задается дефайнами в настройках проекта. При переносе в кайл очевидно эти настройки потерялись.
      Хекс поищу, но большинству он бесполезен, платы могут быть разные.

  21. Вот здесь взял под CoIDE
    http://forum.easyelectronics.ru/viewtopic.php?t=18329&p=328059
    без проблем под «Кал 5» пере ложился и работает.
    8 кб под флэш прошивка требует. А с этого сайта так и не удалось переложить под кал.
    Не знаю, может компилятор что то «оптимизирует» в коде. Между прочим переложный вариант под кал так же 8 кб от флэш требует..
    Вот еще обратил внимание- стар тап файлы отличаются. А в кокосе стар тап файл вообще с расширением «с».
    Попозже попробую собрать со стар тап файлом из этого проекта. Но вообще они по содержимому похожи. По крайней мере обработчики прерываний описаны так же.

  22. Уря, вторая попытка оказалась более успешной! Под «кал 5 » заработало!
    Как всегда оказались виноваты кривые руки. Правда, в каком месте не разобрался, но вторая попытка оказалась успешной. И что не менее важно-с меньшим количеством «танцев». Грешил и на стартап и CMSIS у Вас в проекте более новый, чем у меня. Но в итоге пересобрал с разными версиями CMSIS и все работают.

  23. Кому будет нужен вариант этого проекта под кал 5 — от вас ссылка на файло обменник куда можно будет выложить без «танцев». попытался на депозит-требует регистрации.

  24. Привет, Андрей.
    Спасибо за статью. Получилось прошить МК.
    Подскажи, как документацию читать, чтобы составлять правильные управляющие сообщения?

    • Привет. На здоровье. Какую именно документацию читать нужно?
      Если речь о сообщениях USB Reports то тут два инструмента — официальная программа от консорциума USB (она есть в архиве с примером), а также примеры дескрипторов и описания к ним.

  25. Здравствуйте! Подскажите, если использовать режим передачи bulk, то какие переделки кода необходимы (кроме указания типа передачи в дескрипторе) ? Как я понимаю, мастером шины является хост и тип передачи влияет только на логику работы именно хоста(тайм-ауты, приоритетность…)
    Спасибо!

  26. Всем привет, Подскажите stm32f103С8Tx способен работать как USB HOST. Все примеры что нашел, это имитация HID устройств. А у меня задача USB клавиатуру подключить к МК.

    • Не способен. USB Host есть, например, у F407 и всей F4 линейки. F4 также имеет возможность работы в HighSpeed режиме (но со внешним PHY)

  27. Добрый день RaJa, не расскажете как поднять 103C8 или 103CB с CDC? Разрабатываем блочек управления и под него уже создана управляющая среда для XP говорящая через COM порт.

    • Очень просто — нужно просто создать проект в STM32CubeIDE. Там в разделе Middleware есть готовый класс. Если интересно, напишу отдельную статью

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.