System.IO.Ports与非托管串行端口代码的性能

Irboard

This is obscure, but then, what do I end up blogging about that isn't obscure? I wish I knew why this stuff keeps happening to me.

这是晦涩难懂的,但是接下来,我对不那么晦涩难懂的博客写什么? 我希望我知道为什么这种事情不断发生在我身上。

I'm trying to do a little work with an external Infrared Transmitter from Iguanaworks connected to a 9-pin serial port.

我正在尝试使用连接到9针串行端口的Iguanaworks外部红外变送器进行一些工作。

I'm trying to pulse the IR to emulate a Sony Remote Control.

我正在尝试使IR发出脉冲以模拟Sony遥控器。

I'm using the new System.Diagnostic.Stopwatch class in .NET (a wrapper for the Win32 QueryPerformanceCounter API) that uses Ticks for its unit of measurement. On my system the Frequency was 2992540000 ticks per second.

我在.NET(Win32 QueryPerformanceCounter API的包装器)中使用新的System.Diagnostic.Stopwatch类,该类使用Ticks作为其度量单位。 在我的系统上,频率为每秒2992540000个滴答。

The Sony remote I'm trying to emulate uses a 40kHz frequency, so it wants to flash the LED one cycle once every 1/40000 of a second. That means every 74814 ticks or every 25µs (microseconds are 1/1000000 of a second.)

我要模拟的Sony遥控器使用40kHz频率,因此它想每1/40000秒对LED闪烁一次。 这意味着每74814个滴答声或每25µs(微秒为1/1000000秒)。

I'm trying to send a header pulse of 2.4ms in length and I need to cycle the LED once every 25µs. I turn it on for 8µs and turn if off for 17µs. That means it will cycle 96 (2400µs) times for the header, 24 (1200µs) times for a space or zero, and 48 (600µs)times for a one. An image from San Bergmans illustrates:

我正在尝试发送长度为2.4ms的标题脉冲,我需要每25µs循环一次LED。 我将其打开8µs,然后将其关闭17µs。 这意味着它将为标头循环96(2400µs)次,为空格或零循环24(1200µs)次,为1循环48(600µs)次。 San Bergmans的图片说明:

The Iguanaworks IR uses DTR (Data Terminal Ready) to turn on the IR LED.

Iguanaworks IR使用DTR(数据终端就绪)打开IR LED。

I've started with managed code, because I'm a managed kind of a guy. I started using System.IO.Ports like this:

我从托管代码开始,因为我是一个受管理的人。 我开始像这样使用System.IO.Ports:

public class ManagedIRSerialPort : IIRSerialPort

公共类ManagedIRSerialPort : IIRSerialPort

    {

{

        SerialPort port = null;

SerialPort端口= null ;

        public ManagedIRSerialPort(string portString)

公共ManagedIRSerialPort(字符串portString)

        {

{

            port = new SerialPort(portString);

port =新的SerialPort (portString);

            port.RtsEnable = true; //needed for power!

port.RtsEnable = true ; //需要动力!

            port.BaudRate = 115200;

port.BaudRate = 115200;

            port.StopBits = StopBits.One;

port.StopBits = StopBits .One;

            port.Parity = Parity.None;

port.Parity =奇偶校验.None;

            port.DataBits = 7;

port.DataBits = 7;

            port.Handshake = Handshake.None;

port.Handshake =握手.None;

        }

}

        public void Open()

公共无效Open()

        {

{

            port.Open();

port.Open();

        }

}

        public void On()

公共无效On()

        {

{

            port.DtrEnable = true;

port.DtrEnable = true ;

        }

}

        public void Off()

公共无效Off()

        {

{

            port.DtrEnable = false;

port.DtrEnable = false ;

        }

}

        public void Close()

公共无效Close()

        {

{

            port.Close();

port.Close();

        }

}

    }

}

But I just couldn't get it to cycle fast enough. Remember, I need the header to take 2400µs total. In this screenshot, you can see it's taking an average of 30000µs! That sucks.

但是我只是无法使其循环足够快。 记住,我需要接头总共花费2400µs。 在此屏幕截图中,您可以看到平均花费30000µs! 糟透了。

So I futzed with this for a while, and then Reflector'd around. I noticed the implementation of set_dtrEnable inside of System.IO.Ports.SerialStream was WAY more complicated than it needed to be for my purposes.

所以我对此进行了一段时间的修饰,然后反射器在周围。 我注意到System.IO.Ports.SerialStream中set_dtrEnable的实现比我的目的要复杂得多。

//Reflector'd Microsoft code
internal bool DtrEnable

//反射器的Microsoft代码
内部布尔DtrEnable

{

{

      get

得到

      {

{

            int num1 = this.GetDcbFlag(4);

int num1 = this .GetDcbFlag(4);

            return (num1 == 1);

return (num1 == 1);

      }

}

      set

      {

{

            int num1 = this.GetDcbFlag(4);

int num1 = this .GetDcbFlag(4);

            this.SetDcbFlag(4, value ? 1 : 0);

这个.SetDcbFlag(4,value?1:0);

            if (!UnsafeNativeMethods.SetCommState(this._handle, ref this.dcb))

如果(!UnsafeNativeMethods.SetCommState(此._handle,引用此.dcb))

            {

{

                  this.SetDcbFlag(4, num1);

这个.SetDcbFlag(4,num1);

                  InternalResources.WinIOError();

InternalResources.WinIOError();

            }

}

            if (!UnsafeNativeMethods.EscapeCommFunction(this._handle, value ? 5 : 6))

if (!UnsafeNativeMethods.EscapeCommFunction( this ._handle,value?5:6))

            {

{

                  InternalResources.WinIOError();

InternalResources.WinIOError();

            }

}

      }

}

}

}

All I figured I needed to do was call the Win32 API EscapeCommFunction to set the DTR pin high. One thing I learned quickly was that calling EscapeCommFunction was 4 times faster than calling SetCommState for the purposes of raising DTR.

我想我需要做的就是调用Win32 API EscapeCommFunction将DTR引脚设置为高电平。 我很快了解到的一件事是,为了提高DTR,调用EscapeCommFunction比调用SetCommState快4倍。

public class UnmanagedIRSerialPort : IIRSerialPort

公共类UnmanagedIRSerialPort : IIRSerialPort

{

{

    IntPtr portHandle;

IntPtr portHandle;

    DCB dcb = new DCB();

DCB dcb =新的DCB ();

    string port = String.Empty;

字符串端口=字符串。空;

    public UnmanagedIRSerialPort(string portString)

公共UnmanagedIRSerialPort(字符串portString)

    {

{

        port = portString;

port = portString;

    }

}

    public void Open()

公共无效Open()

    {

{

        portHandle = CreateFile("COM1",

portHandle = CreateFile( “ COM1” ,

              EFileAccess.GenericRead | EFileAccess.GenericWrite,

EFileAccess .GenericRead | EFileAccess .GenericWrite,

              EFileShare.None,

EFileShare .None,

              IntPtr.Zero,

IntPtr的.Zero,

              ECreationDisposition.OpenExisting,

ECreationDisposition .OpenExisting,

              EFileAttributes.Overlapped, IntPtr.Zero);

EFileAttributes .Overlapped, IntPtr .Zero);

        GetCommState(portHandle, ref dcb);

GetCommState(portHandle, ref dcb);

        dcb.RtsControl = RtsControl.Enable;

dcb.RtsControl = RtsControl .Enable;

        dcb.DtrControl = DtrControl.Disable;

dcb.DtrControl = DtrControl .Disable;

        dcb.BaudRate = 115200;

dcb.BaudRate = 115200;

        SetCommState(portHandle, ref dcb);

SetCommState(portHandle, ref dcb);

    }

}

    public void On()

公共无效On()

    {

{

        EscapeCommFunction(portHandle, SETDTR);

EscapeCommFunction(portHandle,SETDTR);

        //dcb.DtrControl = DtrControl.Enable;

//dcb.DtrControl = DtrControl.Enable;

        //SetCommState(portHandle, ref dcb);

// SetCommState(portHandle,ref dcb);

    }

}

    public void Off()

公共无效Off()

    {

{

        EscapeCommFunction(portHandle, CLRDTR);

EscapeCommFunction(portHandle,CLRDTR);

        //dcb.DtrControl = DtrControl.Disable;

//dcb.DtrControl = DtrControl.Disable;

        //SetCommState(portHandle, ref dcb);

// SetCommState(portHandle,ref dcb);

    }

}

    public void Close()

公共无效Close()

    {

{

        CloseHandle(portHandle);

CloseHandle(portHandle);

    }

}

    #region Interop Serial Port Stuff

#region互操作性串行端口资料

        [DllImport("kernel32.dll")]

[ DllImport ( “ kernel32.dll” )]

    static extern bool GetCommState(IntPtr hFile, ref DCB lpDCB);

静态外部布尔GetCommState( IntPtr hFile, ref DCB lpDCB);

    [DllImport("kernel32.dll")]

[ DllImport ( “ kernel32.dll” )]

    static extern bool SetCommState(IntPtr hFile, [In] ref DCB lpDCB);

静态外部布尔SetCommState( IntPtr hFile,[ In ] ref DCB lpDCB);

    [DllImport("kernel32.dll", SetLastError = true)]

[ DllImport ( “ kernel32.dll” ,SetLastError = true )]

    public static extern bool CloseHandle(IntPtr handle);

公共静态外部布尔CloseHandle( IntPtr handle);

    [DllImport("kernel32.dll", SetLastError = true)]

[ DllImport ( “ kernel32.dll” ,SetLastError = true )]

    static extern bool EscapeCommFunction(IntPtr hFile, int dwFunc);

静态外部布尔EscapeCommFunction( IntPtr hFile, int dwFunc);

   
    //Snipped so you don't go blind...full file below!


//已删除,因此您不会盲目...下面的完整文件!

    #endregion

#endregion

}

}

Here's the NOT COMPLETE (Remember, it just does DTR) Unmanaged Serial Port class. Thanks PInvoke.NET for the structures to kick start this)!:File Attachment: UnmanagedIRSerialPort.cs (10 KB)

这是NOT COMPLETE(请记住,它只是DTR)非托管串行端口类。 感谢PInvoke.NET提供的启动结构!):文件附件:UnmanagedIRSerialPort.cs(10 KB)

As you can see I've got it abstracted away with a common interface so I can switch between managed serial and unmanaged serial quickly. I ran the same tests again, this time with MY serial port stuff:

如您所见,我已经使用通用接口将其抽象化,因此可以在托管串行和非托管串行之间快速切换。 我再次运行相同的测试,这次使用我的串行端口资料:

Sweet, almost 10x faster and darn near where I need it to be. However, it's not consistent enough. I need numbers like 2400, 600, 1200. I'm having to boost the process and thread priority just to get here...

甜,几乎快10倍和织补不远的地方,我需要它的人。 但是,这还不够一致。 我需要2400、600、1200之类的数字。为了到达这里,我必须提高进程和线程优先级。

 previousThreadPriority = System.Threading.Thread.CurrentThread.Priority;

previousThreadPriority = System.Threading。 线程.CurrentThread.Priority;

 System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;

系统线程。 线程.CurrentThread.Priority = System.Threading。 ThreadPriority .Highest;

 System.Diagnostics.Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

系统诊断程序。 Process .GetCurrentProcess()。PriorityClass = ProcessPriorityClass .RealTime;

...and undo it with...

...并使用...撤消它

 System.Threading.Thread.CurrentThread.Priority = previousThreadPriority;

系统线程。 线程.CurrentThread.Priority = previousThreadPriority;

 System.Diagnostics.Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal;

系统诊断程序。 Process .GetCurrentProcess()。PriorityClass = ProcessPriorityClass .Normal;

...and that's just naughty.

...那只是顽皮的。

At this point, it's close, but I'm wondering if it's even possible to flash this thing fast enough. I'm at the limit of my understanding of serial ports (Is DTR affected by Baud Rate? Is 115200 the fastest? Would this be faster in C++ (probably not), or is there a faster way to PInvoke?)

至此,它已经接近了,但是我想知道是否有可能足够快地刷新此内容。 我对串行端口的了解有限(DTR是否受波特率的影响?115200是否最快?在C ++中会更快(可能不是),还是有更快的PInvoke方法?)

Any ideas?

有任何想法吗?

IrimageINTERESTING NOTE:
Remember, you can't see IR (it's Infrared, not in our visible spectrum) but you can see it if you point it at a Webcam, which is what I've been doing to debug.

有趣的提示:请记住,您看不到IR(它是红外的,不在我们的可见光谱范围内),但是如果将其指向网络摄像头,则可以看到它,这是我一直在调试的东西。

ANOTHER ASIDE: This is a uniquely high-power transmitter, that charges up a capacitor in order to provide a range of up to 10-meters. However, it requires a few minutes to charge up. I had no trouble getting output from it using Winlirc (the only officially supported software) but when I used my application, the transmitter would peter out and eventually go dim.

另一面:这是一个独特的大功率发射机,它对电容器充电,以提供长达10米的射程。 但是,充电需要几分钟。 使用Winlirc (唯一受官方支持的软件)从中获取输出没有问题,但是当我使用我的应用程序时,发送器会逐渐耗尽并最终变暗。

I fought with it for a while, then decided to RTFS (where "S" is "Schematic). The board layout is here. Notice that the RTS (Serial Port Ready-To-Send) Pin 7 goes straight to VIN. Duh! <slaps forehead>. They are sipping power off the Ready To Send pin and I'm not setting that pin hot.

我花了一段时间,然后决定使用RTFS(其中“ S”为“原理图”。此处电路板布局。请注意,RTS(串行端口准备发送)引脚7直连VIN。 <拍打额头>。他们正在关闭“准备发送”引脚的电源,并且我没有将该引脚设置为高温。

port = new SerialPort(portString);
port.RtsEnable = true; //needed for power!
port.BaudRate = 115200;
port.StopBits = StopBits.One;
port.Parity = Parity.None;
port.DataBits = 7;
port.Handshake = Handshake.None;

port =新的SerialPort(portString); port.RtsEnable = true; //需要动力! port.BaudRate = 115200; port.StopBits = StopBits.One; port.Parity = Parity.None; port.DataBits = 7; port.Handshake =握手。无;

So, if you ever find yourself using the High-Power LIRC Transmitter/Receiver in an unsupported way writing your own program, remember to set RTS high or you won't get any power.

因此,如果您发现以不受支持的方式编写自己的程序来使用大功率LIRC发射器/接收器,请记住将RTS设置为高电平,否则将无法获得任何功率。

翻译自: https://www.hanselman.com/blog/performance-of-systemioports-versus-unmanaged-serial-port-code

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值