summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorSaulo VerĂ­ssimo <[email protected]>2026-05-16 13:00:30 -0300
committerSaulo VerĂ­ssimo <[email protected]>2026-05-16 13:00:30 -0300
commit3efac1b28c4174b0734ef106ce8496fa39657b47 (patch)
treea9539f697da64235a9fb660cb32789fefa736ff1 /examples
parent43a72cb614e2fdd27788057a6f9e843d458f1ca5 (diff)
midi2: host example - simplify and rename to midi2_host
Drop Feather-specific scaffolding (SSD1306 display, font, PIO-USB pin override) and rename the directory to midi2_host. The example is now a single main.c + tusb_config.h pair following the midi_rx pattern, with a printf-only UMP decoder for Channel Voice messages. Build-tested on adafruit_feather_rp2040_usb_host, daisyseed (stm32h7), mimxrt1010_evk, stm32f407disco, raspberry_pi_pico2, b_u585i_iot2a, portenta_c33, stm32h503nucleo, stlinkv3mini, feather_stm32f405. Ref #3571
Diffstat (limited to 'examples')
-rw-r--r--examples/host/CMakeLists.txt2
-rw-r--r--examples/host/midi2_host/CMakeLists.txt29
-rw-r--r--examples/host/midi2_host/CMakePresets.json6
-rw-r--r--examples/host/midi2_host/Makefile13
-rw-r--r--examples/host/midi2_host/src/main.c165
-rw-r--r--examples/host/midi2_host/src/tusb_config.h (renamed from examples/host/midi2_host_feather/src/tusb_config.h)39
-rw-r--r--examples/host/midi2_host_feather/CMakeLists.txt38
-rw-r--r--examples/host/midi2_host_feather/Makefile27
-rw-r--r--examples/host/midi2_host_feather/src/display.c276
-rw-r--r--examples/host/midi2_host_feather/src/display.h46
-rw-r--r--examples/host/midi2_host_feather/src/font5x7.h107
-rw-r--r--examples/host/midi2_host_feather/src/main.c267
12 files changed, 241 insertions, 774 deletions
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index 2cf2c10af..7c74e3c73 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -13,7 +13,7 @@ set(EXAMPLE_LIST
device_info
hid_controller
midi_rx
- midi2_host_feather
+ midi2_host
msc_file_explorer
msc_file_explorer_freertos
)
diff --git a/examples/host/midi2_host/CMakeLists.txt b/examples/host/midi2_host/CMakeLists.txt
new file mode 100644
index 000000000..0ec03bf5f
--- /dev/null
+++ b/examples/host/midi2_host/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(midi2_host C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/midi2_host/CMakePresets.json b/examples/host/midi2_host/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/host/midi2_host/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/host/midi2_host/Makefile b/examples/host/midi2_host/Makefile
new file mode 100644
index 000000000..f8292385e
--- /dev/null
+++ b/examples/host/midi2_host/Makefile
@@ -0,0 +1,13 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += \
+ src/main.c
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c
new file mode 100644
index 000000000..63b08318c
--- /dev/null
+++ b/examples/host/midi2_host/src/main.c
@@ -0,0 +1,165 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+// Minimal USB-MIDI 2.0 host example.
+// Receives UMP from any MIDI 2.0 device, prints each packet to stdout.
+
+#include <stdio.h>
+#include <string.h>
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "class/midi/midi2_host.h"
+
+//--------------------------------------------------------------------+
+// State
+//--------------------------------------------------------------------+
+
+static uint8_t midi2_idx = 0xFF;
+
+//--------------------------------------------------------------------+
+// UMP printer - shows MT and word(s) in hex; decodes Channel Voice
+//--------------------------------------------------------------------+
+
+static void print_ump(const uint32_t* words, uint8_t wc) {
+ uint8_t mt = (uint8_t)((words[0] >> 28) & 0x0F);
+ uint8_t group = (uint8_t)((words[0] >> 24) & 0x0F);
+
+ if (mt == 0x4 && wc >= 2) {
+ // MIDI 2.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 20) & 0x0F);
+ uint8_t channel = (uint8_t)((words[0] >> 16) & 0x0F);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ switch (status) {
+ case 0x9:
+ printf("[g%u ch%u] M2 NoteOn n=%u vel=%04X attr=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF),
+ (unsigned)(words[1] & 0xFFFF));
+ break;
+ case 0x8:
+ printf("[g%u ch%u] M2 NoteOff n=%u vel=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF));
+ break;
+ case 0xB:
+ printf("[g%u ch%u] M2 CC#%u = %08lX\r\n",
+ group, channel, data1, (unsigned long)words[1]);
+ break;
+ case 0xC:
+ printf("[g%u ch%u] M2 ProgChg %u\r\n",
+ group, channel, (unsigned)((words[1] >> 24) & 0x7F));
+ break;
+ case 0xD:
+ printf("[g%u ch%u] M2 ChanPress %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ case 0xE:
+ printf("[g%u ch%u] M2 PitchBend %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ default:
+ printf("[g%u ch%u] M2 status=0x%X w0=%08lX w1=%08lX\r\n",
+ group, channel, status,
+ (unsigned long)words[0], (unsigned long)words[1]);
+ break;
+ }
+ } else if (mt == 0x2 && wc == 1) {
+ // MIDI 1.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 16) & 0xFF);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ uint8_t data2 = (uint8_t)(words[0] & 0x7F);
+ printf("[g%u] M1 %02X %02X %02X\r\n", group, status, data1, data2);
+ } else {
+ printf("UMP MT=0x%X wc=%u w0=%08lX\r\n",
+ mt, wc, (unsigned long)words[0]);
+ }
+}
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 Host Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t* d) {
+ (void)idx;
+ printf("MIDI2 descriptor: %s, RX cables=%u TX cables=%u\r\n",
+ d->protocol_version ? "MIDI 2.0" : "MIDI 1.0",
+ d->rx_cable_count, d->tx_cable_count);
+}
+
+void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t* m) {
+ midi2_idx = idx;
+ printf("MIDI2 mounted: idx=%u protocol=%s\r\n",
+ idx, m->protocol_version ? "MIDI 2.0" : "MIDI 1.0");
+}
+
+void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)xferred_bytes;
+
+ uint32_t words[16];
+ while (1) {
+ uint32_t n = tuh_midi2_ump_read(idx, words, 16);
+ if (n == 0) break;
+
+ uint32_t i = 0;
+ while (i < n) {
+ uint8_t mt = (uint8_t)((words[i] >> 28) & 0x0F);
+ uint8_t wc = midi2_ump_word_count(mt);
+ if (i + wc > n) break;
+ print_ump(&words[i], wc);
+ i += wc;
+ }
+ }
+}
+
+void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)idx; (void)xferred_bytes;
+}
+
+void tuh_midi2_umount_cb(uint8_t idx) {
+ (void)idx;
+ midi2_idx = 0xFF;
+ printf("MIDI2 unmounted\r\n");
+}
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+
+int main(void) {
+ board_init();
+
+ printf("\r\nTinyUSB Host MIDI 2.0 Example\r\n");
+
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO,
+ };
+ tusb_init(BOARD_TUH_RHPORT, &host_init);
+
+ while (1) {
+ tuh_task();
+ }
+
+ return 0;
+}
diff --git a/examples/host/midi2_host_feather/src/tusb_config.h b/examples/host/midi2_host/src/tusb_config.h
index 362d0e2d0..4bd6ef2ea 100644
--- a/examples/host/midi2_host_feather/src/tusb_config.h
+++ b/examples/host/midi2_host/src/tusb_config.h
@@ -37,6 +37,10 @@ extern "C" {
#error CFG_TUSB_MCU must be defined
#endif
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
@@ -55,34 +59,45 @@ extern "C" {
//--------------------------------------------------------------------+
// Host Configuration
-// Adafruit Feather RP2040 USB Host: PIO-USB on GP16/GP17 (USB-A Host port)
//--------------------------------------------------------------------+
#define CFG_TUH_ENABLED 1
-// PIO-USB Host on rhport 1 (USB-A connector on GP16/GP17)
-#define CFG_TUH_RPI_PIO_USB 1
-#define BOARD_TUH_RHPORT 1
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_MAX3421 1 // use max3421 as host controller
-#ifndef BOARD_TUH_MAX_SPEED
-#define BOARD_TUH_MAX_SPEED OPT_MODE_FULL_SPEED
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
#endif
#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+//------------------------- Board Specific --------------------------+
+
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
//--------------------------------------------------------------------+
// Driver Configuration
//--------------------------------------------------------------------+
#define CFG_TUH_ENUMERATION_BUFSIZE 256
-#define CFG_TUH_HUB 0
-#define CFG_TUH_DEVICE_MAX 1
+#define CFG_TUH_HUB 1
+#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
-// MIDI 2.0 Host
-#define CFG_TUH_MIDI2 1
-#define CFG_TUH_MIDI2_RX_BUFSIZE 512
-#define CFG_TUH_MIDI2_TX_BUFSIZE 512
+// USB-MIDI 2.0 Host
+#define CFG_TUH_MIDI2 CFG_TUH_DEVICE_MAX
+#define CFG_TUH_MIDI2_RX_BUFSIZE 512
+#define CFG_TUH_MIDI2_TX_BUFSIZE 512
#ifdef __cplusplus
}
diff --git a/examples/host/midi2_host_feather/CMakeLists.txt b/examples/host/midi2_host_feather/CMakeLists.txt
deleted file mode 100644
index 5d34b8170..000000000
--- a/examples/host/midi2_host_feather/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-cmake_minimum_required(VERSION 3.20)
-
-include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-
-project(midi2_host_feather C CXX ASM)
-
-family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
-
-# This example requires PIO-USB and Pico SDK (I2C, SSD1306 display)
-if(NOT FAMILY STREQUAL "rp2040")
- return()
-endif()
-
-add_executable(${PROJECT_NAME})
-
-target_sources(${PROJECT_NAME} PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
- ${CMAKE_CURRENT_SOURCE_DIR}/src/display.c
-)
-
-target_include_directories(${PROJECT_NAME} PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}/src
-)
-
-family_configure_host_example(${PROJECT_NAME} noos)
-
-# Adafruit Feather RP2040 USB Host: PIO-USB on GP16/GP17
-target_compile_definitions(${PROJECT_NAME} PRIVATE
- PIO_USB_DP_PIN_DEFAULT=16
-)
-target_compile_options(${PROJECT_NAME} PRIVATE
- -Wno-type-limits
-)
-
-# SSD1306 display (I2C via STEMMA QT)
-target_link_libraries(${PROJECT_NAME} PUBLIC
- hardware_i2c
-)
diff --git a/examples/host/midi2_host_feather/Makefile b/examples/host/midi2_host_feather/Makefile
deleted file mode 100644
index 933d19434..000000000
--- a/examples/host/midi2_host_feather/Makefile
+++ /dev/null
@@ -1,27 +0,0 @@
-# This example requires RP2040 (PIO-USB, Pico SDK I2C, SSD1306 display)
-ifeq (,$(findstring rp2040,$(FAMILY)))
-$(info Skipping midi2_host_feather: requires FAMILY=rp2040)
-all:
- @:
-.DEFAULT:
- @:
-else
-
-include ../../../hw/bsp/family_support.mk
-
-INC += \
- src \
-
-# Example source
-EXAMPLE_SOURCE += \
- src/main.c \
- src/display.c \
-
-SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-
-# Suppress pre-existing warning
-CFLAGS_GCC += -Wno-type-limits
-
-include ../../../hw/bsp/family_rules.mk
-
-endif
diff --git a/examples/host/midi2_host_feather/src/display.c b/examples/host/midi2_host_feather/src/display.c
deleted file mode 100644
index 91039fa16..000000000
--- a/examples/host/midi2_host_feather/src/display.c
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2026 Saulo Verissimo
- *
- * 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.
- */
-
-// SSD1306 OLED display driver (128x64, I2C) for MIDI 2.0 Host example.
-// Adafruit Feather RP2040 USB Host: I2C1 via STEMMA QT (SDA = GP2, SCL = GP3)
-//
-// Three display phases:
-// 1. Splash : title + credits (shown during init)
-// 2. Connecting : spinner animation while waiting for device
-// 3. Live : header + 6 scrolling log lines + status bar
-
-#include "display.h"
-#include <stdio.h>
-#include <string.h>
-#include "pico/stdlib.h"
-#include "hardware/i2c.h"
-#include "hardware/gpio.h"
-
-#define I2C_PORT i2c1
-#define I2C_SDA 2
-#define I2C_SCL 3
-#define I2C_FREQ 400000
-#define SSD1306_ADDR 0x3C
-
-#define SCR_W 128
-#define SCR_H 64
-#define PAGES (SCR_H / 8)
-#define CHARS_PER_LINE 21
-
-//--------------------------------------------------------------------+
-// Framebuffer
-//--------------------------------------------------------------------+
-
-static uint8_t fb[SCR_W * PAGES];
-
-//--------------------------------------------------------------------+
-// Log buffer (6 lines for live view)
-//--------------------------------------------------------------------+
-
-#define LOG_LINES 6
-static char log_lines[LOG_LINES][CHARS_PER_LINE + 1];
-static int log_count = 0;
-
-//--------------------------------------------------------------------+
-// Minimal 5x7 font (ASCII 32-126)
-//--------------------------------------------------------------------+
-
-#include "font5x7.h"
-
-//--------------------------------------------------------------------+
-// SSD1306 I2C low-level
-//--------------------------------------------------------------------+
-
-static void ssd_cmd(uint8_t cmd) {
- uint8_t buf[2] = { 0x00, cmd };
- i2c_write_blocking(I2C_PORT, SSD1306_ADDR, buf, 2, false);
-}
-
-static void ssd_data(const uint8_t* data, size_t len) {
- uint8_t buf[SCR_W + 1];
- buf[0] = 0x40;
- size_t chunk = (len > SCR_W) ? SCR_W : len;
- memcpy(buf + 1, data, chunk);
- i2c_write_blocking(I2C_PORT, SSD1306_ADDR, buf, chunk + 1, false);
-}
-
-static void ssd_flush(void) {
- ssd_cmd(0x21); ssd_cmd(0); ssd_cmd(127);
- ssd_cmd(0x22); ssd_cmd(0); ssd_cmd(7);
- for (int page = 0; page < PAGES; page++) {
- ssd_data(&fb[page * SCR_W], SCR_W);
- }
-}
-
-//--------------------------------------------------------------------+
-// Framebuffer drawing
-//--------------------------------------------------------------------+
-
-static void fb_clear(void) {
- memset(fb, 0, sizeof(fb));
-}
-
-static void fb_char(int x, int y, char c) {
- if (c < 32 || c > 126) c = '?';
- const uint8_t* glyph = font5x7 + (c - 32) * 5;
- int page = y / 8;
- int bit_offset = y % 8;
-
- if (page >= PAGES || x + 5 > SCR_W) return;
-
- for (int col = 0; col < 5; col++) {
- uint8_t column_data = glyph[col];
- fb[(page * SCR_W) + x + col] |= (uint8_t)(column_data << bit_offset);
- if (bit_offset > 0 && page + 1 < PAGES) {
- fb[((page + 1) * SCR_W) + x + col] |= (uint8_t)(column_data >> (8 - bit_offset));
- }
- }
-}
-
-static void fb_string(int x, int y, const char* str) {
- while (*str) {
- fb_char(x, y, *str);
- x += 6;
- if (x + 6 > SCR_W) break;
- str++;
- }
-}
-
-// Draw a horizontal line (1px)
-static void fb_hline(int x0, int x1, int y) {
- int page = y / 8;
- uint8_t mask = (uint8_t)(1 << (y % 8));
- if (page >= PAGES) return;
- for (int x = x0; x <= x1 && x < SCR_W; x++) {
- fb[page * SCR_W + x] |= mask;
- }
-}
-
-//--------------------------------------------------------------------+
-// Phase 1: Splash
-//--------------------------------------------------------------------+
-
-void display_init(void) {
- i2c_init(I2C_PORT, I2C_FREQ);
- gpio_set_function(I2C_SDA, GPIO_FUNC_I2C);
- gpio_set_function(I2C_SCL, GPIO_FUNC_I2C);
- gpio_pull_up(I2C_SDA);
- gpio_pull_up(I2C_SCL);
-
- sleep_ms(100);
-
- // SSD1306 init sequence
- ssd_cmd(0xAE);
- ssd_cmd(0xD5); ssd_cmd(0x80);
- ssd_cmd(0xA8); ssd_cmd(0x3F);
- ssd_cmd(0xD3); ssd_cmd(0x00);
- ssd_cmd(0x40);
- ssd_cmd(0x8D); ssd_cmd(0x14);
- ssd_cmd(0x20); ssd_cmd(0x00);
- ssd_cmd(0xA1);
- ssd_cmd(0xC8);
- ssd_cmd(0xDA); ssd_cmd(0x12);
- ssd_cmd(0x81); ssd_cmd(0xCF);
- ssd_cmd(0xD9); ssd_cmd(0xF1);
- ssd_cmd(0xDB); ssd_cmd(0x40);
- ssd_cmd(0xA4);
- ssd_cmd(0xA6);
- ssd_cmd(0xAF);
-
- // Splash screen
- fb_clear();
- fb_string(4, 8, "TinyUSB MIDI 2.0");
- fb_hline(4, 123, 18);
- fb_string(16, 24, "USB Host Demo");
- fb_string(4, 40, "Feather RP2040");
- fb_string(4, 52, "PIO-USB + SSD1306");
- ssd_flush();
-}
-
-//--------------------------------------------------------------------+
-// Phase 2: Connecting (spinner)
-//--------------------------------------------------------------------+
-
-static const char SPINNER[] = "|/-\\";
-
-void display_connecting(uint32_t elapsed_ms) {
- int idx = (int)((elapsed_ms / 200) % 4);
-
- // Clear bottom 2 pages for spinner area
- memset(&fb[6 * SCR_W], 0, SCR_W);
- memset(&fb[7 * SCR_W], 0, SCR_W);
-
- char line[CHARS_PER_LINE + 1];
- snprintf(line, sizeof(line), "%c Waiting device...", SPINNER[idx]);
- fb_string(4, 52, line);
- ssd_flush();
-}
-
-//--------------------------------------------------------------------+
-// Phase 3: Live view
-//--------------------------------------------------------------------+
-
-// Layout:
-// y=0 : "TinyUSB MIDI 2.0 Host" (header, fixed)
-// y=9 : separator line
-// y=10 : log line 0
-// y=18 : log line 1
-// y=26 : log line 2
-// y=34 : log line 3
-// y=42 : log line 4
-// y=50 : log line 5
-// y=57 : separator line
-// y=58 : status bar
-
-static bool live_mode = false;
-
-static void draw_live_frame(void) {
- fb_clear();
- fb_string(0, 0, "TinyUSB MIDI 2.0 Host");
- fb_hline(0, 127, 9);
- fb_hline(0, 127, 57);
-}
-
-void display_live_begin(void) {
- live_mode = true;
- log_count = 0;
- memset(log_lines, 0, sizeof(log_lines));
-
- draw_live_frame();
- fb_string(0, 58, "Connected");
- ssd_flush();
-}
-
-void display_log(const char* text, uint16_t color) {
- (void)color;
-
- if (!live_mode) {
- display_live_begin();
- }
-
- // Scroll up if full
- if (log_count >= LOG_LINES) {
- for (int i = 0; i < LOG_LINES - 1; i++) {
- strncpy(log_lines[i], log_lines[i + 1], CHARS_PER_LINE);
- log_lines[i][CHARS_PER_LINE] = '\0';
- }
- log_count = LOG_LINES - 1;
- }
-
- strncpy(log_lines[log_count], text, CHARS_PER_LINE);
- log_lines[log_count][CHARS_PER_LINE] = '\0';
- log_count++;
-
- // Redraw: header + log + status
- draw_live_frame();
- for (int i = 0; i < log_count && i < LOG_LINES; i++) {
- fb_string(0, 10 + i * 8, log_lines[i]);
- }
- ssd_flush();
-}
-
-void display_status(const char* text) {
- if (!live_mode) return;
-
- // Clear status area (page 7)
- memset(&fb[7 * SCR_W], 0, SCR_W);
- // Redraw separator (might have been cleared)
- fb_hline(0, 127, 57);
-
- char status[CHARS_PER_LINE + 1];
- strncpy(status, text, CHARS_PER_LINE);
- status[CHARS_PER_LINE] = '\0';
- fb_string(0, 58, status);
- ssd_flush();
-}
diff --git a/examples/host/midi2_host_feather/src/display.h b/examples/host/midi2_host_feather/src/display.h
deleted file mode 100644
index ad8ead81d..000000000
--- a/examples/host/midi2_host_feather/src/display.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2026 Saulo Verissimo
- *
- * 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 DISPLAY_H_
-#define DISPLAY_H_
-
-#include <stdint.h>
-#include <stdbool.h>
-
-// Initialize SSD1306 display and show splash screen
-void display_init(void);
-
-// Show waiting/connecting animation (call periodically)
-void display_connecting(uint32_t elapsed_ms);
-
-// Transition to live message view
-void display_live_begin(void);
-
-// Add a line to the scrolling log area (6 visible lines)
-void display_log(const char* text, uint16_t color);
-
-// Update the status bar (bottom line)
-void display_status(const char* text);
-
-#endif
diff --git a/examples/host/midi2_host_feather/src/font5x7.h b/examples/host/midi2_host_feather/src/font5x7.h
deleted file mode 100644
index e515faac1..000000000
--- a/examples/host/midi2_host_feather/src/font5x7.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Minimal 5x7 font, ASCII 32-126. Public domain.
-// 5 bytes per character (5 columns, 7 rows, LSB=top row)
-
-#ifndef FONT5X7_H_
-#define FONT5X7_H_
-
-#include <stdint.h>
-
-static const uint8_t font5x7[] = {
- 0x00,0x00,0x00,0x00,0x00, // 32 (space)
- 0x00,0x00,0x5F,0x00,0x00, // 33 !
- 0x00,0x07,0x00,0x07,0x00, // 34 "
- 0x14,0x7F,0x14,0x7F,0x14, // 35 #
- 0x24,0x2A,0x7F,0x2A,0x12, // 36 $
- 0x23,0x13,0x08,0x64,0x62, // 37 %
- 0x36,0x49,0x55,0x22,0x50, // 38 &
- 0x00,0x05,0x03,0x00,0x00, // 39 '
- 0x00,0x1C,0x22,0x41,0x00, // 40 (
- 0x00,0x41,0x22,0x1C,0x00, // 41 )
- 0x08,0x2A,0x1C,0x2A,0x08, // 42 *
- 0x08,0x08,0x3E,0x08,0x08, // 43 +
- 0x00,0x50,0x30,0x00,0x00, // 44 ,
- 0x08,0x08,0x08,0x08,0x08, // 45 -
- 0x00,0x60,0x60,0x00,0x00, // 46 .
- 0x20,0x10,0x08,0x04,0x02, // 47 /
- 0x3E,0x51,0x49,0x45,0x3E, // 48 0
- 0x00,0x42,0x7F,0x40,0x00, // 49 1
- 0x42,0x61,0x51,0x49,0x46, // 50 2
- 0x21,0x41,0x45,0x4B,0x31, // 51 3
- 0x18,0x14,0x12,0x7F,0x10, // 52 4
- 0x27,0x45,0x45,0x45,0x39, // 53 5
- 0x3C,0x4A,0x49,0x49,0x30, // 54 6
- 0x01,0x71,0x09,0x05,0x03, // 55 7
- 0x36,0x49,0x49,0x49,0x36, // 56 8
- 0x06,0x49,0x49,0x29,0x1E, // 57 9
- 0x00,0x36,0x36,0x00,0x00, // 58 :
- 0x00,0x56,0x36,0x00,0x00, // 59 ;
- 0x00,0x08,0x14,0x22,0x41, // 60 <
- 0x14,0x14,0x14,0x14,0x14, // 61 =
- 0x41,0x22,0x14,0x08,0x00, // 62 >
- 0x02,0x01,0x51,0x09,0x06, // 63 ?
- 0x32,0x49,0x79,0x41,0x3E, // 64 @
- 0x7E,0x11,0x11,0x11,0x7E, // 65 A
- 0x7F,0x49,0x49,0x49,0x36, // 66 B
- 0x3E,0x41,0x41,0x41,0x22, // 67 C
- 0x7F,0x41,0x41,0x22,0x1C, // 68 D
- 0x7F,0x49,0x49,0x49,0x41, // 69 E
- 0x7F,0x09,0x09,0x01,0x01, // 70 F
- 0x3E,0x41,0x41,0x51,0x32, // 71 G
- 0x7F,0x08,0x08,0x08,0x7F, // 72 H
- 0x00,0x41,0x7F,0x41,0x00, // 73 I
- 0x20,0x40,0x41,0x3F,0x01, // 74 J
- 0x7F,0x08,0x14,0x22,0x41, // 75 K
- 0x7F,0x40,0x40,0x40,0x40, // 76 L
- 0x7F,0x02,0x04,0x02,0x7F, // 77 M
- 0x7F,0x04,0x08,0x10,0x7F, // 78 N
- 0x3E,0x41,0x41,0x41,0x3E, // 79 O
- 0x7F,0x09,0x09,0x09,0x06, // 80 P
- 0x3E,0x41,0x51,0x21,0x5E, // 81 Q
- 0x7F,0x09,0x19,0x29,0x46, // 82 R
- 0x46,0x49,0x49,0x49,0x31, // 83 S
- 0x01,0x01,0x7F,0x01,0x01, // 84 T
- 0x3F,0x40,0x40,0x40,0x3F, // 85 U
- 0x1F,0x20,0x40,0x20,0x1F, // 86 V
- 0x7F,0x20,0x18,0x20,0x7F, // 87 W
- 0x63,0x14,0x08,0x14,0x63, // 88 X
- 0x03,0x04,0x78,0x04,0x03, // 89 Y
- 0x61,0x51,0x49,0x45,0x43, // 90 Z
- 0x00,0x00,0x7F,0x41,0x41, // 91 [
- 0x02,0x04,0x08,0x10,0x20, // 92 backslash
- 0x41,0x41,0x7F,0x00,0x00, // 93 ]
- 0x04,0x02,0x01,0x02,0x04, // 94 ^
- 0x40,0x40,0x40,0x40,0x40, // 95 _
- 0x00,0x01,0x02,0x04,0x00, // 96 `
- 0x20,0x54,0x54,0x54,0x78, // 97 a
- 0x7F,0x48,0x44,0x44,0x38, // 98 b
- 0x38,0x44,0x44,0x44,0x20, // 99 c
- 0x38,0x44,0x44,0x48,0x7F, // 100 d
- 0x38,0x54,0x54,0x54,0x18, // 101 e
- 0x08,0x7E,0x09,0x01,0x02, // 102 f
- 0x08,0x14,0x54,0x54,0x3C, // 103 g
- 0x7F,0x08,0x04,0x04,0x78, // 104 h
- 0x00,0x44,0x7D,0x40,0x00, // 105 i
- 0x20,0x40,0x44,0x3D,0x00, // 106 j
- 0x00,0x7F,0x10,0x28,0x44, // 107 k
- 0x00,0x41,0x7F,0x40,0x00, // 108 l
- 0x7C,0x04,0x18,0x04,0x78, // 109 m
- 0x7C,0x08,0x04,0x04,0x78, // 110 n
- 0x38,0x44,0x44,0x44,0x38, // 111 o
- 0x7C,0x14,0x14,0x14,0x08, // 112 p
- 0x08,0x14,0x14,0x18,0x7C, // 113 q
- 0x7C,0x08,0x04,0x04,0x08, // 114 r
- 0x48,0x54,0x54,0x54,0x20, // 115 s
- 0x04,0x3F,0x44,0x40,0x20, // 116 t
- 0x3C,0x40,0x40,0x20,0x7C, // 117 u
- 0x1C,0x20,0x40,0x20,0x1C, // 118 v
- 0x3C,0x40,0x30,0x40,0x3C, // 119 w
- 0x44,0x28,0x10,0x28,0x44, // 120 x
- 0x0C,0x50,0x50,0x50,0x3C, // 121 y
- 0x44,0x64,0x54,0x4C,0x44, // 122 z
- 0x00,0x08,0x36,0x41,0x00, // 123 {
- 0x00,0x00,0x7F,0x00,0x00, // 124 |
- 0x00,0x41,0x36,0x08,0x00, // 125 }
- 0x10,0x08,0x08,0x10,0x08, // 126 ~
-};
-
-#endif
diff --git a/examples/host/midi2_host_feather/src/main.c b/examples/host/midi2_host_feather/src/main.c
deleted file mode 100644
index 7c996355d..000000000
--- a/examples/host/midi2_host_feather/src/main.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2026 Saulo Verissimo
- *
- * 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.
- */
-
-// MIDI 2.0 Host Example: Adafruit Feather RP2040 USB Host
-//
-// Receives UMP from a MIDI 2.0 Device via PIO-USB (GP16/GP17).
-// SSD1306 OLED (I2C1, GP2/GP3) shows splash, spinner, then live UMP messages.
-
-#include <stdio.h>
-#include <string.h>
-#include "bsp/board_api.h"
-#include "tusb.h"
-#include "hardware/gpio.h"
-#include "class/midi/midi2_host.h"
-#include "class/midi/midi.h"
-#include "display.h"
-
-//--------------------------------------------------------------------+
-// State
-//--------------------------------------------------------------------+
-
-static uint32_t note_count = 0;
-static uint8_t midi2_idx = 0xFF;
-static bool device_connected = false;
-static bool mounted = false;
-
-//--------------------------------------------------------------------+
-// Note name helper
-//--------------------------------------------------------------------+
-
-static const char* NOTE_NAMES[] = {
- "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
-};
-
-static void note_name(uint8_t pitch, char* buf, size_t len) {
- int octave = (pitch / 12) - 1;
- snprintf(buf, len, "%s%d", NOTE_NAMES[pitch % 12], octave);
-}
-
-//--------------------------------------------------------------------+
-// UMP decoder
-//--------------------------------------------------------------------+
-
-static void decode_ump(const uint32_t* words, uint8_t word_count) {
- uint8_t mt = (uint8_t)((words[0] >> 28) & 0x0F);
- char line[64];
-
- if (mt == 0x4 && word_count >= 2) {
- uint8_t status = (uint8_t)((words[0] >> 20) & 0x0F);
- uint8_t channel = (uint8_t)((words[0] >> 16) & 0x0F);
-
- switch (status) {
- case 0x9: {
- uint8_t pitch = (uint8_t)((words[0] >> 8) & 0x7F);
- uint16_t vel = (uint16_t)((words[1] >> 16) & 0xFFFF);
- char nn[6];
- note_name(pitch, nn, sizeof(nn));
- snprintf(line, sizeof(line), "NoteOn %s ch%u v%04X", nn, channel, vel);
- display_log(line, 0x07E0);
- note_count++;
- break;
- }
- case 0x8: {
- uint8_t pitch = (uint8_t)((words[0] >> 8) & 0x7F);
- char nn[6];
- note_name(pitch, nn, sizeof(nn));
- snprintf(line, sizeof(line), "NoteOff %s ch%u", nn, channel);
- display_log(line, 0x8410);
- break;
- }
- case 0xB: {
- uint8_t idx = (uint8_t)((words[0] >> 8) & 0x7F);
- snprintf(line, sizeof(line), "CC%-3u %08lX", idx, (unsigned long)words[1]);
- display_log(line, 0x001F);
- break;
- }
- case 0xC: {
- uint8_t prog = (uint8_t)((words[1] >> 24) & 0x7F);
- snprintf(line, sizeof(line), "ProgChg %u", prog);
- display_log(line, 0xFFE0);
- break;
- }
- case 0xE: {
- snprintf(line, sizeof(line), "PBend %08lX", (unsigned long)words[1]);
- display_log(line, 0xF81F);
- break;
- }
- case 0xD: {
- snprintf(line, sizeof(line), "CPress %08lX", (unsigned long)words[1]);
- display_log(line, 0xFC10);
- break;
- }
- case 0xA: {
- uint8_t pitch = (uint8_t)((words[0] >> 8) & 0x7F);
- char nn[6];
- note_name(pitch, nn, sizeof(nn));
- snprintf(line, sizeof(line), "PolyP %s %08lX", nn, (unsigned long)words[1]);
- display_log(line, 0xFC10);
- break;
- }
- case 0xF: {
- uint8_t pitch = (uint8_t)((words[0] >> 8) & 0x7F);
- uint8_t flags = (uint8_t)(words[0] & 0xFF);
- snprintf(line, sizeof(line), "PN-Mgmt n%u f%02X", pitch, flags);
- display_log(line, 0x07FF);
- break;
- }
- default: {
- snprintf(line, sizeof(line), "M2CVM s=0x%X", status);
- display_log(line, 0xFFFF);
- break;
- }
- }
- } else if (mt == 0x0) {
- uint8_t status = (uint8_t)((words[0] >> 20) & 0x0F);
- if (status == 0x2) {
- uint16_t ts = words[0] & 0xFFFF;
- snprintf(line, sizeof(line), "JR-TS %04X", ts);
- display_log(line, 0x8410);
- }
- } else {
- snprintf(line, sizeof(line), "MT=0x%X w0=%08lX",
- mt, (unsigned long)words[0]);
- display_log(line, 0xFFFF);
- }
-}
-
-//--------------------------------------------------------------------+
-// MIDI 2.0 Host Callbacks
-//--------------------------------------------------------------------+
-
-void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t* d) {
- (void)idx;
- char line[48];
- snprintf(line, sizeof(line), "%s RX=%u TX=%u",
- d->protocol_version ? "MIDI2" : "MIDI1",
- d->rx_cable_count, d->tx_cable_count);
- display_log(line, 0x07E0);
-}
-
-void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t* m) {
- midi2_idx = idx;
- mounted = true;
-
- display_live_begin();
-
- if (m->protocol_version) {
- display_log("MIDI 2.0 ready", 0x07E0);
- } else {
- display_log("MIDI 1.0 device", 0xFFE0);
- }
- display_status("Receiving...");
-}
-
-void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
- (void)xferred_bytes;
-
- uint32_t words[16];
- while (1) {
- uint32_t n = tuh_midi2_ump_read(idx, words, 16);
- if (n == 0) break;
-
- uint32_t i = 0;
- while (i < n) {
- uint8_t mt = (uint8_t)((words[i] >> 28) & 0x0F);
- uint8_t wc = midi2_ump_word_count(mt);
- if (i + wc > n) break;
- decode_ump(&words[i], wc);
- i += wc;
- }
- }
-}
-
-void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
- (void)idx; (void)xferred_bytes;
-}
-
-void tuh_midi2_umount_cb(uint8_t idx) {
- (void)idx;
- midi2_idx = 0xFF;
- device_connected = false;
- mounted = false;
- note_count = 0;
- display_log("Disconnected", 0xF800);
- display_status("Waiting...");
-}
-
-//--------------------------------------------------------------------+
-// Main
-//--------------------------------------------------------------------+
-
-int main(void) {
- board_init();
-
- // Enable 5V to USB-A port (Feather RP2040 USB Host: GP18)
- gpio_init(18);
- gpio_set_dir(18, GPIO_OUT);
- gpio_put(18, 1);
-
- display_init();
- sleep_ms(1500);
-
- tusb_rhport_init_t host_init = {
- .role = TUSB_ROLE_HOST,
- .speed = TUSB_SPEED_FULL,
- };
- tusb_init(BOARD_TUH_RHPORT, &host_init);
-
- uint32_t last_status_ms = 0;
-
- while (1) {
- tuh_task();
-
- uint32_t now = tusb_time_millis_api();
-
- if (!mounted) {
- // Spinner while waiting for device
- if (now - last_status_ms > 200) {
- last_status_ms = now;
- display_connecting(now);
- }
- } else {
- // Update note count every 2 seconds
- if (now - last_status_ms > 2000) {
- last_status_ms = now;
- char line[22];
- snprintf(line, sizeof(line), "Notes: %lu", (unsigned long)note_count);
- display_status(line);
- }
- }
- }
-
- return 0;
-}
-
-// Generic USB device mount/unmount
-void tuh_mount_cb(uint8_t daddr) {
- (void)daddr;
- device_connected = true;
-}
-
-void tuh_umount_cb(uint8_t daddr) {
- (void)daddr;
- device_connected = false;
- mounted = false;
-}