使用 Arduino 的 MIDI 音序器计时精度

电器工程 Arduino 定时 迷笛
2022-01-27 09:48:23

我构建了这些音乐音序器

在此处输入图像描述

只是它不完全是音序器,它是音序器的物理接口。音序器是一个在音序器连接的笔记本电脑上运行的应用程序,这个东西可以让用户在运行中制作鼓循环。这很有趣,但它需要一台笔记本电脑,因为音序器不是“板载”的。

我想要的是在我的设备上进行测序。

现在让我们假设我知道如何解决 USB MIDI 连接的类合规问题,还假设我可以弄清楚如何连接 arduino 以从 5 针 DIN 端口发送 MIDI 音符。我最担心的是随着时间的推移节奏漂移,因为在事件循环的每次运行中以分钟为单位的时间不一致。

我知道的一些事情:

  1. 你不应该依赖delay()控制节奏循环。延迟会停止固件的所有操作,这不起作用,因为我需要在序列运行时轮询物理用户界面以进行更改。

  2. 基于的计算millis()更好,因为固件可以在某个计数过去后继续运行和操作。

  3. 尽管我的物理控件都没有触发中断例程,但某些操作可能会延迟主程序loop()的运行。如果我设计了一个等待用户输入的函数,那么如果millis()计数结束,这显然会导致错过“截止日期”的问题。我知道这个问题是我自己设计的......

问题:

A. 基于 AVR 的 arduino 是否适合轮询用户界面和运行关键任务时序循环的微控制器?我知道现在有一个基于 ARM 的 Arduino,速度要快得多。Teensy 3.0 会是更好的选择吗?这两个都是 3.3V 板,所以这是另一组需要处理的问题……但我现在暂时忽略它。

B. 我应该把任务分成两个微处理器吗?一种用于处理轮询和更新用户界面,另一种用于关键任务时序循环。

C。还有什么?

我的主要目标是完全不用电脑。我也想计算挥杆,但在这种情况下,如果我没有锁定和计时准确的速度,挥杆就没有任何意义。谢谢你的建议!

4个回答

中断是时序敏感任务的朋友,但前提是您将时序关键方面放入中断中,并且没有其他具有更高优先级的中断发生。“基于 AVR”的 Arduino(例如 ATmega328P)上的微控制器具有固定的中断优先级,详见数据表第 58 页。因此,如果您使用 TIMER2 COMPA 作为您的关键时序中断并且没有其他中断,您应该没问题(因为它具有最高优先级)。如果您还想使用较低优先级的中断,则需要确保它们在进入其中断服务程序时都重新启用全局中断:

发生中断时,全局中断使能 I 位被清零,所有中断均被禁用。用户软件可以将逻辑 1 写入 I 位以启用嵌套中断。然后所有启用的中断都可以中断当前的中断程序。

(数据表第 14 页

这在基于 ARM 的 Arduino 上略有不同,因为它们的 Cortex-M3 内核具有“嵌套向量中断控制器”,其中优先级不固定(可以在软件中设置),嵌套中断处理是常态。因此,对于时序关键型应用,基于 ARM 的 Arduino 将为您提供更大的灵活性。但是,我认为这对于您的应用程序并不是必需的。

更大的问题是使用 Arduino 库实现这些东西有多容易。为了获得最佳性能,您可能必须在某种程度上在库之外进行编码,至少对于时序关键位,即完全避免像 delay() 或 millis() 这样的事情。

是否需要拆分取决于您打算进行多少处理。同样,走出图书馆可能会给您带来更好的性能。

通过适当的编程,这绝对可以在 ATmega328P 上完成(在某种程度上取决于鼓循环的复杂性。我假设循环中 ~<50 个鼓事件。这合理吗?)。

请注意,我说的是ATmega328P,不一定是Arduino

Arduino 环境在后台有很多默认的东西,这使得极其确定性的编程(因为你需要一些对时间至关重要的东西)具有挑战性。

您需要在这里问的真正问题是您对编程的兴趣与您对开发仪器的兴趣如何?

虽然我非常有信心在单个 ATmega 上完成您想要的一切(鼓循环、多个模拟输入、LCD、按钮、MIDI 接口),但真正的问题是要完成多少工作才能将所有内容都挤进去?同样,您想学习优化嵌入式 MCU 代码还是构建仪器?如果需要,只需使用更快的 MCU 很容易,但是您需要确定您现在需要的 MCU 性能,因此六个月的工作,您不会意识到您无法让所有东西像您一样快速工作需要。


如果我是你,我要做的第一件事就是让它在没有 arduino 的情况下工作(基本上,把它当作一个原始的 ATmega,并使用 AVR studio 或类似的东西)。然后,您可以更有效地分析您需要什么样的性能,以及 ATmega 是否可以管理它。

一旦你摆脱了 arduino 的东西,你就可以更自由地使用不同的 MCU(它们通常更相似而不是不同。如果你能从它的文档中找出一个,你可能可以为其他人做同样的事情)。

我最近一直在使用 ATxmega 设备,它们真的很棒。您将获得三个中断优先级,这使得管理时间关键的事情变得更加容易。它们也非常适合使用(Sane 外围设计!方便的端口结构!等等......)。

还有来自 NXP 的基于 ARM 的 LPC 设备,以及一些 Atmel 的 ARM 设备(用于 Arduino Due),或 ST 的 STM32 MCU。这些中的任何一个都将具有ATmega 甚至 ATxmega 更高的性能。

更大、更强大的处理器的主要缺点是价格,但除非你制造数千个这样的单元,否则每单元的组装和制造成本将大大超过成本差异(可能只有几美元) 这基本上是无关紧要的。

在我开始考虑计时准确性之前,我需要阅读计时器(还使用 arduino 构建了一个 midi 步进音序器,尽管它保证看起来不如那些 ^^ 酷)。这一系列文章提供了最丰富的信息:

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

现在我认为我获得精确时间的解决方案将是。

A. 使用 AVR arduino

B. 将任务保存在一个微处理器上

C. 明智地使用预分频器、定时器和中断来获得所需的精度。

更新

使用 Arduino 的基本 midi 教程并在查看有关计时器和预分频器的这篇文章后,我想到了以下代码。该代码使用 timer1 和 CTC 模式在每四分之一秒播放一个 midi 音符,每四分之一秒播放一个音符(应该正好是 120 bpm)。可悲的是,尽管这是我得到的最接近的,但它的速度仍然比 120bpm 慢...

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

更新

我已经为此苦苦挣扎了约 24 小时,终于从论坛中得到了一些答案。我认为我上面使用的代码^^非常好。使用 ISR,使用 CTC 模式和预分频器等。在联系论坛后,我认为解决方案不是为了在 midi 音序器上获得精度,而是让我的整个硬件设置(我的合成器和采样器)连接到相同的midi 时钟,无论时钟是否来自 Arduino。

根据您希望从系留计算机过渡到基于 µC 系统的渐进程度,您可能会考虑将 Raspberry Pi 放入该盒子中(零售价 25-35 美元)。这样,您就可以拥有一台带有 USB 端口和 GPIO 引脚的完整(尽管低功耗)基于 linux 的计算机。