Вот и подоспела вторая часть из цикла статей, посвященных реализации класса 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.