Précision de synchronisation du séquenceur MIDI à l'aide de l'Arduino


11

Je construis ces séquenceurs musicaux .

entrez la description de l'image ici

Seulement ce n'est pas exactement un séquenceur, c'est une interface physique pour un séquenceur. Le séquenceur est une application qui s'exécute sur un ordinateur portable auquel le séquenceur se connecte, cette chose permet à l'utilisateur de faire des boucles de batterie à la volée. C'est assez amusant, mais cela nécessite un ordinateur portable car le séquenceur n'est pas «embarqué».

Ce que j'aimerais, c'est faire le séquençage à bord de mon appareil.

Supposons maintenant que je sais comment résoudre la conformité de classe pour la connectivité USB MIDI, et supposons également que je peux comprendre comment câbler un arduino pour envoyer des notes MIDI à partir d'un port DIN à 5 broches. Ce qui m'inquiète le plus, c'est la dérive du tempo dans le temps en raison d'un timing incohérent en nombre de minutes à chaque exécution de la boucle d'événement.

Certaines choses que je sais:

  1. Vous ne devriez pas vous fier delay()à contrôler la boucle de tempo. Le retard arrête toutes les opérations du micrologiciel, et cela ne peut pas fonctionner car j'ai besoin d'interroger l'interface utilisateur physique pour les modifications pendant l'exécution de la séquence.

  2. Les calculs basés sur millis()sont meilleurs car le firmware peut continuer à fonctionner et à agir lorsqu'un certain décompte s'est écoulé.

  3. Même si aucun de mes contrôles physiques ne déclenche des routines d'interruption, certaines opérations peuvent retarder l' loop()exécution du principal . Si je conçois une fonction qui attend la saisie de l'utilisateur, cela peut évidemment causer un problème de manque de «délai» pour agir si le millis()nombre est bien dépassé. Je sais que ce problème est de ma propre conception ...

Des questions:

A. L'arduino basé sur AVR est-il un microcontrôleur approprié pour interroger une interface utilisateur et exécuter une boucle de synchronisation critique pour la mission? Je sais qu'il y a maintenant un Arduino basé sur ARM qui est beaucoup plus rapide. Un Teensy 3.0 serait-il une meilleure alternative? Les deux sont des cartes 3,3 V, c'est donc un autre ensemble de problèmes avec lesquels travailler ... mais je l'ignorerai pour l'instant.

B. Dois-je diviser la tâche en deux microprocesseurs? Un pour gérer l'interrogation et la mise à jour de l'interface utilisateur et un pour la boucle de synchronisation critique pour la mission.

c. Autre chose?

Mon objectif principal est de ne pas avoir à utiliser d'ordinateur du tout. Je veux également calculer le swing, mais dans ce cas, le swing ne veut rien dire si je n'ai pas un tempo verrouillé et précis. Merci pour vos conseils!


1
Arduino met toujours en place des routines d'interruption, provoquant une gigue. Dans de nombreux cas, ce n'est pas un problème, mais il est bon d'en être conscient. noInterrupts();arrête la gigue, mais arrête également toutes les interruptions souhaitées.
jippie

1
Lorsque vous dites "faire un séquençage à bord", cela signifie-t-il configurer les battements par mesure, le BPM et une piste de tick à bord? Ensuite, vous voulez probablement vous souvenir des pressions sur les boutons qui se sont produites dans la barre afin que les "cerveaux" de l'appareil puissent alimenter des notes midi sur votre ordinateur portable? Voulez-vous alors supprimer certains sons de percussion si vous les frappez à nouveau sur la note qui a été enregistrée précédemment? Etc .. jusqu'où voulez-vous aller? Stockage de vos battements? Vous créez une séquence de mesures correspondant à une piste complète? Modifier des barres spécifiques? Modification du tempo de barres spécifiques? Tout cela mange du processeur, alors optez pour le meilleur processeur.
Andy aka

Oui, tout ça.
Steve Cooley

2
C'est un joli étui que vous avez fait!
shuckc

3
En plus de ce que d'autres ont dit, cela ressemble à une idée que vous avez peut-être l'intention de produire et de vendre. Un Arduino coûte 20 $, tandis qu'un AVR coûte 2 $. Non seulement vous gagnerez le contrôle du matériel dont votre application a besoin, mais vous économiserez beaucoup d'argent.
Phil Frost

Réponses:


5

Les interruptions sont votre ami pour chronométrer les tâches sensibles, mais uniquement si vous mettez les aspects critiques de synchronisation dans l'interruption, et qu'aucune autre interruption ne se produit qui a une priorité plus élevée. Les microcontrôleurs de l'Arduino "basé sur AVR" (par exemple l'ATmega328P) ont des priorités d'interruption fixes, comme détaillé à la page 58ff de la fiche technique . Donc, si vous avez utilisé TIMER2 COMPA comme interruption de synchronisation critique et aucune autre interruption, vous devriez être OK (car il a la priorité la plus élevée). Si vous souhaitez également utiliser des interruptions de priorité inférieure, vous devez vous assurer que toutes réactivent les interruptions globales lors de la saisie de leur routine de service d'interruption:

Lorsqu'une interruption se produit, le bit I d'activation d'interruption globale est effacé et toutes les interruptions sont désactivées. Le logiciel utilisateur peut écrire une logique sur le bit I pour activer les interruptions imbriquées. Toutes les interruptions activées peuvent alors interrompre la routine d'interruption en cours.

(p. 14 de la fiche technique )

Ceci est légèrement différent sur les Arduinos basés sur ARM car leur cœur Cortex-M3 a un "contrôleur d'interruption de vecteur imbriqué", où les priorités ne sont pas fixes (peuvent être définies dans le logiciel), et la gestion des interruptions imbriquées est la norme. Ainsi, pour le chronométrage d'applications critiques, l'Arduino basé sur ARM vous offrirait plus de flexibilité. Cependant, je ne pense pas que ce soit vraiment nécessaire pour votre candidature.

La plus grande question est vraiment de savoir comment ces choses peuvent être implémentées facilement avec les bibliothèques Arduino. Pour obtenir les meilleures performances, vous devrez probablement coder en dehors des bibliothèques dans une certaine mesure, au moins pour les bits critiques de synchronisation, c'est-à-dire éviter des choses comme delay () ou millis ().

La nécessité ou non de fractionner dépend de la quantité de traitement que vous comptez effectuer. Encore une fois, sortir des bibliothèques peut potentiellement vous donner de meilleures performances.


3

Cela peut, avec la programmation appropriée, se faire très certainement sur un ATmega328P (en fonction quelque peu de la complexité de la boucle de batterie. Je suppose que ~ <50 événements de batterie dans la boucle. Est-ce raisonnable?).

Notez que j'ai dit ATmega328P , pas nécessairement un Arduino .

L'environnement Arduino a beaucoup de choses par défaut en arrière-plan, ce qui rend la programmation extrêmement déterministe (car vous aurez besoin de quelque chose de temporel critique) difficile.

La vraie question que vous devez vous poser ici est à quel point êtes-vous intéressé par la programmation, à quel point êtes-vous intéressé à développer un instrument?

Bien que je sois tout à fait convaincu qu'il est possible de faire tout ce que vous voulez sur une seule ATmega (boucle de batterie, plusieurs entrées analogiques, LCD, boutons, interface MIDI), la vraie question est de savoir combien de travail cela va être de tout presser? Encore une fois, voulez-vous apprendre à optimiser le code MCU intégré ou à construire des instruments? Il est assez facile d'aller simplement à un MCU plus rapide si nécessaire, mais vous devez déterminer les performances du MCU dont vous avez besoin maintenant , donc six mois de travail, vous ne réalisez pas que vous ne pouvez pas tout faire fonctionner aussi vite que vous avoir besoin.


Si j'étais vous, la première chose que je ferais serait de le faire fonctionner sans truc arduino (en gros, le traiter comme un ATmega brut et utiliser AVR studio ou similaire). Ensuite, vous pouvez analyser de manière beaucoup plus efficace le type de performances dont vous avez besoin et si l'ATmega peut les gérer.

Une fois que vous êtes débarrassé des trucs arduino, vous êtes beaucoup plus libre d'utiliser différents MCU (ils sont généralement plus similaires que différents. Si vous pouvez en trouver un dans sa documentation, vous pouvez probablement faire la même chose pour les autres).

J'ai beaucoup travaillé avec les appareils ATxmega récemment, et ils sont vraiment sympas. Vous obtenez trois priorités d'interruption, ce qui facilite la gestion des éléments à temps critique. Ils sont également très agréables à travailler (conceptions périphériques saines! Structures de ports pratiques! Etc ...).

Il y a aussi les périphériques LPC de NXP, qui sont basés sur ARM, ainsi que certains des périphériques ARM d'Atmel (tels qu'utilisés sur l'Arduino Due), ou les MCU STM32 de ST. N'importe lequel d'entre eux aura des performances nettement supérieures à celles d'un ATmega, ou même d'un ATxmega.

Le principal inconvénient d'un processeur plus gros et plus puissant est le prix, mais à moins que vous ne fabriquiez des milliers de ces unités, les coûts d'assemblage et de fabrication par unité vont tellement dépasser la différence de coût (qui ne sera probablement que de quelques dollars ) qu'il est fondamentalement hors de propos.


Joliment - pour un produit commercial, un Arduino n'est tout simplement pas la voie à suivre - ils sont gourmands en énergie, lents et l'IDE n'est pas conçu pour un code optimal (rapide / petit), plutôt pour la commodité et l'apprentissage facile. Pour un coût moindre, vous pourriez même avoir un STM32 F4 (Cortex M4 32 bits> 100 MHz) ou similaire, bien que ce soit exagéré. Je pense que quelque chose comme l'un des plus petits PIC32, Cortex M3 ou AVR32 est probablement la voie à suivre, comme vous le mentionnez. De nombreuses priorités d'interruption, DMA, périphériques sophistiqués, puissance rapide / faible et beaucoup de RAM en font un choix facile par rapport à un Arduino.
Oli Glaser

@OliGlaser - Je pense que vous devez clairement délimiter entre un Arduino et un ATmega . Vous pouvez faire un petit code rapide sur un ATmega, et cet ATmega peut même être sur une carte Arduino. L'Arduino "IDE", d'autre part, est l'un des éditeurs de code les plus merdiques que j'ai jamais utilisé. D'un autre côté, le chargeur de démarrage OptiBoot est très agréable. Ce n'est pas parce que certaines pièces sont de la merde que vous devez jeter le tout.
Connor Wolf

Absolument - je faisais référence à l'Arduino dans son ensemble, carte et IDE inclus - pas à l'ATmega, qui, j'en suis sûr, est aussi bon que tout autre uC comparable (PIC16 / 18F, etc.) Je l'aurais inclus dans ma liste mais Je pense que le prix entre 8 bits et 16/32 bits étant si proche de nos jours, c'est probablement une bonne idée de dépenser 1 $ supplémentaire et de savoir que vous avez la puissance du processeur à revendre (sauf si vous le mentionnez, nous parlons d'énormes chiffres et construit au prix le plus bas absolu, mais je doute qu'un Arduino aurait été envisagé :-))
Oli Glaser

1

J'avais besoin de lire sur les minuteries avant de commencer à penser à la précision du timing (construire également un séquenceur pas à pas midi avec un arduino, bien qu'il soit garanti d'avoir l'air moins cool que ceux ^^). Cette série d'articles a été la plus informative:

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

En ce moment, je pense que ma solution pour obtenir un timing précis sera.

A. Utilisez l'Arduino AVR

B. Gardez la tâche sur un microprocesseur

C. Utilisez judicieusement les détartreurs, les minuteries et les interruptions pour obtenir la précision dont vous avez besoin.

MISE À JOUR

En utilisant le tutoriel midi de base pour Arduino et après avoir lu cet article sur les minuteries et les pré-détartreurs, le code suivant est ce que j'ai trouvé. Le code utilise le mode timer1 et CTC pour jouer une note midi tous les quarts de seconde et une note éteinte tous les quarts de seconde (qui devrait être exactement 120 bpm). Malheureusement, cela arrive encore plus lentement que 120 bpm bien que ce soit le plus proche que j'ai obtenu ...

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

MISE À JOUR

J'ai du mal avec cela depuis ~ 24 heures maintenant et j'ai finalement obtenu des réponses du forum. Je pense que le code que j'ai utilisé ci-dessus ^^ est plutôt bon. Utilisation de l'ISR, en utilisant le mode CTC et prescalers etc. Après avoir multiplié au forum , je pense que la solution est moins à atteindre une précision sur le séquenceur MIDI, mais obtenir ma configuration matérielle tout (mes synthétiseurs et échantillonneurs) accrochés à la même horloge midi, qu'elle provienne ou non de l'Arduino.


0

Selon la façon dont vous souhaitez progressivement passer d'un ordinateur connecté à un système basé sur µC, vous pouvez envisager de placer un Raspberry Pi à l'intérieur de cette boîte (25 à 35 $ au détail ). De cette façon, vous pouvez avoir un ordinateur Linux complet (quoique de faible puissance) avec des ports USB et des broches GPIO.


Je suis sûr qu'il y a des boucliers d'extension ou tout ce qu'ils appellent pour le Pi, mais la carte mère a 17 broches GPIO. J'utilise toutes les broches du méga Arduino. 31 tacts + 30 LED, 10 entrées analogiques. 70+ E / S.
Steve Cooley

Oh, je voulais dire que si l'objectif immédiat était de supprimer l'ordinateur externe, vous pourriez garder le "séquenceur [qui] est une application qui s'exécute sur un ordinateur portable" et l'exécuter sur le Pi, connecté en interne à votre système existant de la même manière qu'il est connecté maintenant.
Rob Starling

@SteveCooley - On dirait que vous avez besoin de regarder dans les multiplexages IO / matrices de boutons. Vous ne devriez pas avoir besoin d'une ligne IO dédiée complète par bouton.
Connor Wolf

@SteveCooley - Enfer, vous n'avez même pas vraiment besoin d'une matrice de boutons. Vous pouvez faire TOUTES vos E / S numériques en utilisant seulement 4 broches rPi. Accrochez simplement tous les boutons et voyants de certains registres à décalage (parallèle-série pour les boutons, série-parallèle pour les voyants) et pilotez les registres à décalage à partir du port SPI du rPi. Vous devriez facilement obtenir des taux de mise à jour> 1Khz pour toute la matrice à l'aide du matériel SPI.
Connor Wolf du

Si la seule raison pour laquelle vous utilisez un Arduino Mega est l'IO, vous dépensez beaucoup d'argent sur quelque chose qui peut être fait très facilement avec quelques appareils externes, pour moins de 3 $.
Connor Wolf
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.