mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
usb: Added MIDI example + fixed TinyUSB MIDI config
Closes https://github.com/espressif/esp-idf/issues/8541
This commit is contained in:
parent
8078092fae
commit
2dfaec3106
@ -112,6 +112,13 @@ menu "TinyUSB Stack"
|
||||
default "Espressif HID Device"
|
||||
help
|
||||
Name of the HID device
|
||||
|
||||
config TINYUSB_DESC_MIDI_STRING
|
||||
depends on TINYUSB_MIDI_ENABLED
|
||||
string "MIDI Device String"
|
||||
default "Espressif MIDI Device"
|
||||
help
|
||||
Name of the MIDI device
|
||||
endmenu # "Descriptor configuration"
|
||||
|
||||
menu "Massive Storage Class (MSC)"
|
||||
@ -160,6 +167,14 @@ menu "TinyUSB Stack"
|
||||
help
|
||||
CDC FIFO size of TX channel.
|
||||
endmenu # "Communication Device Class"
|
||||
|
||||
menu "MIDI"
|
||||
config TINYUSB_MIDI_ENABLED
|
||||
bool "Enable TinyUSB MIDI feature"
|
||||
default n
|
||||
help
|
||||
Enable TinyUSB MIDI feature.
|
||||
endmenu # "MIDI"
|
||||
endif # TINYUSB
|
||||
|
||||
endmenu # "TinyUSB Stack"
|
||||
|
@ -11,7 +11,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define USB_ESPRESSIF_VID 0x303A
|
||||
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 7
|
||||
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 8
|
||||
|
||||
typedef enum{
|
||||
TINYUSB_USBDEV_0,
|
||||
|
@ -85,6 +85,11 @@ extern "C" {
|
||||
// HID buffer size Should be sufficient to hold ID (if any) + Data
|
||||
#define CFG_TUD_HID_BUFSIZE CONFIG_TINYUSB_HID_BUFSIZE
|
||||
|
||||
#define CFG_TUD_MIDI_EP_BUFSIZE 64
|
||||
#define CFG_TUD_MIDI_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
|
||||
#define CFG_TUD_MIDI_RX_BUFSIZE 64
|
||||
#define CFG_TUD_MIDI_TX_BUFSIZE 64
|
||||
|
||||
// Enabled device class driver
|
||||
#if defined(CONFIG_TINYUSB_CDC_COUNT)
|
||||
#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_COUNT
|
||||
|
@ -14,7 +14,8 @@
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | _PID_MAP(MIDI, 3))
|
||||
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) ) //| _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
|
||||
|
||||
/**** TinyUSB default ****/
|
||||
tusb_desc_device_t descriptor_tinyusb = {
|
||||
@ -55,7 +56,8 @@ tusb_desc_strarray_device_t descriptor_str_tinyusb = {
|
||||
"123456", // 3: Serials, should use chip ID
|
||||
"TinyUSB CDC", // 4: CDC Interface
|
||||
"TinyUSB MSC", // 5: MSC Interface
|
||||
"TinyUSB HID" // 6: HID
|
||||
"TinyUSB HID", // 6: HID
|
||||
"TinyUSB MIDI" // 7: MIDI
|
||||
};
|
||||
/* End of TinyUSB default */
|
||||
|
||||
@ -125,6 +127,12 @@ tusb_desc_strarray_device_t descriptor_str_kconfig = {
|
||||
"",
|
||||
#endif
|
||||
|
||||
#if CONFIG_TINYUSB_MIDI_ENABLED
|
||||
CONFIG_TINYUSB_DESC_MIDI_STRING // 7: MIDI
|
||||
#else
|
||||
"",
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
//------------- HID Report Descriptor -------------//
|
||||
@ -137,34 +145,66 @@ enum {
|
||||
|
||||
//------------- Configuration Descriptor -------------//
|
||||
enum {
|
||||
# if CFG_TUD_CDC
|
||||
#if CFG_TUD_CDC
|
||||
ITF_NUM_CDC = 0,
|
||||
ITF_NUM_CDC_DATA,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_CDC > 1
|
||||
#if CFG_TUD_CDC > 1
|
||||
ITF_NUM_CDC1,
|
||||
ITF_NUM_CDC1_DATA,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_MSC
|
||||
#if CFG_TUD_MSC
|
||||
ITF_NUM_MSC,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_HID
|
||||
#if CFG_TUD_HID
|
||||
ITF_NUM_HID,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MIDI
|
||||
ITF_NUM_MIDI,
|
||||
ITF_NUM_MIDI_STREAMING,
|
||||
#endif
|
||||
|
||||
ITF_NUM_TOTAL
|
||||
};
|
||||
|
||||
enum {
|
||||
TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN + CFG_TUD_MSC * TUD_MSC_DESC_LEN +
|
||||
CFG_TUD_HID * TUD_HID_DESC_LEN
|
||||
TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN +
|
||||
CFG_TUD_CDC * TUD_CDC_DESC_LEN +
|
||||
CFG_TUD_MSC * TUD_MSC_DESC_LEN +
|
||||
CFG_TUD_HID * TUD_HID_DESC_LEN +
|
||||
CFG_TUD_MIDI * TUD_MIDI_DESC_LEN
|
||||
};
|
||||
|
||||
#define EPNUM_MSC ((CFG_TUD_CDC * 2) + 1)
|
||||
#define EPNUM_HID (EPNUM_MSC + 1)
|
||||
//------------- USB Endpoint numbers -------------//
|
||||
enum {
|
||||
// Available USB Endpoints: 5 IN/OUT EPs and 1 IN EP
|
||||
EP_EMPTY = 0,
|
||||
#if CFG_TUD_CDC
|
||||
EPNUM_0_CDC_NOTIF,
|
||||
EPNUM_0_CDC,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_CDC > 1
|
||||
EPNUM_1_CDC_NOTIF,
|
||||
EPNUM_1_CDC,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MSC
|
||||
EPNUM_MSC,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_HID
|
||||
EPNUM_HID,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MIDI
|
||||
EPNUM_MIDI,
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CFG_TUD_HID //HID Report Descriptor
|
||||
uint8_t const desc_hid_report[] = {
|
||||
@ -177,25 +217,30 @@ uint8_t const descriptor_cfg_kconfig[] = {
|
||||
// interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
# if CFG_TUD_CDC
|
||||
#if CFG_TUD_CDC
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x81, 8, 0x02, 0x82, CFG_TUD_CDC_EP_BUFSIZE),
|
||||
# endif
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, 0x80 | EPNUM_0_CDC_NOTIF, 8, EPNUM_0_CDC, 0x80 | EPNUM_0_CDC, CFG_TUD_CDC_EP_BUFSIZE),
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_CDC > 1
|
||||
#if CFG_TUD_CDC > 1
|
||||
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, 4, 0x83, 8, 0x04, 0x84, CFG_TUD_CDC_EP_BUFSIZE),
|
||||
# endif
|
||||
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, 4, 0x80 | EPNUM_1_CDC_NOTIF, 8, EPNUM_1_CDC, 0x80 | EPNUM_1_CDC, CFG_TUD_CDC_EP_BUFSIZE),
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_MSC
|
||||
#if CFG_TUD_MSC
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC, 0x80 | EPNUM_MSC, 64), // highspeed 512
|
||||
# endif
|
||||
#endif
|
||||
|
||||
# if CFG_TUD_HID
|
||||
#if CFG_TUD_HID
|
||||
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(ITF_NUM_HID, 6, HID_PROTOCOL_NONE, sizeof(desc_hid_report), 0x80 | EPNUM_HID, 16, 10)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_MIDI
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MIDI_DESCRIPTOR(ITF_NUM_MIDI, 7, EPNUM_MIDI, 0x80 | EPNUM_MIDI, 64) // highspeed 512
|
||||
#endif
|
||||
};
|
||||
|
||||
/* End of Kconfig driven Descriptor */
|
||||
|
@ -4,12 +4,16 @@ USB Device Driver
|
||||
|
||||
{IDF_TARGET_USB_DP_GPIO_NUM:default="20"}
|
||||
{IDF_TARGET_USB_DM_GPIO_NUM:default="19"}
|
||||
{IDF_TARGET_USB_EP_NUM:default="6"}
|
||||
{IDF_TARGET_USB_EP_NUM_INOUT:default="5"}
|
||||
{IDF_TARGET_USB_EP_NUM_IN:default="1"}
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The driver allows users to use {IDF_TARGET_NAME} chips to develop USB devices on top the TinyUSB stack. TinyUSB is integrating with ESP-IDF to provide USB features of the framework. Using this driver the chip works as a composite device supporting to represent several USB devices simultaneously. Currently, only the communications device class (CDC) type of the device with the ACM (Abstract Control Model) subclass is supported.
|
||||
The driver allows users to use {IDF_TARGET_NAME} chips to develop USB devices on a top of the TinyUSB stack. TinyUSB is integrated with ESP-IDF to provide USB features of the framework. Using this driver the chip works as a composite device supporting several USB devices simultaneously. Currently, only the Communications Device Class (CDC) type of the device with the Abstract Control Model (ACM) subclass and the Musical Instrument Digital Interface (MIDI) are supported.
|
||||
|
||||
Our USB-OTG implementation is limited to {IDF_TARGET_USB_EP_NUM} number of USB endpoints ({IDF_TARGET_USB_EP_NUM_INOUT} IN/OUT endpoints and {IDF_TARGET_USB_EP_NUM_IN} IN endpoint) - find more information in `technical reference manual <{IDF_TARGET_TRM_EN_URL}>`_.
|
||||
|
||||
Features
|
||||
--------
|
||||
@ -17,6 +21,7 @@ Features
|
||||
- Configuration of device and string USB descriptors
|
||||
- USB Serial Device (CDC-ACM)
|
||||
- Input and output through USB Serial Device
|
||||
- USB MIDI Device
|
||||
|
||||
|
||||
Hardware USB Connection
|
||||
@ -134,6 +139,8 @@ The table below describes the code examples available in the directory :example:
|
||||
- How to set up {IDF_TARGET_NAME} chip to work as a Generic USB Device with a user-defined descriptor
|
||||
* - :example:`peripherals/usb/device/tusb_serial_device`
|
||||
- How to set up {IDF_TARGET_NAME} chip to work as a USB Serial Device
|
||||
* - :example:`peripherals/usb/device/tusb_midi`
|
||||
- How to set up {IDF_TARGET_NAME} chip to work as a USB MIDI Device
|
||||
|
||||
|
||||
API Reference
|
||||
|
6
examples/peripherals/usb/device/tusb_midi/CMakeLists.txt
Normal file
6
examples/peripherals/usb/device/tusb_midi/CMakeLists.txt
Normal 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.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(tusb_midi)
|
83
examples/peripherals/usb/device/tusb_midi/README.md
Normal file
83
examples/peripherals/usb/device/tusb_midi/README.md
Normal file
@ -0,0 +1,83 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# TinyUSB MIDI Device Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to set up ESP chip to work as a USB MIDI Device.
|
||||
It outputs a MIDI note sequence via the native USB port.
|
||||
|
||||
As a USB stack, a TinyUSB component is used.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any ESP board that have USB-OTG supported.
|
||||
|
||||
#### Pin Assignment
|
||||
|
||||
_Note:_ In case your board doesn't have micro-USB connector connected to USB-OTG peripheral, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below.
|
||||
|
||||
See common pin assignments for USB Device examples from [upper level](../../README.md#common-pin-assignments).
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```bash
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## MIDI output
|
||||
|
||||
You can use several programs on your computer to listen to the ESP's MIDI output depending on your operating system, e.g.:
|
||||
|
||||
* Windows: `MIDI-OX`
|
||||
* Linux: `qsynth` with `qjackctl`
|
||||
* macOS: `SimpleSynth`
|
||||
|
||||
## Example Output
|
||||
|
||||
After the flashing you should see the output at idf monitor:
|
||||
|
||||
```
|
||||
I (285) example: USB initialization
|
||||
I (285) tusb_desc:
|
||||
┌─────────────────────────────────┐
|
||||
│ USB Device Descriptor Summary │
|
||||
├───────────────────┬─────────────┤
|
||||
│bDeviceClass │ 0 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bDeviceSubClass │ 0 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bDeviceProtocol │ 0 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bMaxPacketSize0 │ 64 │
|
||||
├───────────────────┼─────────────┤
|
||||
│idVendor │ 0x303a │
|
||||
├───────────────────┼─────────────┤
|
||||
│idProduct │ 0x4008 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bcdDevice │ 0x100 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iManufacturer │ 0x1 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iProduct │ 0x2 │
|
||||
├───────────────────┼─────────────┤
|
||||
│iSerialNumber │ 0x3 │
|
||||
├───────────────────┼─────────────┤
|
||||
│bNumConfigurations │ 0x1 │
|
||||
└───────────────────┴─────────────┘
|
||||
I (455) TinyUSB: TinyUSB Driver installed
|
||||
I (465) example: USB initialization DONE
|
||||
```
|
||||
|
||||
Disconnect UART-to-USB port and connect the native USB port to a computer then the device should show up as a USB MIDI Device while outputting notes.
|
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "tusb_midi_main.c"
|
||||
INCLUDE_DIRS "."
|
||||
)
|
105
examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c
Normal file
105
examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org)
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* SPDX-FileContributor: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "tinyusb.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
static void midi_task_read_example(void *arg)
|
||||
{
|
||||
// The MIDI interface always creates input and output port/jack descriptors
|
||||
// regardless of these being used or not. Therefore incoming traffic should be read
|
||||
// (possibly just discarded) to avoid the sender blocking in IO
|
||||
uint8_t packet[4];
|
||||
bool read = false;
|
||||
for (;;) {
|
||||
vTaskDelay(1);
|
||||
while (tud_midi_available()) {
|
||||
read = tud_midi_packet_read(packet);
|
||||
if (read) {
|
||||
ESP_LOGI(TAG, "Read - Time (ms since boot): %lld, Data: %02hhX %02hhX %02hhX %02hhX",
|
||||
esp_timer_get_time(), packet[0], packet[1], packet[2], packet[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void periodic_midi_write_example_cb(void *arg)
|
||||
{
|
||||
// Example melody stored as an array of note values
|
||||
uint8_t const note_sequence[] = {
|
||||
74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85, 88, 92, 97, 100, 97, 92, 88, 85, 81, 78,
|
||||
74, 69, 66, 62, 57, 62, 66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78, 73, 68, 64, 61,
|
||||
56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102
|
||||
};
|
||||
|
||||
static uint8_t const cable_num = 0; // MIDI jack associated with USB endpoint
|
||||
static uint8_t const channel = 0; // 0 for channel 1
|
||||
static uint32_t note_pos = 0;
|
||||
|
||||
// Previous positions in the note sequence.
|
||||
int previous = note_pos - 1;
|
||||
|
||||
// If we currently are at position 0, set the
|
||||
// previous position to the last note in the sequence.
|
||||
if (previous < 0) {
|
||||
previous = sizeof(note_sequence) - 1;
|
||||
}
|
||||
|
||||
// Send Note On for current position at full velocity (127) on channel 1.
|
||||
uint8_t note_on[3] = {0x90 | channel, note_sequence[note_pos], 127};
|
||||
tud_midi_stream_write(cable_num, note_on, 3);
|
||||
|
||||
// Send Note Off for previous note.
|
||||
uint8_t note_off[3] = {0x80 | channel, note_sequence[previous], 0};
|
||||
tud_midi_stream_write(cable_num, note_off, 3);
|
||||
|
||||
// Increment position
|
||||
note_pos++;
|
||||
|
||||
// If we are at the end of the sequence, start over.
|
||||
if (note_pos >= sizeof(note_sequence)) {
|
||||
note_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "USB initialization");
|
||||
|
||||
tinyusb_config_t const tusb_cfg = {
|
||||
.device_descriptor = NULL, // If device_descriptor is NULL, tinyusb_driver_install() will use Kconfig
|
||||
.string_descriptor = NULL,
|
||||
.external_phy = false // In the most cases you need to use a `false` value
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
ESP_LOGI(TAG, "USB initialization DONE");
|
||||
|
||||
// Periodically send MIDI packets
|
||||
int const tempo = 286;
|
||||
const esp_timer_create_args_t periodic_midi_args = {
|
||||
.callback = &periodic_midi_write_example_cb,
|
||||
/* name is optional, but may help identify the timer when debugging */
|
||||
.name = "periodic_midi"
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "MIDI write task init");
|
||||
esp_timer_handle_t periodic_midi_timer;
|
||||
ESP_ERROR_CHECK(esp_timer_create(&periodic_midi_args, &periodic_midi_timer));
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_midi_timer, tempo * 1000));
|
||||
|
||||
// Read recieved MIDI packets
|
||||
ESP_LOGI(TAG, "MIDI read task init");
|
||||
xTaskCreate(midi_task_read_example, "midi_task_read_example", 2 * 1024, NULL, 5, NULL);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_MIDI_ENABLED=y
|
@ -125,6 +125,12 @@ xtensa:
|
||||
allowed_licenses:
|
||||
- Apache-2.0 #Files added to the xtensa component by us
|
||||
- MIT #Cadence sources
|
||||
tinyusb:
|
||||
include:
|
||||
- 'examples/peripherals/usb/device/tusb_midi/'
|
||||
allowed_licenses:
|
||||
- Apache-2.0
|
||||
- MIT
|
||||
|
||||
# files matching this section do not perform the check
|
||||
# file patterns starting with ! are negated, meaning files matching them won't match the section.
|
||||
|
Loading…
x
Reference in New Issue
Block a user