Войти

Показать полную графическую версию : с# - не могу понять принцип чтения com-порта


Carmageddon
02-12-2013, 16:33
Здравствуйте. Имеется устройство, оно принимает от меня пакет данных (6 байт), и отправляет ответ - 28 байт.
Не могу понять как организовать чтение?
на данный момент на форму брошены 2 кнопки - нажимаю на первую, происходит :
byte[] a = { 0x02, 0x4B, 0x00, 0x12, 0x04, 0x01 };
port.Open();
port.Write(a, 0, a.Length);
Соответственно вторая кнопка - чтение :
byte[] b = new byte[28];
port.Read(b, 0, b.Length);
Клацаю на первую, после на вторую - все читается и обрабатывается.
Но(!) - если запихать это в одну процедуру - то ничего не получается, т.к. устройству нужно 600 мс для посылки ответа, т.е. в буфер для чтения попадают нули, 28 байт 0x00 - пустой буфер, т.к. устройство попросту не успевает прислать ответ.
Как правильно поступить в этом случае?
Могу предположить установить паузу, но это - костыль.
Как правильно выходить из данной ситуации?

pva
02-12-2013, 18:06
Варианты:
1. (самый простой) синхронное чтение (либо просто увеличь таймаут), читай сразу 28 байт.
2. если железяка поддерживает аппаратную синхронизацию - включи (и в настройках порта тоже)
3. если железяка поддерживает программную синхронизацию (xon/xoff) - включи в настройках порта
4. если железяка в любом случае шлёт 1-й байт определённый (например 27), укажи его в настройках как event char

Первый вариант подходит в 99.9% случаев

С таймаутом тоже неплохо. Особенно если совместить с поллингом

Carmageddon
02-12-2013, 18:22
Варианты:
1. (самый простой) синхронное чтение (либо просто увеличь таймаут), читай сразу 28 байт. »
А как читать сразу 28 байт? Я вроде так и делаю :
byte[] b = new byte[28];
port.Read(b, 0, b.Length);
Поднимаю таймаут до 1000мс - в массиве байтов все 0x00 :(

Да, я заранее знаю, какие байты будут идти первыми, и кол-во байт.
Поищу завтра event char

pva
03-12-2013, 10:42
покажи как настройки порта выставляешь

Carmageddon
03-12-2013, 11:43
SerialPort MyPort = new SerialPort("COM2", 9600, Parity.Even, 8, StopBits.One);
MyPort.ReadBufferSize = 1024;
MyPort.WriteBufferSize = 1024;
MyPort.WriteTimeout = 1000;
MyPort.ReadTimeout = 1000;

pva
03-12-2013, 14:01
Основываюсь на http://msdn.microsoft.com/ru-ru/library/system.io.ports.serialport%28v=vs.110%29.aspx
глупый хелп, конечно. Совсем обленился микрософт.
Если не забываешь сделать Open(), то попробуй:
1. поотключать аппаратное управление портом
2. вызови 28 раз сподряд ReadByte(). Про неё точно написано что она синхронная. Если заработает - значит используй её либо ищи как работать асинхронно

вот такая штука может быть полезной:

ReceivedBytesThreshold Получает или задает число байтов, содержащихся во внутреннем входном буфере перед наступлением события DataReceived.

Carmageddon
03-12-2013, 15:11
2. вызови 28 раз сподряд ReadByte(). »
Вроде сработало - данные пришли как нужно, но в полном масштабе не тестировал
Дополню таким фактом - при чтении Read() - в портмоне данные приходят (видны) как одна строка. В текущем случае, они видны как один байт на строку (что вполне понятно, т.к. читаю 28 байт в цикле). Но визуально в портмоне данные читаются блоками по 14 байт, между блоками некая задержка.
P.s. - когда читает родная утилита железяки, данные в портмоне видны "одной строкой"
Что ж за странность - читает 14 байт, ложит в массив и небольшая задержка, и дальше читает по 14 байт. Цикл то крутиться на 28
Вот, взгляните (картинка в хайде) :

http://s43.radikal.ru/i101/1312/66/8c765413bdb5.jpg


вот такая штука может быть полезной:
ReceivedBytesThreshold »

Да, кстати, очень хороший вариант.
Однако, стоит заметить о многочисленных отзывах, и официальной информации с msdn - что сие событие не обязано срабатывать на каждый полученный байт. Вдруг мой 28й байт как раз станет тем самым. А после него данные не приходят, соответственно событие уже не сработает :(.
буду тестировать вариант с циклом.

P.s.s. уважаемый pva, а подскажите пожалуйста, где мне просвятиться, о том, что есть "синхронный" и "асинхронный режимы" в доступной форме?

Спасибо за Ваши ответы, очень помогли

pva
03-12-2013, 18:50
Скажу честно, я не знаю. Отвечал на основе опыта работы с портом в C/C++. Принцип примерно такой:
к порту прицеплен драйвер, который держит очередь ввода-вывода и запуливает в порт данные с его скоростью (и читает обратно)
Всё это работает в соседнем процессе, значит можно помещать данные в очередь (пишем в порт) или снимать их оттуда (читаем порт) с небольшими задержками (чтобы не переполнить очередь), и заниматься своими делами. Видимо в этом и вся асинхронность.
здесь написано про настройки драйвера:
http://msdn.microsoft.com/en-us/library/bb202767.aspx
http://msdn.microsoft.com/en-us/library/aa909018.aspx

Это всё как-то намаплено на классы C# - ищи похожие названия

Carmageddon
03-12-2013, 19:39
Прочел ряд статей - насколько я понял, все для ПК по rs232 работает в асинхронном режиме - режиме, где единицей передачи информации является байт, т.е. каждый байт имеет старт и стоп биты, и как правило бит четности.
В противоположность ему, есть более быстрый - синхронный режим, когда поток бит идет непрерывно, но rs232 его не поддерживает.
буду пробовать

pva
03-12-2013, 20:39
Carmageddon, не в этом смысле! Имеется в виду дожидается ли функция результата (синхронный режим) или возвращается сразу, а результат получается полингом, ожиданием события или обратным вызовом (асинхронный режим). Синхронность в межпотоковом смысле.




© OSzone.net 2001-2012