usb: Added MIDI example + fixed TinyUSB MIDI config

Closes https://github.com/espressif/esp-idf/issues/8541
This commit is contained in:
Adam Múdry 2022-04-12 19:41:44 +02:00 committed by BOT
parent 8078092fae
commit 2dfaec3106
11 changed files with 303 additions and 26 deletions

View File

@ -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"

View File

@ -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,

View File

@ -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

View File

@ -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 */

View File

@ -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

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.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tusb_midi)

View 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.

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "tusb_midi_main.c"
INCLUDE_DIRS "."
)

View 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);
}

View File

@ -0,0 +1,2 @@
CONFIG_TINYUSB=y
CONFIG_TINYUSB_MIDI_ENABLED=y

View File

@ -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.