Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.71/56: Рейтинг темы: голосов - 56, средняя оценка - 4.71
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712

И снова работа с COM портом

16.11.2016, 14:03. Показов 12046. Ответов 67
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую всех. Уже измучился и измаялся весь пытаясь написать функцию. Помогите, пожалуйста...

Описание задачи
Создается программа для работы с прибором. Соединение с прибором осуществляется посредством преобразователей, которые при подключении к компьютеру создают виртуальный COM порт. Преобразователь может подключаться к прибору посредством проводного (RS-485) или беспроводного (инфракрасный канал) соединения. Прибор всегда является ведущим, то есть информацию выдает только по запросу от компьютера (получил корректный запрос - выдал ответ). Целостность запросов и ответов определяется циклическим избыточным кодом в конце запроса или ответа.

Текущая реализация
На данный момент я создал класс для работы с прибором. Экземпляр этого класса будет создаваться и работать в отдельном потоке. В классе будут реализованы конкретные функции для работы с прибором (например - прочитать серийный номер прибора). Каждая из этих функций подготавливает соответствующий запрос прибору и вызывает внутреннюю функцию обмена данными SwapData. Указанная функция, по сути, выполняет основную работу класса: отправляет созданный запрос в COM порт и получает ответ из COM порта. Именно данную функцию я не могу написать. Начало функции SwapData выглядит примерно так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int SwapData()
{
 // COMHandle - хендл открытого COM порта
 // Data - вектор байт для запроса и ответа
 // eCOM... - константы ошибок обмена
 DWORD dw;
 
 /* Очистка буферов и очередей COM порта */
 if(!PurgeComm(COMHandle, 0x0F)) return eCOMPurge;                              // Не удается очистить буферы COM порта: возвращаю ошибку
 
 /* Отправка данных в COM порт */
 if(!WriteFile(COMHandle, &Data.front(), Data.size(), &dw, NULL) ||             // Не удается отправить данные в COM порт или
     dw != Data.size()) return eCOMWrite;                                       // отправлены не все данные: возвращаю ошибку
 
 /* Чтение данных из COM порта */
}
К приведенной части кода функции претензий нет: буферы очищаются, запрос отправляется (при этом функция WriteFile ждет пока не отправятся все данные, ибо синхронный режим). Проблема возникает с приемом ответа.

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

Попытка реализации чтения данных
В нижеприведенном коде я реализовал алгоритм, когдаПривожу код, который отвечает за прием данных:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 // COMHandle - хендл открытого COM порта
 // COMSpeed - скорость COM порта в бит/с
 COMSTATE cs;
 DWORD dw;
 
 /* Цикл ожидания ответа от прибора */
 for(DWORD in = 0, vl = 0, st = GetTickCount();;)
  {
   /* Останавливаю программу на время приема одного байта */
   Sleep(10000 / COMSpeed);
 
   /*  В буфер COM порта поступили новые данные */
   if(ClearCommError(COMHandle, &dw, &cs) && !dw && cs.cbInQue > in)
    {
     in = cs.cbInQue; /* Сохраняю количество данных в буфере COM порта */
     vl = 0; /* Обнуляю количество "пустых циклов" (когда данные не поступили) */
     continue;
    }
 
   /* Прерываю цикл ожидания ответа от прибора если:
      - в буфере COM порта нет данных и время ожидания превысило 3 секунды;
      - в буфере COM порта есть данные и было более одного "пустого цикла" */
   if((!in && ((GetTickCount() - st) > 3000)) || (in && ++vl > 1)) break;
  }
 
 /* Выполняю чтение данных из буфера COM порта */
 if(!cs.cbInQue) return eNotData;
 Data.resize(cs.cbInQue);
 if(!ReadFile(COMHandle, &Data.front(), Data.size(), &dw, NULL) ||
     dw != Data.size())) return eCOMRead;
Данный код отлично работает на скорости 300 бит/с, но на скорости 9600 бит/с цикл приема завершается до приема всех данных.

Помогите советом как реализовать прием данных максимально быстро и полно.
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
16.11.2016, 14:03
Ответы с готовыми решениями:

Работа с COM портом
по COM порту через Agilen подключается вольтметр с адресом 1. необходимо "прозвонить" все порты и автоматически сделать рабочим тот,...

работа с COM портом
здравствуйте всем,я в форумах в первие ,и просил бы не судить очень строго. помогите пожалуйста , хочу реализовать приложение работающую с...

Работа с COM-портом
Подскажите, какие компоненты нужны для работы с ком-портами? а именно (хотя бы для начала) отправка шестнадцатиричного кода на порт, к...

67
 Аватар для ITDeveloper
86 / 86 / 6
Регистрация: 14.01.2011
Сообщений: 265
16.11.2016, 14:25
На сколько я понимаю, для подобных задач, удобней использовать асинхронный режим работы с компортом. Хорошее описание во вложенной pdf
Вложения
Тип файла: pdf COM_port.pdf (488.8 Кб, 179 просмотров)
2
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
16.11.2016, 16:16  [ТС]
ITDeveloper, спасибо за документ. Я его уже читал ранее. Там запись и чтение сделаны асинхронно и в отдельных потоках. И мне уже давно советовали использовать асинхронный режим. Но его использование не решает моей проблемы. Попробую объяснить.

Отправка данных в COM порт
Допустим я реализую чтение и запись асинхронно и даже, возможно, в отдельных потоках. По отправке данных в порт вопросов нет. Хотя для этого придется создавать сигнальный объект и после отправки данных ждать установки его в сигнальное состояние. В синхронном же режиме надо просто отправить данные в порт. Функция WriteFile не вернет управление пока все данные не будут отправлены. То есть все то же самое, но в случае асинхронного режима реализация сложнее. Таким образом выигрыша от использования асинхронного режима при отправке данных я пока не вижу. Вижу проигрыш.

Чтение данных из COM порта
А вот чтение в асинхронном режиме вызывает вопрос: как программа определит, что все данные ответа прибора из COM порта прочитаны? Причем определить это желательно максимально близко к тому моменту, когда в порт поступил последний байт ответа на запрос. Как уже говорилось выше, нужно учитывать, что ответ на запрос может не поступить или поступить не в таком количестве, которое ожидается.

В общем, для синхронного и асинхронного режима надо реализовать такой алгоритм:
1. После отправки запроса начать отсчет времени ожидания ответа.
2. Если в течении времени Х в порт не поступило ни одного байта, сообщить об отсутствии ответа.
3. Если в порт поступил хотя бы один байт, значит прибор начал отправлять ответ и между поступлением байт в порт (в теории) будет одинаковое время Т.
4. Если после приема байта прошло время большее, чем Т, значит прибор передал весь ответ.
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
16.11.2016, 16:39
d7d1cd
-создаем поток
-в потоке бесконечный цикл в котором Sleep(200)
-так же в потоке проверка флага "запросить устройство"
-программа устанавливает этот флаг
-когда поток видит что флаг установлен он делает WriteFile посылая данные которые программа любезно поместила во входной буфер доступный из потока и входит в бесконечный цикл где делает ReadFile читающий данные в выходной буфер потока
-в этом самом бесконечном цикле анализируется флаг "прервать запрос" - если программа установила этот флаг, то флаги "прервать запрос" и "запросить устройство" сбрасывается, устанавливается флаги "запрос завершен" и "прервано"
-если были ошибки WriteFile или ReadFile мы реагируем на них так как этого требует задача (наверное, эти ошибки фатальны и мы должны установить флаги "запрос завершен" и "ошибка")
-ReadFile может вернуть ноль если был таймаут - помним об этом
-ReadFile может вернуть не все данные которые послало устройство, а лишь очередную порцию - после каждого ReadFile анализируем выходной буфер потока что бы понять какого рода данные нам пришли распознавая различные типы ответа устройства опираясь на документацию
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
16.11.2016, 16:43
-когда мы логически фиксируем окончание ответа выходим из цикла и устанавливаем флаг "запрос завершен" - в выходном буфере потока будут наши данные
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
16.11.2016, 16:44
-при желании программа контролирует время в течение которого поток пытается обработать запрос - если оно превышено программа устанавливает флаг "прервать запрос"
0
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
16.11.2016, 20:00
Цитата Сообщение от d7d1cd Посмотреть сообщение
Помогите советом как реализовать прием данных максимально быстро и полно.
Может пригодиться. когда-то реализовывал прослушивание com в потоке следующим образом.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void DCommThread::ThreadProc(){
 SetEvent(RunEvent);
 SetCommMask(Handle, EV_RXCHAR);
 DWORD dwEvtMask;
 DWORD dwOvRes;
 DWORD dwErrorFlags;
 COMSTAT ComStat;
 while(FConnected){
  dwEvtMask = 0;
  if(!WaitCommEvent(Handle, &dwEvtMask, /*&Overlapped*/NULL)){
   if(GetLastError() == ERROR_IO_PENDING){
    GetOverlappedResult(Handle, &Overlapped, &dwOvRes, true);
   } else {
//    DoError();
    continue;
   }
  }
  if(FConnected==false) break;
  if((dwEvtMask & EV_RXCHAR) != EV_RXCHAR)
   continue;
  ClearCommError(Handle, &dwErrorFlags, &ComStat);
  int n = ComStat.cbInQue;
  if(n > BufferSize) n = BufferSize;
  if(n == 0) continue;
 
//  Lock(); блокирование потока
  if(DataSize+n>BufferSize){
   DWORD d=BufferSize-n;
   if(d) memcpy(Buffer, Buffer+DataSize-d, d);
   DataSize=d;
  }
  if(!ReadFile(Handle, Buffer+DataSize, n, (DWORD *) &n, &Overlapped)){
   if(GetLastError()==ERROR_IO_PENDING){
    WaitForSingleObject(Overlapped.hEvent, INFINITE);
    GetOverlappedResult(Handle, &Overlapped, (DWORD *) &n, true);
   } else {
//    DoError();
   }
  }
  DataSize+=n;
  if(n) 
   Notify((DWORD) Owner); // формирование события наличия данных
//  Unlock(); разблокировка потока
 }
}
0
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
16.11.2016, 20:04  [ТС]
vxg, спасибо за алгоритм. Вот только есть вопрос: чем Ваш алгоритм ожидания ответа лучше моего кода, описанного в первом посте в разделе Попытка реализации чтения данных? Если в цикл добавить функцию ReadFile, которая "читает данные в выходной буфер потока" и "анализировать выходной буфер потока чтобы понять какого рода данные нам пришли, распознавая различные типы ответа устройства опираясь на документацию", то получится то же самое, что и у Вас, только без потоков и асинхронного режима. Иначе говоря, получится проще, но столь же эффективно.

Анализ выходного буфера потока не так прост как кажется, причем даже опираясь на документацию. Простой пример. У прибора запросили какие-то данные размером 10 байт, которые он начал передавать. Программа, например, получила первые 5 байт: 00 02 00 70 A0. Анализируя их программа вычислила, что последние 2 байта 70 A0 - это циклический избыточный код первых трех байт 00 02 00. Кроме этого второй байт 02 подходит под код ошибки. Программа делает вывод, что прибор сообщил об ошибке номер 02 и прекращает прием, сообщив о ней. А прибор то не сообщал об ошибке. Он после этого прислал еще 5 байт, но его уже никто не слышал...

Я рассуждаю следующим образом. Если прибор начал присылать данные в порт, то каждый последующий байт будет поступать в него практически с одной и той же задержкой Т, которая является временем передачи одного байта и которая зависит от скорости обмена. Соответственно, "увидев" первый байт в порту, программа "поймет", что прибор начал присылать данные и будет "ждать" следующий байт время Т. Если по истечении времени Т (или, для надежности, по истечении времени 2Т) новый байт не поступил, то программа "считает", что прибор передал все данные. Далее эти данные за один раз считываются в буфер. В этом алгоритме отсутствует необходимость анализировать пришедшие данные во время приема. Кроме этого ожидание поступления всех отправленных данных прибором осуществляется максимально эффективно: программа будет "впустую" ждать только время 2Т.
0
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
16.11.2016, 20:05
На всякий случай функции открытия и закрытия порта и создания/удаления потока

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
bool __dclcall DCommThread::Open(){
 char name[16]="\\\\.\\COM";
 
 itoa(Owner->Port, name+7, 10);
// name[3]=0x30+Owner->Port;
 Handle=CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 0);
 if(Handle==INVALID_HANDLE_VALUE)
  Handle=NULL;
 else {
  DCB CommState;
  GetCommState(Handle, &CommState);
  CommState.BaudRate        = BaudRate;
  CommState.fBinary         = TRUE;
  CommState.fParity         = TRUE;
  CommState.fOutxCtsFlow    = FALSE;
  CommState.fOutxDsrFlow    = FALSE;
  CommState.fDsrSensitivity = FALSE;
  CommState.fNull           = FALSE;
  CommState.ByteSize        = DataBits;
  CommState.Parity          = Parity;
  CommState.StopBits        = StopBits;
  CommState.fRtsControl     = RTS_CONTROL_ENABLE;
  CommState.fDtrControl     = DTR_CONTROL_DISABLE;
  CommState.fAbortOnError   = FALSE;
  SetCommState(Handle, &CommState);
  SetupComm(Handle, 1024, 1024);
  memset(&Overlapped,0,sizeof(Overlapped));
  PurgeComm(Handle, PURGE_RXCLEAR | PURGE_TXCLEAR);
  FConnected=true;
  RunEvent = CreateEvent(0, false, false, NULL);
  Thread=CreateThread(NULL, NULL, &StaticThreadProc, this, 0, &ThreadID);
  WaitForSingleObject(RunEvent, INFINITE);
  CloseHandle(RunEvent);
 }
 return Handle>0;
}
//---------------------------------------------------------------------------
bool __dclcall DCommThread::Close(){
 if(Handle){
  FConnected = false;
  SetCommMask(Handle, EV_BREAK);
  WaitForSingleObject(Thread, INFINITE);
  CloseHandle(Thread);
  CloseHandle(Handle);
  Handle=NULL;
  return true;
 }
 return false;
}
//---------------------------------------------------------------------------
0
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
16.11.2016, 20:13  [ТС]
dstar, я отправил запрос в порт, а Ваш поток прослушивания начал получать ответ. Как он поймет, что все данные поступили и больше данных не будет? Как я уже много раз говорил, определить это надо как можно быстрее.
0
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
16.11.2016, 20:27
Если данных больше нет, он остановится на строке 10 и будет ждать новых данных

Добавлено через 3 минуты
В противном случае будет их постоянно читать, пока все не получит и о каждом прочитанном блоке уведомит Вас в с строке 42
0
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
16.11.2016, 20:40  [ТС]
Цитата Сообщение от dstar Посмотреть сообщение
Если данных больше нет, он остановится на строке 10 и будет ждать новых данных
Это все понятно. Но вот конкретная задача: я отправил запрос и жду ответ. Как мне определить, что все данные, которые на данный момент поступили в буфер - это весь ответ прибора? Не какая-то его часть, а весь ответ и что больше данных от прибора не будет и можно приступить к обработке ответа.
0
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
16.11.2016, 20:45
Цитата Сообщение от d7d1cd Посмотреть сообщение
отправил запрос и жду ответ. Как мне определить, что все данные
Это вроде просто. После отправки данных устанавливаете значение некоторой переменной функцией GetTickCount. После каждого получения ответа значение этой переменной так же устанавливаете функцией GetTickCount. Так же после отправки данных ставите бесконечный цикл в котором проверяете сколько времени прошло с момента инициализации переменной. Если Ваш прибор реагирует сразу же, то в каждом цикле значение прошедшего времени будет не велико. а значит ждете пока оно не превысит порогового значения, т.е. передача закончилась. Если же с момента передачи данных вообще не поступало, то выходите из цикла с ошибкой того что отправленная команда осталась без ответа
0
 Аватар для ITDeveloper
86 / 86 / 6
Регистрация: 14.01.2011
Сообщений: 265
17.11.2016, 06:08
Цитата Сообщение от d7d1cd Посмотреть сообщение
на один и тот же запрос прибор может ответить разным количеством байт
По идее прибор должен как то сигнализировать о конце сообщения! В вашем случае не так?
1
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
17.11.2016, 08:45  [ТС]
Цитата Сообщение от ITDeveloper Посмотреть сообщение
По идее прибор должен как то сигнализировать о конце сообщения! В вашем случае не так?
Если бы прибор сообщал о конце сообщения, то эта тема не появилась бы на форуме. У прибора ModBus подобный протокол обмена. Массив данных от прибора или для прибора состоит из, собственно, самих данных и двух байт циклического избыточного кода CRC16 этих данных в конце сообщения. Вот и все.

Добавлено после более внимательного чтения протокола обмена
Нашел в протоколе обмена следующее: "Критерием окончания запроса или ответа является гарантированный тайм-аут, длительность которого зависит от выбранной скорости обмена. Стандартные длительности тайм-аута: ...около 5 мс для скорости 9600 бод". Из этого следует, что если байты перестали поступать порт в течении более чем 5 мс, то прибор точно больше ничего не пришлет (кстати, прибор, видимо, точно так же определят, что компьютер больше ничего не пришлет). Из всего этого делаю вывод: метод dstar в этом посте мне подходит.

Применение метода dstar к моей функции
Начну с того, что мне не очень хочется делать отдельный поток для чтения данных. Конечно, его использование вполне оправдано с точки зрения использования времени процессора: поток не занимает процессорное время пока в порт не поступит байт. Однако, согласно методу dstar, одновременно с этим должен существовать бесконечный цикл, который будет постоянно проверять сколько прошло времени с момента приема последнего байта. А уж если использовать сигналы при работе с портом, то нельзя ли использовать их и тут, чтобы процессор не крутил впустую этот цикл? И еще вопрос: как использовать асинхронный режим, чтобы для функций чтения и записи не создавать отдельных потоков? То есть мне хочется реализовать всю работу по отправке запроса и получению ответа в единственной функции SwapData.
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
17.11.2016, 08:57
d7d1cd, в моем алгоритме нет никакого асинхронного чтения и он лучше вашего предложения в плане производительности так как ничего не ждёт и несравненно более терпим к паузам в передаче будучи ориентирован именно на содержимое сообщения, а не на неверное в общем случае допущение о равномерной передаче. Ваш первый код оборвёт приём при тайм-ауте - просто задайте его равным нужному вам значению и не потребуется анализировать паузы однако в общем случае это неверно. Правильный анализ сообщения не может привести к описанной вами ложной реакции - адекватные протоколы либо содержат длину сообщения в заголовке либо делят сообщения некой заданной уникальной начальной и/или конечной последовательностью либо по заголовку пакета позволяют однозначно определить его тип и длину по документации. Покажите пример запроса.
0
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
17.11.2016, 09:22  [ТС]
Цитата Сообщение от vxg Посмотреть сообщение
Покажите пример запроса.
Пожалуйста. Запрос на чтение 10 байт из памяти №3 по адресу 0xA070 (байты в шестнадцатеричном виде, последние 2 байта - циклический избыточный код CRC16 предыдущих 6 байт):
00 06 03 A0 70 0A 2D BA

В памяти может хранится все, что угодно. Поэтому ответ может быть таким (сами данные расположены со 2 по 11 байты, последние 2 байта - циклический избыточный код CRC16 предыдущих 11 байт):
00 02 80 71 A0 14 DA 07 EE 89 10 FD D0

Хочу обратить внимание, что первые 4 байта подходят под ответ прибора об ошибке: код ошибки 02, 3 и 4 байты - это циклический избыточный код CRC16 первого и второго байта. Хотя тут ошибки нет, а просто так "сложились звезды байты".
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
17.11.2016, 09:55
d7d1cd, а можно пожалуйста по байтно описать что должно идти в запросе и что присылается в ответе в случае успеха и в случае ошибки? а то я не разгадал что такое 00 06 в запросе (следующие за ними 03 - это наверное номер (!?) памяти, а A0 - это наверное размер читаемых байт) и что такое 00 в ответе
0
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
17.11.2016, 11:13  [ТС]
Цитата Сообщение от vxg Посмотреть сообщение
а можно пожалуйста по байтно описать что должно идти в запросе и что присылается в ответе в случае успеха и в случае ошибки?
Имеем запрос: 00 06 03 A0 70 0A 2D BA, где по порядку:
00 - сетевой адрес прибора. У каждого прибора есть свой сетевой адрес для обращения к конкретному прибору, когда на линии RS-485 приборов несколько. Если прибор один, то к нему можно обращаться через сетевой адрес 0.
06 - код запроса на чтение памяти.
03 - номер памяти, откуда необходимо произвести чтение.
A0 70 - адрес в памяти, с которого будет производится чтение.
0A - количество байт, которое необходимо прочитать.
2D BA - циклический избыточный код CRC16 запроса

Получаем ответ: 00 02 80 71 A0 14 DA 07 EE 89 10 FD D0, где по порядку:
00 - сетевой адрес прибора. Если запрос был с нулевым адресом, то прибор тоже отвечает нулем в этом байте.
02 80 71 A0 14 DA 07 EE 89 10 - запрошенные 10 байт информации, расположенные в памяти №3 по адресу 0xA070.
FD D0 - циклический избыточный код CRC16 ответа.

При ошибке (если память №3 отсутствует в приборе) получаем ответ: 00 02 80 71, где:
00 - сетевой адрес прибора. Если запрос был с нулевым адресом, то прибор тоже отвечает нулем в этом байте.
02 - код ошибки о недопустимости запроса.
80 71 - циклический избыточный код CRC16 ответа.

P.S. Сообщение об ошибке всегда состоит из 4-х байт.
0
Модератор
 Аватар для vxg
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
17.11.2016, 12:52
d7d1cd, да, ваш протокол не позволяет однозначно различить ответы, придется либо принять допущение о паузе в конце ответа либо проверять являются ли байты 80 71 контрольной суммой для первых двух с вероятностью ложной фиксации ошибки если первые два байта данных будут совпадать с контрольной суммой, печальный протокол. тогда я бы на вашем месте настроил тайм-ауты и засчитывал конец передачи если ReadFile выходит по тайм-ауту (возвращает ноль)

Добавлено через 42 секунды
никаких Sleep в этом случае не нужно - все сделает сама система

Добавлено через 51 секунду
сверхточного вычисления тайм-аута тоже можно не делать - заложить с запасом, наверняка в течение 200 мс устройство не может молчать
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
17.11.2016, 12:52
Помогаю со студенческими работами здесь

Работа с COM портом
Здравствуйте! В процессе написания программы работы с устройством, подключенным к ПК через преобразователь интерфейсов RS485 - USB,...

Работа с Com портом
Привет всем! У меня собственно такая ситуация, есть bluetooth и телефон Nokia устанавливаю соединение через виртуальный com порт, мне...

Работа с COM портом в Builder C++ 6.0
Добрый день, уважаемые форумчане. Никогда не спрашивал совета на форумах, но нужна подсказка. Кто располагает временем, буду очень...

Работа с COM портом на С++ Builder 6
Работаю я с GSM модемами, пишу под них программу на C++ Builder 6. Посылаю в модем, через COM порт соответственно, AT команды (строку...

Работа с USB портом.
Доброго всем времени суток! Суть задачи в следующем: Есть устройство (USB принтер этикеток) и им можно управлять при помощи...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru