在 C# 中使用 SerialPort 通过 USB(PC/Arduino)的通信速度似乎“剪辑"了 [英] Communication speed over USB (PC/Arduino) using SerialPort in C# seems to "clip"

查看:66
本文介绍了在 C# 中使用 SerialPort 通过 USB(PC/Arduino)的通信速度似乎“剪辑"了的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

准备好冗长的解释 - 抱歉.

Prepare for a lengthy explanation - sorry.

我正在使用 Visual Studio 2019 用 C# 构建一个应用程序,它将与一些 Arduino MEGA 2560 R3 设备进行通信.连接是通过 USB Serial(如果我没记错的话,这是 USB CDC,对吧?).

I'm building an application in C#, using Visual Studio 2019, that will communicate with some Arduino MEGA 2560 R3 devices. The connection is through USB Serial (if I'm not mistaken, this is USB CDC, right?).

在各种波特率下,甚至高达 2000000 的波特率都可以正常通信.

Communication runs fine over all kinds of baudrates, even up to 2000000.

为了确保我的 Arduino 可以跟随"我的 PC 发送的命令速率,我使用的是 ACK 协议,这就像我的 Arduino 用序列A\n"确认每个命令一样简单.

To make sure that my Arduino can "follow" the command rate sent by my PC, I'm using an ACK-protocol, which is as simple as my Arduino confirming each command with the sequence "A\n".

为了对通信进行压力测试,我让应用程序发送0123456789\n"命令尽可能快,等待A\n"每个命令发送后.即发送11个字符,接收2个字符,共13个字符.

In an attempt to stress test the communication, I let the application send "0123456789\n" commands as fast as possible, waiting for the "A\n" after each command sent. This means sending 11 characters, and receiving 2 characters, a total of 13 characters.

这就是谜团开始的地方.

从 38400 波特开始,Tx/Rx 周期似乎削减为每秒 244.如果我将波特率增加到 2000000,它将保持在每秒 244 个 Tx/Rx 周期.这是怎么回事!

Starting from 38400 baud, the Tx/Rx cycle seems to clip at 244 per second. If I increase the baudrate up to 2000000, it remains at 244 Tx/Rx cycles per second. What is going here!

以下是PC代码,这是一个用Visual Studio 2019编写的简单控制台应用程序:

Below is the PC code, which is a simple Console application written in Visual Studio 2019:

using System;
using System.Diagnostics;
using System.IO.Ports;


namespace BasicComTest
{
    class Program
    {
        public static SerialPort _serialPort;


        static void Main(string[] args)
        {
            _serialPort = new SerialPort();
            _serialPort.BaudRate = 115200;
            _serialPort.PortName = "COM4";
            _serialPort.Handshake = Handshake.None;
            _serialPort.NewLine = "\n";


            _serialPort.ReadTimeout = 2000;
            _serialPort.WriteTimeout = 2000;


            _serialPort.Open();


            _serialPort.WriteLine("");


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


            ulong count = 0;


            while (!Console.KeyAvailable)
            {
                _serialPort.WriteLine($"0123456789");
                string ack = _serialPort.ReadLine();


                count++;
                if (count == 1000)
                {
                    sw.Stop();
                    Debug.WriteLine($"1000 commands in {sw.ElapsedMilliseconds} - {1000000/sw.ElapsedMilliseconds} commands/sec");
                    count = 0;
                    sw.Restart();
                }
            }


            _serialPort.Close();
        }
    }
}

以下是简单的 Arduino 草图(我正在使用 ISR 例程以尝试提高速度):

And below is the simple Arduino sketch (I'm using an ISR routine in an attempt to increase speed):

volatile uint8_t rcvd = 0;

String sCommandRx;
String sCommand;
bool bCommandRx;

ISR(USART0_RX_vect)
{
  // received data
  rcvd = UDR0;

  if (rcvd == '\n')
  {
    sCommandRx = sCommand;
    bCommandRx = true;
    sCommand = "";
  }
  else
    sCommand += (char)rcvd;
}

void setup()
{
  UBRR0H = 0;
  UBRR0L = 0;

  // USART initialization with 16MHz system clock, 8-none-1, RX interrupt
  UCSR0A = 1<<U2X0 | 0<<MPCM0;
  UCSR0B = 1<<RXCIE0 | 0<<TXCIE0 | 0<<UDRIE0 | 1<<RXEN0 | 1<<TXEN0 | 0<<UCSZ02;
  UCSR0C = 0<<UMSEL01 | 0<<UMSEL00 | 0<<UPM01 | 0<<UPM00 | 0<<USBS0 | 1<<UCSZ01 | 1<<UCSZ00 | 0<<UCPOL0;

  sCommand = "";
}

void loop()
{
  if (bCommandRx)
  {
    if (sCommandRx == "0123456789")
    {
      UDR0 = 'A';
      while((UCSR0A & 1<<TXC0)==0);
      UCSR0A |= 1<<TXC0;
     
      UDR0 = '\n';
      while((UCSR0A & 1<<TXC0)==0);
      UCSR0A |= 1<<TXC0;
    }

    bCommandRx = false;
  }
}

我已经可以保证 Arduino 代码完美运行,因为我用 2 个 Arduino 背靠背对其进行了测试,以排除它们导致剪辑.如果你想阅读完整的故事,你可以在 Arduino 论坛上找到它:

I can already guarantee that the Arduino code works perfect, because I tested it with 2 Arduino's back to back to rule out that they are causing the clipping. If you want to read the full story, you will find it on the Arduino forum here:

https://forum.arduino.cc/index.php?topic=719361.0

如果我用 2 个背靠背的 Arduino 测量通信速度,那么它以 2000000 波特的速度达到每秒 6060 个 Tx/Rx 设置(您必须计算一些开销,测量大约为 100 毫秒对于 1000 个命令 - 您可以在 Arduino 论坛中阅读完整报告).

If I measure the communication speed with the 2 back-to-back Arduino's, then it goes as fast as 6060 Tx/Rx sets per second at 2000000 baud (you have to count some overhead, which is measured at about 100 msec for 1000 commands - you can read the full report in the Arduino forum).

但是使用我的控制台应用程序,我获得了最大 每秒 244 个 Tx/Rx 集.除了(这让它变得更奇怪),如果我构建A\n"在我的 Arduino 的 ISR 路由中,我得到了 每秒 488 个 Tx/Rx 集的两倍:

But using my console application, I get maximum 244 Tx/Rx sets per second. Except (and this makes it even weirder), if I build the "A\n" in the ISR routing in my Arduino, then I'm getting exactly the double of 488 Tx/Rx sets per second:

volatile uint8_t rcvd = 0;


ISR(USART0_RX_vect)
{
    // received data
    rcvd = UDR0;
    
    // reply with "A\n" after receiving "\n"
    if(rcvd == '\n')
    {
        UDR0 = 'A';
        while((UCSR0A & 1<<TXC0)==0);
        UCSR0A |= 1<<TXC0;
        
        UDR0 = '\n';
        while((UCSR0A & 1<<TXC0)==0);
        UCSR0A |= 1<<TXC0;
    }
}

void setup()
{
    // set baud rate (500 Kbps)
    UBRR0H = 0;
    UBRR0L = 3;

    // USART initialization with 16MHz system clock, 8-none-1, RX interrupt
    UCSR0A = 1<<U2X0 | 0<<MPCM0;
    UCSR0B = 1<<RXCIE0 | 0<<TXCIE0 | 0<<UDRIE0 | 1<<RXEN0 | 1<<TXEN0 | 0<<UCSZ02;
    UCSR0C = 0<<UMSEL01 | 0<<UMSEL00 | 0<<UPM01 | 0<<UPM00 | 0<<USBS0 | 1<<UCSZ01 | 1<<UCSZ00 | 0<<UCPOL0;
}

void loop(){
}

但是我是否把A\n"的传输在 loop() 或 ISR 中,背靠背 Arduino 的结果保持不变.

But whether I put the transmission of "A\n" in the loop() or in the ISR, the results of back-to-back Arduino's remains the same.

很抱歉我的描述很长而且很详细.但我的问题显然很简单.这个剪辑的原因是什么?

我有一个理论.虽然看了很多差评"关于 SerialPort,我认为原因在于它通过 USB(Arduino 与 USB 2.0 一起使用).我已经在Arduino论坛上解释了这个理论(我什至在下面稍微更正了我的解释):

I have a theory. Although I have read a lot of "bad comments" about SerialPort, I think that the reason has to be found in the fact that this goes over USB (Arduino works with USB 2.0). I have explained the theory on the Arduino forum (I even corrected my explanation slightly below):

如果我没记错的话,USB 2.0 HID 设备(我猜 CDC 也是一样)的轮询率为 1 毫秒.我使用过一些 PIC 微控制器,并在我的 PC 上构建了我自己的 USB 堆栈以与它通信,我有一些限制,每秒 1000 个命令,或每秒 500 个 Tx/Rx 集.正如您在上面看到的那样,我使用 SerialPort 可以获得的最快结果是每秒 488 个 Tx/Rx 集,这略慢但非常接近.当然可能会有一些转换延迟",在串行和 USB 之间造成一些不同步,因此丢失了一些 USB 帧.把A\n"loop() 中的答案(不在 ISR 中)只给了我一半(244)个命令.我认为将答案放入 ISR 使其反应如此之快,以至于数据仍然可以捎带"它.在下一帧.如果我将答案放在 loop() 中,它就会错过火车,并占用了一些稍后的 1 毫秒帧.无论如何,这些只是目前的疯狂猜测,所以我当然可能完全错了.

更新:

只是尝试在我的 C# 程序中处理一些较低级别的文件,使用 Kernel32.CreateFile、Kernel32.WriteFile 和 Kernel32.ReadFile,但得到了完全相同的结果,这意味着每秒 244 个 Tx/Rx 集.甚至尝试调整时间 (COMMTIMEOUTS),但没有任何改变.

Just tried to work with some lower level file handling in my C# program, using Kernel32.CreateFile, Kernel32.WriteFile and Kernel32.ReadFile, but got exactly the same results which means 244 Tx/Rx sets per second. Even tried to play with the timings (COMMTIMEOUTS), but nothing changed.

我还尝试发送和接收更大的字符串,似乎如果我对 Tx 和 Rx 部分使用最多 72 + '\n',它仍然保持稳定.使用更多字符开始降低每秒发送/接收设置的数量,直到我对发送和接收部分都使用 76 + '\n'.然后它再次稳定在每秒 162 个 Tx/Rx 组.

I also tried with sending and receiving larger strings of characters, and it seems that if I use up to 72 + '\n' for both the Tx and Rx portion, it remains stable. Using more characters starts dropping the Tx/Rx sets per second number, until I use 76 + '\n' for both the Tx and Rx portion. Then it remains stable again at 162 Tx/Rx sets per second.

对于我的应用程序,每秒 244 个 Tx/Rx 集已经足够了,但仍然很想知道这个限制是从哪里来的.

For my application, 244 Tx/Rx sets per second is good enough, but still am curious to understand from where that limitation comes.

推荐答案

这是一个旧线程,但我还是要发表评论.我也遇到过同样的问题.你看到消息爆裂,然后保持,然后爆裂等.这个问题在我的 arduino 方面得到了解决,但由于我不使用 Mega 而是一个 Teensy(使用 Teensyduino,Arduino 的扩展)这个解决方案可能是对你不可用.无论如何,SerialUSB.send_now() 为我解决了这个问题.我不知道这个函数在底层做了什么,但它可能是一个起点.

This is an old thread, but I comment anyway. I have experienced the same problem. You see the messages burst, then hold, then burst etc. The problem was solved on the arduino side for me, but since I don't use the Mega but instead a Teensy (with Teensyduino, an extension to Arduino) this solution is maybe not available for you. Anyway, SerialUSB.send_now() solved the problem for me. I have no clue what this function does on low level, but it can perhaps be a starting point.

这篇关于在 C# 中使用 SerialPort 通过 USB(PC/Arduino)的通信速度似乎“剪辑"了的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆