Precisión de tiempo del secuenciador MIDI usando el Arduino

11

Yo construyo estos secuenciadores de música .

ingrese la descripción de la imagen aquí

Solo que no es exactamente un secuenciador, es una interfaz física para un secuenciador. El secuenciador es una aplicación que se ejecuta en una computadora portátil a la que se conecta el secuenciador, esto le permite al usuario hacer bucles de batería sobre la marcha. Es bastante divertido, pero requiere una computadora portátil porque el secuenciador no está 'integrado'.

Lo que me encantaría es hacer la secuencia a bordo de mi dispositivo.

Ahora supongamos que sé cómo resolver el cumplimiento de la clase para la conectividad USB MIDI, y también supongamos que puedo descubrir cómo conectar un arduino para enviar notas MIDI desde un puerto DIN de 5 pines. Lo que más me preocupa es la deriva del tempo a lo largo del tiempo debido al tiempo inconsistente en cantidades de minutos en cada ejecución del ciclo de eventos.

Algunas cosas que sé:

  1. No debe confiar en delay()controlar el bucle de tempo. La demora detiene toda operación del firmware, y eso no puede funcionar porque necesito sondear la interfaz física del usuario para ver los cambios mientras se ejecuta la secuencia.

  2. Los cálculos basados ​​en millis()son mejores porque el firmware puede continuar funcionando y actuar cuando ha transcurrido un cierto recuento.

  3. Aunque ninguno de mis controles físicos está activando rutinas de interrupción, algunas operaciones pueden retrasar la loop()ejecución de la unidad principal . Si diseño una función que espera la entrada del usuario, eso obviamente puede causar un problema de perder una "fecha límite" para actuar si la millis()cuenta ha terminado. Sé que este problema es de mi propio diseño ...

Preguntas:

A. ¿Es el arduino basado en AVR un microcontrolador apropiado para sondear una interfaz de usuario y ejecutar un ciclo de sincronización de misión crítica? Sé que ahora hay un Arduino basado en ARM que es mucho más rápido. ¿Teensy 3.0 sería una mejor alternativa? Ambas son placas de 3.3V, así que ese es otro conjunto de problemas con los que trabajar ... pero lo ignoraré por ahora.

B. ¿Debería dividir la tarea en dos microprocesadores? Uno para manejar el sondeo y actualizar la interfaz de usuario y otro para el ciclo de sincronización de misión crítica.

C. ¿Algo más?

Mi objetivo principal es no tener que usar una computadora en absoluto. También quiero calcular el swing, pero en este caso, el swing no significa nada si no tengo un tempo bloqueado y preciso. ¡Gracias por su consejo!

Steve Cooley
fuente
1
Arduino siempre establece algunas rutinas de interrupción, lo que causa inquietud. En muchos casos esto no es un problema, pero es bueno tenerlo en cuenta. noInterrupts();detiene el jitter, pero también detiene todas las interrupciones deseadas.
jippie
1
Cuando dices "haz la secuencia a bordo", ¿eso significa configurar los tiempos por compás, BPM y una pista de tick a bordo? Entonces, ¿presumiblemente desea recordar las pulsaciones de botones que ocurrieron dentro de la barra para que los "cerebros" del dispositivo puedan alimentar las notas midi a su computadora portátil? Entonces, ¿desea eliminar ciertos sonidos de percusión si los golpea nuevamente sobre la nota que se grabó previamente? Etc .. ¿hasta dónde quieres llegar? ¿Almacenamiento de tus latidos? ¿Crear una secuencia de barras que corresponda a una pista completa? ¿Edición de barras específicas? ¿Alteración temporal de barras específicas? Todo come CPU, así que busca la mejor CPU.
Andy alias
Sí, todo eso.
Steve Cooley
2
¡Ese es un caso encantador que has hecho!
shuckc
3
Además de lo que otros han dicho, esto parece un pensamiento que tal vez tengas la intención de producir y vender. Un Arduino cuesta $ 20, mientras que un AVR cuesta $ 2. No solo obtendrá el control sobre el hardware que requiere su aplicación, sino que también ahorrará mucho dinero.
Phil Frost el

Respuestas:

5

Las interrupciones son su amigo para las tareas delicadas de tiempo, pero solo si coloca los aspectos críticos de tiempo en la interrupción, y no hay otras interrupciones que tengan mayor prioridad. Los microcontroladores en el Arduino "basado en AVR" (por ejemplo, el ATmega328P) tienen prioridades de interrupción fijas como se detalla en la página 58ff de la hoja de datos . Entonces, si usó TIMER2 COMPA como su interrupción de tiempo crítica y ninguna otra interrupción, debería estar bien (ya que tiene la máxima prioridad). Si también desea usar interrupciones de menor prioridad, debe asegurarse de que todas ellas vuelvan a habilitar las interrupciones globales al ingresar a su rutina de servicio de interrupciones:

Cuando ocurre una interrupción, el bit I de habilitación de interrupción global se borra y todas las interrupciones se deshabilitan. El software del usuario puede escribir la lógica uno en el bit I para habilitar las interrupciones anidadas. Todas las interrupciones habilitadas pueden interrumpir la rutina de interrupción actual.

(p. 14 de la hoja de datos )

Esto es ligeramente diferente en Arduinos basados ​​en ARM, ya que su núcleo Cortex-M3 tiene un "Controlador de interrupción vectorial anidado", donde las prioridades no son fijas (se pueden configurar en el software), y el manejo de interrupciones anidadas es la norma. Entonces, para cronometrar aplicaciones críticas, el Arduino basado en ARM le daría más flexibilidad. Sin embargo, no creo que sea realmente necesario para su aplicación.

La pregunta más importante es qué tan fácil se pueden implementar estas cosas con las bibliotecas Arduino. Para obtener el mejor rendimiento, probablemente tendrá que codificar fuera de las bibliotecas hasta cierto punto, al menos para los bits críticos de tiempo, es decir, evitar cosas como delay () o millis () por completo.

Si necesita o no dividirse depende de cuánto procesamiento tenga intención de hacer. Nuevamente, salir de las bibliotecas puede potencialmente brindarle un mejor rendimiento.

fm_andreas
fuente
3

Esto, con la programación adecuada, definitivamente se puede hacer en un ATmega328P (dependiendo en cierta medida de la complejidad del loop de batería. Supongo que ~ <50 eventos de batería en el loop. ¿Es razonable?).

Tenga en cuenta que dije ATmega328P , no necesariamente un Arduino .

El entorno Arduino tiene muchas cosas predeterminadas que se ejecutan en segundo plano, lo que hace que la programación extremadamente determinista (ya que necesitará algo crítico para el tiempo) sea un desafío.

La verdadera pregunta que debe hacerse aquí es: ¿qué tan interesado está en la programación y qué tan interesado está en desarrollar un instrumento?

Si bien estoy bastante seguro de que es posible hacer todo lo que desee en un solo ATmega (bucle de batería, múltiples entradas analógicas, LCD, botones, interfaz MIDI), la verdadera pregunta es ¿cuánto trabajo será exprimir todo? Nuevamente, ¿desea aprender a optimizar el código MCU incorporado o construir instrumentos? Es muy fácil sólo tiene que ir a un rápido MCU si es necesario, pero es necesario para determinar el rendimiento MCU lo que necesita ahora , por lo que seis meses de trabajo en, no se dan cuenta que no puedes bastante conseguir que todo funcione tan rápido como necesitar.


Si yo fuera usted, lo primero que haría es hacerlo funcionar sin cosas de arduino (básicamente, trátelo como un ATmega en bruto y use AVR studio o similar). Luego, puede analizar de manera mucho más eficaz qué tipo de rendimiento necesita y si ATmega puede administrarlo.

Una vez que esté libre de las cosas de arduino, es mucho más libre de usar diferentes MCU (generalmente son más similares que diferentes. Si puede descubrir uno de su documentación, probablemente pueda hacer lo mismo para los demás).

He estado trabajando con los dispositivos ATxmega MUCHO recientemente, y son realmente agradables. Obtiene tres prioridades de interrupción, que facilitan mucho la gestión de cosas críticas. También son muy agradables para trabajar (¡diseños periféricos sanos! ¡Convenientes estructuras de puertos! Etc ...).

También están los dispositivos LPC de NXP, que están basados ​​en ARM, así como algunos de los dispositivos ARM de Atmel (como se usan en Arduino Due), o las MCU STM32 de ST. Cualquiera de estos tendrá un rendimiento significativamente mayor que un ATmega, o incluso un ATxmega.

La desventaja principal de un procesador más grande y potente es el precio, pero a menos que esté produciendo miles de esas unidades, los costos de ensamblaje y fabricación por unidad excederán en gran medida la diferencia de costo (que probablemente sea solo unos pocos dólares) ) que es básicamente irrelevante.

Connor Wolf
fuente
Bien dicho: para un producto comercial, un Arduino simplemente no es el camino a seguir: tienen mucha energía, son lentos y el IDE no está diseñado para un código óptimo (rápido / pequeño), sino más bien conveniencia y fácil aprendizaje. Por un costo menor, incluso podría tener un STM32 F4 (Cortex M4 de 32 bits> 100MHz) o similar, aunque esto sería excesivo. Creo que algo como uno de los PIC32 más pequeños, Cortex M3 o un AVR32 es probablemente el camino a seguir, como usted menciona. Numerosas prioridades de interrupción, DMA, periféricos sofisticados, potencia rápida / baja y mucha RAM lo convierten en una opción fácil en comparación con un Arduino.
Oli Glaser el
@OliGlaser: creo que debes delinear claramente entre un Arduino y un ATmega . Puede hacer un código pequeño y rápido en un ATmega, y ese ATmega incluso puede estar en una placa Arduino. El "IDE" de Arduino, por otro lado, es uno de los editores de código más mierdos que he usado. Por otro lado, el gestor de arranque OptiBoot es muy bueno. El hecho de que algunas partes sean basura no significa que deba tirar todo.
Connor Wolf
Absolutamente, me refería al Arduino en su conjunto, placa e IDE incluidos, no al ATmega, que estoy seguro es tan bueno como cualquier otro uC comparable (PIC16 / 18F, etc.) Lo hubiera incluido en mi lista pero Creo que con el precio entre 8 y 16/32 bits está tan cerca hoy en día, probablemente sea una buena idea gastar $ 1 extra y saber que tiene la potencia del procesador de sobra (a menos que, como mencione, estamos hablando de grandes cantidades y construido al precio más bajo absoluto, pero luego dudo que un Arduino hubiera sido considerado :-))
Oli Glaser
1

Necesitaba leer sobre los temporizadores antes de comenzar a pensar en la precisión del tiempo (también construir un secuenciador midi con un arduino, aunque está garantizado que se verá menos genial que esos ^^). Esta serie de artículos ha sido la más informativa:

http://maxembedded.com/category/microcontrollers-2/atmel-avr/avr-timers-atmel-avr/

En este momento creo que mi solución para obtener el tiempo preciso será.

A. Use el AVR arduino

B. Mantenga la tarea en un microprocesador

C. Use sabiamente preescaladores, temporizadores e interrupciones para obtener la precisión que se necesita.

ACTUALIZAR

Utilizando el tutorial midi básico para Arduino y después de mirar este artículo sobre temporizadores y preescaladores, se me ocurrió el siguiente código. El código utiliza el modo timer1 y CTC para reproducir una nota midi cada cuarto de segundo y una nota cada cuarto de segundo (que debería ser exactamente 120 lpm). Lamentablemente, esto sigue llegando más lento que 120bpm, aunque esto es lo más cerca que he llegado ...

// Includes
#include <avr/io.h>
#include <avr/interrupt.h>

int last_action=0;

void setup()
{
    //  Set MIDI baud rate:
    Serial.begin(31250);

    // initialize Timer1
    cli();          // disable global interrupts
    TCCR1A = 0;     // set entire TCCR1A register to 0
    TCCR1B = 0;     // same for TCCR1B

    // set compare match register to desired timer count:
    OCR1A = 15624;
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    // Set CS12 bits for 256 prescaler:
    TCCR1B |= (1 << CS12);
    // enable timer compare interrupt:
    TIMSK1 |= (1 << OCIE1A);
    // enable global interrupts:
    sei();
}

void loop()
{
    // do some crazy stuff while my midi notes are playing
}

ISR(TIMER1_COMPA_vect)
{
  // Turn notes on
  if (last_action == 0) {
    send_note(0x90, 60, 0x45);
    last_action = 1;

  // Turn notes off
  } else {
    send_note(0x90, 60, 0x00);
    last_action = 0;
  }
}

//  plays a MIDI note
void send_note(int cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch);
  Serial.write(velocity);
}

ACTUALIZAR

He estado luchando con esto durante ~ 24 horas y finalmente obtuve algunas respuestas del foro. Creo que el código que usé arriba ^^ es bastante bueno. Usando el ISR, usando el modo CTC y los preescaladores, etc. Después de llegar al foro , creo que la solución es menos acerca de lograr precisión en el secuenciador midi, sino de conectar toda mi configuración de hardware (mis sintetizadores y samplers) a la misma reloj midi, ya sea que el reloj provenga o no del Arduino.

crunkchitis
fuente
0

Dependiendo de cuán gradualmente desee hacer la transición de una computadora atada a un sistema basado en µC, puede considerar colocar una Raspberry Pi dentro de esa caja ($ 25-35 al por menor ). De esa manera, puede tener una computadora basada en Linux completa (aunque de baja potencia) con puertos USB y pines GPIO.

Rob Starling
fuente
Estoy seguro de que hay escudos de expansión o como los llamen para el Pi, pero el tablero de valores tiene 17 pines GPIO. Estoy usando cada pin en el mega arduino. 31 tactos + 30 LED, 10 entradas analógicas. 70+ E / S.
Steve Cooley
Ah, quise decir que si el objetivo inmediato era eliminar la computadora externa, podría mantener el "secuenciador [que] es una aplicación que se ejecuta en una computadora portátil" y ejecutarlo en el Pi, conectado internamente a su sistema existente de la misma manera que es conectado ahora
Rob Starling
@SteveCooley: suena como si necesitaras mirar en multiplexación de E / S / matrices de botones. No debería necesitar una línea de E / S dedicada por botón.
Connor Wolf
@SteveCooley - Demonios, en realidad ni siquiera necesitas una matriz de botones. Puede hacer TODAS sus E / S digitales utilizando solo 4 pines rPi. Simplemente cuelgue todos los botones y LED de algunos registros de desplazamiento (paralelo-serie para los botones, serie-paralelo para los LED), y maneje los registros de desplazamiento desde el puerto SPI del rPi. Debería obtener fácilmente tasas de actualización> 1Khz para toda la matriz con la ayuda del hardware SPI.
Connor Wolf
Si la única razón por la que usa un Arduino Mega es el IO, está gastando mucho dinero en algo que se puede hacer muy fácilmente con unos pocos dispositivos externos, por <$ 3.
Connor Wolf