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

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 verbunden ist (versuchte es auf r2 und r3).

Während ich darauf wartete, dass meine MIDI-Buchse per Post ankam, versuchte ich, mein Setup über den seriellen USB-Port mit meinem Computer zu verbinden, zusammen mit Hairless-Midi und LoopMidi. Loopmidi ist ein virtueller MIDI-Port, und Hairless-Midi überbrückt serielle Ports mit MIDI-Ports – virtuell oder anderweitig. Ich habe eine Baudrate von 115200 verwendet, weil ich dachte, dass es nicht schaden könnte, 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 Takt verpasst, egal wie schnell ich das Ding überspanne.

Daher war ich ziemlich aufgeregt, als die MIDI-Buchse ankam. Ich legte es in mein Steckbrett und änderte es

Serial.begin(115200);

zu

Serial.begin(31250);

und in Mixxx getestet. Wenn ich jetzt den Encoder mäßig schnell in eine Richtung drehe, bewegt sich die virtuelle Platte in diese Richtung und dreht sich dann plötzlich in die andere Richtung und dann wieder zurück. Ich nehme an, dass dem Encoder Impulse fehlen?

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

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

Warum passiert das? Ist die USB-Seriell-Schaltung im Arduino zusammen mit etwas freier 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 Baudrate von 31250, die Probleme im Arduino verursacht?

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

BEARBEITEN: Hier ist der relevante Teil des Codes und der relevante Teil der Schaltung. Es gibt ein paar andere Komponenten, die das Problem verschlimmern könnten, aber selbst 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: Verwirrt die seltsame Baudrate irgendwie die Hardware-Interrupts?

Nochmals BEARBEITEN: Ich habe mixxx im Mididebug-Modus ausgeführt und den Datensatz in eine Richtung gespannt. Das stand im Log:

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 der Wiederholung von 63 mit gelegentlichen 62 bis zur plötzlichen Wiederholung von 65 mit gelegentlichen 66. Eine Geschwindigkeit von 64 bedeutet, dass sich das Rad nicht bewegt. 63 bedeutet eine Bewegung gegen den Uhrzeigersinn um einen Impuls. 65 ist ein Impuls im Uhrzeigersinn. Oder umgekehrt, je nachdem wie das Ding verkabelt ist.

Bedeutet das, dass das Problem beim Arduino und nicht bei den Midi-Adaptern liegt?

Bitte fügen Sie den Schaltplan hinzu, mit dem Sie den Arduino mit dem MIDI-Kabel verbinden. Wie lang ist das verwendete Kabel?
Ich habe die Kabel nicht vor mir, aber ein Kabel war etwa 12 Zoll. Die andere etwa 30, Zoll. Die billigen USB-Midi-Adapter haben eingebaute MIDI-Kabel, die wahrscheinlich unter 12 Zoll lang sind.
Haben Sie die Möglichkeit, sich die Wellenform anzusehen? Kannst du die Versorgungsspannung für den ATmega auf dem Arduino überprüfen?
@jippie Ich habe kein Zielfernrohr. Soll ich die Spannung zwischen 5V und Masse prüfen? Oder zwischen bestimmten Pins auf dem Chip selbst (7 und 20; oder 22 und 20)? Bevor ich die Frage gestellt habe, habe ich ein paar verschiedene Netzteile mit dem gleichen Ergebnis ausprobiert. USB-Stromversorgung, 5-V-Gleichstromadapter und 12-V-Gleichstromadapter hatten alle mehr oder weniger das gleiche Ergebnis.
Wie wäre es, wenn Sie eine vorprogrammierte Folge von Bytes senden und sehen, ob diese erfolgreich am Ziel ankommt. Versuchen Sie, den Encoder auszuschließen. Die Spannung an den Stromanschlüssen des Controllers ist am besten, aber achten Sie darauf, nichts kurzzuschließen. Ich denke, das Messen der 5-V- und GND-Pins sollte in Ordnung und sicher sein.

Antworten (2)

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-Quarz haben, müssen Sie durch 3200 teilen, um 19200 zu erhalten.

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

Quarzoszillatorfrequenzen – WikiPedia

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

Ich habe gerade nachgesehen und der Uno läuft mit 16 MHz, mit 16 Zyklen pro Bit für den UART 1M / 31250 = 32, also sollte das Setzen von UBRR auf 31 keinen Fehler geben. Während 31250 nach traditionellen Standards eine ungewöhnliche Baudrate ist, wird sie in gerade MHz-Frequenzen unterteilt, weshalb sie sie wahrscheinlich verwendet haben.
Der Wert von UBRRn sollte 31 (16 MHz/31250) -1 sein, ein exakter Wert, der für den USART optimal ist. Komisch.
Ich habe bei meinen Recherchen etwas darüber gesehen. Ich dachte, es wäre möglicherweise die Ursache meiner Probleme, aber ich war mir nicht sicher, wie ich es rechnen sollte. Dann sollte das also kein Problem sein? Die Mathematik checkt aus?
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 die ungerade Baudrate nicht.

Eine definitive Möglichkeit zum Testen besteht darin, Ihren MIDI-Ausgang mit Ihrem MIDI-Eingang zu verkabeln und das System wie zuvor mit dem seriellen USB einzurichten, es jedoch (über Ihre "virtuelle Midi-Kabel" -Software) an den MIDI-Ausgang des Computers zu leiten . Dadurch wird getestet, ob Ihre USB-MIDI-Hardware tatsächlich zuverlässig mit der Datenrate arbeitet, mit der das Gerät arbeitet. Meiner Erfahrung nach sind viele USB-MIDI-Geräte im Grunde für Keyboards konzipiert (die nur etwa 3 Bytes senden, wenn eine Taste gedrückt oder losgelassen wird) 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 posten Sie ein Schema Ihrer Arduino MIDI-Out-Schaltung. Was verwendest du als Ausgangstreiber?

Ich mache viele Annahmen, aber unter der Annahme, dass der Arduino Uno bei 16 MHz, 31250 bps genau unterstützt wird (0% Fehler) mit einem UBBR-Registerwert von 31, laut diesem: AVR UART- Ratenrechner . Dies zeigt mir, dass die Bitrate von MIDI kein Problem sein sollte (mein Verständnis ist tatsächlich, dass 31250 weitgehend so gewählt wurde, dass es mit einem 1-MHz-Takt einfach zu handhaben ist, im Gegensatz zu den von Fernschreibmaschinen abgeleiteten RS-232-Raten).

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

Wenn Sie MIDI-out mit MIDI-in verdrahten, bleiben dann nicht Baudratenprobleme im Zusammenhang mit der vorliegenden Plattform verborgen, da beide denselben Takt verwenden?
Nun, ich wollte den Treiber eher mit vollständigen Rateninformationen testen. Meine Erfahrung ist, dass einige USB-MIDI-Kabel aufgrund von Pufferüberläufen und dergleichen ersticken, wenn sie an etwas angeschlossen sind, das mehr Daten sendet als ein Keyboard.
Ich werde versuchen, den MIDI-Eingang mit dem MIDI-Ausgang zu verbinden, und mich bei Ihnen melden. Ich habe auch versucht, 31250 seriell zu machen und mir die Ergebnisse in Putty anzusehen. Aber der Bildschirm wurde mit zu viel Zeug überflutet. Ich mache es noch einmal, wenn ich nach Hause komme, und speichere die Datei. Und ich werde versuchen, mir auch das Mixxx-Protokoll anzusehen, das so eingestellt werden kann, dass es Midi-Nachrichten protokolliert.
Können Sie die serial.print ändern, um den Wert der Encoder-Variablen auf die (ASCII-) Konsole zu drucken und ihn über RealTerm anzuzeigen? Ihre Quadratur-Decodierung 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 A, wenn A!=B, dann führt A B. So wie Sie Wenn ich es geschrieben habe, scheint es viel komplexer zu sein, ich werde nicht sagen, dass es nicht funktionieren wird, aber es könnte dort einige unbeabsichtigte Effekte geben.
Außerdem scheint Ihre Programmlogik irgendwie verwirrt zu sein. 'lastreportedPos' ist fast immer 0, außer wenn ein Interrupt in der winzigen Zeitspanne vor der Zuweisung ausgelöst wurde und die vorherige Zeile 'encoderPos' auf 0 gesetzt hat..? 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 es tun, außer dass Sie die Position nicht unmittelbar vorher zurücksetzen ...
Schließlich sollten Sie sich darüber im Klaren sein, dass serial.write() blockiert. Das bedeutet, dass das Hauptprogramm bei jedem serial.write()-Befehl wartet, bis das gesamte Byte übertragen wurde. Interrupts sollten immer noch auftreten, es sei denn, die Routine deaktiviert sie, aber das bedeutet, dass mehr als 99,9 % Ihrer Ausführungszeit in serial.write() verbracht wird, sodass eine Änderung der Baudrate das Verhalten Ihres Programms sehr wohl ändern könnte, wenn es nicht geschrieben wird robust (siehe meine obigen Kommentare).
Wenn ich nur Interrupts bei A hätte, würde ich dann nicht nur die halbe Auflösung bekommen?
Ich habe den Encoder-Code von woanders kopiert. Es ist ein wenig abstrakt, und ich verstehe Teile davon nicht, wie z. Das Zurücksetzen der Position auf 0 war meine schnelle und schmutzige Art, mit dem Rollover von int von 32.767 auf -32.768 umzugehen. Damals schien es besser, schnelle Unterbrechungen auf Kosten einer langsamen Hauptschleife zu haben.
Die Serial.write-Blockierung scheint bei "normalen" Raten keine Probleme zu verursachen. Bei 115200 bewegt sich der Encoder einen Tick (selten 2) zwischen seriellen Schreibvorgängen. Bei 9600 bewegt sich der Encoder möglicherweise um 3 oder 4 Ticks zwischen den Schreibvorgängen, aber dies hat keinen Einfluss auf die Genauigkeit (nur Latenz), da 3 oder 4 Ticks gemeldet werden. Das Problem ist, dass bei 31250 der Encoder-Code nicht mehr funktioniert.