c # — Последовательный порт RS-485 и ограничения связи

#c# #performance #serial-port #communication

#c# #Производительность #последовательный порт #Информационные материалы

Вопрос:

Я пытаюсь установить связь с устройством, использующим RS-485, через последовательный порт. Все работает нормально, пока мы не попытаемся увеличить связь, чтобы проверить ограничение скорости карты, тогда, похоже, возникает странная проблема. По сути, мы отправляем первую команду с изображением в качестве аргументов, а затем другую команду для отображения этого изображения. После каждой команды карта отвечает, что команда была принята хорошо. Но мы достигаем пределов слишком рано, и карта должна обрабатывать гораздо больше.

Итак, мне интересно, поскольку передача и прием проходят по одному и тому же проводу, есть ли какое-то столкновение данных? И я должен ждать, чтобы получить все данные? Является ли SerialDataReceivedEventHandler слишком медленным в этой ситуации и должен ли я продолжать считывать байты в цикле while true в отдельном потоке и сигнализировать другому потоку после получения полного сообщения?

Другая информация :

  • У нас уже есть протокол для обмена данными: startdelimiter, data, CRC16, enddelimiter
  • Отправка двух команд — это то, как мы это делаем, и не может быть изменено.
  • Скорость передачи данных определена на уровне 115200
  • Инженер все еще работает над программой на плате, поэтому проблема также может быть с его стороны.
  • Английский не является моим родным языком, поэтому не стесняйтесь спрашивать, если я не был ясен … 🙂

Я признаю, что программирование на SerialPort не является моей сильной стороной, и я пытался найти какую-то оболочку, но я не нашел ничего, что соответствовало бы моим потребностям. Если у кого-то есть предложение для меня, это было бы здорово, или, может быть, у кого-то есть представление о том, что может быть не так. В любом случае, вот немного кода :

Поток, отправляющий кадры :

     public void SendOne()
        {
            timerLast = Stopwatch.GetTimestamp();

            while (!Paused amp;amp; conn.ClientConnState == Connexion.ConnectionState.Connected)
            {
                timerNow = Stopwatch.GetTimestamp();

                if ((timerNow - timerLast) / (double)Stopwatch.Frequency >= 1 / (double)fps)
                {
                    averageFPS.Add((int)((double)Stopwatch.Frequency / (timerNow - timerLast))   1);
                    if (averageFPS.Count > 10) averageFPS.RemoveAt(0);

                    timerLast = Stopwatch.GetTimestamp();

                    if (atFrame >= toSend.Count - 1)
                    {
                        atFrame = 0;
                        if (!isLoop)
                            Paused = true;
                    }


                    SendColorImage();
                }
}



  public void SendColorImage()
    {
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Request.SendImage, toSend[  atFrame]));
        WaitForResponse();
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Request.DisplayImage, VIP16.DisplayOnArg));
        WaitForResponse();
    }

    private void WaitForResponse()
    {
        Thread.Sleep(25);
    }
  

Итак, WaitForResponse () имеет решающее значение, потому что, если я отправлю другую команду до того, как карта ответит, она сойдет с ума. Хотя я ненавижу использовать Thread.Sleep () потому что это не очень точно, плюс это ограничило бы мою скорость до 20 кадров в секунду, и если я использую что-то ниже 25 мс, вероятность сбоя намного выше. Итак, я собирался изменить поток.Переход в режим «Чтения байтов до получения всего сообщения» и игнорирование события DataReceivedEvent… просто интересно, полностью ли я здесь сбился с пути?

Tx a lot!

UPDATE 1

Сначала спасибо, Брэд и 500 — Внутренняя ошибка сервера! Но я решил пока придерживаться последовательного порта .NET и улучшить поток.Точность перехода в режим ожидания (с указанием времени начала периода). Я решил дождаться получения полного ответа и вот так синхронизировал свои потоки, используя ManualResetEventSlim (для скорости) :

 public static ManualResetEventSlim _waitHandle = new ManualResetEventSlim(false);
  

Затем я изменил SendColorIMage на :

    public void SendColorImage()
    {
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Requetes.SendImage, toSend[  atFrame]));
        WaitForResponse();
        conn.Write(VIP16.bytesToVIP16(0x70C1, VIP16.Requetes.DisplayImage, VIP16.DisplayOnArg));
        WaitForResponse2();
    }

    private void WaitForResponse()
    {
        Connexion._waitHandle.Wait(100);
        Thread.Sleep(20);
    }

    private void WaitForResponse2()
    {
        Connexion._waitHandle.Wait(100);
        //Thread.Sleep(5);
    }
  

С вызовом SerialDataReceivedEventHandler :

     public void Recevoir(object sender, SerialDataReceivedEventArgs e)
    {
        if (!msg.IsIncomplete)
            msg = new Vip16Message();

        lock (locker)
        {
            if (sp.BytesToRead > 0)
            {
                byte[] byteMsg = new byte[sp.BytesToRead];
                sp.Read(byteMsg, 0, byteMsg.Length);
                msg.Insert(byteMsg);
            }
        }

        if (!msg.IsIncomplete)
        {
            _waitHandle.Set();
            if (MessageRecu != null)
                MessageRecu(msg.toByte());
        }
    }
  

Итак, я обнаружил, что после второй команды мне не нужно вызывать Thread.Вообще переходил в режим сна … и после первого мне нужно было спать не менее 20 мс, чтобы карта не вышла из строя. Итак, я предполагаю, что это время, необходимое карте для приема / обработки всего изображения до его пикселя. И столкновение данных на самом деле не должно происходить, поскольку я жду, пока не придет все сообщение, что означает, что проблема не с моей стороны! ДА! :p

Комментарии:

1. Класс последовательного порта .NET всегда доставлял мне много головной боли. В .NET 4.0 стало намного лучше, но у меня все еще возникают проблемы, особенно с USB-адаптерами. Я настоятельно рекомендую CommStudio. Работает отлично. Вы можете получить бесплатную версию здесь: componentsource.com/products/commstudio/downloads.html?rv=42917 Никаких наворотов, как в full, но часть последовательного порта должна быть всем, что вам нужно.

Ответ №1:

Пара указаний:

  • После отправки вам нужно дождаться события «буфер передачи пуст», прежде чем читать ответ. Это EV_TXEMPTY в неуправляемом, я не помню, как это инкапсулировано на управляемой стороне — наш код RS485 предшествует .Компонент сетевого взаимодействия.

  • Вы можете перепрограммировать микросхему таймера с помощью вызова timeBeginPeriod (1), чтобы получить разрешение в 1 миллисекунду в потоке.Режим ожидания ().

  • Как бы то ни было, мы переходим в спящий режим ненадолго (1 мс) после отправки, а затем входим в цикл чтения, в котором продолжаем попытки чтения (опять же, с задержкой в 1 мс между попытками чтения) с порта, пока не будет получен полный ответ (или пока не истечет время ожидания или счетчик повторных попыток).).

Вот объявление импорта для timeBeginPeriod — я не верю, что оно доступно напрямую в .NET (пока?):

 [DllImport("winmm.dll")]
internal static extern uint timeBeginPeriod(uint period);   
  

Я надеюсь, что это поможет.