#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);
Я надеюсь, что это поможет.