Skip to content
C
/********************************************************************************
 * @author: iLx1
 * @date: 2024-09-10 12:58:42
 * @filepath: \multi_pad_esp\hardware\usb_hw\usb_hw.c
 * @description: usb 硬件配置
 * @email: colorful_ilx1@163.com
 * @copyright: Copyright (c) iLx1, All Rights Reserved.
 ********************************************************************************/

#include "usb_hw.h"

#include <stdio.h>

#include "class/cdc/cdc_device.h"
#include "class/hid/hid_device.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "report_desc.h"
#include "sdkconfig.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"

static const char *TAG = "cdc+hid";

// cdc 接收缓冲数组
static uint8_t cdc_rec_buff[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];

/********************************************************************************
 * @brief: 字符串描述符
 ********************************************************************************/
static const char *hid_string_descriptor[6] = {
    // array of pointer to string descriptors
    (char[]){0x09, 0x04},  // 0: is supported language is English (0x0409)
    "TinyUSB",             // 1: Manufacturer
    "TinyUSB Device",      // 2: Product
    "123456",              // 3: Serials, should use chip ID
    "HID interface",       // 4: HID
    "My debugging CDC interface",  // 5: CDC
};

/********************************************************************************
 * @brief: 配置描述符
 ********************************************************************************/
static const uint8_t hid_configuration_descriptor[] = {
    // Configuration number, interface count, string index, total length,
    // attribute, power in mA
    TUD_CONFIG_DESCRIPTOR(1, 3, 0, TUSB_DESC_TOTAL_LEN,
                          TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),

    // Interface number, string index, boot protocol, report descriptor len, EP
    // In address, size & polling interval
    TUD_HID_DESCRIPTOR(0, 4, false, USBD_HID_REPORT_DESC_SIZE, EPNUM_HID, 16,
                       10),

    // Interface number, string index, EP notification address and size, EP data
    // address (out, in) and size.
    TUD_CDC_DESCRIPTOR(1, 5, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN,
                       64),
};

/********************************************************************************
 * @brief: 获取报告描述符回调
 * @param {uint8_t} instance
 * @return {*}
 ********************************************************************************/
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance) {
  return hid_desc_report;
}

/********************************************************************************
 * @brief: 获取描述符回调
 * @return {*}
 ********************************************************************************/
uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id,
                               hid_report_type_t report_type, uint8_t *buffer,
                               uint16_t reqlen) {
  (void)instance;
  (void)report_id;
  (void)report_type;
  (void)buffer;
  (void)reqlen;

  return 0;
}

/********************************************************************************
 * @brief: SET_REPORT 控制请求回调
 * @return {*}
 ********************************************************************************/
void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id,
                           hid_report_type_t report_type, uint8_t const *buffer,
                           uint16_t bufsize) {}

/********************************************************************************
 * @brief: hid 初始化配置
 * @return {*}
 ********************************************************************************/
static void hid_device_init(void) {
  ESP_LOGI(TAG, "USB initialization");
  const tinyusb_config_t tusb_cfg = {
      .device_descriptor = NULL,
      .string_descriptor = hid_string_descriptor,
      .string_descriptor_count =
          sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]),
      .external_phy = false,
      .configuration_descriptor = hid_configuration_descriptor,
  };

  ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
  ESP_LOGI(TAG, "USB initialization DONE");
}

/********************************************************************************
 * @brief: 发送 HID 键盘数据
 * @return {*}
 ********************************************************************************/
static void app_send_hid_demo(void) {
  // 发送键盘按键
  ESP_LOGI(TAG, "Sending Keyboard report");
  uint8_t keycode[6] = {HID_KEY_A};
  tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, keycode);
  vTaskDelay(pdMS_TO_TICKS(50));
  tud_hid_keyboard_report(HID_ITF_PROTOCOL_KEYBOARD, 0, NULL);
}

/********************************************************************************
 * @brief: 接收数据回调
 * @param {int} itf
 * @param {cdcacm_event_t} *event
 * @return {*}
 ********************************************************************************/
static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) {
  /* initialization */
  size_t rx_size = 0;

  /* read */
  esp_err_t ret = tinyusb_cdcacm_read(itf, cdc_rec_buff,
                                      CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
  if (ret == ESP_OK) {
    ESP_LOGI(TAG, "Data from channel %d:", itf);
    ESP_LOG_BUFFER_HEXDUMP(TAG, cdc_rec_buff, rx_size, ESP_LOG_INFO);
  } else {
    ESP_LOGE(TAG, "Read error");
  }

  /* write back */
  tinyusb_cdcacm_write_queue(itf, cdc_rec_buff, rx_size);
  tinyusb_cdcacm_write_flush(itf, 0);
}

/********************************************************************************
 * @brief: DTR RTS 变更
 * @param {int} itf
 * @param {cdcacm_event_t} *event
 * @return {*}
 ********************************************************************************/
static void tinyusb_cdc_line_state_changed_callback(int itf,
                                                    cdcacm_event_t *event) {
  int dtr = event->line_state_changed_data.dtr;
  int rts = event->line_state_changed_data.rts;
  ESP_LOGI(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr,
           rts);
}

/********************************************************************************
 * @brief: cdc 设备初始化
 * @return {*}
 ********************************************************************************/
void cdc_device_init(void) {
  tinyusb_config_cdcacm_t acm_cfg = {
      .usb_dev = TINYUSB_USBDEV_0,
      .cdc_port = TINYUSB_CDC_ACM_0,
      .rx_unread_buf_sz = 64,
      .callback_rx =
          &tinyusb_cdc_rx_callback,  // the first way to register a callback
      .callback_rx_wanted_char = NULL,
      .callback_line_state_changed = &tinyusb_cdc_line_state_changed_callback,
      .callback_line_coding_changed = NULL};

  ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg));
  /* the second way to register a callback */
  ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(
      TINYUSB_CDC_ACM_0, CDC_EVENT_LINE_STATE_CHANGED,
      &tinyusb_cdc_line_state_changed_callback));

#if (CONFIG_TINYUSB_CDC_COUNT > 1)
  acm_cfg.cdc_port = TINYUSB_CDC_ACM_1;
  ESP_ERROR_CHECK(tusb_cdc_acm_init(&acm_cfg));
  ESP_ERROR_CHECK(tinyusb_cdcacm_register_callback(
      TINYUSB_CDC_ACM_1, CDC_EVENT_LINE_STATE_CHANGED,
      &tinyusb_cdc_line_state_changed_callback));
#endif
}

/********************************************************************************
 * @brief: usb 设备初始化
 * @return {*}
 ********************************************************************************/
void usb_device_init(void) {
  // 初始化 boot 按键
  const gpio_config_t boot_button_config = {
      .pin_bit_mask = BIT64(APP_BUTTON),
      .mode = GPIO_MODE_INPUT,
      .intr_type = GPIO_INTR_DISABLE,
      .pull_up_en = true,
      .pull_down_en = false,
  };
  ESP_ERROR_CHECK(gpio_config(&boot_button_config));
  hid_device_init();
  cdc_device_init();

  while (1) {
    // 判断 tinyusb 挂载
    if (tud_mounted()) {
      static bool send_hid_data = true;
      if (send_hid_data) {
        app_send_hid_demo();
      }
      send_hid_data = !gpio_get_level(APP_BUTTON);
    }
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

Released under the GPL License.