Using DMA to Access a High Speed ​​Serial Port

I am using the serialport component in C # and it works well! But the question is how to handle high speed data transfer as quickly as possible (for example 2 Mbps).

As I researched about this, I found that memory can be accessed directly (using DMA like this link ). Can anyone tell me how I can define and use it in my application?

+3


source to share


3 answers


No, the [C #] tag puts that million miles out of reach. The snippet of code on this web page is not real, it is just a "template". It does things that you can't do in C #, like handling interrupts, getting the physical memory address of buffers, directly programming device registers. For machines that can execute C # code other than the Micro Framework, this can only be done by device drivers.

This will be the kind of code that can run on a microcontroller, such as a processor, that does not run on a secure operating system. Even then it gets stretched, it calls DMA with unsettled magic, never actually triggering a transfer on a transfer, for example. No DMA controller signs are also required to allow bus access between devices. This is fake code.



When using real hardware, you always get a device driver with it, which takes care of talking to the device. If the device does support DMA, which is very unusual, then the device driver programmer will not use it. The SerialPort class that you use in your C # program uses an operating system api that is generic to any type of serial port device. It passes your I / O requests to the device driver to get the job done.

The interface between the operating system api and the device driver is covered by the IOCTL. This MSDN page contains documents for Windows. There's a pretty close match between IOCTL and api, the api layer is pretty thin. When you look closely, it will be obvious that none of these have anything to do with DMA. They cannot, this is strictly a description of the driver implementation.

+6


source


I believe you don't need to make serial access faster, but instead tweak your C # application to process data faster. Run the profiler of your choice and determine what percentage of the time is consumed in the methods of the serialport components. I predict this will be quite low, meaning that any efforts to speed up the serial port will be wasted.



+1


source


You got it completely wrong.

First of all, you're in an environment where you don't have direct access to hardware (Windows), so something like this is basically impossible without writing a kernel driver (and you don't want to believe me).

Secondly, the operating system and its drivers are already very optimized, if you want to use DMA transfers, they should already be doing it.

Third, you won't get these speeds if your serial controller does not support it, and usually not, a PC RS232 controller is usually up to 115200baud, but some controllers get up to 1MB.

But there is another option: go USB without USB: D

From your question, I assume you are interacting with some kind of microcontroller from a PC and you don't want to program the USB driver for the controller (or it doesn't have USB capability), so a very good option is to use an RS-232 to USB cable which usually supports very fast speeds, I've personally used FTDI RS-232 3v3 and it gets up to 3MB ( http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_TTL-232R_CABLES.pdf ).

In the end, you will be programming the normal serial port code, but it will use the more advanced USB interface (this is another benefit, not all PCs today come with a serial port).

After that, to benefit from the speedup, remember to set the port to a very large read / write buffer (at least 1 MB), perform a non-blocking receive procedure, and send large chunks of data (which must match in the write buffer).

Remember that your device must be at the same speed as the one you chose, so if you set it to 2-3 Mbps, your device must run the serial interface at the same speed.

Here is an example of the receiving part of what I described:

    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();
        }
    }

      

+1


source







All Articles