add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
# The following five lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(knob_power_save)

View File

@@ -0,0 +1,47 @@
## Knob Power Save Example
This example demonstrates how to utilize the `knob` component in conjunction with the light sleep low-power mode.
* `knob` [Component Introduction](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/knob.html)
## Hardware
Any GPIO on any development board can be used in this example, for the default GPIO, please refer to the table below.
| Hardware | GPIO |
| :-----------------: | :---: |
| Encoder A (Phase A) | GPIO1 |
| Encoder B (Phase B) | GPIO2 |
## Build and Flash
Build the project and flash it to the board, then run the monitor tool to view the serial output:
* Run `. ./export.sh` to set IDF environment
* Run `idf.py set-target esp32xx` to set target chip
* Run `idf.py -p PORT flash monitor` to build, flash and monitor the project
(To exit the serial monitor, type `Ctrl-]`.)
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
## Example Output
```
I (316) pm: Frequency switching config: CPU_MAX: 80, APB_MAX: 80, APB_MIN: 10, Light sleep: ENABLED
I (322) sleep: Code start at 0x42000020, total 106515, data start at 0x3c020020, total 46384 Bytes
I (331) gpio: GPIO[1]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (341) gpio: GPIO[2]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (350) Knob: Iot Knob Config Succeed, encoder A:1, encoder B:2, direction:0, Version: 0.1.4
I (359) main_task: Returned from app_main()
I (1503) knob_power_save: knob event KNOB_LEFT, -1
I (1503) knob_power_save: Wake up from light sleep, reason 7
I (1691) knob_power_save: knob event KNOB_LEFT, -2
I (1691) knob_power_save: Wake up from light sleep, reason 7
I (1860) knob_power_save: knob event KNOB_LEFT, -3
I (1860) knob_power_save: Wake up from light sleep, reason 7
I (1940) knob_power_save: knob event KNOB_LEFT, -4
I (1940) knob_power_save: Wake up from light sleep, reason 7
I (2017) knob_power_save: knob event KNOB_LEFT, -5
I (2017) knob_power_save: Wake up from light sleep, reason 7
```

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "knob_power_save.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,74 @@
menu "Example Configuration"
choice EXAMPLE_MAX_CPU_FREQ
prompt "Maximum CPU frequency"
default EXAMPLE_MAX_CPU_FREQ_80 if !IDF_TARGET_ESP32H2
default EXAMPLE_MAX_CPU_FREQ_96 if IDF_TARGET_ESP32H2
depends on PM_ENABLE
help
Maximum CPU frequency to use for dynamic frequency scaling.
config EXAMPLE_MAX_CPU_FREQ_80
bool "80 MHz"
config EXAMPLE_MAX_CPU_FREQ_96
bool "96 MHz"
depends on IDF_TARGET_ESP32H2
config EXAMPLE_MAX_CPU_FREQ_120
bool "120 MHz"
depends on IDF_TARGET_ESP32C2
config EXAMPLE_MAX_CPU_FREQ_160
bool "160 MHz"
depends on !IDF_TARGET_ESP32C2
config EXAMPLE_MAX_CPU_FREQ_240
bool "240 MHz"
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
endchoice
config EXAMPLE_MAX_CPU_FREQ_MHZ
int
default 80 if EXAMPLE_MAX_CPU_FREQ_80
default 96 if EXAMPLE_MAX_CPU_FREQ_96
default 120 if EXAMPLE_MAX_CPU_FREQ_120
default 160 if EXAMPLE_MAX_CPU_FREQ_160
default 240 if EXAMPLE_MAX_CPU_FREQ_240
choice EXAMPLE_MIN_CPU_FREQ
prompt "Minimum CPU frequency"
default EXAMPLE_MIN_CPU_FREQ_10M if !IDF_TARGET_ESP32H2
default EXAMPLE_MIN_CPU_FREQ_32M if IDF_TARGET_ESP32H2
depends on PM_ENABLE
help
Minimum CPU frequency to use for dynamic frequency scaling.
Should be set to XTAL frequency or XTAL frequency divided by integer.
config EXAMPLE_MIN_CPU_FREQ_40M
bool "40 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
config EXAMPLE_MIN_CPU_FREQ_20M
bool "20 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
config EXAMPLE_MIN_CPU_FREQ_10M
bool "10 MHz (use with 40MHz XTAL)"
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
config EXAMPLE_MIN_CPU_FREQ_26M
bool "26 MHz (use with 26MHz XTAL)"
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_13M
bool "13 MHz (use with 26MHz XTAL)"
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_32M
bool "32 MHz (use with 32MHz XTAL)"
depends on IDF_TARGET_ESP32H2
depends on XTAL_FREQ_32 || XTAL_FREQ_AUTO
endchoice
config EXAMPLE_MIN_CPU_FREQ_MHZ
int
default 40 if EXAMPLE_MIN_CPU_FREQ_40M
default 20 if EXAMPLE_MIN_CPU_FREQ_20M
default 10 if EXAMPLE_MIN_CPU_FREQ_10M
default 26 if EXAMPLE_MIN_CPU_FREQ_26M
default 13 if EXAMPLE_MIN_CPU_FREQ_13M
default 32 if EXAMPLE_MIN_CPU_FREQ_32M
endmenu

View File

@@ -0,0 +1,6 @@
version: "0.0.1"
dependencies:
idf: ">=4.4"
knob:
version: "*"
override_path: "../../../../components/knob"

View File

@@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_pm.h"
#include "iot_knob.h"
#include "esp_sleep.h"
#include "esp_idf_version.h"
#define ENCODER_A_GPIO 1
#define ENCODER_B_GPIO 2
static const char *TAG = "knob_power_save";
static knob_handle_t knob = NULL;
const char *knob_event_table[] = {
"KNOB_LEFT",
"KNOB_RIGHT",
"KNOB_H_LIM",
"KNOB_L_LIM",
"KNOB_ZERO",
};
static void knob_event_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "knob event %s, %d", knob_event_table[(knob_event_t)data], iot_knob_get_count_value(knob));
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) {
ESP_LOGI(TAG, "Wake up from light sleep, reason %d", cause);
}
}
static void knob_init(uint32_t encoder_a, uint32_t encoder_b)
{
knob_config_t cfg = {
.default_direction = 0,
.gpio_encoder_a = encoder_a,
.gpio_encoder_b = encoder_b,
#if CONFIG_PM_ENABLE
.enable_power_save = true,
#endif
};
knob = iot_knob_create(&cfg);
assert(knob);
esp_err_t err = iot_knob_register_cb(knob, KNOB_LEFT, knob_event_cb, (void *)KNOB_LEFT);
err |= iot_knob_register_cb(knob, KNOB_RIGHT, knob_event_cb, (void *)KNOB_RIGHT);
err |= iot_knob_register_cb(knob, KNOB_H_LIM, knob_event_cb, (void *)KNOB_H_LIM);
err |= iot_knob_register_cb(knob, KNOB_L_LIM, knob_event_cb, (void *)KNOB_L_LIM);
err |= iot_knob_register_cb(knob, KNOB_ZERO, knob_event_cb, (void *)KNOB_ZERO);
ESP_ERROR_CHECK(err);
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
void power_save_init(void)
{
esp_pm_config_t pm_config = {
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
.light_sleep_enable = true
#endif
};
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
}
#else
void power_save_init(void)
{
#if CONFIG_IDF_TARGET_ESP32
esp_pm_config_esp32_t pm_config = {
#elif CONFIG_IDF_TARGET_ESP32S2
esp_pm_config_esp32s2_t pm_config = {
#elif CONFIG_IDF_TARGET_ESP32C3
esp_pm_config_esp32c3_t pm_config = {
#elif CONFIG_IDF_TARGET_ESP32S3
esp_pm_config_esp32s3_t pm_config = {
#elif CONFIG_IDF_TARGET_ESP32C2
esp_pm_config_esp32c2_t pm_config = {
#endif
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
.light_sleep_enable = true
#endif
};
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
}
#endif
void app_main(void)
{
power_save_init();
knob_init(ENCODER_A_GPIO, ENCODER_B_GPIO);
}

View File

@@ -0,0 +1,9 @@
# Enable support for power management
CONFIG_PM_ENABLE=y
# Enable tickless idle mode
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
# Put related source code in IRAM
CONFIG_PM_SLP_IRAM_OPT=y
CONFIG_PM_RTOS_IDLE_OPT=y
# Use 1000Hz freertos tick to lower sleep time threshold
CONFIG_FREERTOS_HZ=1000

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(usb_surface_dial)

View File

@@ -0,0 +1,49 @@
## USB HID Surface Dial
This example shows how to use the ESP32-Sx USB function to emulate a Windows knob that allows volume control, page up and down, and more.
## Hardware Required
![surface_dial_physical](_static/surface_dial_physical.jpg)
- Any ESP32-Sx development board with **knob** functionality.
- The knob can be a series of rotary encoders with push function such as EC11
- It is also possible to use three buttons that simulate: press, left turn, right turn
- Hardware Connection
- GPIO19 to USB_D-
- GPIO20 to USB_D+
- GPIO42 to EC11_E
- GPIO1 to EC11_A
- GPIO2 to EC11_C
![schematic](_static/schematic.png)
![schematic](_static/package.png)
## Build and Flash
1. Set up the `ESP-IDF` environment variablesyou can refer [Set up the environment variables](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#step-4-set-up-the-environment-variables), Linux can using:
```
. $HOME/esp/esp-idf/export.sh
```
2. Set ESP-IDF build target to `esp32s2` or `esp32s3`
```bash
idf.py set-target esp32s2
```
3. Build, Flash, output log
```bash
idf.py build flash monitor
```
## How To Use
* Connect the USB to the Windows USB port and wait for the USB device to finish installing
* Press and hold the button to wake up the Windows wheel
![Surface dial](_static/surface_dial.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 KiB

View File

@@ -0,0 +1,8 @@
idf_component_register(
SRCS "main.c" "usb_descriptors.c"
INCLUDE_DIRS "."
)
idf_component_get_property(tusb_lib leeebo__tinyusb_src COMPONENT_LIB)
cmake_policy(SET CMP0079 NEW)
target_link_libraries(${tusb_lib} PRIVATE ${COMPONENT_LIB})

View File

@@ -0,0 +1,24 @@
menu "Example Configuration"
choice DEVELOPMENT_BOARD_SELECTION
prompt "Select the development board you are using"
default ESP32_S3_USB_OTG if IDF_TARGET_ESP32S3
default ESP32_S2_GENERIC if IDF_TARGET_ESP32S2
help
Select this option to choose the board for the example.
config ESP32_S3_USB_OTG
bool "ESP32 S3 USB OTG"
depends on IDF_TARGET_ESP32S3
config ESP32_S3_GENERIC
bool "ESP32 S3 GENERIC"
depends on IDF_TARGET_ESP32S3
config ESP32_S2_GENERIC
bool "ESP32 S2 GENERIC"
depends on IDF_TARGET_ESP32S2
endchoice
endmenu

View File

@@ -0,0 +1,19 @@
## IDF Component Manager Manifest File
dependencies:
## Required IDF version
idf:
version: ">=4.4.0"
espressif/button:
version: ">=2.3.0"
override_path: "../../../../../components/button"
leeebo/tinyusb_src:
version: ">=0.0.4"
espressif/knob:
version: ">=0.1.0"
override_path: "../../../../../components/knob"
espressif/esp32_s3_usb_otg:
version: "^1.5.1"
rules:
- if: "target in [esp32s3]"
lvgl/lvgl: #temp to workaround bsp issue
version: "9.2.0"

View File

@@ -0,0 +1,154 @@
/*
* SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_private/usb_phy.h"
#include "sdkconfig.h"
#include "tusb.h"
#include "tusb_config.h"
#include "iot_button.h"
#include "iot_knob.h"
#ifdef CONFIG_ESP32_S3_USB_OTG
#include "bsp/esp-bsp.h"
#endif
#define TAG "DIAL"
#define GPIO_BUTTON 42
#define GPIO_KNOB_A 1
#define GPIO_KNOB_B 2
#define REPORT_ID 1
#define DIAL_R 0xC8
#define DIAL_L 0x38
#define DIAL_PRESS 0x01
#define DIAL_RELEASE 0x00
#define DIAL_R_F 0x14
#define DIAL_L_F 0xEC
static button_handle_t s_btn = 0;
static knob_handle_t s_knob = 0;
static usb_phy_handle_t s_phy_hdl;
static void usb_phy_init(void)
{
// Configure USB PHY
usb_phy_config_t phy_conf = {
.controller = USB_PHY_CTRL_OTG,
.otg_mode = USB_OTG_MODE_DEVICE,
.target = USB_PHY_TARGET_INT,
};
usb_new_phy(&phy_conf, &s_phy_hdl);
}
static void surface_dial_report(uint8_t key)
{
uint8_t _dial_report[2];
_dial_report[0] = key;
_dial_report[1] = 0;
if (key == DIAL_L || key == DIAL_L_F) {
_dial_report[1] = 0xff;
}
tud_hid_report(REPORT_ID, _dial_report, 2);
}
static void _button_press_down_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "BTN: BUTTON_PRESS_DOWN");
surface_dial_report(DIAL_PRESS);
}
static void _button_press_up_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "BTN: BUTTON_PRESS_UP[%"PRIu32"]", iot_button_get_ticks_time((button_handle_t)arg));
surface_dial_report(DIAL_RELEASE);
}
static void _knob_right_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "KONB: KONB_RIGHT,count_value:%"PRId32"", iot_knob_get_count_value((button_handle_t)arg));
surface_dial_report(DIAL_L);
}
static void _knob_left_cb(void *arg, void *data)
{
ESP_LOGI(TAG, "KONB: KONB_LEFT,count_value:%"PRId32"", iot_knob_get_count_value((button_handle_t)arg));
surface_dial_report(DIAL_R);
}
static void _button_init(void)
{
button_config_t cfg = {
.type = BUTTON_TYPE_GPIO,
.long_press_time = 1000,
.short_press_time = 200,
.gpio_button_config = {
.gpio_num = GPIO_BUTTON,
.active_level = 0,
},
};
s_btn = iot_button_create(&cfg);
iot_button_register_cb(s_btn, BUTTON_PRESS_DOWN, _button_press_down_cb, NULL);
iot_button_register_cb(s_btn, BUTTON_PRESS_UP, _button_press_up_cb, NULL);
}
static void _knob_init(void)
{
knob_config_t cfg = {
.default_direction = 0,
.gpio_encoder_a = GPIO_KNOB_A,
.gpio_encoder_b = GPIO_KNOB_B,
};
s_knob = iot_knob_create(&cfg);
if (NULL == s_knob) {
ESP_LOGE(TAG, "knob create failed");
}
iot_knob_register_cb(s_knob, KNOB_LEFT, _knob_left_cb, NULL);
iot_knob_register_cb(s_knob, KNOB_RIGHT, _knob_right_cb, NULL);
}
void app_main(void)
{
#ifdef CONFIG_ESP32_S3_USB_OTG
bsp_usb_mode_select_device();
#endif
usb_phy_init();
_button_init();
_knob_init();
tusb_init();
size_t timeout_tick = pdMS_TO_TICKS(10);
while (1) {
tud_task();
vTaskDelay(timeout_tick);
}
}
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)
{
// TODO not Implemented
(void) instance;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
return 0;
}
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)
{
(void) instance;
(void) report_id;
(void) report_type;
(void) buffer;
(void) bufsize;
}

View File

@@ -0,0 +1,114 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#ifndef _TUSB_CONFIG_H_
#define _TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
// RHPort number used for device can be defined by board.mk, default to port 0
#ifndef BOARD_TUD_RHPORT
#define BOARD_TUD_RHPORT 0
#endif
// RHPort max operational speed can defined by board.mk
#ifndef BOARD_TUD_MAX_SPEED
#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_FREERTOS
#endif
// Espressif IDF requires "freertos/" prefix in include path
#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
#define CFG_TUSB_OS_INC_PATH freertos/
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// Enable Device stack
#define CFG_TUD_ENABLED 1
// Default is max speed that hardware controller could support with on-chip PHY
#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
#define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_HID 1
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_EP_BUFSIZE 8
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@@ -0,0 +1,200 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// HID Report Descriptor
//--------------------------------------------------------------------+
// Keyboard Report Descriptor Template
#define TUD_HID_REPORT_DESC_DIAL(...) \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( 0x0e ) ,\
HID_COLLECTION ( HID_COLLECTION_APPLICATION ) ,\
/* Report ID if any */\
__VA_ARGS__ \
HID_USAGE_PAGE ( HID_USAGE_PAGE_DIGITIZER ) ,\
HID_USAGE ( 0x21 ) ,\
HID_COLLECTION ( HID_COLLECTION_PHYSICAL ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_BUTTON ) ,\
HID_USAGE ( 1 ) ,\
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 1 ) ,\
HID_LOGICAL_MIN ( 0 ) ,\
HID_LOGICAL_MAX ( 1 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
HID_USAGE ( HID_USAGE_DESKTOP_DIAL ) ,\
HID_REPORT_COUNT ( 1 ) ,\
HID_REPORT_SIZE ( 15 ) ,\
HID_UNIT_EXPONENT( 15 ) ,\
HID_UNIT ( 0x14 ) ,\
HID_PHYSICAL_MIN_N ( -3600, 2 ) ,\
HID_PHYSICAL_MAX_N ( 3600, 2 ) ,\
HID_LOGICAL_MIN_N ( -3600, 2 ) ,\
HID_LOGICAL_MAX_N ( 3600, 2 ) ,\
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_RELATIVE) ,\
HID_COLLECTION_END ,\
HID_COLLECTION_END \
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_DIAL(HID_REPORT_ID(1))
};
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
{
if (itf == 0) {
return desc_hid_report;
}
return NULL;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum {
ITF_NUM_HID,
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_DESC_LEN )
#define EPNUM_HID 0x81
uint8_t const desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 4, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const *string_desc_arr [] = {
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
"Surface Dial", // 4: Interface 1 String
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if (index == 0) {
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
} else {
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) {
return NULL;
}
const char *str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t) strlen(str);
if (chr_count > 31) {
chr_count = 31;
}
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
return _desc_str;
}

View File

@@ -0,0 +1,2 @@
CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESP32_S3_USB_OTG=y