Использование DMA для доступа к высокоскоростному последовательному порту

Я использую компонент serialport в C#, и он работает хорошо! Но вопрос в том, как можно быстрее обрабатывать высокоскоростные (например, 2 Мбит / с) передачи данных.

Поскольку я исследовал это, я обнаружил, что к памяти можно получить доступ напрямую (используя DMA, как эта ссылка). Кто-нибудь может сказать мне, как я могу определить и использовать его в моем приложении?

3 ответа

Решение

Нет, тег [C#] делает этот миллион миль недоступным. Фрагмент кода на этой веб-странице не настоящий, это просто "шаблон". Он делает то, что вы не можете делать в C#, например, обрабатывает прерывания, получает адрес физической памяти буферов, напрямую программирует регистры устройства. На машинах, которые могут выполнять код C#, не считая Micro Framework, это может быть сделано только драйверами устройств.

Это будет тот код, который может работать на микроконтроллере, процессор, который не работает с операционной системой защищенного режима. Даже тогда, когда он растягивается, он вызывает DMA с помощью неустановленной магии, фактически никогда не начиная передачу на передачу. Никаких признаков контроллера DMA также не требуется для разрешения доступа к шине между устройствами. Это поддельный код.

Когда вы используете реальное оборудование, вы всегда получаете драйвер устройства, который заботится о том, чтобы общаться с устройством. Если устройство на самом деле поддерживает DMA, что очень необычно, программист драйвера устройства не избежит его использования. Класс SerialPort, который вы используете в программе на C#, использует API операционной системы, универсальный для любого типа устройства с последовательным портом. Он передает ваши запросы ввода / вывода драйверу устройства, чтобы выполнить работу.

Интерфейс между API операционной системы и драйвером устройства покрывается IOCTL. На этой странице MSDN представлены документы для Windows. Между IOCTL и API существует довольно близкое соответствие, слой API довольно тонкий. Если вы посмотрите поближе, станет очевидно, что ни один из них не имеет никакого отношения к DMA. Они не могут, это просто деталь реализации драйвера.

Вы поняли это совершенно неправильно.

Прежде всего, вы находитесь в среде, в которой у вас нет прямого доступа к аппаратному обеспечению (Windows), поэтому то, что вы описали, в принципе невозможно без написания драйвера ядра (и вы не хотите, поверьте мне).

Во-вторых, операционная система и ее драйверы уже очень оптимизированы, если ей нужно использовать передачи DMA, она должна это уже делать.

В-третьих, вы не получите эти скорости, если ваш последовательный контроллер не поддерживает их, и обычно они этого не делают, контроллер ПК RS232 обычно имеет скорость до 115200 бод, но некоторые контроллеры получают до 1 МБ.

Но есть и другой вариант, иди USB без USB:D

Исходя из вашего вопроса, я полагаю, вы взаимодействуете с микроконтроллером какого-либо типа с ПК и не хотите программировать драйвер USB для контроллера (или он не поддерживает USB), поэтому очень хорошим вариантом является используйте кабель RS-232 - USB, который обычно поддерживает очень высокие скорости, я лично использовал FTDI RS-232 3v3, и он достигает 3 МБ ( http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_TTL-232R_CABLES.pdf).

В конце вы будете программировать обычный код последовательного порта, но он будет использовать более расширенный интерфейс USB (это еще одно преимущество, не все ПК сегодня оснащены последовательным портом).

После этого, чтобы по-настоящему выиграть от ускорения, не забудьте установить для порта очень большой буфер чтения / записи (не менее 1 МБ), выполнить неблокирующую процедуру приема и отправить большие порции данных (которые должны уместиться в записи буфер).

Помните, что ваше устройство должно соответствовать выбранной скорости, поэтому, если вы установите его на 2-3 Мбит / с, ваше устройство должно запускать последовательный интерфейс с той же скоростью.

Вот пример получения части того, что я описал:

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

Я считаю, что вам не нужно делать последовательный доступ более быстрым, и вместо этого настраивайте свое приложение на C#, чтобы быстрее обрабатывать передачу данных. Запустите профилировщик по вашему выбору и измерьте, какой процент времени тратится в методах компонента serialport. Я предсказываю, что он будет довольно низким, то есть все усилия по ускорению создания последовательного порта будут потрачены напрасно.

Другие вопросы по тегам