Timing-Genauigkeit des MIDI-Sequenzers mit dem Arduino


11

Ich baue diese Musiksequenzer .

Geben Sie hier die Bildbeschreibung ein

Nur ist es nicht gerade ein Sequenzer, es ist eine physische Schnittstelle für einen Sequenzer. Der Sequenzer ist eine Anwendung, die auf einem Laptop ausgeführt wird, mit dem der Sequenzer verbunden ist. Auf diese Weise kann der Benutzer Drum-Loops im laufenden Betrieb erstellen. Es macht ziemlich viel Spaß, erfordert aber einen Laptop, da der Sequenzer nicht an Bord ist.

Am liebsten würde ich die Sequenzierung an Bord meines Geräts durchführen.

Nehmen wir nun an, ich weiß, wie man die Klassenkonformität für USB-MIDI-Konnektivität löst, und nehmen wir an, ich kann herausfinden, wie ein Arduino verkabelt wird, um MIDI-Noten von einem 5-poligen DIN-Port zu senden. Was mich am meisten beunruhigt, ist die zeitliche Verschiebung des Tempos aufgrund des inkonsistenten Timings in Minutenbeträgen über jeden Lauf der Ereignisschleife.

Einige Dinge, die ich weiß:

  1. Sie sollten sich nicht darauf verlassen delay(), die Tempo-Schleife zu steuern. Verzögerung stoppt den gesamten Betrieb der Firmware, und das kann nicht funktionieren, da ich die physische Benutzeroberfläche nach Änderungen abfragen muss, während die Sequenz ausgeführt wird.

  2. Berechnungen basierend auf millis()sind besser, da die Firmware nach Ablauf einer bestimmten Anzahl weiterarbeiten und funktionieren kann.

  3. Obwohl keine meiner physischen Steuerungen Interrupt-Routinen auslöst, können einige Vorgänge die loop()Ausführung der Hauptsteuerung verzögern . Wenn ich eine Funktion entwerfe, die auf Benutzereingaben wartet, kann dies offensichtlich dazu führen, dass eine "Frist" verpasst wird, wenn die millis()Anzahl weit überschritten ist. Ich weiß, dass dieses Problem von mir selbst verursacht wurde ...

Fragen:

A. Ist das AVR-basierte Arduino ein geeigneter Mikrocontroller, um eine Benutzeroberfläche abzufragen und eine geschäftskritische Zeitschleife auszuführen? Ich weiß, dass es jetzt ein ARM-basiertes Arduino gibt, das viel schneller ist. Wäre ein Teensy 3.0 eine bessere Alternative? Beide sind 3,3-V-Karten, das ist also eine weitere Reihe von Problemen, mit denen man arbeiten muss ... aber das werde ich vorerst ignorieren.

B. Soll ich die Aufgabe in zwei Mikroprozessoren aufteilen? Eine für das Abrufen und Aktualisieren der Benutzeroberfläche und eine für die geschäftskritische Zeitschleife.

c. Etwas anderes?

Mein Hauptziel ist es, überhaupt keinen Computer benutzen zu müssen. Ich möchte auch den Swing berechnen, aber in diesem Fall bedeutet Swing nichts, wenn ich kein gesperrtes und zeitgenaues Tempo habe. Danke für deinen Rat!


1
Arduino richtet immer einige Interrupt-Routinen ein, die Jitter verursachen. In vielen Fällen ist dies kein Problem, aber es ist gut, sich dessen bewusst zu sein. noInterrupts();stoppt den Jitter, stoppt aber auch alle gewünschten Interrupts.
Jippie

1
Wenn Sie "Sequenzierung an Bord durchführen" sagen, bedeutet dies, dass Sie die Beats pro Takt, BPM und einen Tick-Track an Bord einrichten? Dann möchten Sie sich vermutlich an die Tastendrücke in der Leiste erinnern, damit das "Gehirn" des Geräts Ihrem Laptop MIDI-Noten zuführen kann? Möchten Sie dann bestimmte Percussion-Sounds entfernen, wenn Sie sie erneut über die zuvor aufgenommene Note schlagen? Usw. wie weit willst du gehen? Speicherung Ihrer Beats? Eine Folge von Balken erstellen, die einer vollständigen Spur entsprechen? Bestimmte Balken bearbeiten? Tempoänderung bestimmter Takte? Es frisst alles CPU, also wählen Sie die beste CPU.
Andy aka

Ja, alles.
Steve Cooley

2
Das ist ein süß aussehender Fall, den Sie gemacht haben!
Shuckc

3
Zusätzlich zu dem, was andere gesagt haben, sieht dies wie ein Gedanke aus, den Sie vielleicht produzieren und verkaufen möchten. Ein Arduino kostet 20 US-Dollar, während ein AVR 2 US-Dollar kostet. Sie erhalten nicht nur die Kontrolle über die Hardware, die Ihre Anwendung benötigt, sondern sparen auch viel Geld.
Phil Frost

Antworten:


5

Interrupts sind Ihr Freund für zeitkritische Aufgaben, aber nur, wenn Sie die zeitkritischen Aspekte in den Interrupt einfließen lassen und keine anderen Interrupts mit höherer Priorität auftreten. Die Mikrocontroller des "AVR-basierten" Arduino (z. B. des ATmega328P) haben feste Interrupt-Prioritäten, wie auf Seite 58ff des Datenblattes beschrieben . Wenn Sie also TIMER2 COMPA als kritischen Timing-Interrupt und keine anderen Interrupts verwendet haben, sollten Sie in Ordnung sein (da es die höchste Priorität hat). Wenn Sie auch Interrupts mit niedrigerer Priorität verwenden möchten, müssen Sie sicherstellen, dass alle globalen Interrupts wieder aktivieren, wenn Sie ihre Interrupt-Serviceroutine eingeben:

Wenn ein Interrupt auftritt, wird das Global Interrupt Enable I-Bit gelöscht und alle Interrupts werden deaktiviert. Die Anwendersoftware kann logisch eins in das I-Bit schreiben, um verschachtelte Interrupts zu aktivieren. Alle aktivierten Interrupts können dann die aktuelle Interruptroutine unterbrechen.

(S. 14 des Datenblattes )

Dies ist bei ARM-basierten Arduinos etwas anders, da ihr Cortex-M3-Kern über einen "Nested Vector Interrupt Controller" verfügt, bei dem die Prioritäten nicht festgelegt sind (in der Software festgelegt werden können) und die Behandlung verschachtelter Interrupts die Norm ist. Für zeitgesteuerte kritische Anwendungen bietet Ihnen das ARM-basierte Arduino mehr Flexibilität. Ich denke jedoch nicht, dass dies für Ihre Bewerbung wirklich notwendig ist.

Die größere Frage ist wirklich, wie einfach diese Dinge mit den Arduino-Bibliotheken implementiert werden können. Um die beste Leistung zu erzielen, müssen Sie wahrscheinlich bis zu einem gewissen Grad außerhalb der Bibliotheken codieren, zumindest für die zeitkritischen Bits, dh Dinge wie delay () oder millis () insgesamt vermeiden.

Ob Sie teilen müssen oder nicht, hängt davon ab, wie viel Verarbeitung Sie beabsichtigen. Auch hier kann das Verlassen der Bibliotheken möglicherweise zu einer besseren Leistung führen.


3

Dies kann mit der entsprechenden Programmierung definitiv auf einem ATmega328P erfolgen (abhängig von der Komplexität des Drum-Loops. Ich gehe von ~ <50 Drum-Ereignissen im Loop aus. Ist das sinnvoll?).

Beachten Sie, dass ich ATmega328P sagte , nicht unbedingt ein Arduino .

In der Arduino-Umgebung werden im Hintergrund viele Standardfunktionen ausgeführt, was eine äußerst deterministische Programmierung (da Sie für etwas zeitkritisches benötigen) schwierig macht.

Die eigentliche Frage, die Sie hier stellen müssen, ist, wie interessiert Sie an der Programmierung sind und wie interessiert Sie an der Entwicklung eines Instruments?

Ich bin mir zwar ziemlich sicher, dass mit einem einzigen ATmega alles möglich ist (Drum-Loop, mehrere analoge Eingänge, LCD, Tasten, MIDI-Interface), aber die eigentliche Frage ist, wie viel Arbeit es kosten wird, alles einzudrücken? Möchten Sie erneut lernen, eingebetteten MCU-Code zu optimieren oder Instrumente zu erstellen? Es ist ganz einfach, einfach zu einem schnelleren MCU gehen , wenn nötig, aber Sie müssen die MCU Leistung bestimmen , was Sie brauchen jetzt , so sechs Monate Arbeit in, Sie erkennen nicht , Sie nicht können ganz alle Arbeit so schnell wie möglich bekommen brauchen.


Wenn ich Sie wäre, würde ich es als erstes ohne Arduino-Zeug zum Laufen bringen (im Grunde genommen als rohes ATmega behandeln und AVR-Studio oder ähnliches verwenden). Dann können Sie viel effektiver analysieren, welche Art von Leistung Sie benötigen und ob der ATmega sie verwalten kann.

Sobald Sie frei von Arduino sind, können Sie viel mehr verschiedene MCUs verwenden (diese sind im Allgemeinen ähnlicher als unterschiedlich. Wenn Sie eine aus der Dokumentation herausfinden können, können Sie dies wahrscheinlich auch für andere tun).

Ich habe in letzter Zeit viel mit den ATxmega-Geräten gearbeitet und sie sind wirklich nett. Sie erhalten drei Interrupt-Prioritäten, die die Verwaltung zeitkritischer Inhalte vereinfachen. Es ist auch sehr schön, mit ihnen zu arbeiten (vernünftige Peripheriedesigns! Praktische Portstrukturen! Usw. ...).

Es gibt auch die LPC-Geräte von NXP, die ARM-basiert sind, sowie einige der ARM-Geräte von Atmel (wie sie auf dem Arduino Due verwendet werden) oder die STM32-MCUs von ST. Jedes dieser Geräte bietet eine deutlich höhere Leistung als ein ATmega oder sogar ein ATxmega.

Der Hauptnachteil eines größeren, leistungsstärkeren Prozessors ist der Preis. Wenn Sie jedoch nicht Tausende dieser Einheiten herstellen, werden die Montage- und Herstellungskosten pro Einheit den Kostenunterschied (der wahrscheinlich nur wenige Dollar betragen wird) erheblich übersteigen ) dass es grundsätzlich irrelevant ist.


Schön ausgedrückt - für ein kommerzielles Produkt ist ein Arduino einfach nicht der richtige Weg - sie sind leistungshungrig, langsam und die IDE ist nicht für optimalen (schnellen / kleinen) Code ausgelegt, sondern für Komfort und einfaches Lernen. Für geringere Kosten könnten Sie sogar einen STM32 F4 (32-Bit-Cortex M4> 100 MHz) oder ähnliches haben, obwohl dies übertrieben wäre. Ich denke, so etwas wie einer der kleineren PIC32, Cortex M3 oder ein AVR32 ist wahrscheinlich der richtige Weg, wie Sie erwähnen. Zahlreiche Interrupt-Prioritäten, DMA, hochentwickelte Peripheriegeräte, schneller / geringer Stromverbrauch und viel RAM machen es im Vergleich zu einem Arduino zu einer einfachen Wahl.
Oli Glaser

@OliGlaser - Ich denke, Sie müssen klar zwischen einem Arduino und einem ATmega unterscheiden . Sie können kleinen, schnellen Code auf einem ATmega ausführen, und dieser ATmega kann sich sogar auf einem Arduino-Board befinden. Die Arduino "IDE" hingegen ist einer der beschissensten Code-Editoren, die ich je benutzt habe. Andererseits ist der OptiBoot-Bootloader sehr schön. Nur weil einige Teile Mist sind, heißt das nicht, dass Sie das Ganze wegwerfen sollten.
Connor Wolf

Absolut - ich bezog mich auf das Arduino als Ganzes, einschließlich Board und IDE - nicht auf das ATmega, das sicher so gut ist wie jedes andere vergleichbare uC (PIC16 / 18F usw.), das ich in meine Liste aufgenommen hätte, aber Ich denke, da der Preis zwischen 8-Bit und 16/32-Bit heutzutage so nahe ist, ist es wahrscheinlich eine gute Idee, einen zusätzlichen Dollar auszugeben und zu wissen, dass Sie über die Prozessorleistung verfügen (es sei denn, Sie erwähnen, wir sprechen von großen Zahlen und zum absolut niedrigsten Preis gebaut, aber dann bezweifle ich, dass ein Arduino in Betracht gezogen worden wäre :-))
Oli Glaser

1

Ich musste mich über Timer informieren, bevor ich über Timing-Genauigkeit nachdachte (auch den Bau eines Midi-Step-Sequenzers mit einem Arduino, obwohl er garantiert weniger cool aussieht als diese ^^). Diese Artikelserie war die informativste:

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

Im Moment denke ich, dass meine Lösung für ein genaues Timing sein wird.

A. Verwenden Sie das AVR-Arduino

B. Halten Sie die Aufgabe auf einem Mikroprozessor

C. Verwenden Sie mit Bedacht Vorskalierer, Timer und Interrupts, um die erforderliche Präzision zu erzielen.

AKTUALISIEREN

Mit dem grundlegenden Midi-Tutorial für Arduino und nachdem ich mir diesen Artikel über Timer und Prescaler angesehen habe, habe ich mir den folgenden Code ausgedacht. Der Code verwendet den Timer1- und CTC-Modus, um jede Viertelsekunde eine Midi-Note und jede Viertelsekunde eine Note abzuspielen (die genau 120 Schläge pro Minute betragen sollte). Leider kommt dies immer noch nur langsamer als 120 bpm, obwohl dies der nächste ist, den ich bekommen habe ...

// 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);
}

AKTUALISIEREN

Ich habe jetzt seit ~ 24 Stunden damit zu kämpfen und endlich einige Antworten aus dem Forum bekommen. Ich denke, dass der Code, den ich oben verwendet habe ^^, ziemlich gut ist. Verwenden des ISR, Verwenden des CTC-Modus und von Prescalern usw. Nachdem ich mich an das Forum gewandt habe, denke ich, dass es bei der Lösung weniger darum geht, Präzision auf dem Midi-Sequenzer zu erreichen, sondern mein gesamtes Hardware-Setup (meine Synthesizer und Sampler) an das gleiche anzuschließen Midi-Uhr, ob die Uhr vom Arduino stammt oder nicht.


0

Abhängig davon, wie schrittweise Sie den Übergang von einem angebundenen Computer zu einem µC-basierten System durchführen möchten, können Sie einen Raspberry Pi in diese Box legen (25 bis 35 US-Dollar im Einzelhandel ). Auf diese Weise können Sie einen vollständigen (wenn auch stromsparenden) Linux-basierten Computer mit USB-Anschlüssen und GPIO-Pins haben.


Ich bin sicher, es gibt Erweiterungsschilde oder wie auch immer sie für den Pi genannt werden, aber die Standardplatine hat 17 GPIO-Pins. Ich benutze jeden einzelnen Pin des Arduino Mega. 31 Takte + 30 LEDs, 10 analoge Eingänge. 70+ E / A.
Steve Cooley

Oh, ich meinte, wenn das unmittelbare Ziel darin bestand, den externen Computer zu entfernen, könnten Sie den "Sequenzer [der] eine Anwendung ist, die auf einem Laptop ausgeführt wird" und ihn auf dem Pi ausführen, der intern mit Ihrem vorhandenen System genauso verbunden ist, wie er ist jetzt verbunden.
Rob Starling

@SteveCooley - Klingt so, als müssten Sie sich mit IO-Multiplexing / Button-Matrizen befassen. Sie sollten nicht eine ganze dedizierte E / A-Leitung pro Taste benötigen.
Connor Wolf

@SteveCooley - Verdammt, du brauchst nicht einmal eine Knopfmatrize. Sie können ALLE Ihre digitalen E / A mit nur 4 rPi-Pins ausführen. Hängen Sie einfach alle Tasten und LEDs an einige Schieberegister (parallel-seriell für die Tasten, seriell-parallel für die LEDs) und steuern Sie die Schieberegister über den SPI-Port des rPi. Mithilfe der SPI-Hardware sollten Sie problemlos Aktualisierungsraten von> 1 kHz für die gesamte Matrix erhalten.
Connor Wolf

Wenn der einzige Grund, warum Sie ein Arduino Mega verwenden, das E / A ist, geben Sie viel Geld für etwas aus, das mit ein paar externen Geräten für <3 US-Dollar sehr einfach erledigt werden kann.
Connor Wolf
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.