Gibt es einen Grund, warum die Verwendung einer Baudrate von 31250 in einem Arduino MIDI-Projekt Probleme verursachen könnte?

8

Ich habe einen optischen Encoder mit 600 Impulsen pro Umdrehung (und einige andere Dinge), der über die Interrupt-Pins 2 und 3 mit einem Arduino Uno (auf r2 und r3 ausprobiert) verbunden ist.

Während ich darauf wartete, dass meine MIDI-Buchse per Post einging, versuchte ich, mein Setup zusammen mit hairless-midi und loopMidi über den seriellen USB-Anschluss mit meinem Computer zu verbinden. Loopmidi ist ein virtueller MIDI-Port, und hairless-midi verbindet serielle Ports mit virtuellen oder anderen MIDI-Ports. Ich habe eine Baudrate von 115200 verwendet, weil ich dachte, es könnte nicht schaden, zu hoch zu gehen. In Mixxx schien alles ziemlich gut zu funktionieren. Wirklich gut, eigentlich. Es schien sehr reaktionsschnell und genau zu sein. Der Encoder hat keinen Beat verpasst, egal wie schnell ich das Ding überspanne.

Ich war also ziemlich aufgeregt, als die MIDI-Buchse ankam. Ich legte es in mein Steckbrett und zog mich um

Serial.begin(115200);

zu

Serial.begin(31250);

und testete es in Mixxx. Wenn ich den Encoder jetzt mäßig schnell in eine Richtung drehe, bewegt sich der virtuelle Datensatz in diese Richtung und dreht sich dann plötzlich in die andere Richtung und dann wieder zurück. Ich gehe davon aus, dass dem Encoder Impulse fehlen?

Ich habe es in zwei verschiedenen 6-Dollar-USB-Midi-Kabeln sowie in meinem M-Audio Fast Track Ultra versucht.

Dann dachte ich, dass es vielleicht etwas mit der niedrigeren Baudrate zu tun hat (115200 vs 31250). Ich habe die Rate auf 38400 geändert und bin über USB seriell gegangen. Es hat super funktioniert. Ich habe es sogar mit 19200 versucht. Perfekt. Sogar bei 9600 hat es funktioniert.

Warum passiert dies? Ist die USB-serielle Schaltung im Arduino zusammen mit einer kostenlosen Software wirklich zuverlässiger als ein Midi-Kabel und ein 300-Dollar-Audio-Interface, selbst wenn das Arduino auf sehr niedrige Baudraten eingestellt ist? Oder gibt es etwas an der seltsamen 31250-Baudrate, das Probleme im Arduino verursacht?

Ich hatte keine Chance, die 31250-Rate über die USB-Serie zu verwenden, da haarloses Midi diese Rate nicht zulässt.

EDIT: Hier ist der relevante Teil des Codes und der relevante Teil der Schaltung. Es gibt einige andere Komponenten, die das Problem möglicherweise verschlimmern, aber auch ohne diese Komponenten funktioniert der optische Encoder bei 31250 nicht.

    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;

  }

die Grundschaltung

Es ist komisch. Eine andere Möglichkeit: Beeinträchtigt die seltsame Baudrate irgendwie die Hardware-Interrupts?

Nochmals BEARBEITEN: Ich habe mixxx im Mididebug-Modus ausgeführt und den Datensatz in eine Richtung verschoben. Dies war im Protokoll:

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"

Es geht also von einer Wiederholung von 63 mit einer gelegentlichen 62 zu einer plötzlichen Wiederholung von 65 mit einer gelegentlichen 66. Eine Geschwindigkeit von 64 bedeutet, dass sich das Rad nicht bewegt. 63 bedeutet, einen Impuls gegen den Uhrzeigersinn zu bewegen. 65 ist ein Impuls im Uhrzeigersinn. Oder umgekehrt, je nachdem, wie das Ding verkabelt ist.

Bedeutet das, dass das Problem im Arduino und nicht in den Midi-Adaptern liegt?

Der Phil Lee
quelle
Bitte fügen Sie den Schaltplan hinzu, mit dem Sie das Arduino an das MIDI-Kabel anschließen. Wie lang ist das Kabel, das Sie verwenden?
Jippie
Ich habe die Kabel nicht vor mir, aber ein Kabel war ungefähr 30 cm lang. Der andere um die 30 Zoll. Die billigen USB-Midi-Adapter verfügen über integrierte MIDI-Kabel, die wahrscheinlich weniger als 30 cm lang sind.
Der Phil Lee
Haben Sie einen Spielraum, um die Wellenform zu betrachten? Können Sie die Versorgungsspannung für den ATmega auf dem Arduino überprüfen?
Jippie
@ Jippie Ich habe keinen Bereich. Soll ich die Spannung zwischen 5 V und Erdungsstift überprüfen? Oder zwischen bestimmten Pins auf dem Chip selbst (7 und 20; oder 22 und 20)? Bevor ich die Frage stellte, habe ich einige verschiedene Netzteile mit dem gleichen Ergebnis ausprobiert. USB-Stromversorgung, 5-V-DC-Adapter und 12-V-DC-Adapter hatten alle mehr oder weniger das gleiche Ergebnis.
Der Phil Lee
1
Wie wäre es, wenn Sie eine vorprogrammierte Folge von Bytes senden und prüfen, ob diese erfolgreich am Ziel ankommt. Versuchen Sie, den Encoder auszuschließen. Die Spannung an den Stromanschlüssen des Controllers ist am besten. Achten Sie darauf, dass Sie nichts kurzschließen. Ich denke, das Messen der 5V- und GND-Pins sollte in Ordnung und sicher sein.
Jippie

Antworten:

6

Ihre Baudrate ist kein ganzzahliger Teiler Ihrer MCU-Uhr. Die Baudrate wird von der Uhr der MCU geteilt. Es ist einfach, 9600, 19200 und andere Raten mit einem ganzzahligen Teiler der Uhr zu erhalten. Wenn Sie beispielsweise einen 6,144-MHz-Kristall haben, müssen Sie durch 3200 dividieren, um 19200 zu erhalten.

Für die ungeraden Datenraten in verschiedenen Anwendungen (Audio, analoges Video und viele andere) werden spezifische Kristalle verwendet, um einen ganzzahligen Divisor zu erhalten. Beispielsweise könnte eine NTSC-Schaltung einen 5,034963-MHz-Kristall zum Erzeugen der verschiedenen Synchronisationssignale aufweisen, siehe

Quarzoszillatorfrequenzen - WikiPedia

Wenn Ihre MCU über einen internen Taktgenerator verfügt, versuchen Sie, ihn auf einen anderen Wert einzustellen, um einen ganzzahligen Divisor zu erhalten. Andernfalls ist der Bitfehler zu hoch.

Lior Bilia
quelle
4
Ich habe es mir gerade angesehen und der Uno läuft mit 16 MHz, mit 16 Zyklen pro Bit für den UART 1M / 31250 = 32, sodass das Setzen von UBRR auf 31 keinen Fehler geben sollte. Während 31250 nach traditionellen Maßstäben eine ungewöhnliche Baudrate ist, teilt sie sich in gleichmäßige MHz-Frequenzen auf, weshalb sie wahrscheinlich verwendet wurden.
PeterJ
1
Der UBRRn-Wert sollte 31 (16 MHz / 31250) -1 betragen, ein genauer Wert, der für den USART optimal ist. Seltsam.
Lior Bilia
Ich habe bei meinen Suchen etwas darüber gesehen. Ich dachte, es sei möglicherweise die Ursache meiner Probleme, aber ich war mir nicht sicher, wie ich rechnen sollte. Das sollte also kein Problem sein? Die Mathematik checkt aus?
Der Phil Lee
Der Teiler ist optimal, mit 0% Fehler (normalerweise führen die Teiler einen kleinen Fehler ein). Als nächstes würde ich die USART-Routinen überprüfen, vielleicht unterstützen sie nicht die ungerade Baudrate.
Lior Bilia
4

Eine definitive Möglichkeit zum Testen besteht darin, Ihren MIDI-Ausgang mit Ihrem MIDI-Eingang zu verbinden und das System wie zuvor über USB-Seriell einzurichten, es jedoch (über Ihre Software für das virtuelle Midi-Kabel) an den MIDI-Ausgang des Computers weiterzuleiten . Dadurch wird geprüft, ob Ihre USB-MIDI-Hardware tatsächlich zuverlässig mit der Datenrate arbeitet, mit der das Gerät arbeitet. Nach meiner Erfahrung sind viele USB-MIDI-Geräte im Wesentlichen für Keyboards konzipiert (die beim Drücken oder Loslassen einer Taste nur etwa 3 Byte senden) und haben seltsame Überlaufprobleme, wenn Sie mit der vollen 31,25-Kbit-Rate senden. Dies ist auch sehr stark vom Betriebssystem / Treiber abhängig.

Ich denke, Sie möchten auch sicherstellen, dass der Sender / Leitungstreiber funktioniert. Bitte veröffentlichen Sie eine schematische Darstellung Ihrer Arduino MIDI-Out-Schaltung. Was verwenden Sie als Ausgabetreiber?

Ich mache viele Annahmen, aber unter der Annahme, dass der Arduino Uno bei 16 MHz 31250 Bit / s mit einem UBBR-Registerwert von 31 genau unterstützt wird (0% Fehler), lautet dies: AVR UART- Ratenrechner . Dies zeigt mir, dass die Bitrate von MIDI kein Problem sein sollte (nach meinem Verständnis wurde 31250 im Gegensatz zu den von Teletyp-Maschinen abgeleiteten RS-232-Raten weitgehend so gewählt, dass sie mit einem 1-MHz-Takt einfach zu handhaben sind).

Außerdem wäre ein Dump der Nachrichten, die der PC über einen MIDI-Sniffer oder -Monitor empfängt, beim Debuggen sehr nützlich.

Zuofu
quelle
Wenn Sie MIDI-Out mit MIDI-In verbinden, bleiben dann keine Probleme mit der Baudrate, die mit der Plattform verbunden sind, verborgen, da beide dieselbe Uhr teilen?
Scott Seidman
1
Nun, ich wollte den Fahrer eher mit vollständigen Tarifinformationen testen. Ich habe die Erfahrung gemacht, dass einige USB-MIDI-Kabel aufgrund von Pufferüberläufen und dergleichen verstopfen, wenn sie an etwas angeschlossen werden, das mehr Daten als ein Keyboard sendet.
Zuofu
Ich werde versuchen, MIDI-Eingang mit MIDI-Ausgang zu verbinden und mich bei Ihnen zu melden. Ich habe auch versucht, 31250 durch Serien zu machen und mir die Ergebnisse in Putty anzusehen. Aber der Bildschirm wurde mit zu viel Zeug überflutet. Ich werde es wieder tun, wenn ich nach Hause komme, und die Datei speichern. Und ich werde auch versuchen, das Mixxx-Protokoll zu betrachten, das so eingestellt werden kann, dass MIDI-Nachrichten protokolliert werden.
Der Phil Lee
Können Sie den Wert von serial.print ändern, um den Wert der Encoder-Variablen in (ascii) console zu drucken, und ihn über RealTerm anzeigen? Ihr Quadraturdecodierungs-Setup ist ungewöhnlich. Es ist normalerweise keine gute Idee, sowohl bei A als auch bei B zu unterbrechen. Wenn Sie beispielsweise bei einer Änderung von A unterbrechen, wenn A == B, dann führt B zu A, wenn A! = B, dann führt A zu B. So wie Sie Wenn es geschrieben ist, scheint es viel komplexer zu sein. Ich werde nicht sagen, dass es nicht funktioniert, aber es könnte einige unbeabsichtigte Auswirkungen geben.
Zuofu
Außerdem scheint Ihre Programmlogik etwas verwirrt zu sein. 'lastreportedPos' ist fast immer 0, außer wenn ein Interrupt in der winzigen Zeitspanne vor seiner Zuweisung ausgelöst wurde und die vorherige Zeileneinstellung 'encoderPos' auf 0 gesetzt wurde. Warum ist lastreportedPos nicht signiert, während encoderPos signiert ist? Die typische Art, einen Encoder zu verwenden, besteht darin, die Position zu verfolgen (entweder von einem Indexsignal, falls vorhanden, oder von einer beliebigen Position beim Einschalten). Wenn Sie eine relative Position senden möchten, verwenden Sie ein Delta-x wie Sie, außer dass Sie die Position nicht unmittelbar vor ...
Zuofu