MIDI2USB - la música nos conecta

Conversor ruso-chino-estadounidense de MIDI a USB.Fig.1 Conversor ruso-chino-estadounidense de MIDI a USB. Foto del autor.



A la gente le encanta la música. Mucha gente sabe tocar instrumentos musicales. Y algunos intentan improvisar e incluso componer música. Los instrumentos musicales electrónicos se pueden conectar a su computadora para obtener más posibilidades creativas. Esto parece ser un asunto simple, pero la mayoría de los adaptadores USB-MIDI chinos baratos funcionan de manera mediocre. A quién le importa cómo hice mi adaptador MIDI2USB, los invito a leer



Formulación del problema



Hace un par de años, mi sobrino que estudia música empezó a improvisar y componer música. Quería que su obra no se perdiera, pero logré grabar sus estudios musicales solo en un dictáfono. La calidad de esta grabación fue insatisfactoria. Quería grabar notas directamente en Cubase o MuseScore y luego editarlas. Para ello, decidí comprar un adaptador (convertidor) USB a MIDI chino.



Anécdota en el tema
:

— , !

— , ?

— !


Este cable adaptador es barato y no funciona bien. La transferencia de datos desde el sintetizador (piano eléctrico) al ordenador no funciona. Si toca con un dedo, puede grabar varias notas, y cuando toca un acorde o toca escalas, el adaptador cuelga y se convierte en un ladrillo. Otra dirección, es decir La transferencia de datos desde la computadora al sintetizador funciona bien. Se pueden encontrar historias similares en las reseñas de muchos compradores.



Formas de finalizar el adaptador chino



Hay muchas discusiones en Internet sobre cómo mejorar o modificar el adaptador chino. En algunas versiones de este adaptador se proporciona un optoacoplador, pero no soldado, que proporciona aislamiento galvánico entre la computadora y el sintetizador. Por desgracia, en mi caso, la revisión fue difícil, porque en lugar de un optoacoplador, se instalan dos transistores NPN. Tenga en cuenta que el estándar MIDI especifica directamente el uso de un optoaislador, por ejemplo, PC900V o 6N138. Los optoacopladores H11L1M (DIP-8) o H11L1SM (SO-6) tienen características similares. Se pueden utilizar otros componentes con parámetros adecuados.



Adaptador chino en proceso de desmontaje.

Figura 2. Adaptador chino en proceso de desmontaje. Foto del autor.



La foto muestra que hay suficiente espacio en la caja para acomodar el optoaislador y los elementos relacionados. Algunos artesanos sueldan los componentes existentes e instalan un optoaislador con un "kit de carrocería" en su lugar. Obviamente, esta operación requiere no solo conocimientos, sino también buenas habilidades motoras manuales.



Pero no es suficiente proporcionar aislamiento óptico entre el instrumento musical y la computadora. También se requiere un oscilador o resonador de cristal de precisión para garantizar que el UART serial esté sincronizado de acuerdo con el estándar MIDI. El adaptador chino que compré carece no solo de un optoacoplador, sino también de un resonador de cuarzo. Por supuesto, hay microcircuitos en los que las unidades de reloj se calibran en fábrica, pero no hay nada como esto. En general, el rendimiento de este producto chino es bajo. Hay adaptadores integrados en el chip CH345, un convertidor de USB a MIDI en el paquete SSOP-20, pero este no es mi caso. El microchip CH345 tiene etiquetas USB de hardware ID de proveedor: 1a86, ID de producto: 752d. Sin embargo, cualquier microcircuito "izquierdo" puede (y lo hace) dar los mismos identificadores e incluso puede "pretender" ser cualquier cosa.



El último pequeño defecto que encontré en el adaptador chino es el software (firmware). Más precisamente, este es un tamaño de búfer pequeño para puntos finales (EndPoints), solo 8 bytes. Esto es suficiente para transmitir las notas presionadas, porque el mensaje MIDI a través de la interfaz USB consta de 4 bytes (número de cable, número de comando y 2 bytes de datos). Pero cualquier extensión, por ejemplo SysEx, puede ser más grande.



Después de un tiempo compré otro cable adaptador, que se llamaba "Interfaz MIDI USB profesional". Este adaptador era significativamente más caro y funcionó significativamente mejor, pero aún con errores. Esto se manifestó en el hecho de que después de unos minutos de tocar el sintetizador, de repente comenzó a perder las pulsaciones de tecla, o viceversa, no percibió la liberación de la tecla. Me decepcionaron los resultados de los adaptadores chinos y decidí seguir el consejo: "Si quieres hacer algo bien, hazlo tú mismo".



Pieza de hardware



Primero, era necesario pensar en el esquema del dispositivo futuro y estudiar la experiencia de otros ingenieros. El adaptador existente se veía muy bien en el exterior, así que decidí usar una carcasa, LED y cables blindados. Además, en Moscú, los cables MIDI son más caros que un adaptador chino prefabricado. Saqué el tablero chino, medí sus dimensiones y comencé a estudiar el estándar MIDI y proyectos MIDI exitosos en el dominio público.





Fig. 3 Adaptador USB-MIDI en estuche y con cables.



En el momento de escribir este artículo, conozco varios proyectos interesantes:



  1. Diagrama de la documentación del chip CH345 de Nanjing Qinheng Microelectronics.
  2. Proyectos antiguos sobre microcontroladores Atmega con implementación de software del protocolo USB. Usan el modo de baja velocidad, que está en desuso y no es compatible con Windows 7.
  3. Biblioteca MIDIUSB para placas Arduino con soporte de hardware para interfaz USB (Atmega32u4, Cortex-M), así como Maple, etc.


Los diagramas esquemáticos eléctricos de todos los proyectos contienen muchos fragmentos de muestra basados ​​en las recomendaciones del estándar MIDI . Por lo tanto, quedaba por elegir un microcontrolador con soporte para modo USB Full Speed, encontrar un optoacoplador PC900V y un enchufe DIN-5 (MIDI) a la venta.



Disposición del tablero
MIDI2USB Schematics




El corazón de mi adaptador MIDI2USB es un microcontrolador EFM8UB20F64G de 8 bits de Silicon Laboratories. Me gusta mucho y lo uso siempre que puedo. Este controlador es el sucesor (después del cambio de marca) del controlador C8051F380, que reemplazó al legendario C8051F320, un desarrollo exitoso de Cygnal, que fue comprado por SiLabs en 2003.



Enumeraré mis argumentos a favor del microcontrolador EFM8UB20F64:



  • facilidad de desarrollo de software, que se expresa en presencia de GPIO, SPI, UART, USB, PCA rápidos y fáciles de usar;
  • 8051 núcleos mejorados (1-2 ciclos por instrucción, 48MIPS), cambio de frecuencia "sobre la marcha";
  • regulador de voltaje incorporado, tolerancia de salida a + 5V, corriente hasta 100 mA;
  • generador de reloj preciso incorporado con calibración desde USB-host (± 0,25%);
  • disponibilidad de bibliotecas USBXpress, VCPXpress, USB Device API y ejemplos para un inicio rápido;
  • pura errata.


Es un placer programar este controlador como hay pocos registros y puede concentrarse en resolver un problema aplicado. Por desgracia, las operaciones aritméticas (especialmente las de 32 bits) son lentas, pero por lo demás EFM8 es bueno. Desarrollar software para dispositivos USB no es una tarea fácil. Y aquí, la principal ventaja de los controladores SiLabs son las bibliotecas USBXpress, VCPXpress, USB Device API. Incluso Texas Instruments usa controladores C8051F320 en sus tarjetas SmartRF.



El optoacoplador es el segundo componente más importante del adaptador. Decidí tomar el Sharp PC900V porque es exactamente lo que se indica en el diagrama de especificaciones MIDI recomendado. La peculiaridad de este optoacoplador son los tiempos de encendido y apagado rápidos (1μs y 2μs), así como la presencia de una salida digital. Pero también hay desventajas: el gran tamaño del microcircuito (7x10 mm) y el desgaste en un 50% después de 5 años de funcionamiento. Las dimensiones del optoacoplador no permitían marcar todos los componentes en un lado del tablero. Tampoco quería renunciar al conector MIDI, que ocupaba mucho espacio.



La parte posterior de la placa con un optoacoplador y LED.

Fig. 4 Parte posterior de la placa con optoacoplador PC900V y LED. Foto del autor.



La etapa de salida se ensambla de acuerdo con el esquema estándar recomendado en un chip lógico 74LVC2G04, que consta de dos inversores. El propósito principal de este componente es convertir los niveles de señal lógica de 3V => 5V y proporcionar una corriente de salida de al menos 10 mA.



Otra anécdota
:

- , , , , , …

. :

— - ?

— , !


El resto de componentes realizan funciones auxiliares y no afectan significativamente el funcionamiento del dispositivo. Las resistencias, condensadores, diodos y LED se pueden reemplazar razonablemente. En lugar de un conector mini-USB, puedes poner un micro-USB o hacer un conector de clavija para soldar el cable, como hacen los chinos. El conector MIDI ocupa mucho espacio y no cabe en la carcasa, por lo que solo se utiliza en la versión con adaptador sin carcasa. Las señales MIDI-IN y MIDI-OUT se enrutan al encabezado del pin para el cableado del cable. En general, la ubicación de los LED y los conectores debe ajustarse para su ubicación óptima en la carcasa.





Fig.5 Versiones depuradas y en caja del adaptador MIDI2USB. Foto del autor.



La corriente de consumo total no supera los 50 mA. Consta de las siguientes partes:



  • microcontrolador, 15mA;
  • tres LED, 15mA (3x5mA);
  • microcircuito 74LVC2G04, 10 mA;
  • optoacoplador PC900V, 10 mA.


El PCB de 2 capas fue fabricado por estadounidenses en OSH Park , 1,6 mm de espesor, 0,035 mm de cobre, material FR-4.



Parte del software



La creación de software para equipos es una etapa importante y crucial de desarrollo. Afortunadamente, todos los sistemas operativos modernos tienen controladores para dispositivos MIDI USB. La tarea se reduce y solo necesita escribir el firmware para el adaptador.



Por lo general, uso Keil uVision PK51 junto con Configuration Wizard 2, a veces IAR Embedded Workbench y muy raramente SiLabs Simplicity Studio. Cada entorno tiene ventajas y desventajas. En este proyecto decidí usar IAR porque quería tener "C con clases". Además, el compilador IAR proporciona acceso a todos los bits de los registros del sistema. Por ejemplo, P2_bit.B0 = 1; o PCA0MD_bit.WDTE = 0;



No es necesario utilizar "constantes mágicas" o expresiones de bits de varios niveles que estén llenas de CMSIS o "SI_EFM8UB2_Register_Enums.h". Por desgracia, toda esta funcionalidad está declarada en el archivo ioEFM8UB20F64G.h, que resultó ser incompatible con las bibliotecas si_toolchain.h (por ejemplo, la macro B0..B3). No traduje el proyecto a Keil uVision PK51, sino que simplemente escribí un código C compatible para todos los entornos de desarrollo.



El código del proyecto se divide en varias partes funcionales



  1. El archivo "main.c" contiene el punto de entrada, declaraciones de variables globales, una llamada para inicializar periféricos y el ciclo principal del programa.
  2. El archivo "init.c" contiene configuraciones para el reloj, puertos, UART y sus interrupciones.
  3. El archivo descriptors.c contiene descriptores USB para el dispositivo Audio Class.
  4. El archivo "midi.c" contiene dos funciones para convertir mensajes MIDI en eventos USB y viceversa. Se utiliza una máquina de estado.
  5. El archivo "usbconfig.h" contiene macros y definiciones (#define) para configurar los modos de funcionamiento de la biblioteca de API de dispositivos USB.


Echemos un vistazo a la función main () con los puertos, periféricos y el bucle principal.



int main( void )
{
	WDT_Init();                             // Disable WDTimer (not used)
	PORT_Init();                            // Initialize ports (UART, LEDs)
	SYSCLK_Init();                          // Set system clock to 48MHz
	UART0_Init();                           // Initialize UART0 @31250, 8-N-1
	USBD_Init( &usbInitStruct );            // Initialize USB, clock calibrate
	LED_IN  = 1;                            // Blink LED
	LED_OUT = 1;                            // Blink LED
	IE_EA   = 1;                            // Global enable IRQ

	while(1)
	{
		//--- MIDI => USB
		if( nMidiCount > 0 )
		{
			IE_EA  = 0;                     // Begin: Critical section
			if( USB_STATUS_OK==USBD_Write(EP1IN,aMidiBuffer,nMidiCount,false) )
			{
				nMidiCount = 0;             // Reset MIDI data byte counter
			}
			IE_EA  = 1;                     // End of: Critical section
			LED_IN = 0;                     // Turn off input LED
		}

		//--- USB => MIDI
		if( nUsbCount )
		{
			uint8_t i;
			LED_OUT = 1;                    // Turn on Led on New packet
			for(i = 0; i < nUsbCount; i++)  // Process every data byte
			{
				USB2MIDI( aUsbBuffer[i] );  // Convert USB packet into MIDI
			}
			nUsbCount = 0;                  // Reset counter
			USBD_Read(EP2OUT, aUsbBuffer, sizeof(aUsbBuffer), true);
			LED_OUT = 0;                    // Turn off Led, when done
		}
	}
}


La biblioteca de SiLabs para dispositivos USB consta de un conjunto de subrutinas que se compilan e incluyen en el proyecto según la configuración del archivo “usbconfig.h”. Esto recuerda mucho a la biblioteca "libusb, V-USB" que se encuentra en el código para microcontroladores de Atmel (ahora Microchip). Cabe señalar que SiLabs tiene una biblioteca buena y conveniente desde el punto de vista de un programador.



Los descriptores (descriptores) del dispositivo, la configuración y las interfaces juegan un papel importante en el funcionamiento de cualquier dispositivo USB. Usando estos descriptores, el dispositivo informa al host (computadora) sobre sus requisitos, capacidades, parámetros, etc. Una función para manejar solicitudes de descriptores generalmente se encuentra en cada biblioteca USB, y el programador solo debe completar correctamente las estructuras de datos que contienen estos descriptores.



Código con descriptores
SI_SEGMENT_VARIABLE
(usbDeviceDesc[], const USB_DeviceDescriptor_TypeDef, SI_SEG_CODE) =
{
	USB_DEVICE_DESCSIZE,               // bLength, 18 bytes
	USB_DEVICE_DESCRIPTOR,             // bDescriptorType, 1
	htole16(0x0110),                   // bcdUSB Ver, 1.10
	0x00,                              // bDeviceClass, 0 for Audio
	0x00,                              // bDeviceSubClass, 0 for Audio
	0x00,                              // bDeviceProtocol, 0 for Audio
	SLAB_USB_EP1IN_MAX_PACKET_SIZE,    // bMaxPacketSize0, 64 bytes
	htole16(0x1209),                   // idVendor, Free GPL (SiLabs 0x10C4)
	htole16(0x7522),                   // idProduct
	htole16(0x0100),                   // bcdDevice, 1.00
	0x01,                              // iManufacturer string
	0x02,                              // iProduct string
	0x03,                              // iSerialNumber (no serial string)
	0x01                               // bNumConfigurations
};


Todos los descriptores, topología y terminología se describen en detalle y en detalle en el estándar "Definición de clase de dispositivo de bus serie universal para dispositivos MIDI" . Y para un inicio rápido y una inmersión en el tema, basta con estudiar la información proporcionada por los programas "usbview.exe" del Windows Driver Kit 7600 o "USB Descriptor Dumper" . Algo que incluso puedes copiar a tu programa.





Fig.6 Información sobre descriptores en el programa "usbview.exe" Los



descriptores y las correspondientes matrices y estructuras se encuentran en la memoria flash del microcontrolador (segmento de código), porque estos datos no cambian (constantes). El almacenamiento de constantes en la memoria flash es un truco de programación típico que le permite ahorrar RAM.



Preste atención a los campos Vendor_ID y Product_ID en la estructura del descriptor del dispositivo. Este es un par de números para identificar de forma única el dispositivo USB. Para obtener dicho número para su dispositivo, debe pagar dinero a la organización USB-IF o enviar una solicitud al propietario del Vendor_ID existente (fabricante del microcontrolador) y obtener el Product_ID. Y puede, por ejemplo, que los chinos utilicen VID y PID más adecuados para otras personas. Para proyectos de código abierto, existe la opción de obtener un Product_ID gratuito .



Otro punto a tener en cuenta al desarrollar dispositivos USB de la clase de audio MIDI Streaming son los conectores (Jack). Los conectores son entidades imaginarias (virtuales) para describir la topología y las conexiones entre un dispositivo y un host. Son de entrada (In Jack) y salida (Out Jack), internos (Embedded) y externos (Externos). Cada conector tiene un Jack_Id único (número de 0 a 15). Los conectores de salida contienen el número de Id. De fuente, es decir, número de conector para la conexión. Finalmente, los puntos finales de audio (EP) funcionan sobre los canales formados (flujos de entrada y salida). Estos son EP Bulk casi comunes que tienen información de enlace de conector en sus descriptores.



Jacks integrados y externos

Figura: 7 Tomas y flujos virtuales a USB (clase MIDI).



Descriptores de jack MIDI
	// EMB:  IN Jack #1 <-----> EXT: OUT Jack #4
	// EMB: OUT Jack #3 <-----> EXT:  IN Jack #2

	//--- Class-Specific MS Interface Header Descriptor, p.40
	USB_MIDI_INTERFACE_DESCSIZE,       // bLength, 7 bytes
	USB_CS_INTERFACE_DESCRIPTOR,       // bDescriptorType, 0x24
	MIDI_CS_IF_HEADER,                 // bDescriptorSubtype, 0x01
	0x00,                              // bcdADC(LSB)
	0x01,                              // bcdADC(MSB), 0x0100 (version)
	0x41,                              // wTotalLength(LSB), 65 bytes
	0x00,                              // wTotalLength(MSB)

	//--- MIDI IN JACK EMB(it connects to the USB OUT Endpoint), p.40
	USB_IN_JACK_DESCSIZE,              // bLength, 6 bytes
	USB_CS_INTERFACE_DESCRIPTOR,       // bDescriptorType, 0x24
	MIDI_CS_IF_IN_JACK,                // bDescriptorSubtype, 0x02
	MIDI_JACK_TYPE_EMB,                // bJackType, 0x01 (embedded)
	1,                                 // bJackID, #1
	0,                                 // Jack string descriptor, unused
	//--- MIDI IN JACK EXT, p.40
	USB_IN_JACK_DESCSIZE,              // bLength, 6 bytes
	USB_CS_INTERFACE_DESCRIPTOR,       // bDescriptorType, 0x24
	MIDI_CS_IF_IN_JACK,                // bDescriptorSubtype, 0x02
	MIDI_JACK_TYPE_EXT,                // bJackType, 0x02 (external)
	2,                                 // bJackID, #2
	0,                                 // Jack string descriptor, unused

	//--- MIDI OUT JACK EMB (connects to IN Endpoint), p.41
	USB_OUT_JACK_DESCSIZE,             // bLength, 9 bytes
	USB_CS_INTERFACE_DESCRIPTOR,       // bDescriptorType, 0x24
	MIDI_CS_IF_OUT_JACK,               // bDescriptorSubtype, 0x03
	MIDI_JACK_TYPE_EMB,                // bJackType, 0x01
	3,                                 // bJackID
	1,                                 // bNrInputPins
	2,                                 // baSourceID, this <=> Jack #2
	1,                                 // baSourcePin
	0,                                 // iJack, unused
	//--- MIDI OUT JACK EXT, p.41
	USB_OUT_JACK_DESCSIZE,             // bLength, 9 bytes
	USB_CS_INTERFACE_DESCRIPTOR,       // bDescriptorType, 0x24
	MIDI_CS_IF_OUT_JACK,               // bDescriptorSubtype, 0x03
	MIDI_JACK_TYPE_EXT,                // bJackType, 0x02
	4,                                 // bJackID
	1,                                 // bNrInputPins
	1,                                 // baSourceID, this <=> Jack #1
	1,                                 // baSourcePin
	0,                                 // iJack, unused




El intercambio de datos en un dispositivo de audio de clase USB MIDI consiste en la transmisión de paquetes de 32 bits (USB-MIDI Event Packet). Se reciben mensajes de 1, 2 o 3 bytes desde el dispositivo MIDI. Cuando se transfiere a través de USB, se agrega un byte principal con un número de cable y un código de comando a estos bytes. Si el paquete tiene menos de 4 bytes, se rellena con 0. En la versión actual del firmware, no se rellena con ceros hasta el borde de 32 bits. Funciona. La pregunta permanece abierta.



Por ejemplo, en el cable # 1, el comando para presionar la tecla Note On (tiempo de transmisión 960us) se convierte en el siguiente paquete:

MIDI: 0x90 0x60 0x7f => USB: 0x19 0x90 0x60 0x7f


Paquete de eventos USB-MIDI

Fig. 8 Esquema del paquete de eventos USB-MIDI de la especificación USB.



typedef union
{
	struct PACKET
	{
		uint8_t  cable : 4;            // Cable Number (we use #0)
		uint8_t  cin   : 4;            // Code Index Number (cmd: 0x08)
		uint8_t  cmd;                  // MIDI command (status byte)
		uint8_t  data1;                // MIDI data byte #1
		uint8_t  data2;                // MIDI data byte #2
	};
	uint8_t buffer[sizeof(struct PACKET)];
} MIDI_EVENT_PACKET;


La conversión directa e inversa se realiza mediante las funciones MIDI2USB () y USB2MIDI () . En estas funciones se utiliza una máquina de estado, cuando, a medida que llegan los datos de entrada, la función pasa del estado inactivo (IDLE) al estado de recibir comandos (STATUS), y luego al estado de recibir datos (DATA) y, finalmente, enviar datos con un retorno a su estado original. Expectativas.



En el protocolo MIDI, los bytes de datos son esencialmente de 7 bits (0..127). Siempre tienen el 8vo bit más significativo puesto a 0. Los comandos (bytes de estado), por el contrario, siempre vienen con el bit más significativo puesto a 1, es decir. tienen valores de 128 a 255.



Tipos de bytes MIDI

Fig. 9 Tipos de bytes en el protocolo MIDI.



Broma sobre la capacidad de dígitos de los números
:

— , ?

— H, .

— , 11-22-33?

— H, 11-22-34.

— H ! , !


Todos los esquemas y fuentes, así como el firmware terminado, están en mi repositorio de git . Licencia MIT.



Software



Después de instalar la placa, se debe programar el microcontrolador. Para hacer esto, puede usar un adaptador de depuración SiLabs C2 patentado / clonado, o J-Link v10 + (con soporte EFM8), o un cargador de arranque actualizado en la fábrica (revisión Rev-B) o, finalmente, Arduino con un script apropiado. Para comprobar y depurar mensajes MIDI, MIDI-OX es de gran ayuda .



MIDI-OX

Fig.10 Interfaz del programa MIDI-OX.



Si trabaja con Cubase, debe instalar los controladores Asio, porque cuando usa DirectSound y DirectInput, hay un retraso entre presionar una tecla y tocar una nota. La latencia no está relacionada con el hardware y es una característica de la implementación del sistema operativo. En general, el dispositivo realiza sus funciones perfectamente con el instrumento Casio CDP-100.



Configuración MIDI de Cubase

Fig.11 Interfaz de Cubase 5.



El firmware experimental generó el mayor flujo posible de notas y otros comandos MIDI. La cacofonía fue terrible, pero todo funcionó según lo previsto. Y con MuseScore 3.2 puede grabar y reproducir archivos mid.



Última broma
1990-. . — . . :

— , !

— , ! — !

— ! !

— … . !

— ! !

. . , , , … . , . :

— , , ?


Resultados del trabajo



¡El adaptador funciona! Parece que logré hacer un buen conversor de MIDI a USB. Para mi dispositivo, utilicé un estuche, algunas partes y cables de un adaptador chino. El conector mini-USB terminó profundamente en la carcasa y tuve que rehacer el cable USB y trabajar con un archivo. Los LED, aunque en ángulo, encajan firmemente en los orificios. La placa debe modificarse para el caso chino.



Cable mini-USB

Figura: 12. Enchufe mini-USB compacto desmontado.



La decisión de utilizar el microcontrolador EFM8UB20 de 8 bits puede parecer controvertida para algunos. Hay otras opciones y controladores, por supuesto. Una forma alternativa es elegir una solución puramente de hardware en el convertidor CH345 y hacer el dispositivo de acuerdo con el circuito de referencia recomendado por los chinos. Pero mi versión es universal, porque le permite cambiar el firmware, agregar la funcionalidad deseada o corregir los errores encontrados. Al final, obtuve conocimiento, experiencia y satisfacción moral con el proyecto terminado. Y finalmente, terminé mi artículo y tú terminaste de leerlo.



Enlaces útiles





Gracias por su atención.



All Articles