使用DMA访问高速串行端口

时间:2015-05-28 06:53:36

标签: c# serial-port dma directmemory

我在c#中使用serialport组件,效果很好!但问题是如何更快地处理高速(例如2 Mbps)数据传输。

正如我研究的那样,我发现可以直接访问内存(使用像this link这样的DMA)。 谁能告诉我如何在我的应用程序中定义和使用它?

3 个答案:

答案 0 :(得分:6)

不,[c#]标签将这一百万英里放在遥不可及的地方。该网页上的代码段不是真实的,它只是一个"模式"。它做了你在C#中无法做的事情,比如处理中断,获取缓冲区的物理内存地址,直接编程设备寄存器。在可以执行C#代码的计算机上,不计算Micro Framework,这只能由设备驱动程序完成。

这种代码可以在微控制器上运行,这种处理器不会与受保护模式的操作系统一起运行。即便如此,它仍然是伸展的,它通过未声明的魔法调用DMA,例如从未实际开始传输。也没有DMA控制器的迹象,需要仲裁设备之间的总线访问。这是假代码。

当您使用真实硬件时,您总是会得到一个带有它的设备驱动程序,负责与设备通信。如果设备实际上支持DMA,非常不寻常,那么设备驱动程序员就不会避免使用它。您在C#程序中使用的SerialPort类使用操作系统api,这是一种适用于任何类型串行端口设备的通用API。它将您的I / O请求传递给设备驱动程序以完成工作。

操作系统api和设备驱动程序之间的接口由IOCTL覆盖。 This MSDN page记录了适用于Windows的内容。在IOCTL和api之间有一个非常接近的匹配,api层非常薄。当你仔细观察时,很明显它们都没有与DMA有任何关系。他们不能,它严格来说是一个驱动程序实现细节。

答案 1 :(得分:1)

我相信您不需要更快地进行串行访问,而是调整您的c#应用程序以更快地处理数据传输。运行您选择的分析器并测量在串行端口组件的方法中花费的时间百分比。我预测这将是相当低的,这意味着任何努力使串行端口更快的努力都将徒劳无功。

答案 2 :(得分:1)

你完全错了。

首先,您处于一个您无法直接访问硬件(Windows)的环境中,因此如果没有编写内核驱动程序,您所描述的内容基本上是不可能的(并且您不会我想,相信我。

其次,操作系统及其驱动程序已经非常优化,如果需要使用DMA传输,它应该已经完成​​了。

第三,除非你的串行控制器支持它,否则你不会获得这些速度,而且他们通常不会,PC的RS232控制器通常高达115200bauds但有些控制器高达1Mb。

但还有另一个选择,没有USB的USB:D

根据您的问题,我认为您将某种类型的微控制器与PC连接,并且您不想为控制器编程USB驱动程序(或者它不具备USB功能),所以一个非常好的选择是使用RS-232到USB电缆,那些通常支持非常快的速度,我个人使用FTDI RS-232 3v3并且它达到3Mb(http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_TTL-232R_CABLES.pdf)。 / p>

最后,您将编写一个普通的串行端口代码,但它将使用更加扩展的USB接口(这是另一个优势,而不是今天所有的PC都带有串口)。

在那之后,要真正从加速中受益,记得设置一个非常大的读/写缓冲区(至少1Mb),做一个非阻塞接收例程并发送大块数据(必须适合在写缓冲区中。)

请记住,您的设备必须与所选的速度匹配,因此,如果您将其设置为2-3Mbps,则设备必须以完全相同的速度运行串行接口。

以下是我所描述的接收部分的一个例子:

    SerialPort sp;
    Queue<byte[]> buffer = new Queue<byte[]>();
    AutoResetEvent dataAvailable = new AutoResetEvent(false);
    Thread processThread;

    public void Start()
    {
        //Start the processing thread
        processThread = new Thread(ProcessData);
        processThread.Start();

        //Open the serial port at 3Mbps and with buffers of 3Mb
        sp = new SerialPort("COM12", 3145728, Parity.None, 8, StopBits.One);
        sp.ReadBufferSize = 1024 * 1024 * 3;
        sp.WriteBufferSize = 1024 * 1024 * 3;
        sp.DataReceived += sp_DataReceived;
        sp.Open();
    }

    //This thread processes the stored chunks doing the less locking possible
    void ProcessData(object state)
    {

        while (true)
        {

            dataAvailable.WaitOne();

            while (buffer.Count > 0)
            {

                byte[] chunk;

                lock (buffer)
                    chunk = buffer.Dequeue();

                //Process the chunk here as you wish

            }

        }

    }

    //The receiving function only stores data in a list of chunks
    void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        while (sp.BytesToRead > 0)
        { 
            byte[] chunk = new byte[sp.BytesToRead];
            sp.Read(chunk, 0, chunk.Length);

            lock (buffer)
                buffer.Enqueue(chunk);

            dataAvailable.Set();
        }
    }