Top.Mail.Ru

STM32CubeMx. USB Custom HID. Прием и передача данных.

Вот и подоспела вторая часть из цикла статей, посвященных реализации класса USB Custom HID на микроконтроллерах STM32 при помощи утилиты STM32CubeMx. Как и обещал в конце предыдущей статьи, сегодня мы займемся непосредственно обменом данными между хостом и нашим устройством.

Начнем, пожалуй, с передачи данных хосту. Тут все просто - для передачи в HAL реализована специальная функция:

uint8_t USBD_CUSTOM_HID_SendReport(USBD_HandleTypeDef *pdev,
                                   uint8_t *report,
                                   uint16_t len)

Аргументы у функции типичные для любой передающей функции - указатель на передаваемые данные и количество байт, которые нужно отправить. Кроме того, мы должны передать в функцию объект USBD_HandleTypeDef. Эта переменная у нас определена в файле usnd_custom_hid_if:

USBD_HandleTypeDef *hUsbDevice_0;

Поскольку передачу мы будем производить из функции main(), то давайте сразу же объявим в файле main.c:

extern USBD_HandleTypeDef *hUsbDevice_0;

Передавать мы будем (как решили в предыдущей статье) по 4 байта, поэтому заодно объявим и массив для хранения передаваемых данных:

uint8_t dataToSend[4];

В принципе с приготовлениями на этом все, теперь просто вызываем функцию отправки, ну и не забываем заполнить массив данных какими-нибудь тестовыми значениями:

/* USER CODE BEGIN 2 */
dataToSend[0] = 'S';
dataToSend[1] = 'n';
dataToSend[2] = 'd';
dataToSend[3] = '\0';
/* USER CODE END 2 */

/* USER CODE BEGIN 3 */
/* Infinite loop */
while (1)
{
	HAL_Delay(1000);
	USBD_CUSTOM_HID_SendReport(hUsbDevice_0, dataToSend, 4);
}
/* USER CODE END 3 */

Теперь наше устройство будет отправлять данные хосту раз в секунду. Как видите, с передачей все оказалось совсем несложно.

На самом деле, с приемом тоже нет никаких трудностей и если его организация и сложнее, то ненамного. Итак, предлагаю реализовать следующее - пусть хост у нас отправляет два разных пакета, к примеру строки "Rcv" и "Snd". Мы же будем в зависимости от принятого пакета гасить или зажигать светодиод (на моей плате я использую PC6). Вот в общем-то и все, переходим к реализации...

Объявим в файле usbd_custom_hid_if.c буфер для сохранения принятых данных:

/* USER CODE BEGIN 2 */
uint8_t dataToReceive[4];
/* USER CODE END 2 */

А теперь идем в функцию CUSTOM_HID_OutEvent_FS(), тут то мы и будем принимать и анализировать данные:

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
	/* USER CODE BEGIN 6 */
	USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDevice_0->pClassData;

	for (uint8_t i = 0; i < 4; i++)
	{
		dataToReceive[i] = hhid->Report_buf[i];
	}

	if ((dataToReceive[0] == 'R') && (dataToReceive[1] == 'c') && (dataToReceive[2] == 'v'))
	{
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
	}

	if ((dataToReceive[0] == 'S') && (dataToReceive[1] == 'n') && (dataToReceive[2] == 'd'))
	{
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
	}

	return(0);
	/* USER CODE END 6 */
}

Собственно, тут нет ничего фантастического и непонятного - все принятые данные сохраняются в наш объект для работы с USB (hUsbDevice_0). Мы просто копируем их оттуда и сохраняем в массив. Ну а после просто анализируем данные и в зависимости от того, какой пакет принят, меняем состояние вывода, к которому подключен светодиод. Вот и все!

Статья получилась небольшая, но все аспекты, которые я хотел описать, я описал ) В следующей статье, посвященной USB Custom HID мы напишем свою собственную программу для хоста (ПК), которая и будет отправлять данные нашему устройству, а на сегодня на этом заканчиваем, до встречи в новых статьях 🤝

Следующая статья по Custom HID - USB Custom HID.

Подписаться
Уведомить о
guest

49 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Alexey
Alexey
8 лет назад

Спасибо! Очень ценно и полезно разобраться в этом! Ждём QT!!!

Alexey
Alexey
8 лет назад

Уважаемый автор, очень ждём продолжения :))))

Сергей
Сергей
8 лет назад

Здесь, пожалуй, самое простое и понятное описание как работать с HID. До этого перерыл весь интернет. Но ваши статьи больше всего пригодились.

Egor
Egor
8 лет назад

При считывании dataToReceive в dataToReceive[0] записывается ID репорта. Так что надо не с нулевого элемента данные смотреть, а с первого.

Egor
Egor
8 лет назад

Интересен такой вопрос. Можно ли пин перевести из Output состояния в Input и какой вообще функцией смотреть содержимое Input-пинов. Подскажите, если кто знает, пожалуйста! Может в Cube об этом уже позаботились и есть простой набор функций?

Egor
Egor
8 лет назад

Ага, спасибо)
Но больший интерес вызывает вопрос с изменением инпут на аутпут и наоборот. Например: в Кубе задал пинам аутпут изначально, после компиляции кубом, где-то в полученных сорсах появляется описание пинов и там будет присвоен их Mode -> Output.. Вопрос: если мне теперь надо их читать. Как поменять их мод на инпут. Просто этой структуре, в которой они описаны, присвоить Mode =Input? И если знаете, можете сказать где создается структура пинов и присваивается Mode.

Egor
Egor
8 лет назад

Похоже, что менять можно просто создав две функции инициализации, одну для инпут, другую для аутпут одних и тех же пинов. Вроде как ни на что не ругается, завтра проверю как оно на практике.
К слову сурсы инициализации пинов и всего такого Cube пихает прямо в main.c

Egor
Egor
8 лет назад

Еще один нюанс по поводу структуры буфера.
При передаче данных хосту в новой версии куба надо так же первым байтом передавать ID репорта!
Buffer[0]=0x03; // номер репорта - 3
Buffer[1] = d; // передаваемый байт
USBD_CUSTOM_HID_SendReport(hUsbDevice_0, Buffer, 2);
Так же будьте бдительны - при тех настройках дескриптора репорта, что выложены на сайте, размер передаваемых данных ограничен 1. Т.е. в моем примере d будет или 0, или 1. Чтобы передавать данные больше 1, надо дескриптор дополнить LOGICAL_MINIMUM и LOGICAL_MAXIMUM
0x85, 0x03, // REPORT_ID (4)
0x09, 0x03, // USAGE (Vendor Usage 4)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x82, // INPUT (Data,Var,Abs,Vol)

Ефим
Ефим
7 лет назад

передача на пк прошла успешно, правда промониторить получилось только Simple USB Logger , а вот чем отправить на контроллер команды??
а так в целом оба урока похожи на статью https://habrahabr.ru/post/208026/ , только в урезанном варианте (если я не ошибаюсь)

Aleksey
Aleksey
7 лет назад

Добрый день! Как можно посмотреть на ПК принятые байты с HID? Есть ли такие возможности в Windoцs? Посоветуйте готовый софт?

Andreno
7 лет назад

Спасибо, очень хорошая и полезная статья.

У меня только 2 вопросика

1 USBD_CUSTOM_HID_SendReport(hUsbDevice_0, dataToSend, 4); всегда возвращает usbok, даже когда мк не подключен к компьютеру. это нормально?

2 при однократной отправки данных в мк все нормально, но если Далее ещё раз отправить данные то в QT пишет no sened data. у меня по таймеру уходит массив, затем второй но он уже похоже не доходит, видимо я что-то не так делаю?

Слава Панас
Слава Панас
7 лет назад

У меня нет такой строки в проекте
USBD_HandleTypeDef *hUsbDevice_0;

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Не подскажите как теперь с этим быть, с тем что этого больше нет: USBD_HandleTypeDef *hUsbDevice_0;
возможно надо исправить на USBD_HandleTypeDef *hUsbDevice_FS;
или я не прав?

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Почините плз. статью, а то новый куб с ней не совместим и начинающим не понять что с этим делать. 🙂
Я не справился.. 🙁

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

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

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Вот мой проект, я пытался сделать как Вы говорите, но оно не отправляет данные 🙁
https://cloud.mail.ru/public/HWWF/Cg9Byb45W
Устройство определяется, но никакого обмена данными нет, заранее Спасибо.
Ваш Email не нашел.
Мой p_g_g@mail.ru

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Вот наваял проект на C# на базе LibUsbDotNet, но явно что то не то. Устройство видится, открывается, но из него ничего не читается.
https://cloud.mail.ru/public/LEvn/Z4XUpeFNV

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

еще непонятно с приемом данных, у Вас
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDevice_0->pClassData;
for (uint8_t i = 0; i Report_buf[i];
}

но в новом кубе hUsbDeviceFS описан не как указатель, а видимо как объект, не пойму как исправить эту строку..

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Да, Спасибо, мог бы сам догадаться 🙂 Да мозги уже кипят.. У Вас написано:
#define CUSTOM_HID_EPIN_ADDR 0x81
#define CUSTOM_HID_EPIN_SIZE 4

#define CUSTOM_HID_EPOUT_ADDR 0x01
#define CUSTOM_HID_EPOUT_SIZE 4
но ведь в этом случае у нас на вход и на выход будет EndPoint c номером 1, а енд поинта номер 2 не будет вообще, это правильно?
Вот проект, имхо все правильно, но оно не работает 🙁
Может что скажете..
https://cloud.mail.ru/public/GZN2/P5XZaMWza

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Нашел врага, это со стороны компа не работало 🙁
Спасибо за помощь!

PGG
PGG
Ответ на комментарий  Aveal
7 лет назад

Возникла проблема, возможно и другим будет интересно. При непрерывном обмене данными с хостом, каждые 5 секунд пропадает одна трансакция со стороны девайса. Причем этот эффект иногда пропадает при перевключении USB кабеля в комп, пропадает до следующего перевключения. В этом случае общая скорость передачи данных увеличивается почти в 2 раза. Дело не в таймауте со стороны приемника... Любопытен факт, что если убрать из описания репорта строку: 0x85, 0x01, // REPORT_ID (1) то начинает теряться одна трансакция в 1 секунду. Может что посоветуете? Обмен идет на максимальной длине репорта 0х40 в обе стороны.

PGG
PGG
Ответ на комментарий  Слава Панас
7 лет назад

А Вы нашли ответ на этот вопрос? А то я только на это наткнулся и пока идей нет 🙁

Павел
Павел
7 лет назад

Уважаемый автор, подскажите где брали информацию по работе с Custom HID при использовании CubeMX (какие функции дописывать, где лежит дескриптор CUSTOM_HID_ReportDesc_FS и т.д.)?
На сайте STMicroelectronics я не смог найти ответов. Нашел мануал на микроконтроллер (у меня STM32F3Discovery), но там только описание регистров.

Павел
Павел
Ответ на комментарий  Aveal
7 лет назад

На компьютере отслеживаю данные приходящие с МК в USBLyzer. С контроллера на комп всё приходит, но как теперь отправить данные на МК?
Есть готовые программы?

Пробовал USB HID Demonstrator Release 1.0.2 от STM. Устройство определяет но не получается изменить длину Output report и ничего не приходит на МК.

Иван
Иван
7 лет назад

Можно ссылку на обещанную программу для ПК ?

Михаил
6 лет назад

Здравствуйте!
Вопрос не про HID, но все же задам здесь.
Пытаюсь сделать устройство, которое определялось бы как usb принтер, дабы использовать его в качестве переходника для печати на китайских термопринтерах (для одного импортного прибора).
Создал в кубе Custom HID, переопределил дескрипторы в соответствии с http://www.usb.org/developers/docs/devclass_docs/usbprint11a021811.pdf
VID\PID взял из https://www.xmos.com/download/private/AN00126%3A-USB-Printer-Device-Class%282.0.2rc1%29.pdf
Теперь в диспетчере устройств оно определяется как "поддержка USB принтера" и драйвер устанавливается даже.
К сожалению не смог разобраться как собственно принимать данные от компа, какую функцию использовать и как.
Возможно вы сможете что-то посоветовать?

Михаил
Ответ на комментарий  Михаил
6 лет назад

Немного разобрался.
В файле usbd_customhid.c изменил функцию USBD_CUSTOM_HID_DataOut :

static uint8_t USBD_CUSTOM_HID_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
// USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)pdev->pClassData;
// ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
// hhid->Report_buf[1]);
USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR , buff,
USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
for (int i=0; i<USBD_CUSTOMHID_OUTREPORT_BUF_SIZE; i++)
{
if (buff[i]!=0)
{
send_to_uart1(buff[i]);
}
}
Теперь при печати файла с компьютера (или прибора) данные принимаются и пересылаются в serial принтер, но только не первый раз после прошивки или включения.В чем может быть еще дело?

Михаил
Ответ на комментарий  Aveal
6 лет назад

Нет, после ресета прошивка работает.
При печати в первый раз попадаем в функцию USBD_CUSTOM_HID_DataOut (где стоит точка остановки), но в buff ничего не записывается или записываются нули, если повторить. то все отрабатывает как надо.

ЭдМахалыч
ЭдМахалыч
6 лет назад

День добрый. Отличная статья! Подскажите, пожалуйста, что возвращает функция uint8_t USBD_CUSTOM_HID_SendReport(..., ..., ...) при передаче? Кол-во переданных байт или код ошибки. Заранее спасибо!

Владимир
Владимир
5 лет назад

Не нашёл третьей части Вашей статьи, там где программа для ПК.
Дайте ссылку пожалуйста.

49
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x