简体   繁体   English

串口性能 - VB.NET与C ++和Boost

[英]Serial port performance - VB.NET vs C++ & Boost

I'm having trouble understanding why my application takes so long to communicate with a device over a serial port on Windows 10. I've written two small test applications to try to see what makes it so slow. 我无法理解为什么我的应用程序需要很长时间才能通过Windows 10上的串行端口与设备通信。我已经编写了两个小型测试应用程序,试图看看是什么让它变得如此之慢。 Here's the code for both of them: 这是两个代码:

''VB.NET code    
Imports System.IO.Ports

Module Module1

    Sub Main()
        Dim port As New SerialPort("COM3", 921600, Parity.None, 8, 1)

        port.Open()

        port.DtrEnable = True
        port.RtsEnable = True

        Dim profiler As New Stopwatch

        profiler.Start()

        For i As Integer = 1 To 100
            port.Write("1PA?" & vbCrLf)
            port.ReadLine()
            port.Write("TB" & vbCrLf)
            port.ReadLine()
        Next

        profiler.Stop()

        Console.WriteLine("Average: " & profiler.ElapsedMilliseconds / 100 & "ms")

        Console.ReadKey()
    End Sub

End Module

And: 和:

//C++ code
#include <iostream>
#include <string>

#include "boost/asio/io_service.hpp"
#include "boost/asio/serial_port.hpp"
#include "boost/asio/read_until.hpp"
#include "boost/asio/write.hpp"
#include "boost/asio/streambuf.hpp"
#include "boost/asio/buffer.hpp"
#include "boost/thread.hpp"
#include "boost/ref.hpp"
#include "boost/lexical_cast.hpp"

using boost::asio::io_service;
using boost::asio::serial_port;
using boost::asio::streambuf;

size_t read_until(serial_port& port, streambuf& buf, const std::string& delim)
{
    return boost::asio::read_until(port, buf, delim);
}

void complete(const boost::system::error_code& error, std::size_t bytes_transferred)
{
    if (error)
        std::cout << "Error\n";
}

int main()
{
    std::cout << "Starting...\n";

    io_service io;
    serial_port port(io, "COM3");
    streambuf buf(1000);

    boost::posix_time::ptime t0 = boost::posix_time::microsec_clock::local_time();

    port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
    port.set_option(boost::asio::serial_port_base::parity());
    port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port::flow_control::hardware));
    port.set_option(boost::asio::serial_port_base::baud_rate(921600));
    port.set_option(boost::asio::serial_port_base::character_size(8));


    for (int i = 0; i < 100; ++i)
    {
        boost::asio::write(port, boost::asio::buffer("1PA?\r\n", 6));

        read_until(port, buf, "\r\n");
        buf.consume(buf.size());

        boost::asio::write(port, boost::asio::buffer("TB\r\n", 4));

        read_until(port, buf, "\r\n");
        buf.consume(buf.size());
    }

    boost::posix_time::ptime tE = boost::posix_time::microsec_clock::local_time();

    std::cout << (tE-t0).total_milliseconds() << '\n';

    std::cin.get();
}

The problem is that the VB.NET code reports an average of ~6ms per loop iteration (ie 3ms per write/read pair), while the C++ code takes over 60ms per iteration. 问题是VB.NET代码每循环迭代平均报告约6ms(即每个写/读对3ms),而C ++代码每次迭代占用超过60ms。

The rest of the project is written in C++, so I need to improve that code and can't simply use the other one. 项目的其余部分是用C ++编写的,所以我需要改进代码,不能简单地使用另一个代码。 Currently, the fastest way I found is to communicate over TCP/IP with a VB.NET application that routes TCP/IP to a serial port. 目前,我发现最快的方法是通过TCP / IP与将TCP / IP路由到串行端口的VB.NET应用程序进行通信。 Strangely, this is more than twice as fast as the direct C++ implementation despite the additional steps involved. 奇怪的是,尽管涉及额外的步骤,这仍然是直接C ++实现的两倍多。

Is there anything I'm missing, perhaps a setting in the C++ implementation? 有什么我想念的,也许是C ++实现中的一个设置? I've already tried all flow-control options, different buffer sizes, ... 我已经尝试过所有流量控制选项,不同的缓冲区大小,......

The 60 ms you are seeing for a write/read/write/read sequence corresponds pretty well to typical defaults for scheduled transfers. 您在写入/读取/写入/读取序列中看到的60 ms与预定传输的典型默认值非常相似。 Here's the port configuration for an FTDI (very popular chipset for USB serial ports) 这是FTDI的端口配置(非常流行的USB串口芯片组)

在此输入图像描述

If the serial port initialization code doesn't explicitly set timeouts, what you get is the scheduled transfers every 16ms. 如果串行端口初始化代码没有明确设置超时,那么你得到的是每16ms的预定传输。

If you instead call SetCommTimeouts , you can arrange for the USB device to forward the received data buffer every time there's a gap on the serial RX wire. 如果您改为调用SetCommTimeouts ,则可以安排USB设备每次在串行RX线上有间隙时转发接收的数据缓冲区。 To do this, set the ReadIntervalTimeout to the transfer time for just a couple bytes. 为此,将ReadIntervalTimeout设置为传输时间仅几个字节。 At 921 kbaud, each byte takes 10-11 microseconds, so the lowest possible timeout of 1 millisecond for ReadIntervalTimeout corresponds to a gap of about 92 bytes. 在921 kbaud,每个字节需要10-11微秒,因此ReadIntervalTimeout的最小可能超时1毫秒对应于大约92字节的间隙。

However, due to the different quality-of-implementation of drivers that come with various USB devices, it's possible to run into devices that don't have hardware support for inter-character timeout. 但是,由于各种USB设备附带的驱动程序的实现质量不同,因此可能会遇到没有硬件支持来进行字符间超时的设备。 In such cases, it is probably best to disable ReadIntervalTimeout and ReadTotalTimeoutMultiplier , and just use ReadTotalTimeoutConstant . 在这种情况下,最好禁用ReadIntervalTimeoutReadTotalTimeoutMultiplier ,并使用ReadTotalTimeoutConstant

Using either of these timeout configurations, the data transfer across USB will occur more timely and your code will make progress. 使用这些超时配置中的任何一种,USB上的数据传输将更加及时,您的代码将取得进展。

However, the USB latency will still be on the order of 3 milliseconds per receive (1 while the RX line is busy, 1 ms idle to trigger the timeout, and another 1 ms to wait for the next USB timeslot). 但是,USB延迟仍将是每次接收3毫秒的数量级(当RX线路忙时为1毫秒,1毫秒空闲以触发超时,另外1毫秒等待下一个USB时隙)。 To do better than 1 message every 3 milliseconds, you need to pipeline so that multiple messages are in-flight, by replacing the stop-and-wait protocol with some sliding window scheme. 为了比每3毫秒更好的消息,你需要通过一些滑动窗口方案替换stop-and-wait协议来管道,以便多个消息在飞行中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM