c # - SerialPort RS-485 и пределы связи - PullRequest
3 голосов
/ 25 мая 2011

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

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

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

  • У нас уже есть протокол для связи: startdelimiter, data, CRC16, enddelimiter
  • Отправка двумя командами - это способ, которым мы это делаем, и его нельзя изменить.
  • BaudRate определено в 115200
  • Инженер все еще работает над программой на карте, поэтому проблема также может быть на его конце.
  • Английский не мой родной язык, поэтому не стесняйтесь спрашивать, если я не ясно ...:)

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

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

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

            while (!Paused && 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 мс, риск сбоя гораздо выше.Поэтому я собирался изменить Thread.Sleep на «Чтение байтов, пока не будет получено все сообщение» и игнорировать DataReceivedEvent ... просто интересно, если я полностью не в курсе?

Tx много!

ОБНОВЛЕНИЕ 1

Первое спасибо Брэду и 500 - Внутренняя ошибка сервера!Но я решил пока придерживаться последовательного порта .NET и улучшить точность Thread.Sleep (с timebeginperiod).Я решил дождаться получения полного ответа и синхронизировал свои потоки примерно так, используя 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.Sleep ... а после первой мне нужно было поспать хотя бы20 мс для карты, чтобы не врезаться.Так что я думаю, что это время, когда карта должна получить / обработать все изображение до его пикселя.И столкновение данных на самом деле не должно происходить, так как я жду, пока не прибудет целое сообщение, что означает, что проблема не на моем конце!ДА!: p

1 Ответ

1 голос
/ 25 мая 2011

Пара указателей:

  • После отправки вам нужно дождаться события опустошения буфера передачи, прежде чем читать ответ.Это неуправляемый EV_TXEMPTY, я не помню, как он инкапсулирован на управляемой стороне - наш код RS485 предшествует компоненту .NET comport.

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

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

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

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

Надеюсь, это поможет.

...