Arduino串行写入跳过/延迟消息

问题描述 投票:0回答:2

想法:我和一个朋友正在用(Elegoo)Arduino-Mega构建节拍器,另外,它还可以通过串行端口发送MIDI-Clock信号。与设置的BPM值同步闪烁LED,通过旋转编码器进行BPM控制,其他一切都很好。仅通过串行发送MIDI信号会让我们头疼。

问题:每个节拍,MIDI时钟信号(0xF8)需要发送24次。因此,我们只需计算时钟滴答之间的时间,然后经过时间间隔,就可以通过串行发送0xF8。简单。但是,当我们将其连接到Ditto X4吉他弯管器时,节拍器的LED闪烁和弯管器的LED闪烁不同步。因此,我们在C#.NET中编写了一个小脚本,以验证通过串行发送的内容,结果表明,根据设置的BPM,某些消息根本没有发送或被延迟,这导致循环程序计算出的BPM与我们尝试发送(Screenshot of script output)。

但是我们在这里完全迷路了。为什么有些消息被延迟/未发送?即使在“正常”波特率(如9600)上,问题也相同。而且它似乎与Arduino CPU的使用量或BPM设置无关:

Set BPM:      Lost Message every x Messages:
  300                      24-26
  150                      10-12
  50                       4-5

我们还测试了Arduino Uno R3(同样来自Elegoo),但问题是相同的。

此示例脚本可用于复制问题:

#include <Arduino.h> //Einbinden der Arduino Bibliothek

//Timer Variables
unsigned long startTimeMIDI = 0;
unsigned long currentTime = 0;
unsigned long intervalLED;
unsigned long intervalMIDI;

short counter_BPM = 300 * 2; // Internally we use BMP*2

void setup()
{
  Serial.begin(31250); //Forced by the MIDI standard
  while ( !Serial ) /*wait for serial init*/ ;
}

void loop()
{
  currentTime = micros();

  intervalLED = (120000000/counter_BPM); //60000000*(BPM/2)
  intervalMIDI = intervalLED/24; //Midi Clock has to be sent 24 times for each beat

  if (currentTime - startTimeMIDI > intervalMIDI){
    Serial.write(0xF8);    //send MIDI Clock signal
    startTimeMIDI = currentTime;  //reset timer value
  }
}

这是用于监视发送内容的C#脚本:

static void Main(string[] args)
    {
        serial = new SerialPort("COM4", 31250);
        serial.Open();

        int cycleSize = 50; //Averaging over 50 Values

        long[] latencyList = new long[cycleSize+1];

        Stopwatch watch = new Stopwatch();
        watch.Start();

        int n = 0;
        while(true)
        {
            n++;
            watch.Start();

            int response = serial.ReadByte();
            watch.Stop();

            long latency = watch.ElapsedTicks/(Stopwatch.Frequency/(1000L*1000L));
            watch.Reset();

            if (n <= cycleSize)
            {
                latencyList[n] = latency;
            }
            else
            {
                latencyList[n % cycleSize] = latency;
            }

            double average = latencyList.Average();
            Console.WriteLine("" + n + " " + latency.ToString("000000") + "µs - response:" + response + " - Average: " + average.ToString("##0.00") + " - BPM: " + (int)(60000000/(average * 24)));

        }
}
c# arduino serial-port send midi
2个回答
0
投票

脚本计算相对于实际发送前一条消息的时间而言,下一条消息的时间。这意味着所有延迟都会加起来。

相反,应该从发送最后一条消息开始,以固定间隔计算下一次:

void setup()
{
  Serial.begin(31250); //Forced by the MIDI standard
  while ( !Serial ) /*wait for serial init*/ ;

  intervalLED = (120000000/counter_BPM); //60000000*(BPM/2)
  intervalMIDI = intervalLED/24; //Midi Clock has to be sent 24 times for each beat

  startTimeMIDI = micros() + intervalMIDI;
}

void loop()
{
  if (micros() >= startTimeMIDI) {
    Serial.write(0xF8);             //send MIDI Clock signal
    startTimeMIDI += intervalMIDI;  //next timer value
  }
}

0
投票

为什么某些消息被延迟/未发送?即使在“正常”波特率(如9600)上,问题也相同。而且它似乎不随Arduino CPU的使用而扩展或设置BPM

这似乎与您测量消息之间的时间有关。

C#代码使用具有以下注释的Stopwatch类:

在多处理器计算机上,线程在哪个处理器上运行无关紧要。但是,由于BIOS或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的计时结果。要指定线程的处理器相似性,请使用ProcessThread.ProcessorAffinity方法。

我的重点从https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netcore-3.1中添加

因此,当控制台应用程序在执行过程中切换处理器内核时,您可能会获得不同的计时结果。

通过使用以下命令来设置控制台应用程序的运行时,可以通过设置处理器关联来避免此问题:

  • 代码中的ProcessThread.ProcessorAffinity
  • 使用START/affinity flag
  • Task Manager

失败,测量消息之间的时间间隔的另一种方法是CRO / DSO,甚至使用一个Arduino测量另一个消息的输出。

但是,当我们将它连接到Ditto X4吉他弯管器时,节拍器的LED闪烁和弯管器的LED闪烁不同步。

对于您的实际应用,不清楚这个问题有多快,LED和Looper不同步多少。

有不同程度的几个问题需要考虑:

  • micros()分辨率为4 µs。由于整数舍入问题和轮询延迟,每拍节来自Arduino的消息可能会耗费4-8 µs。
  • Arduino时钟频率精度。从Elegoo网站上的图像可以看出,它们的电路板使用的陶瓷谐振器不如晶体稳定和准确。

这些差异在总体方案中很小,但更重要的是,[[无关如果Arduino是发出时钟信号的“导体”。我的猜测是所连接的MIDI设备上的LED不是从Arduino的MIDI时钟信号得出其频率。

© www.soinside.com 2019 - 2024. All rights reserved.