¿Hay alguna razón por la cual el uso de una velocidad de transmisión de 31250 en un proyecto Arduino MIDI podría causar problemas?

Tengo un codificador óptico de 600 pulsos por revolución (y algunas otras cosas) conectado a un Arduino Uno (lo probé en r2 y r3) a través de los pines de interrupción 2 y 3.

Mientras esperaba que llegara mi conector MIDI por correo, traté de conectar mi configuración a mi computadora a través del puerto serial USB, junto con midi sin pelo y loopMidi. Loopmidi es un puerto MIDI virtual, y el midi sin pelo une los puertos serie con los puertos MIDI, virtuales o no. Usé una tasa de baudios de 115200, porque pensé que no estaría de más ir demasiado alto. Todo parecía funcionar bastante bien en Mixxx. Muy bien, en realidad. Parecía muy receptivo y preciso. El codificador no perdió el ritmo, sin importar qué tan rápido amplíe esa cosa.

Así que estaba muy emocionado cuando llegó el conector MIDI. Lo puse en mi protoboard y cambié

Serial.begin(115200);

a

Serial.begin(31250);

y lo probé en Mixxx. Ahora, si giro el codificador moderadamente rápido en una dirección, el registro virtual se moverá en esa dirección y luego girará repentinamente hacia el otro lado y luego volverá. ¿Supongo que al codificador le faltan pulsos?

Lo probé en dos cables usb-midi diferentes de $6, así como en mi M-Audio Fast Track Ultra.

Luego pensé que tal vez tenía algo que ver con la tasa de baudios más baja (115200 frente a 31250). Cambié la tarifa a 38400 y pasé por USB serial. Funcionó muy bien. Incluso probé 19200. Perfecto. Incluso en 9600, funcionó.

¿Por qué está pasando esto? ¿Es el circuito usb-serial en Arduino, junto con algún software gratuito, realmente más confiable que un cable midi y una interfaz de audio de $ 300, incluso cuando el arduino está configurado a velocidades de transmisión muy bajas? ¿O hay algo en la extraña tasa de baudios de 31250 que causa problemas en el Arduino?

No he tenido la oportunidad de intentar usar la tasa 31250 a través del usb-serial, porque midi sin pelo no permite esa tasa.

EDITAR: Aquí está la parte relevante del código y la parte relevante del circuito. Hay algunos otros componentes que podrían empeorar el problema, pero incluso sin esos componentes, el codificador óptico no funciona en 31250.

    enum PinAssignments {
  encoderPinA = 2,   // rigth
  encoderPinB = 3,   // left
};

volatile int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 0;   // change management

boolean A_set = false;              
boolean B_set = false;

void setup() {

  pinMode(encoderPinA, INPUT_PULLUP); 
  pinMode(encoderPinB, INPUT_PULLUP); 

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);
    Serial.begin(31250);
}

void loop() {

 if (encoderPos != lastReportedPos){
   Serial.write(0xB0);
   Serial.write(0x27);
   Serial.write(64 + encoderPos - lastReportedPos);
   encoderPos = 0;
   lastReportedPos = encoderPos;

 }


}

// Interrupt on A changing state
void doEncoderA(){

    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;




}

// Interrupt on B changing state, same as A above
void doEncoderB(){

    B_set = !B_set;
    if( B_set && !A_set ) 
      encoderPos -= 1;

  }

el circuito basico

Es raro. Otra posibilidad: ¿la extraña tasa de baudios interfiere de alguna manera con las interrupciones del hardware?

EDITAR nuevamente: ejecuté mixxx en modo mididebug y amplí el registro en una dirección. Esto estaba en el registro:

Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3E"
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F"
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F"
... for a while and then ...
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41"
...
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x41" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F" 
Debug [Controller]: "MIDI status 0xB0 (ch 1, opcode 0xB), ctrl 0x27, val 0x3F"

Entonces pasa de repetir 63 con un 62 ocasional a repetir repentinamente 65 con un 66 ocasional. Una velocidad de 64 significa que la rueda no se mueve. 63 significa moverse en sentido antihorario un pulso. 65 es un pulso en el sentido de las agujas del reloj. O viceversa, dependiendo de cómo esté conectado.

¿Eso implica que el problema está en el arduino y no en los adaptadores midi?

Agregue el diagrama de circuito que usa para conectar el Arduino al cable MIDI. ¿Cuánto mide el cable que usas?
No tengo los cables delante de mí, pero un cable tenía unas 12 pulgadas. El otro alrededor de 30 pulgadas. Los adaptadores USB-Midi baratos tienen cables MIDI incorporados que probablemente tengan menos de 12 pulgadas de largo.
¿Tiene un alcance para mirar la forma de onda? ¿Puedes verificar el voltaje de la fuente de alimentación para el ATmega en el Arduino?
@jippie No tengo un alcance. ¿Quieres que verifique el voltaje a través de 5V y el pin de tierra? ¿O entre pines específicos en el propio chip (7 y 20; o 22 y 20)? Antes de publicar la pregunta, probé algunas fuentes de alimentación diferentes con el mismo resultado. La alimentación USB, el adaptador de CC de 5 V y el adaptador de CC de 12 V tuvieron más o menos el mismo resultado.
¿Qué tal enviar una secuencia de bytes preprogramada y ver si llega con éxito al destino? Trate de descartar el codificador. El voltaje a través de los pines de alimentación del controlador es mejor, pero tenga cuidado de no cortocircuitar nada. Supongo que medir los pines 5V y GND debería ser correcto y seguro.

Respuestas (2)

Su tasa de baudios no es un divisor entero de su reloj MCU. La tasa de baudios se divide del reloj de la MCU. Es fácil obtener 9600, 19200 y otras tasas con un divisor entero del reloj. por ejemplo, si tiene un cristal de 6.144MHz, para obtener 19200 necesita dividir por 3200.

Para las velocidades de datos impares en diversas aplicaciones (audio, video analógico y muchas otras), se utilizan cristales específicos para obtener un divisor de números enteros, por ejemplo, un circuito NTSC puede tener un cristal de 5,034963 MHz para generar las diversas señales de sincronización, consulte

Frecuencias del oscilador de cristal - WikiPedia

Si su MCU tiene un generador de reloj interno, intente ajustarlo a un valor diferente para obtener un divisor entero; de lo contrario, el error de bit será demasiado alto.

Acabo de echar un vistazo y el Uno funciona a 16 MHz, a 16 ciclos por bit para el UART 1M/31250 = 32, por lo que establecer UBRR en 31 no debería dar ningún error. Si bien 31250 es una tasa de baudios inusual según los estándares tradicionales, se divide en frecuencias pares de MHz, por lo que probablemente la usaron.
El valor de UBRRn debe ser 31 (16MHz/31250) -1, un valor exacto que es óptimo para el USART. Extraño.
Vi algo sobre esto en mis búsquedas. Pensé que posiblemente era la causa de mis problemas, pero no estaba seguro de cómo hacer los cálculos. Entonces, ¿esto no debería ser un problema entonces? ¿Las matemáticas se comprueban?
El divisor es óptimo, con 0% de error (usualmente los divisores introducen un pequeño error), a continuación revisaría las rutinas USART, quizás no soporten la impar tasa de baudios.

Una forma definitiva de probar es conectar su salida MIDI a su entrada MIDI y configurar el sistema usando el puerto serie USB como antes, pero enrutándolo (a través de su software de 'cable midi virtual') al puerto de salida MIDI en la computadora . Esto probará para ver si su hardware USB MIDI está funcionando de manera confiable a la velocidad de datos a la que funciona el dispositivo. En mi experiencia, muchos dispositivos USB MIDI están diseñados básicamente para teclados (que solo envían 3 bytes cuando se presiona o suelta una tecla) y tienen extraños problemas de desbordamiento cuando transmites a la velocidad máxima de 31,25 Kbit. Esto también depende mucho del sistema operativo/controlador.

Supongo que también querrá asegurarse de que el transmisor/controlador de línea esté funcionando. Publique un esquema de su circuito Arduino MIDI-out. ¿Qué estás usando como controlador de salida?

Estoy haciendo muchas suposiciones, pero suponiendo que Arduino Uno a 16 MHz, 31250 bps es exactamente compatible (0% de error) con un valor de registro UBBR de 31, de acuerdo con esto: Calculadora de tasa AVR UART . Esto me indica que la tasa de bits de MIDI no debería ser un problema (de hecho, tengo entendido que 31250 fue elegido en gran medida para que fuera fácil trabajar con un reloj de 1 MHz, a diferencia de las tasas RS-232 derivadas de la máquina de teletipo).

Además, un volcado de los mensajes que recibe la PC a través de un rastreador o monitor MIDI sería muy útil para la depuración.

Si conecta la salida MIDI a la entrada MIDI, ¿no permanecerían ocultos los problemas de velocidad en baudios asociados con la plataforma en cuestión, ya que ambos comparten el mismo reloj?
Bueno, tenía más ganas de probar el controlador con información de tarifa completa. Mi experiencia es que algunos cables USB-MIDI se ahogan debido a desbordamientos de búfer y cosas por el estilo cuando se conectan a algo que envía más datos que un teclado.
Intentaré conectar la entrada MIDI a la salida MIDI y me pondré en contacto contigo. También intenté hacer 31250 en serie y ver los resultados en Putty. Pero la pantalla se inundó con demasiadas cosas. Lo haré de nuevo cuando llegue a casa y guardaré el archivo. Y también intentaré mirar el registro de Mixxx, que se puede configurar para registrar mensajes midi.
¿Puede cambiar serial.print para imprimir en (ascii) la consola el valor de la variable del codificador y mirarlo a través de RealTerm? Su configuración de decodificación en cuadratura es inusual. Por lo general, no es una buena idea interrumpir tanto en A como en B. Por ejemplo, si interrumpe en cualquier cambio de A, si A==B entonces B adelanta a A, si A!=B entonces A precede a B. La forma en que tenerlo escrito parece mucho más complejo, no voy a decir que no funcionará, pero puede haber algunos efectos no deseados allí.
Además, la lógica de su programa parece algo confusa. 'lastreportedPos' es casi siempre 0, excepto si se disparó una interrupción en el pequeño intervalo de tiempo antes de que se asignara y la línea anterior estableciera 'encoderPos' en 0..? Además, ¿por qué lastreportedPos no está firmado, mientras que encoderPos está firmado? La forma típica de usar un codificador es realizar un seguimiento de la posición (ya sea desde una señal de índice, si tiene una, o desde alguna posición arbitraria en el encendido). Si desea enviar una posición relativa, use un delta-x como lo está haciendo, excepto que no restablece la posición justo antes...
Finalmente, debe tener en cuenta que serial.write() está bloqueando. Esto significa que el programa principal esperará en cada comando serial.write() hasta que se transmita el byte completo. Las interrupciones aún deberían ocurrir a menos que la rutina las deshabilite, pero esto significa que el 99.9+% de su tiempo de ejecución se gasta en serial.write (), por lo que cambiar la velocidad en baudios podría cambiar el comportamiento de su programa si no está escrito. robustamente (ver mis comentarios anteriores).
Si solo tuviera interrupciones en A, ¿no obtendría solo la mitad de la resolución?
Copié el código del codificador de otro lugar. Es un poco abstracto, y no entiendo partes como el lastencodedpos sin firmar (¿creo que tiene algo que ver con las matemáticas del complemento 2?), Pero parecía ser el ejemplo de ejecución más rápido que pude encontrar. Restablecer la posición a 0 fue mi forma rápida y sucia de lidiar con el rollover de int de 32,767 a -32,768. En ese momento, parecía mejor tener interrupciones rápidas a expensas de un bucle principal lento.
En cuanto al bloqueo serial.write, no parece causar problemas a velocidades "normales". En 115200, el codificador mueve un tic (rara vez 2) entre escrituras en serie. En 9600, el codificador puede mover 3 o 4 pasos entre escrituras, pero no afecta la precisión (solo la latencia) porque se informan 3 o 4 pasos. El problema es que en 31250, el código del codificador ya no funciona.