Ich baue diese Musiksequenzer .
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 läuft, mit dem der Sequenzer verbunden ist. Mit diesem Ding kann der Benutzer Drum-Loops im Handumdrehen erstellen. Es macht ziemlich viel Spaß, erfordert aber einen Laptop, da der Sequenzer nicht „an Bord“ ist.
Was ich lieben würde, ist die Sequenzierung an Bord meines Geräts.
Nehmen wir nun an, ich weiß, wie ich die Klassenkonformität für USB-MIDI-Konnektivität lösen kann, und nehmen wir auch an, ich kann herausfinden, wie man ein Arduino verkabelt, um MIDI-Noten von einem 5-poligen DIN-Anschluss zu senden. Worüber ich mir am meisten Sorgen mache, ist die Tempodrift im Laufe der Zeit aufgrund von inkonsistentem Timing in Minutenbeträgen bei jedem Durchlauf der Ereignisschleife.
Einige Dinge, die ich weiß:
Sie sollten sich nicht darauf verlassen delay()
, die Temposchleife zu steuern. Die 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.
Berechnungen basierend auf millis()
sind besser, da die Firmware weiterarbeiten und handeln kann, wenn eine bestimmte Anzahl abgelaufen ist.
Obwohl keine meiner physischen Steuerungen Interrupt-Routinen auslöst, können einige Operationen die loop()
Ausführung der Hauptroutine verzögern. Wenn ich eine Funktion entwerfe, die auf Benutzereingaben wartet, kann dies offensichtlich zu einem Problem führen, wenn eine "Frist" verpasst wird, um zu handeln, wenn die millis()
Zählung weit vorbei 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 missionskritische 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? Beides sind 3,3-V-Platinen, also ist das eine weitere Reihe von Problemen, mit denen man arbeiten kann ... aber ich werde das vorerst ignorieren.
B. Soll ich die Aufgabe auf zwei Mikroprozessoren aufteilen? Einer für die Abfrage und Aktualisierung der Benutzeroberfläche und einer für die unternehmenskritische Zeitschleife.
c. Etwas anderes?
Mein Hauptziel ist es, überhaupt keinen Computer benutzen zu müssen. Ich möchte auch für Swing rechnen, aber in diesem Fall bedeutet Swing nichts, wenn ich kein festgelegtes und genaues Tempo habe. Danke für deinen Rat!
Interrupts sind Ihr Freund für zeitkritische Aufgaben, aber nur, wenn Sie die zeitkritischen Aspekte in den Interrupt stecken und es keine anderen Interrupts gibt, die eine höhere Priorität haben. Die Mikrocontroller auf dem "AVR-basierten" Arduino (z. B. der ATmega328P) haben feste Interrupt-Prioritäten, wie auf Seite 58ff des Datenblatts beschrieben . Wenn Sie also TIMER2 COMPA als kritischen Timing-Interrupt und keine anderen Interrupts verwendet haben, sollten Sie in Ordnung sein (da er die höchste Priorität hat). Wenn Sie auch Interrupts mit niedrigerer Priorität verwenden möchten, müssen Sie sicherstellen, dass alle von ihnen globale Interrupts wieder aktivieren, wenn Sie in ihre Interrupt-Serviceroutine eintreten:
Wenn ein Interrupt auftritt, wird das globale Interrupt-Freigabe-I-Bit gelöscht und alle Interrupts werden deaktiviert. Die Benutzersoftware kann eine logische Eins in das I-Bit schreiben, um verschachtelte Interrupts zu aktivieren. Alle aktivierten Interrupts können dann die aktuelle Interrupt-Routine unterbrechen.
(S. 14 des Datenblatts )
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 von verschachtelten Interrupts die Norm ist. Für zeitkritische Anwendungen bietet Ihnen der ARM-basierte Arduino also mehr Flexibilität. Ich glaube aber nicht, dass das für deine 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() ganz vermeiden.
Ob Sie aufteilen 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.
Dies kann mit der entsprechenden Programmierung auf jeden Fall auf einem ATmega328P durchgeführt werden (abhängig von der Komplexität des Drum-Loops. Ich gehe von ~ <50 Drum-Events im Loop aus. Ist das vernünftig?).
Beachten Sie, dass ich ATmega328P gesagt habe , nicht unbedingt ein Arduino .
In der Arduino-Umgebung laufen im Hintergrund viele Standardfunktionen ab, die eine extrem deterministische Programmierung (wie Sie sie für etwas Timing-kritisches benötigen) zu einer Herausforderung machen.
Die eigentliche Frage, die Sie hier stellen müssen, ist, wie interessiert Sie am Programmieren sind, im Vergleich dazu, wie interessiert Sie daran sind, ein Instrument zu entwickeln?
Obwohl ich ziemlich zuversichtlich bin, dass es möglich ist, alles, was Sie wollen, auf einem einzigen ATmega zu machen (Drum-Loop, mehrere analoge Eingänge, LCD, Tasten, MIDI-Interface), ist die eigentliche Frage, wie viel Arbeit es sein wird, alles unterzubringen? Wollen Sie noch einmal lernen, eingebetteten MCU-Code zu optimieren oder Instrumente zu bauen? Es ist ziemlich einfach, bei Bedarf einfach zu einer schnelleren MCU zu wechseln, aber Sie müssen die MCU-Leistung bestimmen, die Sie jetzt benötigen , also erkennen Sie nach sechs Monaten Arbeit nicht, dass Sie nicht alles so schnell zum Laufen bringen können wie Sie müssen.
Wenn ich Sie wäre, würde ich es als erstes ohne Arduino-Zeug zum Laufen bringen (behandeln Sie es im Grunde wie einen rohen ATmega und verwenden Sie AVR Studio oder ähnliches). Dann können Sie viel effektiver analysieren, welche Art von Leistung Sie benötigen und ob der ATmega sie bewältigen kann.
Sobald Sie von dem Arduino-Zeug befreit sind, können Sie verschiedene MCUs viel freier verwenden (sie sind im Allgemeinen ähnlicher als unterschiedlich. Wenn Sie eine aus ihrer Dokumentation herausfinden können, können Sie wahrscheinlich dasselbe 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 Dinge viel einfacher machen. Es ist auch wirklich schön, mit ihnen zu arbeiten (vernünftige Peripherie-Designs! Praktische Port-Strukturen! Etc ...).
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 davon hat deutlich mehr Leistung als ein ATmega oder sogar ein ATxmega.
Der Hauptnachteil eines größeren, leistungsstärkeren Prozessors ist der Preis, aber wenn Sie nicht Tausende dieser Einheiten herstellen, werden die Montage- und Herstellungskosten pro Einheit die Kostendifferenz (die wahrscheinlich nur ein paar Dollar betragen wird) so stark übersteigen ), dass es im Grunde egal ist.
Ich musste mich über Timer informieren, bevor ich anfing, über Timing-Genauigkeit nachzudenken (auch den Bau eines Midi-Step-Sequenzers mit einem Arduino, obwohl es 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, um ein genaues Timing zu erhalten, sein wird.
A. Verwenden Sie das AVR-Arduino
B. Behalten Sie die Aufgabe auf einem Mikroprozessor
C. Vorteilhafte Verwendung von Prescalern, Timern und Interrupts, um die erforderliche Genauigkeit zu erreichen.
AKTUALISIEREN
Unter Verwendung des grundlegenden Midi-Tutorials für Arduino und nachdem ich diesen Artikel über Timer und Prescaler gelesen habe, habe ich mir den folgenden Code ausgedacht. Der Code verwendet timer1 und den CTC-Modus, um jede Viertelsekunde eine Midi-Note und jede Viertelsekunde eine Note zu spielen (was genau 120 bpm sein sollte). Leider kommt dies immer noch langsamer als 120 bpm, obwohl dies das nächste ist, das 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 kämpfe jetzt seit ~ 24 Stunden damit und habe 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 Prescaler usw. Nachdem ich mich an das Forum gewandt habe, denke ich, dass die Lösung weniger darin besteht, die Präzision des Midi-Sequenzers zu erreichen, sondern mein gesamtes Hardware-Setup (meine Synthesizer und Sampler) an dasselbe anzuschließen Midi Clock, egal ob die Uhr vom Arduino kommt oder nicht.
Je nachdem, wie schrittweise Sie den Übergang von einem angebundenen Computer zu einem µC-basierten System vollziehen möchten, sollten Sie in Betracht ziehen, einen Raspberry Pi in diese Box zu packen (25-35 US-Dollar im Einzelhandel ). Auf diese Weise können Sie einen vollwertigen (wenn auch stromsparenden) Linux-basierten Computer mit USB-Anschlüssen und GPIO-Pins haben.
Jippie
noInterrupts();
stoppt den Jitter, stoppt aber auch alle gewünschten Interrupts.Andi aka
Steve Cooley
shuckc
Steve Cooley
Phil Frost
Steve Cooley