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

C# Arduino串行写入跳过/延迟消息,c#,arduino,serial-port,send,midi,C#,Arduino,Serial Port,Send,Midi,创意: 我和一个朋友正在用(Elegoo)Arduino Mega构建一个节拍器,它还可以通过串口发送MIDI时钟信号。 闪烁的LED与设定的BPM值同步,BPM控制旋转编码器和其他一切工作正常。只有通过串行方式发送MIDI信号才让我们头疼 问题: MIDI时钟信号(0xF8)需要在每个节拍上发送24次。因此,我们只需计算时钟滴答声之间的时间,在时间间隔过去后,我们通过串行发送0xF8。容易的。 但是当我们把它连接到一个同上的X4吉他活套上时,我们的节拍器和活套的LED闪烁不同步。因此,我们在C

创意: 我和一个朋友正在用(Elegoo)Arduino Mega构建一个节拍器,它还可以通过串口发送MIDI时钟信号。 闪烁的LED与设定的BPM值同步,BPM控制旋转编码器和其他一切工作正常。只有通过串行方式发送MIDI信号才让我们头疼

问题: MIDI时钟信号(0xF8)需要在每个节拍上发送24次。因此,我们只需计算时钟滴答声之间的时间,在时间间隔过去后,我们通过串行发送0xF8。容易的。 但是当我们把它连接到一个同上的X4吉他活套上时,我们的节拍器和活套的LED闪烁不同步。因此,我们在C#.NET中编写了一个小脚本来验证通过串行发送的是什么,结果是,根据设置的BPM,一些消息根本没有发送或延迟,这导致活套计算出与我们尝试发送的BPM不同的BPM()

但我们在这里完全迷路了。为什么有些邮件被延迟/未发送?即使在“正常”波特率(如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
  }
}
<代码>包含了它,并且在各种各样的BPMS上足够接近,以考虑它足够精确),并且这些活套的闪烁导致彼此之间的漂移太快以至于无法接受。我们把LED灯放在彼此的旁边,它们在大约30秒内从同步变为交替闪烁,所以在现场音乐会上,一切都会分崩离析。
由于loopers LED闪烁是由接收到的MIDI输入触发的,因此我们查看了发送的时钟信号的一致性,并发现了信号之间的奇数延迟。

该脚本计算下一条消息的时间相对于上一条消息实际发送的时间。这意味着任何延误都会累积起来

相反,将下一次计算为一个固定的时间间隔,从上次应发送的消息开始计算:

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
  }
}
为什么有些邮件被延迟/未发送?即使在“正常”波特率(如9600)上,问题也是一样的。而且它似乎不随Arduino CPU使用率或设置BPM而扩展

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

C#代码使用带有以下注释的
秒表
类:

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

我的重点是从

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

当console应用程序运行时,您可以通过以下方式设置处理器关联性来避免此问题:

  • ProcessThread.ProcessorAffinity
    在代码中
  • 使用
    START
    /affinity
如果做不到这一点,另一种测量消息间隔时间的方法是CRO/DSO,或者甚至使用一个Arduino来测量另一个的输出

但是当我们把它连接到一个同上的X4吉他活套上时,我们的节拍器和活套的LED闪烁不同步

至于您的实际应用,从问题中不清楚LED和活套的不同步速度和程度

有不同的问题需要考虑:

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

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

一年多之后,我将最终回答我自己的问题。 与此同时,我有可能使用示波器来分析时间。 事实证明,LED和串行端口输出漂移明显分开。 我不知道为什么,据我所知,Arduino的串行输出与cpu时钟是异步处理的,但我没想到会有这么大的漂移

对于我们的问题,一个非常简单的解决方案就是简单地从本机串行端口切换到软件串行端口。这样cpu不仅可以处理闪烁,还可以处理串行通信,因此示波器上看不到漂移


至于对C#脚本的评论和调试,有些人已经说对了,用这样简单的方法,如此精确的计时是不可靠的。arduino确实没有丢弃消息,但“只是”漂移严重。

您想过使用该软件吗?它可能无法解决您的问题,但在处理MIDI和Arduino时可能会有所帮助。如果您正在观看Arduino串行终端上的输出,您是否也会遇到此问题?为什么在
if(currentTime-startTimeMIDI>intervalMIDI)的末尾有分号{;
?最后一个:在布线时,您是否像MIDI标准一样使用解耦功能?@Tom_C感谢您指出这一点。我修复了它,这是一个简单的复制粘贴错误。在另一个主题上:是的,我已经尝试过使用MIDI库。但如果您查看源代码,该库在MIDI时钟信号方面做了完全相同的事情(通过串行发送0xF8)。自
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
  }
}